#include "gui.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include EXT_OBJECT_REGISTER_CPP(Gui) namespace { struct { ext::freetype::Glyph glyph; std::unordered_map> cache; } glyphs; struct { pod::Vector2ui current = { ext::vulkan::width, ext::vulkan::height, }; pod::Vector2ui reference = { 1280, 720, }; } size; /* std::mutex mutex; uint64_t uid = 0; struct Job { uint64_t uid; }; std::queue jobs; */ struct GlyphDescriptor { struct { alignas(16) pod::Matrix4f model[2]; } matrices; struct { alignas(16) pod::Vector4f offset; alignas(16) pod::Vector4f color; int32_t mode = 0; float depth = 0.0f; int32_t sdf = false; int32_t shadowbox = false; alignas(16) pod::Vector4f stroke; float weight; int32_t spread; float scale; } gui; }; struct GlyphBox { struct { float x, y, w, h; } box; uint64_t code; pod::Vector3f color; }; pod::Matrix4 matrix; std::vector<::GlyphBox> generateGlyphs( ext::Gui& gui, std::string string = "" ) { uf::Serializer& metadata = gui.getComponent(); std::string font = "./data/fonts/" + metadata["text settings"]["font"].asString(); if ( ::glyphs.cache[font].empty() ) { ext::freetype::initialize( ::glyphs.glyph, font ); } if ( string == "" ) { string = metadata["text settings"]["string"].asString(); } pod::Transform<>& transform = gui.getComponent>(); std::vector<::GlyphBox> gs; struct { struct { float x = 0; float y = 0; } origin; struct { float x = 0; float y = 0; } cursor; struct { float sum = 0; float len = 0; float proc = 0; float tab = 4; } average; struct { float x = 0; float y = 0; } biggest; struct { float w = 0; float h = 0; } box; struct { std::vector container; std::size_t index = 0; } colors; } stat; float scale = metadata["text settings"]["scale"].asFloat(); { pod::Vector3f color = { metadata["text settings"]["color"][0].asFloat(), metadata["text settings"]["color"][1].asFloat(), metadata["text settings"]["color"][2].asFloat(), }; stat.colors.container.push_back(color); } unsigned long COLORCONTROL = 0x7F; { std::string text = string; std::regex regex("\\%\\#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})\\%"); std::unordered_map colors; std::smatch match; bool matched = false; int maxTries = 128; while ( matched = std::regex_search( text, match, regex ) && --maxTries > 0 ) { struct { std::string str; int dec; } r, g, b; r.str = match[1].str(); g.str = match[2].str(); b.str = match[3].str(); { std::stringstream stream; stream << r.str; stream >> std::hex >> r.dec; } { std::stringstream stream; stream << g.str; stream >> std::hex >> g.dec; } { std::stringstream stream; stream << b.str; stream >> std::hex >> b.dec; } pod::Vector3f color = { r.dec / 255.0f, g.dec / 255.0f, b.dec / 255.0f }; stat.colors.container.push_back(color); text = uf::string::replace( text, "%#" + r.str + g.str + b.str + "%", "\x7F" ); } if ( maxTries == 0 ) { text += "\n(error formatting)"; } string = text; } std::wstring_convert, wchar_t> convert; std::wstring str = convert.from_bytes(string); if ( str.size() == 0 ) return gs; // Calculate statistics { // Find tallest glyph for new line for ( auto it = str.begin(); it != str.end(); ++it ) { unsigned long c = *it; if ( c == '\n' ) continue; if ( c == '\t' ) continue; if ( c == 0x01 ) continue; std::string key = ""; { key += std::to_string(c) + ";"; key += metadata["text settings"]["padding"][0].asString() + ","; key += metadata["text settings"]["padding"][1].asString() + ";"; key += metadata["text settings"]["spread"].asString() + ";"; key += metadata["text settings"]["size"].asString() + ";"; key += metadata["text settings"]["font"].asString() + ";"; key += metadata["text settings"]["sdf"].asString(); } uf::Glyph& glyph = ::glyphs.cache[font][key]; if ( !glyph.generated() ) { glyph.setPadding( { metadata["text settings"]["padding"][0].asUInt(), metadata["text settings"]["padding"][1].asUInt() } ); glyph.setSpread( metadata["text settings"]["spread"].asUInt() ); if ( metadata["text settings"]["sdf"].asBool() ) glyph.useSdf(true); glyph.generate( ::glyphs.glyph, c, metadata["text settings"]["size"].asInt() ); } stat.biggest.x = std::max( (float) stat.biggest.x, (float) glyph.getSize().x); stat.biggest.y = std::max( (float) stat.biggest.y, (float) glyph.getSize().y); stat.average.sum += glyph.getSize().x; ++stat.average.len; } stat.average.proc = stat.average.sum / stat.average.len; stat.average.tab *= stat.average.proc; // Calculate box: Second pass required because of tab stat.cursor.x = 0; stat.cursor.y = 0; stat.origin.x = 0; stat.origin.y = 0; for ( auto it = str.begin(); it != str.end(); ++it ) { unsigned long c = *it; if ( c == '\n' ) { stat.cursor.y += stat.biggest.y; stat.cursor.x = 0; continue; } else if ( c == '\t' ) { // Fixed movement vs Real Tabbing if ( false ) { stat.cursor.x += stat.average.tab; } else { stat.cursor.x = ((stat.cursor.x / stat.average.tab) + 1) * stat.average.tab; } continue; } else if ( c == COLORCONTROL ) { continue; } std::string key = ""; { key += std::to_string(c) + ";"; key += metadata["text settings"]["padding"][0].asString() + ","; key += metadata["text settings"]["padding"][1].asString() + ";"; key += metadata["text settings"]["spread"].asString() + ";"; key += metadata["text settings"]["size"].asString() + ";"; key += metadata["text settings"]["font"].asString() + ";"; key += metadata["text settings"]["sdf"].asString(); } uf::Glyph& glyph = ::glyphs.cache[font][key]; ::GlyphBox g; g.box.w = glyph.getSize().x; g.box.h = glyph.getSize().y; g.box.x = stat.cursor.x + glyph.getBearing().x; g.box.y = stat.cursor.y - glyph.getBearing().y; // - (glyph.getSize().y - glyph.getBearing().y); stat.cursor.x += (glyph.getAdvance().x); } stat.origin.x = ( !metadata["text settings"]["world"].asBool() && transform.position.x != (int) transform.position.x ) ? transform.position.x * ::size.current.x : transform.position.x; stat.origin.y = ( !metadata["text settings"]["world"].asBool() && transform.position.y != (int) transform.position.y ) ? transform.position.y * ::size.current.y : transform.position.y; if ( metadata["text settings"]["origin"].isArray() ) { stat.origin.x = metadata["text settings"]["origin"][0].asInt(); stat.origin.y = metadata["text settings"]["origin"][1].asInt(); } else if ( metadata["text settings"]["origin"] == "top" ) stat.origin.y = ::size.current.y - stat.origin.y - stat.biggest.y;// else stat.origin.y = stat.origin.y; if ( metadata["text settings"]["align"] == "right" ) stat.origin.x = ::size.current.x - stat.origin.x - stat.box.w;// else stat.origin.x = stat.origin.x; else if ( metadata["text settings"]["align"] == "center" ) stat.origin.x -= stat.box.w * 0.5f; } // Render Glyphs stat.cursor.x = 0; stat.cursor.y = stat.biggest.y; for ( auto it = str.begin(); it != str.end(); ++it ) { unsigned long c = *it; if ( c == '\n' ) { if ( metadata["text settings"]["direction"] == "down" ) stat.cursor.y -= stat.biggest.y; else stat.cursor.y += stat.biggest.y; stat.cursor.x = 0; continue; } else if ( c == '\t' ) { // Fixed movement vs Real Tabbing if ( false ) { stat.cursor.x += stat.average.tab; } else { stat.cursor.x = ((stat.cursor.x / stat.average.tab) + 1) * stat.average.tab; } continue; } else if ( c == ' ' ) { stat.cursor.x += stat.average.tab / 4.0f; continue; } else if ( c == COLORCONTROL ) { ++stat.colors.index; continue; } std::string key = ""; { key += std::to_string(c) + ";"; key += metadata["text settings"]["padding"][0].asString() + ","; key += metadata["text settings"]["padding"][1].asString() + ";"; key += metadata["text settings"]["spread"].asString() + ";"; key += metadata["text settings"]["size"].asString() + ";"; key += metadata["text settings"]["font"].asString() + ";"; key += metadata["text settings"]["sdf"].asString(); } uf::Glyph& glyph = ::glyphs.cache[font][key]; ::GlyphBox g; g.code = c; g.box.w = glyph.getSize().x; g.box.h = glyph.getSize().y; g.box.x = stat.cursor.x + (glyph.getBearing().x); g.box.y = stat.cursor.y - glyph.getBearing().y; // - (glyph.getSize().y - glyph.getBearing().y); stat.cursor.x += (glyph.getAdvance().x); try { g.color = stat.colors.container.at(stat.colors.index); } catch ( ... ) { std::cout << "Invalid color index `" << stat.colors.index << "` for string: " << string << ": (" << stat.colors.container.size() << ")" << std::endl; g.color = { metadata["text settings"]["color"][0].asFloat(), metadata["text settings"]["color"][1].asFloat(), metadata["text settings"]["color"][2].asFloat(), }; } gs.push_back(g); } return gs; } void loadGui( ext::Gui& gui, uf::Image& image ) { uf::Serializer& metadata = gui.getComponent(); metadata["render"] = true; { // this->addAlias(); gui.addAlias(); } uf::GuiMesh& mesh = gui.getComponent(); /* get original image size (before padding) */ { metadata["original size"]["x"] = image.getDimensions().x; metadata["original size"]["y"] = image.getDimensions().y; // image.padToPowerOfTwo(); metadata["current size"]["x"] = image.getDimensions().x; metadata["current size"]["y"] = image.getDimensions().y; } /* pod::Vector2f correction = { (metadata["current size"]["x"].asInt() - metadata["original size"]["x"].asInt()) / (metadata["current size"]["x"].asFloat()), (metadata["current size"]["y"].asInt() - metadata["original size"]["y"].asInt()) / (metadata["current size"]["y"].asFloat()) }; mesh.vertices = { { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, { {-1.0f, -1.0f}, {0.0f, 1.0f-correction.y}, }, { {1.0f, -1.0f}, {1.0f-correction.x, 1.0f-correction.y}, }, { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, { {1.0f, -1.0f}, {1.0f-correction.x, 1.0f-correction.y}, }, { {1.0f, 1.0f}, {1.0f-correction.x, 0.0f}, } }; */ std::string suffix = ""; { std::string _ = gui.getRootParent().getComponent()["shaders"]["gui"]["suffix"].asString(); if ( _ != "" ) suffix = _ + "."; } if ( gui.getName() == "Gui: Text" ) { ::GlyphBox g; g.box.x = metadata["text settings"]["box"][0].asFloat(); g.box.y = metadata["text settings"]["box"][1].asFloat(); g.box.w = metadata["text settings"]["box"][2].asFloat(); g.box.h = metadata["text settings"]["box"][3].asFloat(); mesh.vertices = { {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, {{ g.box.x, g.box.y }, { 0.0f, 1.0f }}, {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, {{ g.box.x, g.box.y + g.box.h }, { 0.0f, 0.0f }}, {{ g.box.x + g.box.w, g.box.y }, { 1.0f, 1.0f }}, {{ g.box.x + g.box.w, g.box.y + g.box.h }, { 1.0f, 0.0f }}, }; for ( auto& vertex : mesh.vertices ) { vertex.position.x /= ::size.reference.x; vertex.position.y /= ::size.reference.y; } mesh.initialize(true); mesh.graphic.bindUniform<::GlyphDescriptor>(); struct { std::string vertex = "./data/shaders/gui.text.vert.spv"; std::string fragment = "./data/shaders/gui.text.frag.spv"; } filenames; if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui.text."+suffix+"frag.spv"; mesh.graphic.initializeShaders({ {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT} }); } else { mesh.vertices = { { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, { {-1.0f, -1.0f}, {0.0f, 1.0f}, }, { {1.0f, -1.0f}, {1.0f, 1.0f}, }, { {-1.0f, 1.0f}, {0.0f, 0.0f}, }, { {1.0f, -1.0f}, {1.0f, 1.0f}, }, { {1.0f, 1.0f}, {1.0f, 0.0f}, } }; mesh.initialize(true); mesh.graphic.bindUniform(); struct { std::string vertex = "./data/shaders/gui.vert.spv"; std::string fragment = "./data/shaders/gui.frag.spv"; } filenames; if ( metadata["shaders"]["vertex"].isString() ) filenames.vertex = metadata["shaders"]["vertex"].asString(); if ( metadata["shaders"]["fragment"].isString() ) filenames.fragment = metadata["shaders"]["fragment"].asString(); else if ( suffix != "" ) filenames.fragment = "./data/shaders/gui."+suffix+"frag.spv"; mesh.graphic.initializeShaders({ {filenames.vertex, VK_SHADER_STAGE_VERTEX_BIT}, {filenames.fragment, VK_SHADER_STAGE_FRAGMENT_BIT} }); } mesh.graphic.texture.loadFromImage( image ); mesh.graphic.initialize( "Gui" ); mesh.graphic.autoAssign(); { pod::Transform<>& transform = gui.getComponent>(); uf::GuiMesh& mesh = gui.getComponent(); auto& texture = mesh.graphic.texture; pod::Vector2f textureSize = { metadata["original size"]["x"].asFloat(), metadata["original size"]["y"].asFloat() }; if ( metadata["scaling"].asString() == "fixed" ) { transform.scale = pod::Vector3{ (float) textureSize.x / ::size.current.x, (float) textureSize.y / ::size.current.y, 1 }; } else if ( metadata["scaling"].asString() == "fixed-1080p" ) { transform.scale = pod::Vector3{ (float) textureSize.x / 1920, (float) textureSize.y / 1080, 1 }; } } } } void ext::Gui::initialize() { uf::Object::initialize(); // alias Mesh types { // this->addAlias(); this->addAlias(); } { const uf::Scene& world = this->getRootParent(); const uf::Serializer& _metadata = world.getComponent(); ::size.current = { _metadata["window"]["size"]["x"].asFloat(), _metadata["window"]["size"]["y"].asFloat(), }; } uf::Serializer& metadata = this->getComponent(); uf::Asset& assetLoader = this->getComponent(); // master gui manager this->addHook( "window:Resized", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( !this->hasComponent() ) return "false"; pod::Vector2ui size; { size.x = json["window"]["size"]["x"].asUInt64(); size.y = json["window"]["size"]["y"].asUInt64(); } // ::matrix = uf::matrix::ortho( 0.0f, (float) size.x, 0.0f, (float) size.y ); // ::matrix = uf::matrix::ortho( size.x * -0.5f, size.x * 0.5f, size.y * -0.5f, size.y * 0.5f ); // ::matrix = uf::matrix::ortho( size.x * -1.0f, size.x * 1.0f, size.y * -1.0f, size.y * 1.0f ); ::size.current = size; // ::size.reference = size; return "true"; } ); if ( this->m_name == "Gui Manager" ) { return; } this->addHook( "glyph:Load.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; unsigned long c = json["glyph"].asUInt64(); std::string font = "./data/fonts/" + metadata["text settings"]["font"].asString(); std::string key = ""; { key += std::to_string(c) + ";"; key += metadata["text settings"]["padding"][0].asString() + ","; key += metadata["text settings"]["padding"][1].asString() + ";"; key += metadata["text settings"]["spread"].asString() + ";"; key += metadata["text settings"]["size"].asString() + ";"; key += metadata["text settings"]["font"].asString() + ";"; key += metadata["text settings"]["sdf"].asString(); } uf::Glyph& glyph = ::glyphs.cache[font][key]; uf::Image image; { const uint8_t* buffer = glyph.getBuffer(); uf::Image::container_t pixels; std::size_t len = glyph.getSize().x * glyph.getSize().y; pixels.insert( pixels.end(), buffer, buffer + len ); image.loadFromBuffer( pixels, glyph.getSize(), 8, 1, true ); } loadGui( *this, image ); return "true"; }); this->addHook( "asset:Load.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string filename = json["filename"].asString(); if ( uf::string::extension(filename) != "png" ) return "false"; uf::Scene& world = this->getRootParent(); uf::Asset& assetLoader = world.getComponent(); const uf::Image* imagePointer = NULL; try { imagePointer = &assetLoader.get(filename); } catch ( ... ) {} if ( !imagePointer ) return "false"; uf::Image image = *imagePointer; loadGui( *this, image ); return "true"; }); this->addHook( "window:Resized", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( !this->hasComponent() ) return "false"; pod::Vector2ui size; { size.x = json["window"]["size"]["x"].asUInt64(); size.y = json["window"]["size"]["y"].asUInt64(); } pod::Transform<>& transform = this->getComponent>(); uf::GuiMesh& mesh = this->getComponent(); auto& texture = mesh.graphic.texture; pod::Vector2f textureSize = { metadata["original size"]["x"].asFloat(), metadata["original size"]["y"].asFloat() }; if ( metadata["text settings"].isObject() ) { } else if ( metadata["scaling"].asString() == "fixed" ) { transform.scale = pod::Vector3{ (float) textureSize.x / size.x, (float) textureSize.y / size.y, 1 }; } else if ( metadata["scaling"].asString() == "fixed-1080p" ) { transform.scale = pod::Vector3{ (float) textureSize.x / 1920, (float) textureSize.y / 1080, 1 }; } return "true"; } ); if ( metadata["_config"]["clickable"].asBool() ) { uf::Timer clickTimer(false); clickTimer.start(); this->addHook( "gui:Clicked.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( !metadata["events"]["click"].isObject() ) return "true"; uf::Serializer payload = metadata["events"]["click"]["payload"]; this->callHook(metadata["events"]["click"]["name"].asString(), payload); return "true"; }); this->addHook( "window:Mouse.Click", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( !this->hasComponent() ) return "false"; uf::Serializer& metadata = this->getComponent(); if ( metadata["world"].asBool() ) return "true"; if ( !metadata["render"].asBool() ) return "true"; if ( metadata["box"] == Json::nullValue ) return "true"; bool down = json["mouse"]["state"].asString() == "Down"; bool clicked = false; if ( down ) { pod::Vector2ui position; { position.x = json["mouse"]["position"]["x"].asInt() > 0 ? json["mouse"]["position"]["x"].asUInt() : 0; position.y = json["mouse"]["position"]["y"].asInt() > 0 ? json["mouse"]["position"]["y"].asUInt() : 0; } pod::Vector2f click; { click.x = (float) position.x / (float) ::size.current.x; click.y = (float) position.y / (float) ::size.current.y; float x = (click.x = (click.x * 2.0f) - 1.0f); float y = (click.y = (click.y * 2.0f) - 1.0f); for (int i = 0, j = metadata["box"].size() - 1; i < metadata["box"].size(); j = i++) { float xi = metadata["box"][i]["x"].asFloat(), yi = metadata["box"][i]["y"].asFloat(); float xj = metadata["box"][j]["x"].asFloat(), yj = metadata["box"][j]["y"].asFloat(); bool intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) clicked = !clicked; } } /* if ( clicked ) std::cout << "Clicked on " << this->m_name << std::endl; else { std::cout << click.x << ", " << click.y << " " << this->m_name << metadata["box"] << std::endl; } */ } metadata["clicked"] = clicked; if ( metadata["clicked"].asBool() && clickTimer.elapsed().asDouble() >= 1 ) { clickTimer.reset(); std::cout << "Calling hook" << std::endl; this->callHook("gui:Clicked.%UID%"); } return "true"; } ); } if ( metadata["_config"]["hoverable"].asBool() ) { this->addHook( "window:Mouse.Moved", [&](const std::string& event)->std::string{ uf::Serializer json = event; if ( !this->hasComponent() ) return "false"; uf::Serializer& metadata = this->getComponent(); if ( metadata["world"].asBool() ) return "true"; if ( !metadata["render"].asBool() ) return "true"; if ( metadata["box"] == Json::nullValue ) return "true"; bool down = json["mouse"]["state"].asString() == "Down"; bool clicked = false; pod::Vector2ui position; { position.x = json["mouse"]["position"]["x"].asInt() > 0 ? json["mouse"]["position"]["x"].asUInt() : 0; position.y = json["mouse"]["position"]["y"].asInt() > 0 ? json["mouse"]["position"]["y"].asUInt() : 0; } pod::Vector2f click; { click.x = (float) position.x / (float) ::size.current.x; click.y = (float) position.y / (float) ::size.current.y; float x = (click.x = (click.x * 2.0f) - 1.0f); float y = (click.y = (click.y * 2.0f) - 1.0f); for (int i = 0, j = metadata["box"].size() - 1; i < metadata["box"].size(); j = i++) { float xi = metadata["box"][i]["x"].asFloat(), yi = metadata["box"][i]["y"].asFloat(); float xj = metadata["box"][j]["x"].asFloat(), yj = metadata["box"][j]["y"].asFloat(); bool intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); if (intersect) clicked = !clicked; } } metadata["hovered"] = clicked; return "true"; } ); } if ( metadata["text settings"]["string"].isString() ) { uf::Serializer defaultSettings; defaultSettings.readFromFile("./data/entities/gui/text/string.json"); if ( metadata["text settings"]["padding"].isNull() ) metadata["text settings"]["padding"] = defaultSettings["metadata"]["text settings"]["padding"]; if ( metadata["text settings"]["spread"].isNull() ) metadata["text settings"]["spread"] = defaultSettings["metadata"]["text settings"]["spread"]; if ( metadata["text settings"]["weight"].isNull() ) metadata["text settings"]["weight"] = defaultSettings["metadata"]["text settings"]["weight"]; if ( metadata["text settings"]["size"].isNull() ) metadata["text settings"]["size"] = defaultSettings["metadata"]["text settings"]["size"]; if ( metadata["text settings"]["scale"].isNull() ) metadata["text settings"]["scale"] = defaultSettings["metadata"]["text settings"]["scale"]; if ( metadata["text settings"]["sdf"].isNull() ) metadata["text settings"]["sdf"] = defaultSettings["metadata"]["text settings"]["sdf"]; if ( metadata["text settings"]["kerning"].isNull() ) metadata["text settings"]["kerning"] = defaultSettings["metadata"]["text settings"]["kerning"]; if ( metadata["text settings"]["font"].isNull() ) metadata["text settings"]["font"] = defaultSettings["metadata"]["text settings"]["font"]; float delay = 0.0f; float scale = metadata["text settings"]["scale"].asFloat(); std::vector<::GlyphBox> glyphs = generateGlyphs(*this); for ( auto& glyph : glyphs ) { // append new child // ext::Gui* glyphElement = new ext::Gui; // this->addChild(*glyphElement); // glyphElement->load("./entities/gui/text/letter.json"); size_t uid = this->loadChild("./entities/gui/text/letter.json"); ext::Gui* glyphElement = (ext::Gui*) this->findByUid( uid ); uf::Serializer& pMetadata = glyphElement->getComponent(); pMetadata["events"] = metadata["events"]; pMetadata["text settings"] = metadata["text settings"]; pMetadata["text settings"].removeMember("string"); pMetadata["text settings"]["letter"] = (wchar_t) glyph.code; pMetadata["text settings"]["color"][0] = glyph.color[0]; pMetadata["text settings"]["color"][1] = glyph.color[1]; pMetadata["text settings"]["color"][2] = glyph.color[2]; pMetadata["text settings"]["box"][0] = glyph.box.x; pMetadata["text settings"]["box"][1] = glyph.box.y; pMetadata["text settings"]["box"][2] = glyph.box.w; pMetadata["text settings"]["box"][3] = glyph.box.h; pMetadata["_config"]["hoverable"] = metadata["_config"]["hoverable"]; pMetadata["_config"]["clickable"] = metadata["_config"]["clickable"]; glyphElement->initialize(); pod::Transform<>& pTransform = glyphElement->getComponent>(); pTransform.scale.x = scale; pTransform.scale.y = scale; pTransform.reference = this->getComponentPointer>(); uf::Serializer payload; payload["glyph"] = (uint64_t) glyph.code; if ( metadata["text settings"]["scroll speed"].isNumeric() ) { glyphElement->queueHook("glyph:Load.%UID%", payload, delay); delay += metadata["text settings"]["scroll speed"].asFloat(); } else { glyphElement->callHook("glyph:Load.%UID%", payload); } } } } void ext::Gui::tick() { uf::Object::tick(); uf::Serializer& metadata = this->getComponent(); if ( metadata["text settings"]["fade in speed"].isNumeric() && !metadata["system"]["faded in"].asBool() ) { float speed = metadata["text settings"]["fade in speed"].asFloat(); float alpha = metadata["text settings"]["color"][3].asFloat(); speed *= uf::physics::time::delta; if ( alpha < 1 && alpha + speed > 1 ) { alpha = 1; speed = 0; metadata["system"]["faded in"] = true; } if ( alpha + speed <= 1 ) { metadata["text settings"]["color"][3] = alpha + speed; } } } void ext::Gui::render() { uf::Object::render(); uf::Serializer& metadata = this->getComponent(); /* Update uniforms */ if ( this->hasComponent() ) { auto& scene = this->getRootParent(); auto& mesh = this->getComponent(); auto& camera = scene.getController()->getComponent(); auto& transform = this->getComponent>(); if ( !mesh.generated ) return; pod::Vector4 offset = { metadata["uv"][0].asFloat(), metadata["uv"][1].asFloat(), metadata["uv"][2].asFloat(), metadata["uv"][3].asFloat() }; int mode = 0; if ( !metadata["shader"].isNull() ) mode = metadata["shader"].asInt(); if ( this->m_name == "Gui: Text" ) { ::GlyphDescriptor uniforms; if ( !metadata["text settings"]["color"].isArray() ) { metadata["text settings"]["color"][0] = 1.0f; metadata["text settings"]["color"][1] = 1.0f; metadata["text settings"]["color"][2] = 1.0f; metadata["text settings"]["color"][3] = 1.0f; } if ( !metadata["text settings"]["stroke"].isArray() ) { metadata["text settings"]["stroke"][0] = 0.0f; metadata["text settings"]["stroke"][1] = 0.0f; metadata["text settings"]["stroke"][2] = 0.0f; metadata["text settings"]["stroke"][3] = 1.0f; } pod::Vector4 color = { metadata["text settings"]["color"][0].asFloat(), metadata["text settings"]["color"][1].asFloat(), metadata["text settings"]["color"][2].asFloat(), metadata["text settings"]["color"][3].asFloat() }; pod::Vector4 stroke = { metadata["text settings"]["stroke"][0].asFloat(), metadata["text settings"]["stroke"][1].asFloat(), metadata["text settings"]["stroke"][2].asFloat(), metadata["text settings"]["stroke"][3].asFloat() }; if ( uf::Window::isKeyPressed("V") ) { metadata["text settings"]["weight"] = metadata["text settings"]["weight"].asFloat() + uf::physics::time::delta; std::cout << metadata["text settings"]["weight"].asFloat() << std::endl; } if ( uf::Window::isKeyPressed("C") ) { metadata["text settings"]["weight"] = metadata["text settings"]["weight"].asFloat() - uf::physics::time::delta; std::cout << metadata["text settings"]["weight"].asFloat() << std::endl; } if ( uf::Window::isKeyPressed("N") ) { metadata["text settings"]["spread"] = metadata["text settings"]["spread"].asFloat() + uf::physics::time::delta; std::cout << metadata["text settings"]["spread"].asFloat() << std::endl; } if ( uf::Window::isKeyPressed("B") ) { metadata["text settings"]["spread"] = metadata["text settings"]["spread"].asFloat() - uf::physics::time::delta; std::cout << metadata["text settings"]["spread"].asFloat() << std::endl; } uniforms.gui.offset = offset; uniforms.gui.color = color; uniforms.gui.stroke = stroke; uniforms.gui.mode = mode; uniforms.gui.sdf = metadata["text settings"]["sdf"].asBool(); uniforms.gui.shadowbox = metadata["text settings"]["shadowbox"].asBool(); uniforms.gui.weight = metadata["text settings"]["weight"].asFloat(); // float uniforms.gui.spread = metadata["text settings"]["spread"].asInt(); // int uniforms.gui.scale = metadata["text settings"]["scale"].asFloat(); // float if ( !metadata["text settings"]["depth"].isNull() ) uniforms.gui.depth = metadata["text settings"]["depth"].asFloat(); else uniforms.gui.depth = 0.0f; for ( std::size_t i = 0; i < 2; ++i ) { if ( metadata["text settings"]["world"].asBool() ) { auto& scene = this->getRootParent(); auto& camera = scene.getController()->getComponent(); pod::Transform<> flatten = uf::transform::flatten( this->getComponent>() ); auto model = uf::transform::model( flatten ); auto view = camera.getView(i); auto projection = camera.getProjection(i); uniforms.matrices.model[i] = projection * view * model; } else { uf::Matrix4 translation, rotation, scale; pod::Transform<> flatten = uf::transform::flatten(transform, false); // make our own flattened position, for some reason this causes z to be 6.6E+28 flatten.position = transform.position + transform.reference->position; flatten.orientation.w *= -1; rotation = uf::quaternion::matrix(flatten.orientation); // pod::Vector3f offsetSize = { ::size.current.x, ::size.current.y, 1 }; // translation = uf::matrix::translate( uf::matrix::identity(), flatten.position * offsetSize ); translation = uf::matrix::translate( uf::matrix::identity(), flatten.position ); scale = uf::matrix::scale( scale, transform.scale ); // uniforms.matrices.model[i] = ::matrix * translation * rotation * scale; uniforms.matrices.model[i] = translation * rotation * scale; } } mesh.graphic.updateBuffer( uniforms, 0, false ); // calculate click box { auto& model = uniforms.matrices.model[0]; auto& mesh = this->getComponent(); auto& vertices = mesh.vertices; int i = 0; for ( auto& vertex : vertices ) { auto& position = vertex.position; auto translated = uf::matrix::multiply( model, { position.x, position.y, 0.0f, 1.0f } ); // points.push_back( translated ); metadata["box"][i]["x"] = translated.x; metadata["box"][i]["y"] = translated.y; ++i; } } } else { if ( !metadata["color"].isArray() ) { metadata["color"][0] = 1.0f; metadata["color"][1] = 1.0f; metadata["color"][2] = 1.0f; metadata["color"][3] = 1.0f; } pod::Vector4 color = { metadata["color"][0].asFloat(), metadata["color"][1].asFloat(), metadata["color"][2].asFloat(), metadata["color"][3].asFloat() }; uf::StereoGuiMeshDescriptor uniforms; uniforms.gui.offset = offset; uniforms.gui.color = color; uniforms.gui.mode = mode; if ( !metadata["depth"].isNull() ) uniforms.gui.depth = metadata["depth"].asFloat(); else uniforms.gui.depth = 0; for ( std::size_t i = 0; i < 2; ++i ) { if ( metadata["world"].asBool() ) { /* pod::Transform<> flatten = uf::transform::flatten(camera.getTransform(), true); pod::Matrix4 rotation = uf::quaternion::matrix( uf::vector::multiply( { 1, 1, 1, -1 }, flatten.orientation) ); uniforms.matrices.model[i] = ::matrix * rotation; */ auto& scene = this->getRootParent(); auto& camera = scene.getController()->getComponent(); pod::Transform<> flatten = uf::transform::flatten( this->getComponent>() ); auto model = uf::transform::model( flatten ); auto view = camera.getView(i); auto projection = camera.getProjection(i); uniforms.matrices.model[i] = projection * view * model; } else { uf::Matrix4 translation, rotation, scale; pod::Transform<> flatten = uf::transform::flatten(transform, false); flatten.orientation.w *= -1; rotation = uf::quaternion::matrix( flatten.orientation ); scale = uf::matrix::scale( scale, transform.scale ); translation = uf::matrix::translate( uf::matrix::identity(), flatten.position ); uniforms.matrices.model[i] = translation * scale * rotation; } } mesh.graphic.updateBuffer( uniforms, 0, false ); // calculate click box { auto& model = uniforms.matrices.model[0]; auto& mesh = this->getComponent(); auto& vertices = mesh.vertices; int i = 0; for ( auto& vertex : vertices ) { auto& position = vertex.position; auto translated = uf::matrix::multiply( model, { position.x, position.y, 0.0f, 1.0f } ); // points.push_back( translated ); metadata["box"][i]["x"] = translated.x; metadata["box"][i]["y"] = translated.y; ++i; } } } } else if ( this->m_name != "Gui Manager" ) { bool hovered = false; bool clicked = false; for ( uf::Entity* entity : this->getChildren() ) { if ( !entity ) continue; if ( entity->getUid() == 0 ) continue; uf::Serializer& pMetadata = entity->getComponent(); if ( pMetadata["hovered"].asBool() ) hovered = true; if ( pMetadata["clicked"].asBool() ) clicked = true; } metadata["clicked"] = clicked; metadata["hovered"] = hovered; } if ( metadata["debug"]["moveable"].asBool() ) { pod::Transform<>& transform = this->getComponent>(); pod::Vector2f step = { 4 / ::size.current.x, 4 / ::size.current.y, }; if ( uf::Window::isKeyPressed("U") ) { step.x = (step.y = uf::physics::time::delta * 0.5f); } if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("J") ) { transform.scale.x += step.x * -1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; } if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("L") ) { transform.scale.x += step.x * 1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; } if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("I") ) { transform.scale.y += step.y * -1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; } if ( uf::Window::isKeyPressed("O") && uf::Window::isKeyPressed("K") ) { transform.scale.y += step.y * 1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Scale: " << flat.scale.x << ", " << flat.scale.y << std::endl; } if ( uf::Window::isKeyPressed("J") ) { transform.position.x += step.x * -1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; } if ( uf::Window::isKeyPressed("L") ) { transform.position.x += step.x * 1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; } if ( uf::Window::isKeyPressed("I") ) { transform.position.y += step.y * -1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; } if ( uf::Window::isKeyPressed("K") ) { transform.position.y += step.y * 1; pod::Transform<> flat = uf::transform::flatten( transform ); std::cout << this->m_name << ": Position: " << flat.position.x << ", " << flat.position.y << std::endl; } } } void ext::Gui::destroy() { if ( this->hasComponent() ) { auto& mesh = this->getComponent(); mesh.graphic.destroy(); mesh.destroy(); } /* std::cout << "Destroying " << this->m_name; if ( this->m_name == "Gui: Text" ) { uf::Serializer& metadata = this->getComponent(); std::cout << ", " << metadata["text settings"]["letter"].asString(); } std::cout << std::endl; */ uf::Object::destroy(); }