mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-04-10 06:14:07 +08:00
feat: add font class and render font function
This commit is contained in:
@@ -16,6 +16,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
|||||||
|
|
||||||
|
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
|
find_package(Freetype REQUIRED)
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(EGL REQUIRED egl)
|
pkg_check_modules(EGL REQUIRED egl)
|
||||||
@@ -75,6 +76,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
src/texture_manager.cpp
|
src/texture_manager.cpp
|
||||||
src/tools/math_tools.cpp
|
src/tools/math_tools.cpp
|
||||||
src/tools/shader_tools.cpp
|
src/tools/shader_tools.cpp
|
||||||
|
src/tools/font.cpp
|
||||||
src/tools/log.cpp
|
src/tools/log.cpp
|
||||||
src/window.cpp
|
src/window.cpp
|
||||||
)
|
)
|
||||||
@@ -102,6 +104,7 @@ target_link_libraries(${PROJECT_NAME}
|
|||||||
glm::glm
|
glm::glm
|
||||||
OpenGL::GL
|
OpenGL::GL
|
||||||
soil2
|
soil2
|
||||||
|
Freetype::Freetype
|
||||||
)
|
)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
|||||||
BIN
assets/fonts/IBMPlexSans-Regular.ttf
Normal file
BIN
assets/fonts/IBMPlexSans-Regular.ttf
Normal file
Binary file not shown.
@@ -16,9 +16,11 @@ public:
|
|||||||
static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
|
static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
|
||||||
static void window_focus_callback(GLFWwindow* window, int focused);
|
static void window_focus_callback(GLFWwindow* window, int focused);
|
||||||
static void window_reshape_callback(GLFWwindow* window, int new_width, int new_height);
|
static void window_reshape_callback(GLFWwindow* window, int new_width, int new_height);
|
||||||
|
|
||||||
static int start_cubed_application(int argc, char** argv);
|
static int start_cubed_application(int argc, char** argv);
|
||||||
|
|
||||||
|
static float delte_time();
|
||||||
|
static float get_fps();
|
||||||
private:
|
private:
|
||||||
Camera m_camera;
|
Camera m_camera;
|
||||||
TextureManager m_texture_manager;
|
TextureManager m_texture_manager;
|
||||||
@@ -30,7 +32,12 @@ private:
|
|||||||
|
|
||||||
GLuint m_texture_array;
|
GLuint m_texture_array;
|
||||||
|
|
||||||
|
inline static double last_time = glfwGetTime();
|
||||||
|
inline static double current_time = glfwGetTime();
|
||||||
|
inline static double delta_time = 0.0f;
|
||||||
|
inline static double fps_time_count = 0.0f;
|
||||||
|
inline static int frame_count = 0;
|
||||||
|
inline static int fps;
|
||||||
|
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|||||||
@@ -30,11 +30,13 @@ private:
|
|||||||
GLuint m_proj_loc;
|
GLuint m_proj_loc;
|
||||||
|
|
||||||
GLuint m_sky_vbo;
|
GLuint m_sky_vbo;
|
||||||
|
GLuint m_text_vbo;
|
||||||
GLuint m_outline_indices_vbo;
|
GLuint m_outline_indices_vbo;
|
||||||
GLuint m_outline_vbo;
|
GLuint m_outline_vbo;
|
||||||
GLuint m_ui_vbo;
|
GLuint m_ui_vbo;
|
||||||
|
|
||||||
GLuint m_sky_program;
|
GLuint m_sky_program;
|
||||||
|
GLuint m_text_program;
|
||||||
GLuint m_outline_program;
|
GLuint m_outline_program;
|
||||||
GLuint m_ui_program;
|
GLuint m_ui_program;
|
||||||
GLuint m_world_program;
|
GLuint m_world_program;
|
||||||
@@ -47,5 +49,7 @@ private:
|
|||||||
std::vector<Vertex2D> m_ui;
|
std::vector<Vertex2D> m_ui;
|
||||||
void render_outline();
|
void render_outline();
|
||||||
void render_sky();
|
void render_sky();
|
||||||
|
void render_text();
|
||||||
void render_ui();
|
void render_ui();
|
||||||
|
|
||||||
};
|
};
|
||||||
35
include/Cubed/tools/font.hpp
Normal file
35
include/Cubed/tools/font.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
struct Character {
|
||||||
|
GLuint texture_id;
|
||||||
|
glm::ivec2 size;
|
||||||
|
glm::ivec2 bearing;
|
||||||
|
GLuint advance;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Font {
|
||||||
|
public:
|
||||||
|
Font();
|
||||||
|
~Font();
|
||||||
|
|
||||||
|
static void render_text(GLuint program, GLuint vbo, const std::string& text, float x, float y, float scale, const glm::vec3& color);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FT_Library m_ft;
|
||||||
|
FT_Face m_face;
|
||||||
|
|
||||||
|
std::unordered_map<char8_t, Character> m_characters;
|
||||||
|
|
||||||
|
void load_character(char8_t c);
|
||||||
|
void setup_font_character();
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
15
src/app.cpp
15
src/app.cpp
@@ -6,13 +6,6 @@
|
|||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
static double last_time = glfwGetTime();
|
|
||||||
static double current_time = glfwGetTime();
|
|
||||||
static double delta_time = 0.0f;
|
|
||||||
static double fps_time_count = 0.0f;
|
|
||||||
static int frame_count = 0;
|
|
||||||
static int fps;
|
|
||||||
|
|
||||||
App::App() {
|
App::App() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -164,3 +157,11 @@ int App::start_cubed_application(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float App::delte_time() {
|
||||||
|
return delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
float App::get_fps() {
|
||||||
|
return fps;
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
#include <Cubed/app.hpp>
|
||||||
#include <Cubed/camera.hpp>
|
#include <Cubed/camera.hpp>
|
||||||
#include <Cubed/config.hpp>
|
#include <Cubed/config.hpp>
|
||||||
#include <Cubed/gameplay/world.hpp>
|
#include <Cubed/gameplay/world.hpp>
|
||||||
#include <Cubed/texture_manager.hpp>
|
#include <Cubed/texture_manager.hpp>
|
||||||
#include <Cubed/renderer.hpp>
|
#include <Cubed/renderer.hpp>
|
||||||
#include <Cubed/tools/cubed_assert.hpp>
|
#include <Cubed/tools/cubed_assert.hpp>
|
||||||
|
#include <Cubed/tools/font.hpp>
|
||||||
#include <Cubed/tools/log.hpp>
|
#include <Cubed/tools/log.hpp>
|
||||||
#include <Cubed/tools/shader_tools.hpp>
|
#include <Cubed/tools/shader_tools.hpp>
|
||||||
|
|
||||||
@@ -23,6 +25,7 @@ Renderer::~Renderer() {
|
|||||||
glDeleteBuffers(1, &m_outline_indices_vbo);
|
glDeleteBuffers(1, &m_outline_indices_vbo);
|
||||||
glDeleteBuffers(1, &m_sky_vbo);
|
glDeleteBuffers(1, &m_sky_vbo);
|
||||||
glDeleteBuffers(1, &m_ui_vbo);
|
glDeleteBuffers(1, &m_ui_vbo);
|
||||||
|
glDeleteBuffers(1, &m_text_vbo);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
glDeleteVertexArrays(NUM_VAO, m_vao.data());
|
glDeleteVertexArrays(NUM_VAO, m_vao.data());
|
||||||
glDeleteProgram(m_world_program);
|
glDeleteProgram(m_world_program);
|
||||||
@@ -45,11 +48,15 @@ void Renderer::init() {
|
|||||||
m_outline_program = Shader::create_shader_program("shaders/outline_v_shader.glsl", "shaders/outline_f_shader.glsl");
|
m_outline_program = Shader::create_shader_program("shaders/outline_v_shader.glsl", "shaders/outline_f_shader.glsl");
|
||||||
m_sky_program = Shader::create_shader_program("shaders/sky_v_shader.glsl", "shaders/sky_f_shader.glsl");
|
m_sky_program = Shader::create_shader_program("shaders/sky_v_shader.glsl", "shaders/sky_f_shader.glsl");
|
||||||
m_ui_program = Shader::create_shader_program("shaders/ui_v_shader.glsl", "shaders/ui_f_shader.glsl");
|
m_ui_program = Shader::create_shader_program("shaders/ui_v_shader.glsl", "shaders/ui_f_shader.glsl");
|
||||||
|
m_text_program = Shader::create_shader_program("shaders/text_v_shader.glsl", "shaders/text_f_shader.glsl");
|
||||||
|
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glDepthFunc(GL_LEQUAL);
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
glEnable(GL_DEBUG_OUTPUT);
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* user_param) {
|
glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* user_param) {
|
||||||
@@ -57,7 +64,6 @@ void Renderer::init() {
|
|||||||
}, nullptr);
|
}, nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
m_vao.resize(NUM_VAO);
|
m_vao.resize(NUM_VAO);
|
||||||
glGenVertexArrays(NUM_VAO, m_vao.data());
|
glGenVertexArrays(NUM_VAO, m_vao.data());
|
||||||
glBindVertexArray(m_vao[0]);
|
glBindVertexArray(m_vao[0]);
|
||||||
@@ -91,6 +97,9 @@ void Renderer::init() {
|
|||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, m_ui.size() * sizeof(Vertex2D), m_ui.data(), GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, m_ui.size() * sizeof(Vertex2D), m_ui.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glGenBuffers(1, &m_text_vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, m_text_vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(float)* 6 * 4, NULL, GL_DYNAMIC_DRAW);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +127,8 @@ void Renderer::render() {
|
|||||||
render_outline();
|
render_outline();
|
||||||
|
|
||||||
render_ui();
|
render_ui();
|
||||||
|
|
||||||
|
render_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::render_outline() {
|
void Renderer::render_outline() {
|
||||||
@@ -170,6 +181,16 @@ void Renderer::render_sky() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::render_text() {
|
||||||
|
glUseProgram(m_text_program);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
m_proj_loc = glGetUniformLocation(m_text_program, "projection");
|
||||||
|
|
||||||
|
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj));
|
||||||
|
Font::render_text(m_text_program, m_text_vbo, std::string{"fps" + std::to_string(static_cast<int>(App::get_fps()))}, 0.0f, 50.0f, 1.0f, glm::vec3(1.0f, 1.0f, 1.0f));
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
void Renderer::render_ui() {
|
void Renderer::render_ui() {
|
||||||
glUseProgram(m_ui_program);
|
glUseProgram(m_ui_program);
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|||||||
12
src/shaders/text_f_shader.glsl
Normal file
12
src/shaders/text_f_shader.glsl
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
in vec2 texCoord;
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
layout (binding = 0) uniform sampler2D text;
|
||||||
|
uniform vec3 textColor;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
vec4 smapled = vec4(1.0, 1.0, 1.0, texture(text, texCoord).r);
|
||||||
|
color = vec4(textColor, 1.0) * smapled;
|
||||||
|
}
|
||||||
11
src/shaders/text_v_shader.glsl
Normal file
11
src/shaders/text_v_shader.glsl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 vertex;
|
||||||
|
out vec2 texCoord;
|
||||||
|
|
||||||
|
uniform mat4 projection;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);
|
||||||
|
texCoord = vec2(vertex.zw);
|
||||||
|
}
|
||||||
112
src/tools/font.cpp
Normal file
112
src/tools/font.cpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#include <Cubed/tools/font.hpp>
|
||||||
|
#include <Cubed/tools/log.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Font::Font() {
|
||||||
|
|
||||||
|
if (FT_Init_FreeType(&m_ft)) {
|
||||||
|
LOG::error("FREETYPE: Could not init FreeType Library");
|
||||||
|
}
|
||||||
|
if (FT_New_Face(m_ft, "assets/fonts/IBMPlexSans-Regular.ttf", 0, &m_face)) {
|
||||||
|
LOG::error("FREETYPE: Failed to load font");
|
||||||
|
}
|
||||||
|
|
||||||
|
FT_Set_Pixel_Sizes(m_face, 0, 48);
|
||||||
|
setup_font_character();
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::~Font() {
|
||||||
|
|
||||||
|
FT_Done_Face(m_face);
|
||||||
|
FT_Done_FreeType(m_ft);
|
||||||
|
for (const auto& [key, character] : m_characters) {
|
||||||
|
glDeleteTextures(1, &character.texture_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::load_character(char8_t c) {
|
||||||
|
if (FT_Load_Char(m_face, c, FT_LOAD_RENDER)) {
|
||||||
|
LOG::error("FREETYTPE: Failed to load Glyph");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GLuint texture;
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
glTexImage2D(
|
||||||
|
GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_RED,
|
||||||
|
m_face->glyph->bitmap.width,
|
||||||
|
m_face->glyph->bitmap.rows,
|
||||||
|
0,
|
||||||
|
GL_RED,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
m_face->glyph->bitmap.buffer
|
||||||
|
);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
Character character = {
|
||||||
|
texture,
|
||||||
|
glm::ivec2(m_face->glyph->bitmap.width, m_face->glyph->bitmap.rows),
|
||||||
|
glm::ivec2(m_face->glyph->bitmap_left, m_face->glyph->bitmap_top),
|
||||||
|
static_cast<GLuint>(m_face->glyph->advance.x)
|
||||||
|
};
|
||||||
|
|
||||||
|
m_characters.insert({c, std::move(character)});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::setup_font_character() {
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
for (char8_t c = 0; c < 128; c++) {
|
||||||
|
load_character(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Font::render_text(GLuint program, GLuint vbo, const std::string& text, float x, float y, float scale, const glm::vec3& color) {
|
||||||
|
static Font font;
|
||||||
|
glUseProgram(program);
|
||||||
|
glUniform3f(glGetUniformLocation(program, "textColor"), color.x, color.y, color.z);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
for (char8_t c : text) {
|
||||||
|
auto it = font.m_characters.find(c);
|
||||||
|
if (it == font.m_characters.end()) {
|
||||||
|
LOG::error("Can't find character {}", static_cast<char>(c));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Character& ch = it->second;
|
||||||
|
float xpos = x + ch.bearing.x * scale;
|
||||||
|
float ypos = y - ch.bearing.y * scale;
|
||||||
|
|
||||||
|
float w = ch.size.x * scale;
|
||||||
|
float h = ch.size.y * scale;
|
||||||
|
|
||||||
|
float vertices[6][4] {
|
||||||
|
{ xpos, ypos + h, 0.0, 1.0 },
|
||||||
|
{ xpos, ypos, 0.0, 0.0 },
|
||||||
|
{ xpos + w, ypos, 1.0, 0.0 },
|
||||||
|
|
||||||
|
{ xpos, ypos + h, 0.0, 1.0 },
|
||||||
|
{ xpos + w, ypos, 1.0, 0.0 },
|
||||||
|
{ xpos + w, ypos + h, 1.0, 1.0 }
|
||||||
|
};
|
||||||
|
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
||||||
|
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||||
|
x += (ch.advance >> 6) * scale;
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
}
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user