Work on text rendering. All just tinkering really
This commit is contained in:
parent
a8082b6885
commit
fa15721c94
@ -33,10 +33,13 @@ namespace egn{
|
||||
struct font_character{
|
||||
GLuint atlas_texture;
|
||||
math::vec2<size_t> atlas_offset;
|
||||
math::vec2<size_t> size;
|
||||
math::vec2<size_t> bearing;
|
||||
math::vec2<size_t> advance;
|
||||
math::vec2<int> size;
|
||||
math::vec2<int> bearing;
|
||||
math::vec2<int> advance;
|
||||
math::vec2<float> texture_coords[4];
|
||||
|
||||
public:
|
||||
float aspect_ratio(void)const;
|
||||
};
|
||||
|
||||
class font
|
||||
@ -44,20 +47,27 @@ namespace egn{
|
||||
private:
|
||||
std::optional<gfx::texture> m_atlas;
|
||||
std::map<int, font_character> m_atlas_data;
|
||||
size_t m_requested_width;
|
||||
size_t m_requested_height;
|
||||
|
||||
public:
|
||||
font(void) = default;
|
||||
font(gfx::texture&& tex, std::map<int, font_character>&& metadata);
|
||||
font(gfx::texture&& tex, std::map<int, font_character>&& metadata, size_t width, size_t height);
|
||||
~font(void) = default;
|
||||
|
||||
font_character& operator[](int character);
|
||||
const font_character& operator[](int character)const;
|
||||
|
||||
gfx::texture release_atlas(void);
|
||||
|
||||
gfx::texture& atlas(void);
|
||||
const gfx::texture& atlas(void)const;
|
||||
|
||||
std::map<int, font_character> release_metadata(void);
|
||||
|
||||
bool valid(void)const;
|
||||
|
||||
math::vec2<size_t> size(void)const;
|
||||
};
|
||||
|
||||
class font_generator
|
||||
|
||||
@ -46,6 +46,8 @@ namespace gfx{
|
||||
|
||||
GLuint raw()const;
|
||||
|
||||
//Set the instanced divisor of this binding point. Used with instanced rendering.
|
||||
void set_instance_divisor(GLuint binding, GLuint divisor);
|
||||
void bind_buffer(const vbo& buffer, GLuint bind_point, GLuint offset, GLuint stride);
|
||||
void bind_element_buffer(const vbo& buffer);
|
||||
|
||||
@ -82,8 +84,6 @@ namespace gfx{
|
||||
|
||||
//Associate this attribute's location with the given buffer binding location
|
||||
void associate_with(GLuint buffer_binding);
|
||||
//Set the instanced divisor of this attribute. Used with instanced rendering.
|
||||
void set_instance_divisor(GLuint divisor);
|
||||
//setters for use in array mode. Same arguments as glVertexAttribPointer but these take arguments
|
||||
//as object counts rather than bytes. a valid vbo must be bound to buffers::array_buffer before these are called.
|
||||
void set_float_array(GLint size, GLsizei offset);
|
||||
|
||||
@ -113,6 +113,8 @@ namespace math{
|
||||
|
||||
template<size_t TR, size_t TC, std::enable_if_t<TR <= R && TC <= C,int> = 0>
|
||||
constexpr matrix(const matrix_base<value_type,TR,TC>& other);
|
||||
template<typename U>
|
||||
constexpr matrix(const matrix<U,R,C>& other);
|
||||
constexpr matrix(const matrix&) = default;
|
||||
constexpr matrix(matrix&&) = default;
|
||||
~matrix() = default;
|
||||
@ -146,6 +148,8 @@ namespace math{
|
||||
constexpr explicit matrix(detail::id_initialize_t);
|
||||
template<size_t TR, size_t TC, std::enable_if_t<TR <= R && TC <= R,int> = 0>
|
||||
constexpr matrix(const matrix_base<value_type,TR,TC>& other);
|
||||
template<typename U>
|
||||
constexpr matrix(const matrix<U,R,R>& other);
|
||||
~matrix() = default;
|
||||
|
||||
//Assignement
|
||||
|
||||
@ -145,6 +145,16 @@ namespace math{
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename T, size_t R, size_t C>
|
||||
template<typename U>
|
||||
constexpr matrix<T,R,C>::matrix(const matrix<U,R,C>& other){
|
||||
for(size_type i = 0;i < C;++i){
|
||||
for(size_type j = 0;j < R;++j){
|
||||
get(i, j) = other.get(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t R, size_t C>
|
||||
template<typename U>
|
||||
constexpr matrix<T,R,C>& matrix<T,R,C>::operator=(const matrix<U,R,C>& m){
|
||||
@ -166,6 +176,16 @@ namespace math{
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename T, size_t R>
|
||||
template<typename U>
|
||||
constexpr matrix<T,R,R>::matrix(const matrix<U,R,R>& other){
|
||||
for(size_type i = 0;i < R;++i){
|
||||
for(size_type j = 0;j < R;++j){
|
||||
get(i, j) = other.get(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T, size_t R>
|
||||
template<typename U>
|
||||
|
||||
@ -43,6 +43,8 @@ namespace math{
|
||||
|
||||
template<size_t TR,typename... Args,std::enable_if_t<TR <= R && (std::is_convertible_v<Args,T> && ...),int> = 0>
|
||||
constexpr vector(const vector<T,TR>& other, Args&&... args);
|
||||
template<typename U>
|
||||
constexpr vector(const vector<U,R>& other);
|
||||
constexpr vector(const vector&) = default;
|
||||
constexpr vector(vector&&) = default;
|
||||
~vector() = default;
|
||||
|
||||
@ -36,6 +36,13 @@ namespace math{
|
||||
}
|
||||
}
|
||||
template<typename T, size_t R>
|
||||
template<typename U>
|
||||
constexpr vector<T,R>::vector(const vector<U,R>& other){
|
||||
for(size_type i = 0;i < R;++i){
|
||||
this->m_data[i] = other[i];
|
||||
}
|
||||
}
|
||||
template<typename T, size_t R>
|
||||
template<typename U, typename... Args>
|
||||
constexpr void vector<T,R>::assign_(size_type offset, U&& u, Args&&... args){
|
||||
this->m_data[offset] = std::forward<U>(u);
|
||||
|
||||
173
include/ttt/font_renderer.hpp
Normal file
173
include/ttt/font_renderer.hpp
Normal file
@ -0,0 +1,173 @@
|
||||
/**
|
||||
This file is a part of our_dick
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OUR_DICK_FONT_RENDERER_HPP
|
||||
#define OUR_DICK_FONT_RENDERER_HPP
|
||||
|
||||
#include "graphics/vbo.hpp"
|
||||
#include "graphics/vao.hpp"
|
||||
#include "graphics/texture.hpp"
|
||||
#include "math/math.hpp"
|
||||
#include "graphics/shader_program.hpp"
|
||||
#include "graphics/resource_manager.hpp"
|
||||
#include "scene.hpp"
|
||||
|
||||
#include "engine/font.hpp"
|
||||
|
||||
static constexpr float pixels_to_screen_space(int dim, int window_dim){
|
||||
const float ratio = 2.0f / window_dim;
|
||||
return dim * ratio;
|
||||
}
|
||||
static constexpr float screen_space_to_pixels(int dim, int window_dim){
|
||||
const float ratio = window_dim / 2.0f;
|
||||
return dim * ratio;
|
||||
}
|
||||
static constexpr float font_scale_ratio(int height, int target_height){
|
||||
return ((float)target_height) / height;
|
||||
}
|
||||
static constexpr float font_aspect_ratio(const egn::font_character& ch){
|
||||
return ((float)ch.size.x()) / ch.size.y();
|
||||
}
|
||||
|
||||
|
||||
class text
|
||||
{
|
||||
private:
|
||||
static constexpr int s_size_per_char = sizeof(GLfloat) * 13;
|
||||
private:
|
||||
egn::font m_font;
|
||||
std::string m_text;
|
||||
gfx::vbo m_vbo;
|
||||
gfx::vao m_vao;
|
||||
|
||||
float m_scale;
|
||||
|
||||
public:
|
||||
text(egn::font& font, const char* string, int target_px, float target_x, float target_y, int screen_width, int screen_height):
|
||||
m_font(font),
|
||||
m_text(string),
|
||||
m_vbo(s_size_per_char * strlen(string), gfx::vbo::usage::DYNAMIC_DRAW)
|
||||
{
|
||||
constexpr float tmp_color[] = {1,1,1,1};
|
||||
|
||||
m_scale = font_scale_ratio(m_font.size().y(), target_px);
|
||||
|
||||
math::vec3f target_pos = {target_x, target_y, -1};
|
||||
size_t offset = 0;
|
||||
for(int i = 0;m_text[i] != 0;++i){
|
||||
const auto ch = m_font[m_text[i]];
|
||||
const math::vec2<int> letter_size = ch.size * m_scale;
|
||||
const math::vec2f letter_screen_size = {pixels_to_screen_space(letter_size.x(), screen_width), pixels_to_screen_space(letter_size.y(), screen_height)};
|
||||
const math::vec3f position = {target_pos.x() + pixels_to_screen_space(ch.bearing.x() * m_scale, screen_width),
|
||||
target_pos.y() - (letter_screen_size.y() - pixels_to_screen_space(ch.bearing.y() * m_scale, screen_height)),
|
||||
target_pos.z()};
|
||||
|
||||
|
||||
m_vbo.buffer(ch.texture_coords[1], sizeof(GLfloat) * 2, offset);
|
||||
offset += sizeof(GLfloat) * 2;
|
||||
m_vbo.buffer(ch.texture_coords[2], sizeof(GLfloat) * 2, offset);
|
||||
offset += sizeof(GLfloat) * 2;
|
||||
m_vbo.buffer(tmp_color, sizeof(GLfloat) * 4, offset);
|
||||
offset += sizeof(GLfloat) * 4;
|
||||
m_vbo.buffer(position, sizeof(GLfloat) * 3, offset);
|
||||
offset += sizeof(GLfloat) * 3;
|
||||
m_vbo.buffer(letter_screen_size, sizeof(GLfloat) * 2, offset);
|
||||
offset += sizeof(GLfloat) * 2;
|
||||
|
||||
target_pos.x() += pixels_to_screen_space(m_scale * ch.advance.x(), screen_width);
|
||||
}
|
||||
|
||||
//attach a vbo to the vao and assign attribute specs
|
||||
m_vao.bind_buffer(m_vbo, 0, 0, sizeof(GLfloat) * 13);
|
||||
|
||||
auto attrib = m_vao.get_attribute(0);
|
||||
attrib.set_float_array(4, 0);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(1);
|
||||
attrib.set_float_array(4, sizeof(GLfloat) * 4);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(2);
|
||||
attrib.set_float_array(3, sizeof(GLfloat) * 8);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(3);
|
||||
attrib.set_float_array(2, sizeof(GLfloat) * 11);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
}
|
||||
void set_position(float x, float y, int screen_width, int screen_height){
|
||||
size_t offset = sizeof(GLfloat) * 8;
|
||||
math::vec3f target_pos = {x, y, -1};
|
||||
for(size_t i = 0;m_text[i] != 0;++i){
|
||||
const auto ch = m_font[m_text[i]];
|
||||
const math::vec2i letter_size = ch.size * m_scale;
|
||||
const math::vec2f letter_screen_size = {pixels_to_screen_space(letter_size.x(), screen_width), pixels_to_screen_space(letter_size.y(), screen_height)};
|
||||
const math::vec3f position = {target_pos.x() + pixels_to_screen_space(ch.bearing.x() * m_scale, screen_width),
|
||||
target_pos.y() - (letter_screen_size.y() - pixels_to_screen_space(ch.bearing.y() * m_scale, screen_height)),
|
||||
target_pos.z()};
|
||||
|
||||
m_vbo.buffer(position, sizeof(GLfloat) * 3, offset);
|
||||
offset += sizeof(GLfloat) * 3 + sizeof(GLfloat) * 8;
|
||||
target_pos.x() += pixels_to_screen_space(m_scale * ch.advance.x(), screen_width);
|
||||
}
|
||||
}
|
||||
|
||||
void render(gfx::shader_program& sh){
|
||||
m_vao.bind();
|
||||
sh.set_uniform("texture1", m_font.atlas());
|
||||
glDrawArrays(GL_POINTS, 0, m_text.length());
|
||||
}
|
||||
};
|
||||
|
||||
class font_renderer
|
||||
{
|
||||
private:
|
||||
struct vertex{
|
||||
math::vec2<GLfloat> pos;
|
||||
math::vec2<GLfloat> tex_coord;
|
||||
};
|
||||
static constexpr vertex s_vertices[] = {
|
||||
{{-1.0f, 1.0f}, {0.0f, 1.0f}},
|
||||
{{-1.0f, -1.0f}, {0.0f, 0.0f}},
|
||||
{{ 1.0f, -1.0f}, {1.0f, 0.0f}},
|
||||
{{ 1.0f, -1.0f}, {1.0f, 0.0f}},
|
||||
{{ 1.0f, 1.0f}, {1.0f, 1.0f}},
|
||||
{{-1.0f, 1.0f}, {0.0f, 1.0f}}
|
||||
};
|
||||
private:
|
||||
gfx::shader_program* m_font_shader;
|
||||
gfx::vbo m_vbo;
|
||||
gfx::vao m_vao;
|
||||
gfx::texture* m_texture;
|
||||
std::unique_ptr<text> m_text;
|
||||
int m_screen_width, m_screen_height;
|
||||
|
||||
public:
|
||||
font_renderer(gfx::resource_manager& res, int width, int height, gfx::texture& base_tex);
|
||||
|
||||
void render(scene&);
|
||||
void resize_viewport(int width, int height);
|
||||
};
|
||||
|
||||
#endif
|
||||
111
include/ttt/font_shader.hpp
Normal file
111
include/ttt/font_shader.hpp
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
This file is a part of our_dick
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OUR_DICK_FONT_SHADER_HPP
|
||||
#define OUR_DICK_FONT_SHADER_HPP
|
||||
|
||||
namespace font_shader{
|
||||
static constexpr char vertex_shader_text[] =
|
||||
R"glsl(
|
||||
#version 430 core
|
||||
layout (location = 0) in vec4 tex_coords;
|
||||
layout (location = 1) in vec4 color;
|
||||
layout (location = 2) in vec3 position;
|
||||
layout (location = 3) in vec2 size;
|
||||
|
||||
out VS_OUT{
|
||||
vec4 tex_coords;
|
||||
vec4 color;
|
||||
vec3 position;
|
||||
vec2 size;
|
||||
}vs_out;
|
||||
|
||||
void main(){
|
||||
vs_out.tex_coords = tex_coords;
|
||||
vs_out.color = color;
|
||||
vs_out.position = position;
|
||||
vs_out.size = size;
|
||||
}
|
||||
)glsl";
|
||||
static constexpr char geometry_shader_text[] =
|
||||
R"glsl(
|
||||
#version 430 core
|
||||
layout (points) in;
|
||||
layout (triangle_strip, max_vertices = 4) out;
|
||||
|
||||
in VS_OUT{
|
||||
vec4 tex_coords;
|
||||
vec4 color;
|
||||
vec3 position;
|
||||
vec2 size;
|
||||
}gs_in[];
|
||||
|
||||
out GS_OUT{
|
||||
vec2 tex_coords;
|
||||
flat vec4 color;
|
||||
}gs_out;
|
||||
void main(){
|
||||
//texture coordinates need done botleft->topleft->botright->topright
|
||||
//whereas the position goes topleft->botleft->topright->botright
|
||||
//because freetype generates bitmaps with inverse y axis to opengl
|
||||
|
||||
gl_Position = vec4(gs_in[0].position.x, gs_in[0].position.y + gs_in[0].size.y, gs_in[0].position.z, 1.0);
|
||||
gs_out.tex_coords = gs_in[0].tex_coords.xy;
|
||||
gs_out.color = gs_in[0].color;
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = vec4(gs_in[0].position, 1.0);
|
||||
gs_out.tex_coords = gs_in[0].tex_coords.xw;
|
||||
gs_out.color = gs_in[0].color;
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = vec4(gs_in[0].position.x + gs_in[0].size.x, gs_in[0].position.y + gs_in[0].size.y, gs_in[0].position.z, 1.0);
|
||||
gs_out.tex_coords = gs_in[0].tex_coords.zy;
|
||||
gs_out.color = gs_in[0].color;
|
||||
EmitVertex();
|
||||
|
||||
gl_Position = vec4(gs_in[0].position.x + gs_in[0].size.x, gs_in[0].position.y, gs_in[0].position.z, 1.0);
|
||||
gs_out.tex_coords = gs_in[0].tex_coords.zw;
|
||||
gs_out.color = gs_in[0].color;
|
||||
EmitVertex();
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
)glsl";
|
||||
static constexpr char fragment_shader_text[] =
|
||||
R"glsl(
|
||||
#version 430 core
|
||||
out vec4 frag_color;
|
||||
|
||||
in GS_OUT{
|
||||
vec2 tex_coords;
|
||||
flat vec4 color;
|
||||
}fs_in;
|
||||
|
||||
uniform sampler2D texture1;
|
||||
|
||||
void main(){
|
||||
vec4 s = texture(texture1, fs_in.tex_coords);
|
||||
float alpha = (s.r + s.g + s.b) / 3;
|
||||
|
||||
frag_color = mix(s, fs_in.color, alpha);
|
||||
}
|
||||
)glsl";
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -27,6 +27,119 @@
|
||||
#include "graphics/resource_manager.hpp"
|
||||
#include "scene.hpp"
|
||||
|
||||
#include "engine/font.hpp"
|
||||
|
||||
static constexpr float pixels_to_screen_space(int dim, int window_dim){
|
||||
const float ratio = 2.0f / window_dim;
|
||||
return dim * ratio;
|
||||
}
|
||||
static constexpr float screen_space_to_pixels(int dim, int window_dim){
|
||||
const float ratio = window_dim / 2.0f;
|
||||
return dim * ratio;
|
||||
}
|
||||
static constexpr float font_scale_ratio(int height, int target_height){
|
||||
return ((float)target_height) / height;
|
||||
}
|
||||
static constexpr float font_aspect_ratio(const egn::font_character& ch){
|
||||
return ((float)ch.size.x()) / ch.size.y();
|
||||
}
|
||||
|
||||
|
||||
class text
|
||||
{
|
||||
private:
|
||||
static constexpr int s_size_per_char = sizeof(GLfloat) * 13;
|
||||
private:
|
||||
egn::font m_font;
|
||||
std::string m_text;
|
||||
gfx::vbo m_vbo;
|
||||
gfx::vao m_vao;
|
||||
|
||||
float m_scale;
|
||||
|
||||
public:
|
||||
text(egn::font& font, const char* string, int target_px, float target_x, float target_y, int screen_width, int screen_height):
|
||||
m_font(font),
|
||||
m_text(string),
|
||||
m_vbo(s_size_per_char * strlen(string), gfx::vbo::usage::DYNAMIC_DRAW)
|
||||
{
|
||||
constexpr float tmp_color[] = {1,1,1,1};
|
||||
|
||||
m_scale = font_scale_ratio(m_font.size().y(), target_px);
|
||||
|
||||
math::vec3f target_pos = {target_x, target_y, -1};
|
||||
size_t offset = 0;
|
||||
for(int i = 0;m_text[i] != 0;++i){
|
||||
const auto ch = m_font[m_text[i]];
|
||||
const math::vec2<int> letter_size = ch.size * m_scale;
|
||||
const math::vec2f letter_screen_size = {pixels_to_screen_space(letter_size.x(), screen_width), pixels_to_screen_space(letter_size.y(), screen_height)};
|
||||
const math::vec3f position = {target_pos.x() + pixels_to_screen_space(ch.bearing.x() * m_scale, screen_width),
|
||||
target_pos.y() - (letter_screen_size.y() - pixels_to_screen_space(ch.bearing.y() * m_scale, screen_height)),
|
||||
target_pos.z()};
|
||||
|
||||
|
||||
m_vbo.buffer(ch.texture_coords[1], sizeof(GLfloat) * 2, offset);
|
||||
offset += sizeof(GLfloat) * 2;
|
||||
m_vbo.buffer(ch.texture_coords[2], sizeof(GLfloat) * 2, offset);
|
||||
offset += sizeof(GLfloat) * 2;
|
||||
m_vbo.buffer(tmp_color, sizeof(GLfloat) * 4, offset);
|
||||
offset += sizeof(GLfloat) * 4;
|
||||
m_vbo.buffer(position, sizeof(GLfloat) * 3, offset);
|
||||
offset += sizeof(GLfloat) * 3;
|
||||
m_vbo.buffer(letter_screen_size, sizeof(GLfloat) * 2, offset);
|
||||
offset += sizeof(GLfloat) * 2;
|
||||
|
||||
target_pos.x() += pixels_to_screen_space(m_scale * ch.advance.x(), screen_width);
|
||||
}
|
||||
|
||||
//attach a vbo to the vao and assign attribute specs
|
||||
m_vao.bind_buffer(m_vbo, 0, 0, sizeof(GLfloat) * 13);
|
||||
|
||||
auto attrib = m_vao.get_attribute(0);
|
||||
attrib.set_float_array(4, 0);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(1);
|
||||
attrib.set_float_array(4, sizeof(GLfloat) * 4);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(2);
|
||||
attrib.set_float_array(3, sizeof(GLfloat) * 8);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(3);
|
||||
attrib.set_float_array(2, sizeof(GLfloat) * 11);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
}
|
||||
void set_position(float x, float y, int screen_width, int screen_height){
|
||||
size_t offset = sizeof(GLfloat) * 8;
|
||||
math::vec3f target_pos = {x, y, -1};
|
||||
for(size_t i = 0;m_text[i] != 0;++i){
|
||||
const auto ch = m_font[m_text[i]];
|
||||
const math::vec2i letter_size = ch.size * m_scale;
|
||||
const math::vec2f letter_screen_size = {pixels_to_screen_space(letter_size.x(), screen_width), pixels_to_screen_space(letter_size.y(), screen_height)};
|
||||
const math::vec3f position = {target_pos.x() + pixels_to_screen_space(ch.bearing.x() * m_scale, screen_width),
|
||||
target_pos.y() - (letter_screen_size.y() - pixels_to_screen_space(ch.bearing.y() * m_scale, screen_height)),
|
||||
target_pos.z()};
|
||||
|
||||
m_vbo.buffer(position, sizeof(GLfloat) * 3, offset);
|
||||
offset += sizeof(GLfloat) * 3 + sizeof(GLfloat) * 8;
|
||||
target_pos.x() += pixels_to_screen_space(m_scale * ch.advance.x(), screen_width);
|
||||
}
|
||||
}
|
||||
|
||||
void render(gfx::shader_program& sh){
|
||||
m_vao.bind();
|
||||
sh.set_uniform("texture1", m_font.atlas());
|
||||
glDrawArrays(GL_POINTS, 0, m_text.length());
|
||||
}
|
||||
};
|
||||
|
||||
class screen_renderer
|
||||
{
|
||||
private:
|
||||
|
||||
@ -20,12 +20,20 @@
|
||||
#include "math/vec.hpp"
|
||||
#include <cmath> //sqrt, round
|
||||
#include <utility> //move
|
||||
#include <memory>
|
||||
|
||||
namespace egn{
|
||||
|
||||
font::font(gfx::texture&& tex, std::map<int, font_character>&& metadata):
|
||||
float font_character::aspect_ratio(void)const{
|
||||
return ((float)size.x()) / size.y();
|
||||
}
|
||||
|
||||
|
||||
font::font(gfx::texture&& tex, std::map<int, font_character>&& metadata, size_t width, size_t height):
|
||||
m_atlas(std::move(tex)),
|
||||
m_atlas_data(std::move(metadata)){}
|
||||
m_atlas_data(std::move(metadata)),
|
||||
m_requested_width(width),
|
||||
m_requested_height(height){}
|
||||
font_character& font::operator[](int character){
|
||||
return m_atlas_data[character];
|
||||
}
|
||||
@ -35,12 +43,21 @@ namespace egn{
|
||||
gfx::texture font::release_atlas(void){
|
||||
return gfx::texture(std::move(*m_atlas));
|
||||
}
|
||||
gfx::texture& font::atlas(void){
|
||||
return *m_atlas;
|
||||
}
|
||||
const gfx::texture& font::atlas(void)const{
|
||||
return *m_atlas;
|
||||
}
|
||||
std::map<int, font_character> font::release_metadata(void){
|
||||
return std::map<int, font_character>(std::move(m_atlas_data));
|
||||
}
|
||||
bool font::valid(void)const{
|
||||
return m_atlas.has_value();
|
||||
}
|
||||
math::vec2<size_t> font::size(void)const{
|
||||
return {m_requested_width, m_requested_height};
|
||||
}
|
||||
|
||||
font_generator::font_generator(const char* file){
|
||||
initialize_freetype_();
|
||||
@ -62,11 +79,11 @@ namespace egn{
|
||||
size_t avg_width = 0;
|
||||
size_t avg_height = 0;
|
||||
for(int i = 0;i < count;++i){
|
||||
if(FT_Load_Char(face, i + start_codepoint, FT_LOAD_RENDER)){
|
||||
if(FT_Load_Char(face, i + start_codepoint, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD)){
|
||||
debug_print_error("Failed to load character '%c' from font '%s'\n", i, face->family_name);
|
||||
continue;
|
||||
}
|
||||
max_width = std::max(max_width, size_t{face->glyph->bitmap.rows});
|
||||
max_width = std::max(max_width, size_t{face->glyph->bitmap.width});
|
||||
max_height = std::max(max_height, size_t{face->glyph->bitmap.rows});
|
||||
avg_width += face->glyph->bitmap.width;
|
||||
avg_height += face->glyph->bitmap.rows;
|
||||
@ -77,7 +94,7 @@ namespace egn{
|
||||
size_t est_height = (max_height * (std::round(count / 20) + 1));
|
||||
|
||||
|
||||
return {est_width, est_height, max_width, max_height};
|
||||
return {est_width / 3, est_height, max_width / 3, max_height};
|
||||
}
|
||||
|
||||
font font_generator::generate_atlas(int start_codepoint, int count, size_t glyph_width, size_t glyph_height){
|
||||
@ -85,46 +102,57 @@ namespace egn{
|
||||
|
||||
atlas_info ai = get_atlas_size_(m_face, start_codepoint, count);
|
||||
|
||||
gfx::texture atlas(GL_RED, ai.width, ai.height, GL_UNSIGNED_BYTE, false);
|
||||
gfx::texture atlas(GL_RGB, ai.width, ai.height, GL_UNSIGNED_BYTE, false);
|
||||
std::map<int, font_character> atlas_metadata;
|
||||
|
||||
math::vec2<size_t> atlas_pos = {0, 0};
|
||||
|
||||
std::unique_ptr<unsigned char[]> dest_data(new unsigned char[ai.max_glyph_height * ai.max_glyph_width * 3]);
|
||||
for(int i = start_codepoint;i < start_codepoint + count;++i){
|
||||
if(FT_Load_Char(m_face, i, FT_LOAD_RENDER)){
|
||||
if(FT_Load_Char(m_face, i, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD)){
|
||||
debug_print_error("Failed to load character '%c', from font '%s'\n", i, m_face->family_name);
|
||||
continue;
|
||||
}
|
||||
auto& bitmap = m_face->glyph->bitmap;
|
||||
const math::vec2<size_t> bitmap_size = {bitmap.width, bitmap.rows};
|
||||
|
||||
const math::vec2<size_t> dest_size = {bitmap.width / 3, bitmap.rows};
|
||||
|
||||
const float ratio_w = 1.0f / ai.width;
|
||||
const float ratio_h = 1.0f / ai.height;
|
||||
const float left = ratio_w * atlas_pos.x();
|
||||
const float right = ratio_w * (atlas_pos.x() + bitmap_size.x());
|
||||
const float right = ratio_w * (atlas_pos.x() + dest_size.x());
|
||||
const float bottom = ratio_h * atlas_pos.y();
|
||||
const float top = ratio_h * (atlas_pos.y() + bitmap_size.y());
|
||||
const float top = ratio_h * (atlas_pos.y() + dest_size.y());
|
||||
|
||||
atlas_metadata.emplace(i, font_character{atlas.raw(),
|
||||
font_character character{atlas.raw(),
|
||||
atlas_pos,
|
||||
bitmap_size,
|
||||
dest_size,
|
||||
{m_face->glyph->bitmap_left, m_face->glyph->bitmap_top},
|
||||
{m_face->glyph->advance.x, m_face->glyph->advance.y},
|
||||
{{left, top}, {left, bottom}, {right, top}, {right, bottom}}});
|
||||
if(atlas_pos.x() + bitmap_size.x() > ai.width){
|
||||
{m_face->glyph->advance.x >> 6, m_face->glyph->advance.y >> 6}, //convert from 1/64th pixels to pixels
|
||||
{{left, top}, {left, bottom}, {right, top}, {right, bottom}}};
|
||||
atlas_metadata.emplace(i, character);
|
||||
if(atlas_pos.x() + dest_size.x() > ai.width){
|
||||
atlas_pos.x() = 0;
|
||||
atlas_pos.y() += ai.max_glyph_height;
|
||||
}
|
||||
atlas.set_subimage(bitmap.buffer, GL_RED, GL_UNSIGNED_BYTE, atlas_pos.x(), atlas_pos.y(), bitmap_size.x(), bitmap_size.y());
|
||||
atlas_pos.x() += bitmap_size.x();
|
||||
|
||||
unsigned char* dest = dest_data.get();
|
||||
unsigned char* src = bitmap.buffer;
|
||||
for(int j = 0;j < bitmap.rows;++j){
|
||||
memcpy(dest, src, bitmap.width);
|
||||
dest += bitmap.width;
|
||||
src += bitmap.pitch;
|
||||
}
|
||||
atlas.set_subimage(dest_data.get(), GL_RGB, GL_UNSIGNED_BYTE, atlas_pos.x(), atlas_pos.y(), dest_size.x(), dest_size.y());
|
||||
atlas_pos.x() += dest_size.x();
|
||||
}
|
||||
return {std::move(atlas), std::move(atlas_metadata)};
|
||||
atlas.generate_mipmap();
|
||||
atlas.set_wrap_mode(gfx::texture::wrap::CLAMP_EDGE);
|
||||
return {std::move(atlas), std::move(atlas_metadata), glyph_width, glyph_height};
|
||||
}
|
||||
|
||||
std::optional<gfx::texture> font_generator::generate_glyph(char character, int width, int height){
|
||||
FT_Set_Pixel_Sizes(m_face, width, height);
|
||||
if(FT_Load_Char(m_face, character, FT_LOAD_RENDER)){
|
||||
if(FT_Load_Char(m_face, character, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD)){
|
||||
debug_print_error("Failed to load character '%c', from font '%s'\n", character, m_face->family_name);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -31,9 +31,6 @@ namespace gfx{
|
||||
glVertexArrayAttribBinding(m_vao, m_index, buffer_binding);
|
||||
}
|
||||
|
||||
void vertex_attribute::set_instance_divisor(GLuint divisor){
|
||||
glVertexArrayBindingDivisor(m_vao, m_index, divisor);
|
||||
}
|
||||
void vertex_attribute::set_float_array(GLint size, GLsizei offset){
|
||||
glVertexArrayAttribFormat(m_vao, m_index, size, GL_FLOAT, GL_FALSE, offset);
|
||||
}
|
||||
@ -80,6 +77,9 @@ namespace gfx{
|
||||
void vao::bind_buffer(const vbo& buffer, GLuint bind_point, GLuint offset, GLuint stride){
|
||||
glVertexArrayVertexBuffer(m_buffer, bind_point, buffer.raw(), offset, stride);
|
||||
}
|
||||
void vao::set_instance_divisor(GLuint binding, GLuint divisor){
|
||||
glVertexArrayBindingDivisor(m_buffer, binding, divisor);
|
||||
}
|
||||
void vao::bind_element_buffer(const vbo& buffer){
|
||||
glVertexArrayElementBuffer(m_buffer, buffer.raw());
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ namespace gfx{
|
||||
bool vbo::buffer(const void* data, size_t datasize, size_t start){
|
||||
const size_t capacity = get_cap();
|
||||
const size_t space_from_end = capacity - start;
|
||||
if(datasize > space_from_end){
|
||||
if(start > capacity || datasize > space_from_end){
|
||||
const size_t difference = datasize - space_from_end;
|
||||
if(!resize(rexy::max(difference, capacity * 2))){
|
||||
return false;
|
||||
|
||||
72
src/ttt/font_renderer.cpp
Normal file
72
src/ttt/font_renderer.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
This file is a part of our_dick
|
||||
Copyright (C) 2022 rexy712
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ttt/font_renderer.hpp"
|
||||
|
||||
#include "graphics/shader.hpp"
|
||||
#include "ttt/font_shader.hpp"
|
||||
#include "util/deferred.hpp"
|
||||
|
||||
#include "engine/font.hpp"
|
||||
|
||||
font_renderer::font_renderer(gfx::resource_manager& res, int width, int height, gfx::texture& base_tex):
|
||||
m_vbo(s_vertices, sizeof(s_vertices), gfx::vbo::usage::DYNAMIC_DRAW),
|
||||
m_texture(&base_tex),
|
||||
m_text(nullptr),
|
||||
m_screen_width(width), m_screen_height(height)
|
||||
{
|
||||
|
||||
m_font_shader = res.emplace_shader("font_shader", util::make_deferred<gfx::shader>(font_shader::vertex_shader_text, gfx::shader::type::VERTEX),
|
||||
util::make_deferred<gfx::shader>(font_shader::geometry_shader_text, gfx::shader::type::GEOMETRY),
|
||||
util::make_deferred<gfx::shader>(font_shader::fragment_shader_text, gfx::shader::type::FRAGMENT)).first;
|
||||
if(m_font_shader->has_link_error()){
|
||||
debug_print_error("%s\n", m_font_shader->get_error().c_str());
|
||||
}
|
||||
|
||||
constexpr int target_pixel = 256;
|
||||
|
||||
egn::font_generator f("assets/Woodwarrior-Regular.otf");
|
||||
auto font = f.generate_atlas('a', 26, 0, target_pixel);
|
||||
|
||||
m_text.reset(new text(font, "pause", target_pixel, -0.8, -0.8, m_screen_width, m_screen_height));
|
||||
|
||||
resize_viewport(width, height);
|
||||
}
|
||||
|
||||
void font_renderer::render(scene&){
|
||||
//Clear the global state to that which this renderer prefers
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glViewport(0, 0, m_screen_width, m_screen_height);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
//Apply our shader and vao
|
||||
m_font_shader->use();
|
||||
// m_font_shader->set_uniform("texture1", *m_texture, 0);
|
||||
// m_vao.bind();
|
||||
m_text->render(*m_font_shader);
|
||||
|
||||
//Draw
|
||||
//glDrawArrays(GL_POINTS, 0, 5);
|
||||
}
|
||||
|
||||
void font_renderer::resize_viewport(int width, int height){
|
||||
m_screen_width = width;
|
||||
m_screen_height = height;
|
||||
}
|
||||
@ -25,34 +25,21 @@
|
||||
#include <utility> //piecewise_construct
|
||||
|
||||
#include "engine/font.hpp"
|
||||
#include "ttt/font_shader.hpp"
|
||||
#include "math/debug.hpp"
|
||||
|
||||
screen_renderer::screen_renderer(gfx::resource_manager& res, int width, int height, gfx::texture& base_tex):
|
||||
m_vbo(s_vertices, sizeof(s_vertices), gfx::vbo::usage::STATIC_DRAW),
|
||||
m_vbo(s_vertices, sizeof(s_vertices), gfx::vbo::usage::DYNAMIC_DRAW),
|
||||
m_texture(&base_tex),
|
||||
m_screen_width(width), m_screen_height(height)
|
||||
{
|
||||
|
||||
m_screen_shader = res.emplace_shader("screen_shader", util::make_deferred<gfx::shader>(screen_shader::vertex_shader_text, gfx::shader::type::VERTEX),
|
||||
util::make_deferred<gfx::shader>(screen_shader::fragment_shader_text, gfx::shader::type::FRAGMENT)).first;
|
||||
|
||||
/* TODO TEMP
|
||||
egn::font_generator f("assets/Butler_Regular.otf");
|
||||
auto font = f.generate_atlas('A', 26, 0, 1024);
|
||||
m_texture = res.emplace_texture("font_atlas_test", font.release_atlas()).first;//util::deferred_function(fn, 256)).first;
|
||||
m_texture->set_filter(gfx::texture::minfilter::NEAREST, gfx::texture::magfilter::NEAREST);
|
||||
|
||||
for(size_t i = 0;i < 4;++i){
|
||||
auto& coords = font['A'].texture_coords[i];
|
||||
debug_print_warn("(%f, %f)\n", coords.x(), coords.y());
|
||||
if(m_screen_shader->has_link_error()){
|
||||
debug_print_error("%s\n", m_screen_shader->get_error().c_str());
|
||||
}
|
||||
m_vbo.buffer(font['Z'].texture_coords[1], sizeof(GLfloat) * 2, offsetof(vertex, tex_coord) + (sizeof(vertex) * 0));
|
||||
m_vbo.buffer(font['Z'].texture_coords[0], sizeof(GLfloat) * 2, offsetof(vertex, tex_coord) + (sizeof(vertex) * 1));
|
||||
m_vbo.buffer(font['Z'].texture_coords[2], sizeof(GLfloat) * 2, offsetof(vertex, tex_coord) + (sizeof(vertex) * 2));
|
||||
m_vbo.buffer(font['Z'].texture_coords[2], sizeof(GLfloat) * 2, offsetof(vertex, tex_coord) + (sizeof(vertex) * 3));
|
||||
m_vbo.buffer(font['Z'].texture_coords[3], sizeof(GLfloat) * 2, offsetof(vertex, tex_coord) + (sizeof(vertex) * 4));
|
||||
m_vbo.buffer(font['Z'].texture_coords[1], sizeof(GLfloat) * 2, offsetof(vertex, tex_coord) + (sizeof(vertex) * 5));
|
||||
*/
|
||||
//attach a vbo to the vao and assign attribute specs
|
||||
|
||||
auto attrib = m_vao.get_attribute(0);
|
||||
m_vao.bind_buffer(m_vbo, 0, 0, sizeof(vertex));
|
||||
attrib.set_float_array(2, 0);
|
||||
@ -62,7 +49,6 @@ screen_renderer::screen_renderer(gfx::resource_manager& res, int width, int heig
|
||||
attrib.set_float_array(2, offsetof(vertex, tex_coord));
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
resize_viewport(width, height);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user