diff --git a/data/layout.xml b/data/layout.xml index 8603411..42106ae 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -1,5 +1,13 @@ - - + + + + + + + + + + diff --git a/engine/app.cpp b/engine/app.cpp index 425d0ec..078adf3 100644 --- a/engine/app.cpp +++ b/engine/app.cpp @@ -9,6 +9,39 @@ void App::create() height = 800; } +void App::update_layout() +{ + YGNodeCalculateLayout(y_root, YGUndefined, YGUndefined, YGDirectionLTR); + std::stack y_stack; + y_stack.push(y_root); + auto y_current = y_root; + while (y_current) + { + y_stack.pop(); + auto ctx = reinterpret_cast(YGNodeGetContext(y_current)); + if (!ctx) + { + float x = YGNodeLayoutGetLeft(y_current); + float y = YGNodeLayoutGetTop(y_current); + ctx = new glm::vec2(x, y); + YGNodeSetContext(y_current, ctx); + } + + int n = YGNodeGetChildCount(y_current); + for (int i = 0; i < n; i++) + { + auto y_child = YGNodeGetChild(y_current, i); + auto ctx_child = reinterpret_cast(YGNodeGetContext(y_child)); + if (!ctx_child) ctx_child = new glm::vec2(); + ctx_child->x = ctx->x + YGNodeLayoutGetLeft(y_child); + ctx_child->y = ctx->y + YGNodeLayoutGetTop(y_child); + YGNodeSetContext(y_child, ctx_child); + y_stack.emplace(y_child); + } + y_current = y_stack.size() ? y_stack.top() : nullptr; + } +} + void App::init() { static const char* shader_v = @@ -56,52 +89,131 @@ void App::init() auto w = att::Width(10.f); printf("type: %d\n", att::value("Height")); - std::vector> s; - auto y_root = YGNodeNew(); + y_root = YGNodeNew(); + YGNodeStyleSetWidth(y_root, width); + YGNodeStyleSetHeight(y_root, height); + YGNodeStyleSetFlexDirection(y_root, YGFlexDirectionRow); + YGNodeStyleSetFlexWrap(y_root, YGWrapWrap); + //YGNodeStyleSetJustifyContent(y_root, YGJustifyFlexStart); + + using NodePair = std::pair; + std::stack stack; + tinyxml2::XMLDocument xml; auto ret = xml.LoadFile("data\\layout.xml"); - auto root = xml.RootElement(); - auto child = root->FirstChildElement(); - while (child) - { - printf("Element %s: ", child->Name()); - auto attr = child->FirstAttribute(); - auto y_node = YGNodeNew(); - std::unique_ptr shape = std::make_unique(); - while (attr) - { - const auto ka = att::value(attr->Name()); - printf("%s=%s ", attr->Name(), attr->Value()); - switch (ka) - { - case att::kAttribute::Width: - if (strchr(attr->Value(), '%')) - YGNodeStyleSetWidthPercent(y_node, attr->FloatValue()); - else - YGNodeStyleSetWidth(y_node, attr->FloatValue()); - shape->attribs[ka] = std::make_unique(attr->FloatValue()); - break; - case att::kAttribute::Height: - if (strchr(attr->Value(), '%')) - YGNodeStyleSetHeightPercent(y_node, attr->FloatValue()); - else - YGNodeStyleSetHeight(y_node, attr->FloatValue()); - shape->attribs[ka] = std::make_unique(attr->FloatValue()); - break; - } - attr = attr->Next(); - } + auto x_root = xml.RootElement(); - YGNodeInsertChild(y_root, y_node, 0); - printf("\n"); - child = child->NextSiblingElement(); + NodePair current = { y_root, x_root }; + stack.push(current); + while (current.first && current.second) + { + stack.pop(); + auto y_root = current.first; + auto child = current.second->FirstChildElement(); + YGNodeStyleSetPosition(y_root, YGEdgeAll, 0); + YGNodeStyleSetPadding(y_root, YGEdgeAll, 0); + YGNodeStyleSetMargin(y_root, YGEdgeAll, 0); + YGNodeStyleSetPositionType(y_root, YGPositionTypeRelative); + YGNodeStyleSetPosition(y_root, YGEdgeAll, 0); + YGNodeStyleSetPadding(y_root, YGEdgeAll, 0); + YGNodeStyleSetMargin(y_root, YGEdgeAll, 0); + while (child) + { + printf("Element %s: ", child->Name()); + auto y_node = YGNodeNew(); + stack.emplace(y_node, child); + auto attr = child->FirstAttribute(); + if (strcmp("plane", child->Name()) == 0) + { + auto shape = std::make_unique(); + shape->create<5>(100.f, 100.f); + shape->y_node = y_node; + shapes_list.push_back(std::move(shape)); + YGNodeSetContext(y_node, shape.get()); + } + while (attr) + { + const auto ka = att::value(attr->Name()); + printf("%s=%s ", attr->Name(), attr->Value()); + switch (ka) + { + case att::kAttribute::Width: + if (strchr(attr->Value(), '%')) + YGNodeStyleSetWidthPercent(y_node, attr->FloatValue()); + else + YGNodeStyleSetWidth(y_node, attr->FloatValue()); + break; + case att::kAttribute::MinWidth: + if (strchr(attr->Value(), '%')) + YGNodeStyleSetMinWidthPercent(y_node, attr->FloatValue()); + else + YGNodeStyleSetMinWidth(y_node, attr->FloatValue()); + break; + case att::kAttribute::MaxWidth: + YGNodeStyleSetMaxWidth(y_node, attr->FloatValue()); + break; + case att::kAttribute::Height: + if (strchr(attr->Value(), '%')) + YGNodeStyleSetHeightPercent(y_node, attr->FloatValue()); + else + YGNodeStyleSetHeight(y_node, attr->FloatValue()); + break; + case att::kAttribute::MinHeight: + YGNodeStyleSetMinHeight(y_node, attr->FloatValue()); + break; + case att::kAttribute::MaxHeight: + YGNodeStyleSetMaxHeight(y_node, attr->FloatValue()); + break; + case att::kAttribute::Grow: + YGNodeStyleSetFlexGrow(y_node, attr->FloatValue()); + break; + case att::kAttribute::Shrink: + YGNodeStyleSetFlexShrink(y_node, attr->FloatValue()); + break; + case att::kAttribute::FlexDir: + { + YGFlexDirection dir = YGFlexDirectionRow; + if (strcmp("col", attr->Value()) == 0) + dir = YGFlexDirectionColumn; + else if (strcmp("col-reverse", attr->Value()) == 0) + dir = YGFlexDirectionColumnReverse; + else if (strcmp("row", attr->Value()) == 0) + dir = YGFlexDirectionRow; + else if (strcmp("row-reverse", attr->Value()) == 0) + dir = YGFlexDirectionRowReverse; + YGNodeStyleSetFlexDirection(y_node, dir); + break; + } + case att::kAttribute::FlexWrap: + YGNodeStyleSetFlexWrap(y_node, attr->IntValue() ? YGWrapWrap : YGWrapNoWrap); + break; + } + attr = attr->Next(); + } + int n = YGNodeGetChildCount(y_root); + YGNodeInsertChild(y_root, y_node, n); + child = child->NextSiblingElement(); + printf("\n"); + } + current = stack.size() ? stack.top() : NodePair{nullptr, nullptr}; + } + + update_layout(); + + for (auto& s : shapes_list) + { + float w = YGNodeLayoutGetWidth(s->y_node); + float h = YGNodeLayoutGetHeight(s->y_node); + float x = YGNodeLayoutGetLeft(s->y_node); + float y = YGNodeLayoutGetTop(s->y_node); + printf("layout w=%f h=%f x=%f y=%f\n", w, h, x, y); } sampler.create(); shader.create(shader_v, shader_f); shader_color.create(shader_color_v, shader_color_f); shader_uv.create(shader_v, shader_uv_f); - plane.create<5>(50, 50); + plane.create<5>(1, 1); circle.create<10>(25); circle2.create<10>(25, Circle::kUVMapping::Tube); circle3.create<10>(25, 12, Circle::kUVMapping::Tube); @@ -111,11 +223,10 @@ void App::init() if (!tex.load("data/uvs.jpg")) printf("error loading image\n"); - glViewport(0, 0, width, height); glEnable(GL_TEXTURE); glDisable(GL_DEPTH_TEST); glPointSize(5); - glLineWidth(1); + glLineWidth(4); //int n; //glGetIntegerv(GL_NUM_EXTENSIONS, &n); @@ -135,21 +246,15 @@ void App::init() void App::update(float dt) { -// static float theta = 0; -// theta += M_PI * 0.5f * dt; -// float red = fabsf(sinf(theta)); - -// glm::mat4 proj = glm::perspective(glm::radians(85.f), 1.f, .1f, 100.f); -// glm::mat4 model = glm::translate(glm::vec3(0, 0, 0)); -// glm::mat4 view = glm::lookAt(glm::vec3(sinf(theta), 0, 1), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); - - glm::mat4 proj = glm::ortho(0.f, (float)width, (float)height, 0.f, -1.f, 1.f); + glm::mat4 proj = glm::ortho(0.f, width, height, 0.f, -1.f, 1.f); Shape* shapes[] = { &circle, &circle2, &circle3, &circle4, &plane, &rounded, &slice }; //glClearColor(red, 0, 0, 1); + glViewport(0, 0, (GLsizei)width, (GLsizei)height); glClear(GL_COLOR_BUFFER_BIT); - + +/* auto s = glm::scale(glm::vec3(1.5)); int h = 100; for (int i = 0; i < sizeof(shapes)/sizeof(Shape*); i++) @@ -184,6 +289,41 @@ void App::update(float dt) shapes[i]->draw_fill(); tex.unbind(); } +*/ + glActiveTexture(GL_TEXTURE0); + tex.bind(); + sampler.bind(0); + shader.use(); + shader.u_int("tex", 0); + shader_color.use(); + shader_color.u_vec4("col", { 0, 0, 1, 1 }); + + for (auto& s : shapes_list) + { + float w = YGNodeLayoutGetWidth(s->y_node); + float h = YGNodeLayoutGetHeight(s->y_node); + float x = YGNodeLayoutGetLeft(s->y_node); + float y = YGNodeLayoutGetTop(s->y_node); + auto loc = reinterpret_cast(YGNodeGetContext(s->y_node)); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + glm::mat4 pivot = glm::translate(glm::vec3(.5f, .5f, 0.f)); + glm::mat4 scale = glm::scale(glm::vec3(w, h, 1.f)); + glm::mat4 pos = glm::translate(glm::vec3(*loc, 0)); + auto mvp = proj * pos * scale * pivot; + + shader_uv.use(); + shader.u_mat4("mvp", mvp); + plane.draw_fill(); + + shader_color.use(); + shader_color.u_mat4("mvp", mvp); + plane.draw_stroke(); + } + tex.unbind(); + sampler.unbind(); + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //shader_color.use(); //shader_color.u_mat4("mvp", proj * glm::translate(glm::vec3{width/2, height/2, 0.f}) * glm::scale(glm::vec3(8))); @@ -203,3 +343,12 @@ void App::update(float dt) //glLineWidth(2); //circle3.draw_fill(); } + +void App::resize(float w, float h) +{ + width = w; + height = h; + YGNodeStyleSetWidth(y_root, width); + YGNodeStyleSetHeight(y_root, height); + update_layout(); +} diff --git a/engine/app.hpp b/engine/app.hpp index 4a1e200..e0806fa 100644 --- a/engine/app.hpp +++ b/engine/app.hpp @@ -11,15 +11,19 @@ class App Shader shader_color; Shader shader_uv; Plane plane; + std::vector> shapes_list; Circle circle, circle2, circle3, circle4; Rounded rounded; Slice9 slice; Texture2D tex; + YGNodeRef y_root; public: static App I; - int width; - int height; + float width; + float height; void init(); void create(); void update(float dt); + void resize(float w, float h); + void update_layout(); }; diff --git a/engine/main.cpp b/engine/main.cpp index 1805bf8..c31ce4a 100644 --- a/engine/main.cpp +++ b/engine/main.cpp @@ -211,7 +211,7 @@ int main() PIXELFORMATDESCRIPTOR pfd; App::I.create(); - RECT clientRect = { 0, 0, App::I.width, App::I.height }; + RECT clientRect = { 0, 0, (int)App::I.width, (int)App::I.height }; // Inizialize data structures to zero memset(&wc, 0, sizeof(wc)); @@ -288,7 +288,7 @@ int main() wglDeleteContext(hRC); DestroyWindow(hWnd); - hWnd = CreateWindow(wc.lpszClassName, L"New Engine", WS_OVERLAPPEDWINDOW, + hWnd = CreateWindow(wc.lpszClassName, L"UI Layout Engine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, 0, 0, hInst, 0); @@ -350,6 +350,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) case WM_CLOSE: PostQuitMessage(0); break; + case WM_SIZE: + App::I.resize((float)LOWORD(lp), (float)HIWORD(lp)); + App::I.update(0.f); + SwapBuffers(hDC); + break; case WM_KEYDOWN: keys[wp] = true; break; diff --git a/engine/pch.h b/engine/pch.h index c578d9c..7c7d3bb 100644 --- a/engine/pch.h +++ b/engine/pch.h @@ -1,7 +1,6 @@ #pragma once #ifdef __APPLE__ - #include #include #elif _WIN32 #define _USE_MATH_DEFINES @@ -13,6 +12,7 @@ #include #include +#include #include #include #include @@ -20,7 +20,7 @@ #define GLM_FORCE_RADIANS #define GLM_FORCE_SWIZZLE -#define GLM_FORCE_MESSAGES +//#define GLM_FORCE_MESSAGES #define GLM_ENABLE_EXPERIMENTAL #include #include @@ -30,4 +30,3 @@ #include #include -#include diff --git a/engine/shape.hpp b/engine/shape.hpp index 380b9d7..b659698 100644 --- a/engine/shape.hpp +++ b/engine/shape.hpp @@ -2,7 +2,12 @@ namespace att { - enum class kAttribute : uint8_t { Width, Height, Divisions, InnerRadius, OuterRadius }; + enum class kAttribute : uint8_t { + Width, MinWidth, MaxWidth, + Height, MinHeight, MaxHeight, + Divisions, InnerRadius, OuterRadius, + Grow, Shrink, FlexDir, FlexWrap + }; struct AttributeBase { @@ -14,11 +19,10 @@ namespace att template struct Attribute : public AttributeBase { - using type = V; static const kAttribute static_id = T; V value; - Attribute() : value{ 0 }, { id = static_id; } - Attribute(type v) : value(v) { id = static_id; } + Attribute() : value{0} { id = static_id; } + Attribute(V v) : value(v) { id = static_id; } }; struct typemap @@ -29,10 +33,18 @@ namespace att static constexpr typemap map[] = { { "width", kAttribute::Width }, + { "min-width", kAttribute::MinWidth }, + { "max-width", kAttribute::MaxWidth }, { "height", kAttribute::Height }, + { "min-height", kAttribute::MinHeight }, + { "max-height", kAttribute::MaxHeight }, { "divisions", kAttribute::Divisions }, - { "inner_radius", kAttribute::InnerRadius }, - { "outer_radius", kAttribute::OuterRadius }, + { "inner-radius", kAttribute::InnerRadius }, + { "outer-radius", kAttribute::OuterRadius }, + { "grow", kAttribute::Grow }, + { "shrink", kAttribute::Shrink }, + { "dir", kAttribute::FlexDir }, + { "wrap", kAttribute::FlexWrap }, }; constexpr int map_size = sizeof(map) / sizeof(typemap) - 1; constexpr bool same(const char* a, const char* b) @@ -51,18 +63,26 @@ namespace att //constexpr const char* string(kAttribute a) { return names[(int)a]; } - template - constexpr const char* string(const Attribute a) { return names[(int)a.id]; } +// template +// constexpr const char* string(const Attribute a) { return names[(int)a.id]; } #define DECLARE_ATTRIBUTE(N,T) \ struct N : public Attribute \ { using Attribute::Attribute; }; DECLARE_ATTRIBUTE(Width, float); + DECLARE_ATTRIBUTE(MinWidth, float); + DECLARE_ATTRIBUTE(MaxWidth, float); DECLARE_ATTRIBUTE(Height, float); + DECLARE_ATTRIBUTE(MinHeight, float); + DECLARE_ATTRIBUTE(MaxHeight, float); DECLARE_ATTRIBUTE(Divisions, int); DECLARE_ATTRIBUTE(InnerRadius, float); DECLARE_ATTRIBUTE(OuterRadius, float); + DECLARE_ATTRIBUTE(Grow, float); + DECLARE_ATTRIBUTE(Shrink, float); + DECLARE_ATTRIBUTE(FlexDir, int); + DECLARE_ATTRIBUTE(FlexWrap, int); #undef DECLARE_ATTRIBUTE } @@ -76,6 +96,8 @@ protected: GLvoid* ioff[2]; struct vertex_t { glm::vec4 pos; glm::vec2 uvs; }; public: + YGNodeRef y_node; + float x, y; att::AttrubutesMap attribs; bool create_buffers(GLvoid* idx, GLvoid* vertices, int isize, int vsize); void draw_fill() const; @@ -126,12 +148,20 @@ public: create_impl(w.value, h.value, div, idx.get(), vertices.get()); return create_buffers(idx.get(), vertices.get(), sizeof(idx), sizeof(vertices)); } + template + T get_attribute(T def_val) + { + auto ret = attribs.find(def_val.id); + if (ret == attribs.end()) + return def_val; + return *reinterpret_cast(ret->second.get()); + } bool create_attrib() override { - const att::Width* w = reinterpret_cast(attribs[att::kAttribute::Width].get()); - const att::Height* h = reinterpret_cast(attribs[att::kAttribute::Height].get()); - const att::Divisions* div = reinterpret_cast(attribs[att::kAttribute::Height].get()); - return create(*div, *w, *h); + const auto w = get_attribute(att::Width(0)); + const auto h = get_attribute(att::Height(0)); + const auto d = get_attribute(att::Divisions(1)); + return create(d, w, h); } }; diff --git a/engine/texture.cpp b/engine/texture.cpp index c9670c5..7de17d0 100644 --- a/engine/texture.cpp +++ b/engine/texture.cpp @@ -15,7 +15,7 @@ bool Texture2D::create(int width, int height, GLint format, const uint8_t* data) } bool Texture2D::create(const Image& img) { - static GLint formats[] = { GL_R, GL_RG, GL_RGB, GL_RGBA }; + static GLint formats[] = { GL_RED, GL_RG, GL_RGB, GL_RGBA }; return create(img.width, img.height, formats[img.comp - 1], img.data()); } bool Texture2D::load(std::string filename)