NouVeL/ADVect/Graphics.cpp

344 lines
9.4 KiB
C++
Raw Normal View History

2022-08-18 12:17:43 -04:00
#include <ft2build.h>
#include FT_FREETYPE_H
2022-05-09 01:50:09 -04:00
#include "Graphics.h"
#include <bx/math.h>
2022-05-09 01:50:09 -04:00
#include <iostream>
#include <optional>
2022-05-09 01:50:09 -04:00
2022-08-22 02:15:25 -04:00
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
2022-05-09 01:50:09 -04:00
namespace {
using namespace ADVect::Graphics;
bgfx::ShaderHandle loadShader(const std::string& 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);
}
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 }
};
2022-08-22 02:15:25 -04:00
static const uint16_t quad_indices[] =
{
0, 2, 1,
1, 2, 3
};
2022-05-09 01:50:09 -04:00
FT_Library library;
FT_Error error;
struct Font {
FT_Face face = nullptr;
std::unordered_map<NVL::Char, bgfx::TextureHandle> cache{};
void cache_char(NVL::Char c) {
FT_GlyphSlot slot = face->glyph;
error = FT_Load_Char(face, c, FT_LOAD_RENDER);
if (error) {
std::cerr << "ADV: Failed to load character" << std::endl;
}
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
));
}
2022-05-09 01:50:09 -04:00
}
};
std::optional<Font> init_font(const char* path) {
Font f;
2022-05-09 01:50:09 -04:00
error = FT_New_Face(library, path, 0, &f.face);
2022-05-09 01:50:09 -04:00
if (error == FT_Err_Unknown_File_Format) {
2022-08-22 02:15:25 -04:00
std::cerr << "ADV: FreeType Unknown_File_Format: " << error << std::endl;
return std::nullopt;
2022-05-09 01:50:09 -04:00
}
2022-08-18 12:17:43 -04:00
else if (error) {
2022-08-22 02:15:25 -04:00
std::cerr << "ADV: FreeType font loading error: " << error << std::endl;
return std::nullopt;
2022-05-09 01:50:09 -04:00
}
error = FT_Set_Char_Size(f.face, 0, 20 * 64, 72, 72);
if (error) {
2022-08-22 02:15:25 -04:00
std::cerr << "ADV: FreeType Set_Char_Size error: " << error << std::endl;
return std::nullopt;
2022-05-09 01:50:09 -04:00
}
return f;
}
void destroy_font(Font& f) {
FT_Done_Face(f.face);
for (auto it = f.cache.begin(); it != f.cache.end(); it++) {
bgfx::destroy(it->second);
}
}
inline bgfx::ProgramHandle load_shader_program(const std::string& shader) {
return bgfx::createProgram(loadShader(shader + ".vert.bin"), loadShader(shader + ".frag.bin"), true);
}
std::optional<ImageTexture> get_image_texture(const std::string& file) {
ImageTexture i;
i.buffer = stbi_load("image.png", &i.w, &i.h, &i.channels, 0);
if (i.buffer == NULL) {
std::cerr << "ADV: STB IMAGE loading failed" << std::endl;
return std::nullopt;
}
const bgfx::Memory* buf = bgfx::makeRef(i.buffer, i.w * i.h * i.channels * sizeof(u8));
i.tx = bgfx::createTexture2D(
i.w,
i.h,
false,
1,
bgfx::TextureFormat::RGBA8,
BGFX_TEXTURE_NONE | BGFX_SAMPLER_UVW_CLAMP,
buf
);
return i;
}
void destroy_image_texture(ImageTexture& i) {
stbi_image_free(i.buffer);
bgfx::destroy(i.tx);
}
Font regular, bold;
ImageTexture bg;
bgfx::ProgramHandle a_program, img_program;
bgfx::UniformHandle s_texColor;
bgfx::IndexBufferHandle ibh;
bgfx::VertexBufferHandle vbh;
bgfx::VertexLayout pcvDecl;
std::unordered_map<std::string, ImageTexture> imgs;
}
namespace ADVect::Graphics {
bool Init(u16 width, u16 height) {
error = FT_Init_FreeType(&library);
if (error) {
std::cerr << "ADV: FreeType init error: " << error << std::endl;
return false;
}
if (auto f = init_font("SourceHanSerif-Regular.ttc")) regular = *f; else return false;
if (auto f = init_font("SourceHanSerif-Heavy.ttc")) bold = *f; else return false;
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_program = load_shader_program("test");
a_program = load_shader_program("swizzle_aaaa");
s_texColor = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
2022-08-22 02:15:25 -04:00
if (auto i = get_image_texture("image.png")) bg = *i; else return false;
2022-08-22 02:15:25 -04:00
return true;
2022-05-09 01:50:09 -04:00
}
2022-08-21 16:24:13 -04:00
void Shutdown() {
destroy_font(regular);
destroy_font(bold);
2022-05-09 01:50:09 -04:00
FT_Done_FreeType(library);
2022-08-18 12:17:43 -04:00
destroy_image_texture(bg);
for (auto it = imgs.begin(); it != imgs.end(); it++) {
destroy_image_texture(it->second);
}
2022-08-22 02:15:25 -04:00
bgfx::destroy(a_program);
bgfx::destroy(img_program);
bgfx::destroy(ibh);
bgfx::destroy(vbh);
bgfx::destroy(s_texColor);
2022-08-22 02:15:25 -04:00
}
// Draws quad with texture on Z = 0
void DrawTexture(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h, u64 state, const bgfx::ProgramHandle& pg) {
2022-08-22 02:15:25 -04:00
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(state);
bgfx::submit(0, pg);
2022-08-22 02:15:25 -04:00
}
void DrawTextureImage(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h) {
DrawTexture(tex, pos_x, pos_y, w, h, BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_NORMAL, img_program);
}
2022-08-22 02:15:25 -04:00
void DrawTextureImage(const ImageTexture& img, i32 pos_x, i32 pos_y) {
DrawTextureImage(img.tx, pos_x, pos_y, img.w, img.h);
}
2022-08-22 02:15:25 -04:00
ImageTexture* GetImageTextureFromFile(const std::string& file) {
if (imgs.find(file) == imgs.end()) {
ImageTexture img;
if (auto i = get_image_texture(file)) {
img = *i;
imgs[file] = std::move(img);
return &imgs[file];
}
}
return nullptr;
}
2022-08-22 02:15:25 -04:00
void DrawTextureStencilAlpha(const bgfx::TextureHandle& tex, i32 pos_x, i32 pos_y, u32 w, u32 h) {
DrawTexture(tex, pos_x, pos_y, w, h, (BGFX_STATE_DEFAULT | BGFX_STATE_BLEND_ALPHA) & ~(BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS), a_program);
}
2022-08-22 02:15:25 -04:00
void RenderString(const NVL::String& s, u32& pos_x, u32& pos_y, u32 col, u32 style_flags) {
Font* f = &regular;
2022-08-22 02:15:25 -04:00
switch (style_flags) {
case TEXT_NONE: break;
case TEXT_BOLD: f = &bold; break;
2022-08-22 02:15:25 -04:00
case TEXT_ITALIC: break;
case TEXT_ITALIC | TEXT_BOLD: break;
}
FT_GlyphSlot& slot = f->face->glyph;
2022-08-18 12:17:43 -04:00
for (const auto& c : s) {
f->cache_char(c);
pos_x += slot->bitmap_left;
float ypos = pos_y - (slot->bitmap.rows - slot->bitmap_top);
2022-08-18 12:17:43 -04:00
if (slot->bitmap.width != 0) {
DrawTextureStencilAlpha((f->cache)[c], pos_x, ypos, slot->bitmap.width, slot->bitmap.rows);
}
pos_x += slot->advance.x >> 6;
pos_y += slot->advance.y >> 6;
2022-05-09 01:50:09 -04:00
}
}
2022-08-21 16:24:13 -04:00
2022-08-22 02:15:25 -04:00
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);
2022-08-21 16:24:13 -04:00
}
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);
2022-08-22 02:15:25 -04:00
std::vector<NVL::Environment::Markup> ms{};
for (const NVL::Environment::Variable& markup : std::get<std::vector<NVL::Environment::Variable>>(s[1].value)) {
2022-08-22 02:15:25 -04:00
ms.push_back(NVL::Environment::UnpackMarkupVariable(markup));
}
2022-08-21 16:24:13 -04:00
if (ms.empty()) {
2022-08-22 02:15:25 -04:00
RenderString(str, pos_x, pos_y, col, TEXT_NONE);
2022-08-21 16:24:13 -04:00
return;
}
if (ms[0].begin != 0)
2022-08-22 02:15:25 -04:00
RenderString(str.substr(0, ms[0].begin), pos_x, pos_y, col, TEXT_NONE);
2022-08-21 16:24:13 -04:00
// We assume markups are already sorted
for (size_t i = 0; i < ms.size(); i++) {
2022-08-22 02:15:25 -04:00
u32 style_flags = TEXT_NONE;
2022-08-21 16:24:13 -04:00
const NVL::String* ruby = nullptr;
2022-08-22 02:15:25 -04:00
2022-08-21 16:24:13 -04:00
for (const auto& e : ms[i].efs) {
2022-08-22 02:15:25 -04:00
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;
}
2022-08-21 16:24:13 -04:00
}
if (ruby != nullptr) {
u32 temp_x = pos_x, temp_y = pos_y + 20;
2022-08-22 02:15:25 -04:00
RenderString(*ruby, temp_x, temp_y, col, TEXT_NONE);
2022-08-21 16:24:13 -04:00
}
2022-08-22 02:15:25 -04:00
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);
2022-08-21 16:24:13 -04:00
}
if (ms.back().end != str.length())
2022-08-22 02:15:25 -04:00
RenderString(str.substr(ms.back().end), pos_x, pos_y, col, TEXT_NONE);
}
void DrawFunkyAhhShit() {
DrawTextureImage(bg, 0, -40);
2022-08-18 12:17:43 -04:00
}
2022-08-22 02:15:25 -04:00
}