315 lines
9 KiB
C++
315 lines
9 KiB
C++
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include "Graphics.h"
|
|
|
|
#include <bx/math.h>
|
|
|
|
#include <iostream>
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include <stb_image.h>
|
|
|
|
namespace {
|
|
FT_Library library;
|
|
FT_Error error;
|
|
FT_Face face_regular, face_bold;
|
|
|
|
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 }
|
|
};
|
|
|
|
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<NVL::Char, bgfx::TextureHandle> cache_regular {};
|
|
std::unordered_map<NVL::Char, bgfx::TextureHandle> 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<NVL::Char, bgfx::TextureHandle>* 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<NVL::Environment::Variable>& s, u32 pos_x, u32 pos_y, u32 col) {
|
|
const NVL::String& str = std::get<NVL::String>(s[0].value);
|
|
|
|
std::vector<NVL::Environment::Markup> ms{};
|
|
for (const NVL::Environment::Variable& markup : std::get<std::vector<NVL::Environment::Variable>>(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);
|
|
}
|
|
}
|