#include #include FT_FREETYPE_H #include "Graphics.h" #include #include #include namespace { FT_Library library; FT_Error error; FT_Face face; struct PosUVVertex { f32 x; f32 y; f32 z; f32 u; f32 v; }; static PosUVVertex 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 } }; PosUVVertex* get_quad_posUV_verts(f32 L, f32 B, f32 W, f32 H) { return quad_vert; } 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 vsh; bgfx::ShaderHandle fsh; bgfx::ProgramHandle program; bgfx::IndexBufferHandle ibh; bgfx::VertexBufferHandle vbh; bgfx::UniformHandle s_texColor; bgfx::VertexLayout pcvDecl; std::unordered_map cache{}; } namespace ADVect::Graphics { void Init(u16 width, u16 height) { error = FT_Init_FreeType(&library); if (error) { std::cerr << "FreeType init error: " << error << std::endl; } error = FT_New_Face(library, "SourceHanSerif-Heavy.ttc", 0, &face); if (error == FT_Err_Unknown_File_Format) { std::cerr << "FreeType Unknown_File_Format: " << error << std::endl; } else if (error) { std::cerr << "FreeType font loading error: " << error << std::endl; } error = FT_Set_Char_Size(face, 0, 20 * 64, 72, 72); if (error == FT_Err_Unknown_File_Format) { std::cerr << "FreeType Set_Char_Size error: " << error << std::endl; } 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); vsh = loadShader("test.vert.bin"); fsh = loadShader("test.frag.bin"); program = bgfx::createProgram(vsh, fsh, true); s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler); } void Destroy() { FT_Done_Face(face); FT_Done_FreeType(library); bgfx::destroy(vsh); bgfx::destroy(fsh); bgfx::destroy(program); bgfx::destroy(ibh); bgfx::destroy(vbh); bgfx::destroy(s_texColor); for (auto it = cache.begin(); it != cache.end(); it++) { bgfx::destroy(it->second); cache.erase(it); } bgfx::shutdown(); } void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col) { FT_GlyphSlot slot = face->glyph; for (const auto& c : s) { error = FT_Load_Char(face, 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_POINT | 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) { float mtx1[16], mtx2[16], mtx3[16]; bx::mtxTranslate(mtx1, (f32)pos_x, (f32)ypos, 0.0f); bx::mtxScale(mtx2, slot->bitmap.width, slot->bitmap.rows, 1.0f); bx::mtxMul(mtx3, mtx2, mtx1); bgfx::setTransform(mtx3); bgfx::setVertexBuffer(0, vbh); bgfx::setIndexBuffer(ibh); bgfx::setTexture(0, s_texColor, cache[c]); bgfx::setState(BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_ADD); bgfx::submit(0, program); } 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 x = pos_x, y = pos_y; RenderString(s, x, y, col); } void RenderStringMarkup(const std::vector& s, u32 pos_x, u32 pos_y, u32 col) { const NVL::String& str = std::get(s[0].value); // Check ParseMarkup for more information for (const NVL::Environment::Variable& markup : std::get>(s[1].value)) { const auto& range = std::get>(std::get>(markup.value)[0].value); const NVL::Number& begin = std::get(range[0].value), end = std::get(range[1].value); for (const NVL::Environment::Variable& combination : std::get>(std::get>(markup.value)[1].value)) { if (combination.type == NVL::Environment::Type::String) { const NVL::String& ef = std::get(combination.value); RenderString(str.substr(begin, end - begin + 1), pos_x, pos_y, col); } else if (combination.type == NVL::Environment::Type::Array) { const std::vector& ef_t = std::get>(combination.value); const NVL::String& ef = std::get(ef_t[0].value); const std::vector& args = std::get>(ef_t[1].value); RenderString(str.substr(begin, end - begin + 1), pos_x, pos_y, col); } } } // NVL::String str = std::get(s.value); // RenderString(str, pos_x, pos_y, col); } }