diff --git a/include/engine/font.hpp b/include/engine/font.hpp index 18d08f2..b8dd357 100644 --- a/include/engine/font.hpp +++ b/include/engine/font.hpp @@ -33,10 +33,13 @@ namespace egn{ struct font_character{ GLuint atlas_texture; math::vec2 atlas_offset; - math::vec2 size; - math::vec2 bearing; - math::vec2 advance; + math::vec2 size; + math::vec2 bearing; + math::vec2 advance; math::vec2 texture_coords[4]; + + public: + float aspect_ratio(void)const; }; class font @@ -44,20 +47,27 @@ namespace egn{ private: std::optional m_atlas; std::map m_atlas_data; + size_t m_requested_width; + size_t m_requested_height; public: font(void) = default; - font(gfx::texture&& tex, std::map&& metadata); + font(gfx::texture&& tex, std::map&& 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 release_metadata(void); bool valid(void)const; + math::vec2 size(void)const; }; class font_generator diff --git a/include/graphics/vao.hpp b/include/graphics/vao.hpp index f639ce5..10db874 100644 --- a/include/graphics/vao.hpp +++ b/include/graphics/vao.hpp @@ -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); diff --git a/include/math/mat.hpp b/include/math/mat.hpp index ef497b4..d7fda97 100644 --- a/include/math/mat.hpp +++ b/include/math/mat.hpp @@ -113,6 +113,8 @@ namespace math{ template = 0> constexpr matrix(const matrix_base& other); + template + constexpr matrix(const matrix& 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 = 0> constexpr matrix(const matrix_base& other); + template + constexpr matrix(const matrix& other); ~matrix() = default; //Assignement diff --git a/include/math/mat.tpp b/include/math/mat.tpp index b7b0055..0db2a56 100644 --- a/include/math/mat.tpp +++ b/include/math/mat.tpp @@ -145,6 +145,16 @@ namespace math{ } } } + template + template + constexpr matrix::matrix(const matrix& 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 template constexpr matrix& matrix::operator=(const matrix& m){ @@ -166,6 +176,16 @@ namespace math{ } } } + template + template + constexpr matrix::matrix(const matrix& 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 template diff --git a/include/math/vec.hpp b/include/math/vec.hpp index 92ddf4a..c967415 100644 --- a/include/math/vec.hpp +++ b/include/math/vec.hpp @@ -43,6 +43,8 @@ namespace math{ template && ...),int> = 0> constexpr vector(const vector& other, Args&&... args); + template + constexpr vector(const vector& other); constexpr vector(const vector&) = default; constexpr vector(vector&&) = default; ~vector() = default; diff --git a/include/math/vec.tpp b/include/math/vec.tpp index 1c7628f..81d6167 100644 --- a/include/math/vec.tpp +++ b/include/math/vec.tpp @@ -36,6 +36,13 @@ namespace math{ } } template + template + constexpr vector::vector(const vector& other){ + for(size_type i = 0;i < R;++i){ + this->m_data[i] = other[i]; + } + } + template template constexpr void vector::assign_(size_type offset, U&& u, Args&&... args){ this->m_data[offset] = std::forward(u); diff --git a/include/ttt/font_renderer.hpp b/include/ttt/font_renderer.hpp new file mode 100644 index 0000000..be7b817 --- /dev/null +++ b/include/ttt/font_renderer.hpp @@ -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 . +*/ + +#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 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 pos; + math::vec2 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 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 diff --git a/include/ttt/font_shader.hpp b/include/ttt/font_shader.hpp new file mode 100644 index 0000000..cfa52ca --- /dev/null +++ b/include/ttt/font_shader.hpp @@ -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 . +*/ + +#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 diff --git a/include/ttt/screen_renderer.hpp b/include/ttt/screen_renderer.hpp index 931df29..374fff9 100644 --- a/include/ttt/screen_renderer.hpp +++ b/include/ttt/screen_renderer.hpp @@ -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 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: diff --git a/src/engine/font.cpp b/src/engine/font.cpp index eb018ec..ca17aa2 100644 --- a/src/engine/font.cpp +++ b/src/engine/font.cpp @@ -20,12 +20,20 @@ #include "math/vec.hpp" #include //sqrt, round #include //move +#include namespace egn{ - font::font(gfx::texture&& tex, std::map&& metadata): + float font_character::aspect_ratio(void)const{ + return ((float)size.x()) / size.y(); + } + + + font::font(gfx::texture&& tex, std::map&& 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 font::release_metadata(void){ return std::map(std::move(m_atlas_data)); } bool font::valid(void)const{ return m_atlas.has_value(); } + math::vec2 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 atlas_metadata; math::vec2 atlas_pos = {0, 0}; + std::unique_ptr 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 bitmap_size = {bitmap.width, bitmap.rows}; - + const math::vec2 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 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 {}; } diff --git a/src/graphics/vao.cpp b/src/graphics/vao.cpp index 937bad7..81c06ad 100644 --- a/src/graphics/vao.cpp +++ b/src/graphics/vao.cpp @@ -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()); } diff --git a/src/graphics/vbo.cpp b/src/graphics/vbo.cpp index 83d5b56..8f09b3e 100644 --- a/src/graphics/vbo.cpp +++ b/src/graphics/vbo.cpp @@ -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; diff --git a/src/ttt/font_renderer.cpp b/src/ttt/font_renderer.cpp new file mode 100644 index 0000000..562ccd1 --- /dev/null +++ b/src/ttt/font_renderer.cpp @@ -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 . +*/ + +#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(font_shader::vertex_shader_text, gfx::shader::type::VERTEX), + util::make_deferred(font_shader::geometry_shader_text, gfx::shader::type::GEOMETRY), + util::make_deferred(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; +} diff --git a/src/ttt/screen_renderer.cpp b/src/ttt/screen_renderer.cpp index 2d1d79d..dd22adb 100644 --- a/src/ttt/screen_renderer.cpp +++ b/src/ttt/screen_renderer.cpp @@ -25,34 +25,21 @@ #include //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(screen_shader::vertex_shader_text, gfx::shader::type::VERTEX), util::make_deferred(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); }