#include "pch.h" #include "app.hpp" App App::I; // singleton void App::create() { width = 500; height = 500; } glm::vec4 rect_intersection(glm::vec4 a, glm::vec4 b) { // convert from [x,y,w,h] to [x1,y1,x2,y1] a = glm::vec4(a.xy(), a.xy() + a.zw()); b = glm::vec4(b.xy(), b.xy() + b.zw()); auto o = glm::vec4(glm::max(a.xy(), b.xy()), glm::min(a.zw(), b.zw())); o = glm::vec4(o.xy(), glm::max({ 0, 0 }, o.zw() - o.xy())); return o; } 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) { ctx = new glm::vec4(); YGNodeSetContext(y_current, ctx); } if (y_current == y_root) { ctx->x = YGNodeLayoutGetLeft(y_current); ctx->y = YGNodeLayoutGetTop(y_current); ctx->z = YGNodeLayoutGetWidth(y_current); ctx->w = YGNodeLayoutGetHeight(y_current); } 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::vec4(); float x = YGNodeLayoutGetLeft(y_child); float y = YGNodeLayoutGetTop(y_child); float w = YGNodeLayoutGetWidth(y_child); float h = YGNodeLayoutGetHeight(y_child); ctx_child->x = ctx->x + x; ctx_child->y = ctx->y + y; ctx_child->z = w; ctx_child->w = h; *ctx_child = rect_intersection(*ctx_child, *ctx); YGNodeSetContext(y_child, ctx_child); y_stack.emplace(y_child); } y_current = y_stack.size() ? y_stack.top() : nullptr; } } void App::load_layout() { struct stat tmp_info; if (stat("data/layout.xml", &tmp_info) != 0) return; if (tmp_info.st_mtime <= g_file_info.st_mtime) return; g_file_info = tmp_info; if (y_root) YGNodeFreeRecursive(y_root); shapes_list.clear(); y_root = YGNodeNew(); YGNodeStyleSetWidth(y_root, width); //YGNodeStyleSetWidthPercent(y_root, 100); YGNodeStyleSetHeight(y_root, height); //YGNodeStyleSetHeightPercent(y_root, 100); YGNodeStyleSetFlexDirection(y_root, YGFlexDirectionRow); YGNodeStyleSetFlexWrap(y_root, YGWrapWrap); YGNodeStyleSetPadding(y_root, YGEdgeAll, 0); YGNodeStyleSetPadding(y_root, YGEdgeTop, 0); YGNodeStyleSetPadding(y_root, YGEdgeRight, 0); YGNodeStyleSetPadding(y_root, YGEdgeBottom, 0); YGNodeStyleSetPadding(y_root, YGEdgeLeft, 0); //YGNodeStyleSetJustifyContent(y_root, YGJustifyFlexStart); using NodePair = std::pair; std::stack stack; tinyxml2::XMLDocument xml; auto ret = xml.LoadFile("data/layout.xml"); auto x_root = xml.RootElement(); 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); //YGNodeStyleSetPositionType(y_root, YGPositionTypeRelative); while (child) { auto y_node = YGNodeNew(); stack.emplace(y_node, child); auto attr = child->FirstAttribute(); std::unique_ptr shape; if (strcmp("plane", child->Name()) == 0) { shape = std::make_unique(); shape->create<5>(100.f, 100.f); shape->y_node = y_node; YGNodeSetContext(y_node, shape.get()); } printf("Element %s: ", child->Name()); YGNodeStyleSetWidth(y_node, 0); YGNodeStyleSetWidthPercent(y_node, 100); YGNodeStyleSetHeight(y_node, 0); YGNodeStyleSetHeightPercent(y_node, 100); YGNodeStyleSetPadding(y_node, YGEdgeAll, 0); YGNodeStyleSetPadding(y_node, YGEdgeTop, 0); YGNodeStyleSetPadding(y_node, YGEdgeRight, 0); YGNodeStyleSetPadding(y_node, YGEdgeBottom, 0); YGNodeStyleSetPadding(y_node, YGEdgeLeft, 0); YGNodeStyleSetPosition(y_node, YGEdgeAll, 0); YGNodeStyleSetPosition(y_node, YGEdgeTop, 0); YGNodeStyleSetPosition(y_node, YGEdgeRight, 0); YGNodeStyleSetPosition(y_node, YGEdgeBottom, 0); YGNodeStyleSetPosition(y_node, YGEdgeLeft, 0); YGNodeStyleSetOverflow(y_node, YGOverflowHidden); while (attr) { const auto ka = att::value(attr->Name()); printf("%s=%s ", attr->Name(), attr->Value()); switch (ka) { case att::kAttribute::Width: if (strcmp(attr->Value(), "auto") == 0) { YGNodeStyleSetWidth(y_node, YGUndefined); YGNodeStyleSetWidthPercent(y_node, YGUndefined); } else { 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 (strcmp(attr->Value(), "auto") == 0) { YGNodeStyleSetHeight(y_node, YGUndefined); YGNodeStyleSetHeightPercent(y_node, YGUndefined); } else { 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; case att::kAttribute::Padding: { glm::vec4 pad; int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); if (n == 1) { YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x); YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.x); YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.x); YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.x); } else { YGNodeStyleSetPadding(y_node, YGEdgeTop, pad.x); YGNodeStyleSetPadding(y_node, YGEdgeRight, pad.y); YGNodeStyleSetPadding(y_node, YGEdgeBottom, pad.z); YGNodeStyleSetPadding(y_node, YGEdgeLeft, pad.w); } break; } case att::kAttribute::Margin: { glm::vec4 pad; int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); if (n == 1) { YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x); YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.x); YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.x); YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.x); } else { YGNodeStyleSetMargin(y_node, YGEdgeTop, pad.x); YGNodeStyleSetMargin(y_node, YGEdgeRight, pad.y); YGNodeStyleSetMargin(y_node, YGEdgeBottom, pad.z); YGNodeStyleSetMargin(y_node, YGEdgeLeft, pad.w); } break; } case att::kAttribute::Color: { glm::vec4 pad; int n = sscanf(attr->Value(), "%f %f %f %f", &pad.x, &pad.y, &pad.z, &pad.w); if (n == 1) { if (shape) shape->color = glm::vec4(pad.x); } else { if (shape) shape->color = pad; } break; } default: break; } attr = attr->Next(); } int n = YGNodeGetChildCount(y_root); YGNodeInsertChild(y_root, y_node, n); if (shape) shapes_list.push_back(std::move(shape)); child = child->NextSiblingElement(); printf("\n"); } current = stack.size() ? stack.top() : NodePair{ nullptr, nullptr }; } update_layout(); } void App::init() { static const char* shader_v = "#version 150\n" "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 = "#version 150\n" "uniform sampler2D tex;" "in vec3 uv;" "out vec4 frag;" "void main(){" //" frag = texture(tex, uv.xy/uv.z);" " frag = texture(tex, uv.xy);" "}"; static const char* shader_uv_f = "#version 150\n" "uniform sampler2D tex;" "in vec3 uv;" "out vec4 frag;" "void main(){" " frag = vec4(uv.xy,0,1);" "}"; static const char* shader_color_v = "#version 150\n" "uniform mat4 mvp;" "in vec4 pos;" "void main(){" " gl_Position = mvp * pos;" "}"; static const char* shader_color_f = "#version 150\n" "uniform vec4 col;" "out vec4 frag;" "void main(){" " frag = col;" "}"; #ifdef _WIN32 static CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info); // colors: http://stackoverflow.com/questions/4053837/colorizing-text-in-the-console-with-c glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { static std::map colors = { { GL_DEBUG_SEVERITY_NOTIFICATION, 8 }, { GL_DEBUG_SEVERITY_LOW, 8 }, { GL_DEBUG_SEVERITY_MEDIUM, FOREGROUND_GREEN | FOREGROUND_INTENSITY }, { GL_DEBUG_SEVERITY_HIGH, FOREGROUND_RED | FOREGROUND_INTENSITY }, }; SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colors[severity]); printf("%.*s\n", length, message); FlushConsoleInputBuffer(GetStdHandle(STD_OUTPUT_HANDLE)); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), info.wAttributes); }, nullptr); glEnable(GL_DEBUG_OUTPUT); #endif load_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>(1, 1); circle.create<10>(25); circle2.create<10>(25, Circle::kUVMapping::Tube); circle3.create<10>(25, 12, Circle::kUVMapping::Tube); circle4.create<10>(25, 12, Circle::kUVMapping::Planar); rounded.create<3>(50, 50, 10); slice.create(50, 50, 10, .3f); if (!tex.load("data/uvs.jpg")) printf("error loading image\n"); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glPointSize(5); glLineWidth(2); //int n; //glGetIntegerv(GL_NUM_EXTENSIONS, &n); //for (int i = 0; i < n; i++) //{ // const unsigned char* s = glGetStringi(GL_EXTENSIONS, i); // printf("GL ext %03d: %s\n", i, s); //} printf("GL version: %s\n", glGetString(GL_VERSION)); printf("GL vendor: %s\n", glGetString(GL_VENDOR)); printf("GL renderer: %s\n", glGetString(GL_RENDERER)); GLfloat width_range[2]; glGetFloatv(GL_LINE_WIDTH_RANGE, width_range); printf("GL line range: %f - %f\n", width_range[0], width_range[1]); } void App::update(float dt) { 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(.1f, .1f, .1f, 1.f); glViewport(0, 0, (GLsizei)width, (GLsizei)height); glClear(GL_COLOR_BUFFER_BIT); load_layout(); /* auto s = glm::scale(glm::vec3(1.5)); int h = 100; for (int i = 0; i < sizeof(shapes)/sizeof(Shape*); i++) { shader.use(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glActiveTexture(GL_TEXTURE0); tex.bind(); sampler.bind(0); shader.u_int("tex", 0); shader.u_mat4("mvp", proj * glm::translate(glm::vec3{75 + h * 1, 75 + h * i, 0.f}) * s); shapes[i]->draw_fill(); tex.unbind(); shader_color.use(); shader_color.u_mat4("mvp", proj * glm::translate(glm::vec3{75 + h * 2, 75 + h * i, 0.f}) * s); shader_color.u_vec4("col", {1, 1, 1, 1}); shapes[i]->draw_stroke(); shader_color.u_mat4("mvp", proj * glm::translate(glm::vec3{75 + h * 0, 75 + h * i, 0.f}) * s); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); shapes[i]->draw_fill(); shader_uv.use(); shader_uv.u_mat4("mvp", proj * glm::translate(glm::vec3{75 + h * 3, 75 + h * i, 0.f}) * s); shader_uv.u_int("tex", 0); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); shapes[i]->draw_fill(); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); shader_uv.u_mat4("mvp", proj * glm::translate(glm::vec3{75 + h * 4, 75 + h * i, 0.f}) * s); 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", { .3f, .3f, .3f, 1 }); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); for (auto& s : shapes_list) { auto layout = *reinterpret_cast(YGNodeGetContext(s->y_node)); auto y_parent = YGNodeGetParent(s->y_node); if (y_parent && YGNodeGetContext(y_parent)) { auto box = *reinterpret_cast(YGNodeGetContext(y_parent)); glEnable(GL_SCISSOR_TEST); glScissor(box.x, height - box.y - 1 - box.w, box.z, box.w); } //glScissor(10, height - 10 - 1 - 50, 480, 50); glm::mat4 pivot = glm::translate(glm::vec3(.5f, .5f, 0.f)); glm::mat4 scale = glm::scale(glm::vec3(layout.zw(), 1.f)); glm::mat4 pos = glm::translate(glm::vec3(layout.xy(), 0)); auto mvp = proj * pos * scale * pivot; //shader_uv.use(); //shader_uv.u_mat4("mvp", mvp); //plane.draw_fill(); shader_color.u_vec4("col", s->color); shader_color.use(); shader_color.u_mat4("mvp", mvp); plane.draw_fill(); shader_color.u_vec4("col", { 1, 1, 1, 1 }); shader_color.use(); shader_color.u_mat4("mvp", mvp); plane.draw_stroke(); glDisable(GL_SCISSOR_TEST); } 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))); //shader_color.u_vec4("col", {1, 1, 1, 1}); //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //tex.bind(); //shader.use(); //shader.u_mat4("mvp", proj * glm::translate(glm::vec3{ width / 2, height / 2, 0.f }) * glm::scale(glm::vec3(8))); //shader.u_int("tex", 0); //circle3.draw_fill(); //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))); //shader_color.u_vec4("col", {1, 1, 1, 1}); //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(); } void App::mouse_down(int button, float x, float y) { printf("mouse click %f %f\n", x, y); } void App::mouse_move(float x, float y) { } void App::mouse_up(int button, float x, float y) { }