#include #include FT_FREETYPE_H #include "Graphics.h" #include #include #define STB_IMAGE_IMPLEMENTATION #include namespace { FT_Library library; FT_Error error; FT_Face face_regular, face_bold; static struct PosUVVertex { f32 x; f32 y; f32 z; f32 u; f32 v; } quad_vert[] = { { 0.f, 0.f, 0.0f, 0.0f, 0.0f }, { 0.f, 1.f, 0.0f, 0.0f, 1.0f }, { 1.f, 0.f, 0.0f, 1.0f, 0.0f }, { 1.f, 1.f, 0.0f, 1.0f, 1.0f } }; static const uint16_t quad_indices[] = { 0, 2, 1, 1, 2, 3 }; bgfx::ShaderHandle loadShader(const char* FILENAME) { std::string shaderPath{}; switch (bgfx::getRendererType()) { case bgfx::RendererType::Noop: case bgfx::RendererType::Direct3D9: shaderPath = "shaders/dx9/"; break; case bgfx::RendererType::Direct3D11: case bgfx::RendererType::Direct3D12: shaderPath = "shaders/dx11/"; break; case bgfx::RendererType::Gnm: shaderPath = "shaders/pssl/"; break; case bgfx::RendererType::Metal: shaderPath = "shaders/metal/"; break; case bgfx::RendererType::OpenGL: shaderPath = "shaders/glsl/"; break; case bgfx::RendererType::OpenGLES: shaderPath = "shaders/essl/"; break; case bgfx::RendererType::Vulkan: shaderPath = "shaders/spirv/"; break; } std::string filePath = shaderPath + FILENAME; FILE* file = fopen(filePath.c_str(), "rb"); fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); const bgfx::Memory* mem = bgfx::alloc(fileSize + 1); fread(mem->data, 1, fileSize, file); mem->data[mem->size - 1] = '\0'; fclose(file); return bgfx::createShader(mem); } bgfx::ShaderHandle a_vsh, img_vsh; bgfx::ShaderHandle a_fsh, img_fsh; bgfx::ProgramHandle a_program, img_program; bgfx::IndexBufferHandle ibh; bgfx::VertexBufferHandle vbh; bgfx::UniformHandle s_texColor; bgfx::VertexLayout pcvDecl; std::unordered_map cache_regular {}; std::unordered_map cache_bold {}; int w, h, channels; u8* img; bgfx::TextureHandle tx; } namespace ADVect::Graphics { i16 Init(u16 width, u16 height) { error = FT_Init_FreeType(&library); if (error) { std::cerr << "ADV: FreeType init error: " << error << std::endl; return -1; } error = FT_New_Face(library, "SourceHanSans-Regular.ttc", 0, &face_regular); error = FT_New_Face(library, "SourceHanSans-Bold.ttc", 0, &face_bold); if (error == FT_Err_Unknown_File_Format) { std::cerr << "ADV: FreeType Unknown_File_Format: " << error << std::endl; return -1; } else if (error) { std::cerr << "ADV: FreeType font loading error: " << error << std::endl; return -1; } error = FT_Set_Char_Size(face_regular, 0, 20 * 64, 72, 72); error = FT_Set_Char_Size(face_bold, 0, 20 * 64, 72, 72); if (error == FT_Err_Unknown_File_Format) { std::cerr << "ADV: FreeType Set_Char_Size error: " << error << std::endl; return -1; } float view[16]; bx::mtxTranslate(view, 0.f, 0.f, 1.0f); float proj[16]; bx::mtxOrtho(proj, 0.0f, width, 0.0f, height, 0.1f, 100.0f, 0.f, bgfx::getCaps()->homogeneousDepth); bgfx::setViewTransform(0, view, proj); pcvDecl.begin() .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float) .end(); ibh = bgfx::createIndexBuffer(bgfx::makeRef(quad_indices, sizeof(quad_indices))); vbh = bgfx::createVertexBuffer(bgfx::makeRef(quad_vert, sizeof(quad_vert)), pcvDecl); img_vsh = loadShader("test.vert.bin"); img_fsh = loadShader("test.frag.bin"); img_program = bgfx::createProgram(img_vsh, img_fsh, true); a_vsh = loadShader("swizzle_aaaa.vert.bin"); a_fsh = loadShader("swizzle_aaaa.frag.bin"); a_program = bgfx::createProgram(a_vsh, a_fsh, true); s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler); img = stbi_load("image.png", &w, &h, &channels, 0); if (img == NULL) { std::cerr << "ADV: STB IMAGE loading failed" << std::endl; return -1; } const bgfx::Memory* buf = bgfx::makeRef(img, w * h * channels * sizeof(u8)); tx = bgfx::createTexture2D( w, h, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_NONE | BGFX_SAMPLER_UVW_CLAMP, buf ); return 0; } void Shutdown() { FT_Done_Face(face_regular); FT_Done_Face(face_bold); FT_Done_FreeType(library); bgfx::destroy(a_vsh); bgfx::destroy(a_fsh); bgfx::destroy(img_vsh); bgfx::destroy(img_fsh); bgfx::destroy(a_program); bgfx::destroy(img_program); bgfx::destroy(ibh); bgfx::destroy(vbh); bgfx::destroy(s_texColor); for (auto it = cache_regular .begin(); it != cache_regular.end(); it++) { bgfx::destroy(it->second); } for (auto it = cache_bold.begin(); it != cache_bold.end(); it++) { bgfx::destroy(it->second); } bgfx::destroy(tx); } // Draws quad with texture on Z = 0 void DrawTexture(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h) { float mtx1[16], mtx2[16], mtx3[16]; bx::mtxTranslate(mtx1, pos_x, pos_y, 0.0f); bx::mtxScale(mtx2, w, h, 1.0f); bx::mtxMul(mtx3, mtx2, mtx1); bgfx::setTransform(mtx3); bgfx::setVertexBuffer(0, vbh); bgfx::setIndexBuffer(ibh); bgfx::setTexture(0, s_texColor, tex); bgfx::setState(BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_NORMAL); bgfx::submit(0, img_program); } void DrawTextureStencilAlpha(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h) { float mtx1[16], mtx2[16], mtx3[16]; bx::mtxTranslate(mtx1, pos_x, pos_y, 0.0f); bx::mtxScale(mtx2, w, h, 1.0f); bx::mtxMul(mtx3, mtx2, mtx1); bgfx::setTransform(mtx3); bgfx::setVertexBuffer(0, vbh); bgfx::setIndexBuffer(ibh); bgfx::setTexture(0, s_texColor, tex); bgfx::setState((BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_ALPHA) & ~(BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS)); bgfx::submit(0, a_program); } void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col, u32 style_flags) { FT_Face active = face_regular; std::unordered_map* cache = &cache_regular; switch (style_flags) { case TEXT_NONE: break; case TEXT_BOLD: active = face_bold; cache = &cache_bold; break; case TEXT_ITALIC: break; case TEXT_ITALIC | TEXT_BOLD: break; } FT_GlyphSlot slot = active->glyph; for (const auto& c : s) { error = FT_Load_Char(active, c, FT_LOAD_RENDER); if (error) continue; if (cache->find(c) == cache->end() && slot->bitmap.width != 0) { const bgfx::Memory* buf = bgfx::copy(slot->bitmap.buffer, slot->bitmap.pitch * slot->bitmap.rows); cache->emplace(c, bgfx::createTexture2D( slot->bitmap.width, slot->bitmap.rows, false, 1, bgfx::TextureFormat::A8, BGFX_TEXTURE_NONE | BGFX_SAMPLER_UVW_CLAMP, buf )); } pos_x += slot->bitmap_left; float ypos = pos_y - (slot->bitmap.rows - slot->bitmap_top); if (slot->bitmap.width != 0) { DrawTextureStencilAlpha((*cache)[c], pos_x, ypos, slot->bitmap.width, slot->bitmap.rows); } pos_x += slot->advance.x >> 6; pos_y += slot->advance.y >> 6; } } void RenderString(const NVL::String& s, u32&& pos_x, u32&& pos_y, u32 col, u32 style_flags) { u32 x = pos_x, y = pos_y; RenderString(s, x, y, col, style_flags); } void RenderStringMarkup(const std::vector& s, u32 pos_x, u32 pos_y, u32 col) { const NVL::String& str = std::get(s[0].value); std::vector ms{}; for (const NVL::Environment::Variable& markup : std::get>(s[1].value)) { ms.push_back(NVL::Environment::UnpackMarkupVariable(markup)); } if (ms.empty()) { RenderString(str, pos_x, pos_y, col, TEXT_NONE); return; } if (ms[0].begin != 0) RenderString(str.substr(0, ms[0].begin), pos_x, pos_y, col, TEXT_NONE); // We assume markups are already sorted for (size_t i = 0; i < ms.size(); i++) { u32 style_flags = TEXT_NONE; const NVL::String* ruby = nullptr; for (const auto& e : ms[i].efs) { if (e.first == u"b") { style_flags |= TEXT_BOLD; } else if (e.first == u"i") { style_flags |= TEXT_ITALIC; } else if (e.first == u"u") { style_flags |= TEXT_UNDERLINE; } else if (e.first == u"s") { style_flags |= TEXT_STRIKETHROUGH; } else if (e.first == u"o") { style_flags |= TEXT_OVERLINE; } else if (e.first == u"rb") { ruby = &e.second[0]; } else { std::cerr << "ADV: Unrecognized text markup: " << NVL::to_std_string(e.first) << std::endl; } } if (ruby != nullptr) { u32 temp_x = pos_x, temp_y = pos_y + 20; RenderString(*ruby, temp_x, temp_y, col, TEXT_NONE); } RenderString(str.substr(ms[i].begin, ms[i].end - ms[i].begin), pos_x, pos_y, col, style_flags); if (i != ms.size() - 1 && ms[i + 1].begin - 1 != ms[i].end) RenderString(str.substr(ms[i].end, ms[i + 1].begin - ms[i].end), pos_x, pos_y, col, TEXT_NONE); } if (ms.back().end != str.length()) RenderString(str.substr(ms.back().end), pos_x, pos_y, col, TEXT_NONE); } void DrawRandomShit() { DrawTexture(tx, 0, -40, w, h); } }