Rendering some amount of text bby
This commit is contained in:
BIN
assets/hinted-RosaSans-Regular.ttf
Normal file
BIN
assets/hinted-RosaSans-Regular.ttf
Normal file
Binary file not shown.
62
include/engine/flat_object.hpp
Normal file
62
include/engine/flat_object.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
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_ENGINE_FLAT_OBJECT_HPP
|
||||
#define OUR_DICK_ENGINE_FLAT_OBJECT_HPP
|
||||
|
||||
#include "math/vec.hpp"
|
||||
|
||||
namespace egn{
|
||||
|
||||
namespace flat{
|
||||
|
||||
class object_base
|
||||
{
|
||||
protected:
|
||||
math::vec2f m_position;
|
||||
math::vec2f m_scale;
|
||||
float m_orientation;
|
||||
|
||||
public:
|
||||
object_base(void) = default;
|
||||
object_base(const math::vec2f& position, float orientation = 0);
|
||||
object_base(const object_base&) = default;
|
||||
object_base(object_base&&) = default;
|
||||
~object_base(void) = default;
|
||||
|
||||
object_base& operator=(const object_base&) = default;
|
||||
object_base& operator=(object_base&&) = default;
|
||||
|
||||
void translate(const math::vec2f& distance);
|
||||
void rotate(float distance);
|
||||
void scale(const math::vec2f& distance);
|
||||
void look_at(const math::vec2f& target);
|
||||
|
||||
void set_position(const math::vec2f& pos);
|
||||
void set_orientation(float orient);
|
||||
void set_scale(const math::vec2f& scale);
|
||||
|
||||
const math::vec2f& position(void)const;
|
||||
float orientation(void)const;
|
||||
const math::vec2f& scale(void)const;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <utility> //pair
|
||||
#include <memory>
|
||||
#include <algorithm> //min, max
|
||||
|
||||
#include "graphics/texture.hpp"
|
||||
#include "config.hpp"
|
||||
@@ -31,7 +33,6 @@
|
||||
namespace egn{
|
||||
|
||||
struct font_character{
|
||||
GLuint atlas_texture;
|
||||
math::vec2<size_t> atlas_offset;
|
||||
math::vec2<int> size;
|
||||
math::vec2<int> bearing;
|
||||
@@ -42,35 +43,34 @@ namespace egn{
|
||||
float aspect_ratio(void)const;
|
||||
};
|
||||
|
||||
class font
|
||||
class font_atlas : public gfx::texture
|
||||
{
|
||||
public:
|
||||
using map_type = std::map<int, font_character>;
|
||||
|
||||
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;
|
||||
map_type m_atlas_data;
|
||||
size_t m_requested_width, m_requested_height;
|
||||
|
||||
public:
|
||||
font(void) = default;
|
||||
font(gfx::texture&& tex, std::map<int, font_character>&& metadata, size_t width, size_t height);
|
||||
~font(void) = default;
|
||||
font_atlas(gfx::texture&& t, map_type&& map, size_t w, size_t h);
|
||||
font_atlas(const font_atlas&) = default;
|
||||
font_atlas(font_atlas&&) = default;
|
||||
~font_atlas(void) = default;
|
||||
|
||||
font_atlas& operator=(const font_atlas&) = default;
|
||||
font_atlas& operator=(font_atlas&&) = default;
|
||||
|
||||
map_type& metadata(void);
|
||||
const map_type& metadata(void)const;
|
||||
map_type&& release_metadata(void);
|
||||
|
||||
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;
|
||||
math::vec2<size_t> glyph_size(void)const;
|
||||
};
|
||||
|
||||
class font_generator
|
||||
class font
|
||||
{
|
||||
private:
|
||||
static inline bool s_initialized = false;
|
||||
@@ -78,25 +78,84 @@ namespace egn{
|
||||
|
||||
private:
|
||||
FT_Face m_face = nullptr;
|
||||
int m_depth = 3;
|
||||
|
||||
public:
|
||||
font_generator(const char* file);
|
||||
~font_generator(void);
|
||||
font(const char* file, int depth = 3);
|
||||
~font(void);
|
||||
|
||||
template<class... Ts>
|
||||
font_atlas generate_atlas(size_t glyph_w, size_t glyph_h, Ts&&... ranges);
|
||||
|
||||
//params:
|
||||
// start_codepoint: unicode character at which the atlas starts
|
||||
// count: number of characters to make in the atlas
|
||||
// glyph_width: requested pixel width of glyph
|
||||
// glyph_height: requested pixel height of glyph
|
||||
// atlas_width: requested pixel width of atlas
|
||||
// atlas_height: requested pixel height of atlas
|
||||
font generate_atlas(int start_codepoint, int count, size_t glyph_width, size_t glyph_height);
|
||||
std::optional<gfx::texture> generate_glyph(char character, int width, int height);
|
||||
|
||||
private:
|
||||
using atlas_meta = std::tuple<size_t,size_t,size_t,size_t>;
|
||||
template<class... Ts>
|
||||
void generate_atlas_piece_(gfx::texture& atlas, font_atlas::map_type& metadata, math::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range, Ts&&... ranges);
|
||||
void generate_atlas_piece_impl_(gfx::texture& atlas, font_atlas::map_type& metadata, math::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range);
|
||||
template<class... Ts>
|
||||
atlas_meta get_atlas_size_ranges_(Ts&&... ranges);
|
||||
template<class... Ts>
|
||||
void get_atlas_size_ranges_recurse_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range, Ts&&... ranges);
|
||||
void get_atlas_size_ranges_impl_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range);
|
||||
|
||||
private:
|
||||
static bool initialize_freetype_(void);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<class... Ts>
|
||||
void font::generate_atlas_piece_(gfx::texture& atlas, font_atlas::map_type& metadata, math::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range, Ts&&... ranges){
|
||||
generate_atlas_piece_impl_(atlas, metadata, target_pos, info, data_buffer, range);
|
||||
|
||||
if constexpr(sizeof...(ranges) > 0){
|
||||
generate_atlas_piece_(atlas, metadata, target_pos, info, data_buffer, std::forward<Ts>(ranges)...);
|
||||
}
|
||||
}
|
||||
template<class... Ts>
|
||||
font_atlas font::generate_atlas(size_t glyph_w, size_t glyph_h, Ts&&... ranges){
|
||||
FT_Set_Pixel_Sizes(m_face, glyph_w, glyph_h);
|
||||
auto [atlas_width, atlas_height, max_glyph_width, max_glyph_height] = get_atlas_size_ranges_(std::forward<Ts>(ranges)...);
|
||||
const auto format = (m_depth == 3) ? GL_RGB : GL_RED;
|
||||
|
||||
|
||||
gfx::texture atlas(format, atlas_width, atlas_height, GL_UNSIGNED_BYTE, false);
|
||||
font_atlas::map_type atlas_metadata;
|
||||
math::vec2<size_t> target_position = {0, 0};
|
||||
std::unique_ptr<unsigned char[]> dest_data(new unsigned char[max_glyph_height * max_glyph_width * m_depth]);
|
||||
|
||||
generate_atlas_piece_(atlas, atlas_metadata, target_position, std::tuple{atlas_width, atlas_height, max_glyph_width, max_glyph_height}, dest_data.get(), std::forward<Ts>(ranges)...);
|
||||
return {std::move(atlas), std::move(atlas_metadata), glyph_w, glyph_h};
|
||||
}
|
||||
template<class... Ts>
|
||||
auto font::get_atlas_size_ranges_(Ts&&... ranges) -> atlas_meta{
|
||||
size_t avg_width = 0;
|
||||
size_t avg_height = 0;
|
||||
size_t max_glyph_width = 0;
|
||||
size_t max_glyph_height = 0;
|
||||
size_t count = 0;
|
||||
|
||||
get_atlas_size_ranges_recurse_(avg_width, avg_height, max_glyph_width, max_glyph_height, count, std::forward<Ts>(ranges)...);
|
||||
|
||||
avg_width /= count;
|
||||
avg_height /= count;
|
||||
const size_t est_width = avg_width * (std::min(size_t{20}, count) + 1);
|
||||
const size_t est_height = (max_glyph_height * (std::round(count / 20) + 1));
|
||||
|
||||
return {est_width / m_depth, est_height, max_glyph_width / m_depth, max_glyph_height};
|
||||
|
||||
}
|
||||
template<class... Ts>
|
||||
void font::get_atlas_size_ranges_recurse_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range, Ts&&... ranges){
|
||||
get_atlas_size_ranges_impl_(avg_w, avg_h, mgw, mgh, count, range);
|
||||
if constexpr(sizeof...(ranges) > 0){
|
||||
get_atlas_size_ranges_recurse_(avg_w, avg_h, mgw, mgh, count, std::forward<Ts>(ranges)...);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
72
include/engine/text.hpp
Normal file
72
include/engine/text.hpp
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OUR_DICK_ENGINE_TEXT_HPP
|
||||
#define OUR_DICK_ENGINE_TEXT_HPP
|
||||
|
||||
#include "graphics/vbo.hpp"
|
||||
#include "graphics/vao.hpp"
|
||||
#include "graphics/texture.hpp"
|
||||
#include "graphics/shader_program.hpp"
|
||||
#include "font.hpp"
|
||||
#include "math/vec.hpp"
|
||||
#include "flat_object.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace egn{
|
||||
|
||||
class text_gfx
|
||||
{
|
||||
private:
|
||||
static constexpr int s_size_per_char = sizeof(GLfloat) * 12;
|
||||
private:
|
||||
gfx::weak_texture m_atlas;
|
||||
egn::font_atlas::map_type m_font;
|
||||
gfx::vbo m_vbo;
|
||||
gfx::vao m_vao;
|
||||
size_t m_original_height;
|
||||
float m_scale;
|
||||
|
||||
public:
|
||||
text_gfx(const egn::font_atlas& font, int target_px, size_t initsize = 0);
|
||||
text_gfx(const egn::font_atlas& font, int target_px, const char* t);
|
||||
|
||||
void set_text(const char* string);
|
||||
|
||||
void render(gfx::shader_program& sh, const math::vec2f& pos);
|
||||
};
|
||||
|
||||
|
||||
class text : public flat::object_base
|
||||
{
|
||||
private:
|
||||
text_gfx m_gfx;
|
||||
std::string m_text;
|
||||
|
||||
public:
|
||||
text(const egn::font_atlas& font, const char* string, int target_px, float target_x, float target_y);
|
||||
|
||||
void set_text(const char* string);
|
||||
|
||||
void render(gfx::shader_program& sh);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "graphics/texture.hpp"
|
||||
#include "graphics/shader_program.hpp"
|
||||
|
||||
#include "engine/font.hpp"
|
||||
|
||||
namespace gfx{
|
||||
|
||||
namespace detail{
|
||||
@@ -65,6 +67,7 @@ namespace gfx{
|
||||
{
|
||||
private:
|
||||
detail::resource_manager<texture> m_textures;
|
||||
detail::resource_manager<egn::font_atlas> m_fonts;
|
||||
detail::resource_manager<texture_array> m_texture_arrays;
|
||||
detail::resource_manager<shader_program> m_shaders;
|
||||
|
||||
@@ -90,6 +93,12 @@ namespace gfx{
|
||||
gfx::texture* get_texture(const char* file);
|
||||
bool erase_texture(const char* key);
|
||||
|
||||
bool has_font(const char* key)const;
|
||||
template<class... Args>
|
||||
auto emplace_font(const char* key, Args&&... args);
|
||||
egn::font_atlas* get_font(const char* file);
|
||||
bool erase_font(const char* key);
|
||||
|
||||
bool has_shader(const char* key)const;
|
||||
template<class... Args>
|
||||
auto emplace_shader(const char* key, Args&&... args);
|
||||
@@ -133,6 +142,10 @@ namespace gfx{
|
||||
return m_textures.emplace_value(key, std::forward<Args>(args)...);
|
||||
}
|
||||
template<class... Args>
|
||||
auto resource_manager::emplace_font(const char* key, Args&&... args){
|
||||
return m_fonts.emplace_value(key, std::forward<Args>(args)...);
|
||||
}
|
||||
template<class... Args>
|
||||
auto resource_manager::emplace_shader(const char* key, Args&&... args){
|
||||
return m_shaders.emplace_value(key, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -210,8 +210,8 @@ namespace gfx{
|
||||
void set_uniform(GLuint loc, const texture&, GLuint tex_unit = 0);
|
||||
void set_uniform(const char* name, const texture_array& t, GLuint tex_unit = 0);
|
||||
void set_uniform(GLuint loc, const texture_array& t, GLuint tex_unit = 0);
|
||||
void set_uniform(const char* name, const weak_texture_handle&, GLuint tex_unit = 0);
|
||||
void set_uniform(GLuint loc, const weak_texture_handle&, GLuint tex_unit = 0);
|
||||
void set_uniform(const char* name, const weak_texture&, GLuint tex_unit = 0);
|
||||
void set_uniform(GLuint loc, const weak_texture&, GLuint tex_unit = 0);
|
||||
|
||||
//Float vectors
|
||||
void set_uniform(const char* name, const vec2<GLfloat>&);
|
||||
|
||||
@@ -27,25 +27,26 @@ namespace gfx{
|
||||
|
||||
class texture;
|
||||
|
||||
class weak_texture_handle
|
||||
class weak_texture
|
||||
{
|
||||
private:
|
||||
const texture* m_texture;
|
||||
GLuint m_texture;
|
||||
|
||||
public:
|
||||
weak_texture_handle(const texture& tex);
|
||||
weak_texture_handle(const weak_texture_handle&) = default;
|
||||
weak_texture_handle(weak_texture_handle&&) = default;
|
||||
~weak_texture_handle(void) = default;
|
||||
weak_texture(GLuint tex);
|
||||
weak_texture(const texture& tex);
|
||||
weak_texture(const weak_texture&) = default;
|
||||
weak_texture(weak_texture&&) = default;
|
||||
~weak_texture(void) = default;
|
||||
|
||||
weak_texture_handle& operator=(const weak_texture_handle&) = default;
|
||||
weak_texture_handle& operator=(weak_texture_handle&&) = default;
|
||||
weak_texture& operator=(const weak_texture&) = default;
|
||||
weak_texture& operator=(weak_texture&&) = default;
|
||||
|
||||
GLuint raw(void)const;
|
||||
GLsizei get_width(void)const;
|
||||
GLsizei get_height(void)const;
|
||||
|
||||
void bind(void)const;
|
||||
void bind(GLuint target = GL_TEXTURE_2D)const;
|
||||
void bind_unit(GLuint tunit)const;
|
||||
};
|
||||
|
||||
@@ -152,7 +153,7 @@ namespace gfx{
|
||||
//bind to given texture unit and load into given program uniform location
|
||||
void bind_unit(GLuint tunit)const;
|
||||
|
||||
weak_texture_handle create_handle(void)const;
|
||||
weak_texture create_handle(void)const;
|
||||
|
||||
private:
|
||||
bool create_texture_storage_(void);
|
||||
|
||||
@@ -28,145 +28,18 @@
|
||||
#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());
|
||||
}
|
||||
};
|
||||
#include "engine/text.hpp"
|
||||
|
||||
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);
|
||||
font_renderer(gfx::resource_manager& res, int width, int height);
|
||||
|
||||
void render(scene&);
|
||||
void render(egn::text&);
|
||||
void resize_viewport(int width, int height);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,20 +25,20 @@ namespace font_shader{
|
||||
#version 430 core
|
||||
layout (location = 0) in vec4 tex_coords;
|
||||
layout (location = 1) in vec4 color;
|
||||
layout (location = 2) in vec3 position;
|
||||
layout (location = 2) in vec2 offset;
|
||||
layout (location = 3) in vec2 size;
|
||||
|
||||
out VS_OUT{
|
||||
vec4 tex_coords;
|
||||
vec4 color;
|
||||
vec3 position;
|
||||
vec2 offset;
|
||||
vec2 size;
|
||||
}vs_out;
|
||||
|
||||
void main(){
|
||||
vs_out.tex_coords = tex_coords;
|
||||
vs_out.color = color;
|
||||
vs_out.position = position;
|
||||
vs_out.offset = offset;
|
||||
vs_out.size = size;
|
||||
}
|
||||
)glsl";
|
||||
@@ -51,7 +51,7 @@ static constexpr char geometry_shader_text[] =
|
||||
in VS_OUT{
|
||||
vec4 tex_coords;
|
||||
vec4 color;
|
||||
vec3 position;
|
||||
vec2 offset;
|
||||
vec2 size;
|
||||
}gs_in[];
|
||||
|
||||
@@ -59,27 +59,49 @@ static constexpr char geometry_shader_text[] =
|
||||
vec2 tex_coords;
|
||||
flat vec4 color;
|
||||
}gs_out;
|
||||
|
||||
uniform vec2 out_size;
|
||||
uniform vec2 start_pos;
|
||||
|
||||
//map from [0, max_out] to [0, 2]
|
||||
float size_to_screen_space(float x, float max_out){
|
||||
const float ratio = 2.0 / max_out;
|
||||
return x * ratio;
|
||||
}
|
||||
//map from [0, max_out] to [-1, 1]
|
||||
float pos_to_screen_space(float x, float max_out){
|
||||
return size_to_screen_space(x, max_out) - 1.0;
|
||||
}
|
||||
|
||||
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);
|
||||
vec2 start_screen_pos = vec2(pos_to_screen_space(start_pos.x, out_size.x), pos_to_screen_space(start_pos.y, out_size.y));
|
||||
|
||||
const vec3 position = vec3(start_screen_pos.x + size_to_screen_space(gs_in[0].offset.x, out_size.x),
|
||||
start_screen_pos.y + size_to_screen_space(gs_in[0].offset.y, out_size.y),
|
||||
-1);
|
||||
const vec2 size = vec2(size_to_screen_space(gs_in[0].size.x, out_size.x),
|
||||
size_to_screen_space(gs_in[0].size.y, out_size.y));
|
||||
|
||||
gl_Position = vec4(position.x, position.y + size.y, 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);
|
||||
gl_Position = vec4(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);
|
||||
gl_Position = vec4(position.x + size.x, position.y + size.y, 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);
|
||||
gl_Position = vec4(position.x + size.x, position.y, position.z, 1.0);
|
||||
gs_out.tex_coords = gs_in[0].tex_coords.zw;
|
||||
gs_out.color = gs_in[0].color;
|
||||
EmitVertex();
|
||||
@@ -101,9 +123,9 @@ static constexpr char fragment_shader_text[] =
|
||||
|
||||
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);
|
||||
float alpha = (s.r + s.g + s.b) / 3.0;
|
||||
vec4 alpha_texture = vec4(s.rgb, alpha);
|
||||
frag_color = alpha_texture * fs_in.color; //NOT the dot product in glsl
|
||||
}
|
||||
)glsl";
|
||||
}
|
||||
|
||||
@@ -23,12 +23,17 @@
|
||||
#include "engine/input.hpp"
|
||||
#include "graphics/texture.hpp"
|
||||
#include "screen_renderer.hpp"
|
||||
#include "font_renderer.hpp"
|
||||
|
||||
#include "engine/text.hpp"
|
||||
|
||||
class pause_state : public egn::game_state_iface
|
||||
{
|
||||
private:
|
||||
egn::game_state_iface* m_under_state;
|
||||
screen_renderer m_screen_renderer;
|
||||
font_renderer m_font_renderer;
|
||||
egn::text m_text;
|
||||
|
||||
public:
|
||||
pause_state(egn::game& owner, egn::game_state_iface* under);
|
||||
|
||||
@@ -29,117 +29,6 @@
|
||||
|
||||
#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:
|
||||
|
||||
74
src/engine/flat_object.cpp
Normal file
74
src/engine/flat_object.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
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 "engine/flat_object.hpp"
|
||||
#include "math/math_common.hpp"
|
||||
#include <cmath> //signbit, atan
|
||||
|
||||
namespace egn::flat{
|
||||
|
||||
object_base::object_base(const math::vec2f& position, float orientation):
|
||||
m_position(position),
|
||||
m_orientation(orientation){}
|
||||
|
||||
void object_base::translate(const math::vec2f& distance){
|
||||
m_position += distance;
|
||||
}
|
||||
void object_base::rotate(float distance){
|
||||
m_orientation += distance;
|
||||
}
|
||||
void object_base::scale(const math::vec2f& distance){
|
||||
m_scale += distance;
|
||||
}
|
||||
void object_base::look_at(const math::vec2f& target){
|
||||
const math::vec2f rel_target = target - m_position;
|
||||
const float angle = std::atan(rel_target.y() / rel_target.x());
|
||||
const int x_sign = std::signbit(rel_target.x());
|
||||
const int y_sign = std::signbit(rel_target.y());
|
||||
const int quadrant = (x_sign ^ y_sign) + (2 * y_sign);
|
||||
|
||||
//quadrants:
|
||||
// |
|
||||
// 1 | 0
|
||||
//-----------------
|
||||
// 2 | 3
|
||||
// |
|
||||
m_orientation = angle + (90.0_deg * quadrant);
|
||||
}
|
||||
|
||||
void object_base::set_position(const math::vec2f& pos){
|
||||
m_position = pos;
|
||||
}
|
||||
void object_base::set_orientation(float orient){
|
||||
m_orientation = orient;
|
||||
}
|
||||
void object_base::set_scale(const math::vec2f& scale){
|
||||
m_scale = scale;
|
||||
}
|
||||
|
||||
const math::vec2f& object_base::position(void)const{
|
||||
return m_position;
|
||||
}
|
||||
float object_base::orientation(void)const{
|
||||
return m_orientation;
|
||||
}
|
||||
const math::vec2f& object_base::scale(void)const{
|
||||
return m_scale;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,129 +28,111 @@ namespace egn{
|
||||
return ((float)size.x()) / size.y();
|
||||
}
|
||||
|
||||
font_atlas::font_atlas(gfx::texture&& t, map_type&& map, size_t w, size_t h):
|
||||
texture(std::move(t)),
|
||||
m_atlas_data(std::move(map)),
|
||||
m_requested_width(w),
|
||||
m_requested_height(h){}
|
||||
|
||||
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_requested_width(width),
|
||||
m_requested_height(height){}
|
||||
font_character& font::operator[](int character){
|
||||
auto font_atlas::metadata(void) -> map_type&{
|
||||
return m_atlas_data;
|
||||
}
|
||||
auto font_atlas::metadata(void)const -> const map_type&{
|
||||
return m_atlas_data;
|
||||
}
|
||||
auto font_atlas::release_metadata(void) -> map_type&&{
|
||||
return std::move(m_atlas_data);
|
||||
}
|
||||
|
||||
font_character& font_atlas::operator[](int character){
|
||||
return m_atlas_data[character];
|
||||
}
|
||||
const font_character& font::operator[](int character)const{
|
||||
return m_atlas_data.at(character);
|
||||
}
|
||||
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{
|
||||
math::vec2<size_t> font_atlas::glyph_size(void)const{
|
||||
return {m_requested_width, m_requested_height};
|
||||
}
|
||||
|
||||
font_generator::font_generator(const char* file){
|
||||
|
||||
font::font(const char* file, int depth):
|
||||
m_depth(depth)
|
||||
{
|
||||
initialize_freetype_();
|
||||
if(FT_New_Face(s_ft, file, 0, &m_face)){
|
||||
debug_print_error("Failed to initialize font '%s'\n", file);
|
||||
if(int err = FT_New_Face(s_ft, file, 0, &m_face);err){
|
||||
debug_print_error("Failed to initialize font '%s'. Error %d\n", file, err);
|
||||
}
|
||||
}
|
||||
font_generator::~font_generator(void){
|
||||
font::~font(void){
|
||||
FT_Done_Face(m_face);
|
||||
}
|
||||
|
||||
struct atlas_info{
|
||||
size_t width, height;
|
||||
size_t max_glyph_width, max_glyph_height;
|
||||
};
|
||||
atlas_info get_atlas_size_(FT_Face face, int start_codepoint, int count){
|
||||
size_t max_height = 0;
|
||||
size_t max_width = 0;
|
||||
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 | FT_LOAD_TARGET_LCD)){
|
||||
debug_print_error("Failed to load character '%c' from font '%s'\n", i, face->family_name);
|
||||
void font::get_atlas_size_ranges_impl_(size_t& avg_w, size_t& avg_h, size_t& mgw, size_t& mgh, size_t& count, const std::pair<int, int>& range){
|
||||
const int freetype_load_flags = FT_LOAD_RENDER | ((m_depth == 3) ? FT_LOAD_TARGET_LCD : 0);
|
||||
const size_t start = range.first;
|
||||
const size_t range_cnt = range.second;
|
||||
for(size_t i = 0;i < range_cnt;++i){
|
||||
if(int err = FT_Load_Char(m_face, i + start, freetype_load_flags);err){
|
||||
debug_print_error("Failed to load character '%c' from font '%s'\n", (char)i, m_face->family_name);
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
mgw = std::max(mgw, size_t{m_face->glyph->bitmap.width});
|
||||
mgh = std::max(mgh, size_t{m_face->glyph->bitmap.rows});
|
||||
avg_w += m_face->glyph->bitmap.width;
|
||||
avg_h += m_face->glyph->bitmap.rows;
|
||||
++count;
|
||||
}
|
||||
avg_width /= count;
|
||||
avg_height /= count;
|
||||
size_t est_width = avg_width * (std::min(20, count) + 1);
|
||||
size_t est_height = (max_height * (std::round(count / 20) + 1));
|
||||
|
||||
|
||||
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){
|
||||
FT_Set_Pixel_Sizes(m_face, glyph_width, glyph_height);
|
||||
font_character calculate_character(FT_Face face, size_t atlas_w, size_t atlas_h, size_t atlas_x, size_t atlas_y){
|
||||
const math::vec2<int> dest_size = {face->glyph->metrics.width >> 6, face->glyph->metrics.height >> 6};
|
||||
const float ratio_w = 1.0f / atlas_w;
|
||||
const float ratio_h = 1.0f / atlas_h;
|
||||
const float left = ratio_w * atlas_x;
|
||||
const float right = ratio_w * (atlas_x + dest_size.x());
|
||||
const float bottom = ratio_h * atlas_y;
|
||||
const float top = ratio_h * (atlas_y + dest_size.y());
|
||||
|
||||
atlas_info ai = get_atlas_size_(m_face, start_codepoint, count);
|
||||
return font_character {{atlas_x, atlas_y},
|
||||
dest_size,
|
||||
{face->glyph->metrics.horiBearingX >> 6, face->glyph->metrics.horiBearingY >> 6},
|
||||
{face->glyph->metrics.horiAdvance >> 6, 0}, //convert from 1/64th pixels to pixels
|
||||
{{left, top}, {left, bottom}, {right, top}, {right, bottom}}};
|
||||
}
|
||||
|
||||
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]);
|
||||
void pack_font_data_(unsigned char* dest, const unsigned char* src, size_t out_width, size_t out_height, int pitch){
|
||||
for(size_t j = 0;j < out_height;++j){
|
||||
memcpy(dest, src, out_width);
|
||||
dest += out_width;
|
||||
src += pitch;
|
||||
}
|
||||
}
|
||||
void font::generate_atlas_piece_impl_(gfx::texture& atlas, font_atlas::map_type& metadata, math::vec2<size_t>& target_pos, const atlas_meta& info, unsigned char* data_buffer, const std::pair<int,int>& range){
|
||||
const auto& [atlas_width, atlas_height, max_glyph_width, max_glyph_height] = info;
|
||||
const auto format = (m_depth == 3) ? GL_RGB : GL_RED;
|
||||
const int freetype_load_flags = FT_LOAD_RENDER | ((m_depth == 3) ? FT_LOAD_TARGET_LCD : 0);
|
||||
int start_codepoint = range.first;
|
||||
int count = range.second;
|
||||
for(int i = start_codepoint;i < start_codepoint + count;++i){
|
||||
if(FT_Load_Char(m_face, i, FT_LOAD_RENDER | FT_LOAD_TARGET_LCD)){
|
||||
if(FT_Load_Char(m_face, i, freetype_load_flags)){
|
||||
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> 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() + dest_size.x());
|
||||
const float bottom = ratio_h * atlas_pos.y();
|
||||
const float top = ratio_h * (atlas_pos.y() + dest_size.y());
|
||||
|
||||
font_character character{atlas.raw(),
|
||||
atlas_pos,
|
||||
dest_size,
|
||||
{m_face->glyph->bitmap_left, m_face->glyph->bitmap_top},
|
||||
{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;
|
||||
const math::vec2<size_t> out_size = {bitmap.width / m_depth, bitmap.rows};
|
||||
if(target_pos.x() + out_size.x() > atlas_width){
|
||||
target_pos.x() = 0;
|
||||
target_pos.y() += max_glyph_height;
|
||||
}
|
||||
|
||||
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();
|
||||
font_character character = calculate_character(m_face, atlas_width, atlas_height, target_pos.x(), target_pos.y());
|
||||
metadata.emplace(i, character);
|
||||
|
||||
pack_font_data_(data_buffer, bitmap.buffer, bitmap.width, bitmap.rows, bitmap.pitch);
|
||||
atlas.set_subimage(data_buffer, format, GL_UNSIGNED_BYTE, target_pos.x(), target_pos.y(), out_size.x(), out_size.y());
|
||||
target_pos.x() += out_size.x();
|
||||
}
|
||||
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){
|
||||
std::optional<gfx::texture> font::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 | FT_LOAD_TARGET_LCD)){
|
||||
debug_print_error("Failed to load character '%c', from font '%s'\n", character, m_face->family_name);
|
||||
@@ -162,7 +144,7 @@ namespace egn{
|
||||
|
||||
|
||||
|
||||
bool font_generator::initialize_freetype_(void){
|
||||
bool font::initialize_freetype_(void){
|
||||
if(!s_initialized){
|
||||
if(FT_Init_FreeType(&s_ft)){
|
||||
return false;
|
||||
|
||||
106
src/engine/text.cpp
Normal file
106
src/engine/text.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
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 "engine/text.hpp"
|
||||
|
||||
namespace egn{
|
||||
|
||||
text_gfx::text_gfx(const egn::font_atlas& font, int target_px, size_t initsize):
|
||||
m_atlas(font),
|
||||
m_font(font.metadata()),
|
||||
m_vbo(initsize * s_size_per_char, gfx::vbo::usage::DYNAMIC_DRAW),
|
||||
m_original_height(font.glyph_size().y()),
|
||||
m_scale(((float)target_px) / m_original_height){}
|
||||
text_gfx::text_gfx(const egn::font_atlas& font, int target_px, const char* string):
|
||||
text_gfx(font, target_px, strlen(string))
|
||||
{
|
||||
set_text(string);
|
||||
|
||||
//attach a vbo to the vao and assign attribute specs
|
||||
m_vao.bind_buffer(m_vbo, 0, 0, s_size_per_char);
|
||||
|
||||
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(2, sizeof(GLfloat) * 8);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
|
||||
attrib = m_vao.get_attribute(3);
|
||||
attrib.set_float_array(2, sizeof(GLfloat) * 10);
|
||||
attrib.associate_with(0);
|
||||
attrib.enable();
|
||||
}
|
||||
void text_gfx::set_text(const char* string){
|
||||
m_vbo.clear();
|
||||
constexpr float tmp_color[] = {1,1,1,1};
|
||||
|
||||
math::vec2f pos_offset = {0, 0};
|
||||
|
||||
for(int i = 0;string[i] != 0;++i){
|
||||
if(string[i] == '\n'){
|
||||
pos_offset.x() = 0;
|
||||
pos_offset.y() -= m_original_height * m_scale;
|
||||
continue;
|
||||
}
|
||||
const auto ch = m_font[string[i]];
|
||||
const math::vec2<float> letter_size = ch.size * m_scale;
|
||||
const math::vec2f char_offset = {(ch.bearing.x() * m_scale) + pos_offset.x(),
|
||||
((ch.bearing.y() * m_scale) - letter_size.y()) + pos_offset.y()};
|
||||
|
||||
m_vbo.buffer(ch.texture_coords[1], sizeof(GLfloat) * 2);
|
||||
m_vbo.buffer(ch.texture_coords[2], sizeof(GLfloat) * 2);
|
||||
m_vbo.buffer(tmp_color, sizeof(GLfloat) * 4);
|
||||
m_vbo.buffer(char_offset, sizeof(GLfloat) * 2);
|
||||
m_vbo.buffer(letter_size, sizeof(GLfloat) * 2);
|
||||
|
||||
pos_offset.x() += (m_scale * ch.advance.x());
|
||||
}
|
||||
}
|
||||
void text_gfx::render(gfx::shader_program& sh, const math::vec2f& pos){
|
||||
m_vao.bind();
|
||||
sh.set_uniform("texture1", m_atlas);
|
||||
sh.set_uniform("start_pos", pos);
|
||||
glDrawArrays(GL_POINTS, 0, m_vbo.get_size() / s_size_per_char);
|
||||
}
|
||||
|
||||
|
||||
|
||||
text::text(const egn::font_atlas& font, const char* string, int target_px, float target_x, float target_y):
|
||||
flat::object_base(math::vec2f{target_x, target_y}),
|
||||
m_gfx(font, target_px, string),
|
||||
m_text(string){}
|
||||
|
||||
void text::set_text(const char* string){
|
||||
m_text = string;
|
||||
m_gfx.set_text(string);
|
||||
}
|
||||
|
||||
void text::render(gfx::shader_program& sh){
|
||||
m_gfx.render(sh, m_position);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,6 +40,16 @@ namespace gfx{
|
||||
return m_textures.erase_value(key);
|
||||
}
|
||||
|
||||
bool resource_manager::has_font(const char* key)const{
|
||||
return m_fonts.has_value(key);
|
||||
}
|
||||
egn::font_atlas* resource_manager::get_font(const char* file){
|
||||
return m_fonts.get_value(file);
|
||||
}
|
||||
bool resource_manager::erase_font(const char* key){
|
||||
return m_fonts.erase_value(key);
|
||||
}
|
||||
|
||||
bool resource_manager::has_shader(const char* key)const{
|
||||
return m_shaders.has_value(key);
|
||||
}
|
||||
|
||||
@@ -501,10 +501,10 @@ namespace gfx{
|
||||
t.bind_unit(tex_unit);
|
||||
glProgramUniform1i(m_shader_id, loc, tex_unit);
|
||||
}
|
||||
void shader_program::set_uniform(const char* name, const weak_texture_handle& t, GLuint tex_unit){
|
||||
void shader_program::set_uniform(const char* name, const weak_texture& t, GLuint tex_unit){
|
||||
set_uniform(get_uniform_loc(name), t, tex_unit);
|
||||
}
|
||||
void shader_program::set_uniform(GLuint loc, const weak_texture_handle& t, GLuint tex_unit){
|
||||
void shader_program::set_uniform(GLuint loc, const weak_texture& t, GLuint tex_unit){
|
||||
t.bind_unit(tex_unit);
|
||||
glProgramUniform1i(m_shader_id, loc, tex_unit);
|
||||
}
|
||||
|
||||
@@ -301,8 +301,8 @@ namespace gfx{
|
||||
void texture::bind_unit(GLuint tunit)const{
|
||||
glBindTextureUnit(tunit, m_tex_id);
|
||||
}
|
||||
weak_texture_handle texture::create_handle(void)const{
|
||||
return weak_texture_handle(*this);
|
||||
weak_texture texture::create_handle(void)const{
|
||||
return weak_texture(*this);
|
||||
}
|
||||
|
||||
bool texture::create_texture_storage_(void){
|
||||
@@ -526,24 +526,30 @@ namespace gfx{
|
||||
|
||||
|
||||
|
||||
weak_texture_handle::weak_texture_handle(const texture& tex):
|
||||
m_texture(&tex){}
|
||||
weak_texture::weak_texture(GLuint tex):
|
||||
m_texture(tex){}
|
||||
weak_texture::weak_texture(const texture& tex):
|
||||
m_texture(tex.raw()){}
|
||||
|
||||
GLuint weak_texture_handle::raw(void)const{
|
||||
return m_texture->raw();
|
||||
GLuint weak_texture::raw(void)const{
|
||||
return m_texture;
|
||||
}
|
||||
GLsizei weak_texture_handle::get_width(void)const{
|
||||
return m_texture->get_width();
|
||||
GLsizei weak_texture::get_width(void)const{
|
||||
GLint retval;
|
||||
glGetTextureLevelParameteriv(m_texture, 0, GL_TEXTURE_WIDTH, &retval);
|
||||
return retval;
|
||||
}
|
||||
GLsizei weak_texture_handle::get_height(void)const{
|
||||
return m_texture->get_height();
|
||||
GLsizei weak_texture::get_height(void)const{
|
||||
GLint retval;
|
||||
glGetTextureLevelParameteriv(m_texture, 0, GL_TEXTURE_HEIGHT, &retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void weak_texture_handle::bind(void)const{
|
||||
m_texture->bind();
|
||||
void weak_texture::bind(GLuint target)const{
|
||||
glBindTexture(target, m_texture);
|
||||
}
|
||||
void weak_texture_handle::bind_unit(GLuint tunit)const{
|
||||
m_texture->bind_unit(tunit);
|
||||
void weak_texture::bind_unit(GLuint tunit)const{
|
||||
glBindTextureUnit(tunit, m_texture);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,31 +24,21 @@
|
||||
|
||||
#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),
|
||||
font_renderer::font_renderer(gfx::resource_manager& res, int width, int height):
|
||||
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;
|
||||
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&){
|
||||
void font_renderer::render(egn::text& t){
|
||||
//Clear the global state to that which this renderer prefers
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
@@ -56,14 +46,10 @@ void font_renderer::render(scene&){
|
||||
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);
|
||||
m_font_shader->set_uniform("out_size", math::vec2f{m_screen_width, m_screen_height});
|
||||
|
||||
//Draw
|
||||
//glDrawArrays(GL_POINTS, 0, 5);
|
||||
t.render(*m_font_shader);
|
||||
}
|
||||
|
||||
void font_renderer::resize_viewport(int width, int height){
|
||||
|
||||
@@ -24,13 +24,21 @@
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
static egn::font_atlas generate_font_atlas_from_file(const char* file, int target_height){
|
||||
egn::font f(file);
|
||||
return f.generate_atlas(0, target_height, std::pair{' ', ('~' - ' ')});
|
||||
}
|
||||
|
||||
pause_state::pause_state(egn::game& owner, egn::game_state_iface* under):
|
||||
game_state_iface(&owner),
|
||||
m_under_state(under),
|
||||
m_screen_renderer(owner.gfx_resource_manager(), owner.get_width(), owner.get_height(), *owner.gfx_resource_manager().emplace_texture("pause_screen", util::make_deferred<egn::image>("assets/images/pause.png", true)).first){}
|
||||
m_screen_renderer(owner.gfx_resource_manager(), owner.get_width(), owner.get_height(), *owner.gfx_resource_manager().emplace_texture("pause_screen", util::make_deferred<egn::image>("assets/images/pause.png", true)).first),
|
||||
m_font_renderer(owner.gfx_resource_manager(), owner.get_width(), owner.get_height()),
|
||||
m_text(*owner.gfx_resource_manager().emplace_font("hinted-RosaSans-Regular-atlas-50px", util::deferred_function(generate_font_atlas_from_file, "assets/hinted-RosaSans-Regular.ttf", 50)).first, "Pause Menu", 50, 100, 100){}
|
||||
|
||||
void pause_state::enter(){
|
||||
m_screen_renderer.resize_viewport(m_owner->get_width(), m_owner->get_height());
|
||||
m_font_renderer.resize_viewport(m_owner->get_width(), m_owner->get_height());
|
||||
}
|
||||
void pause_state::leave(){}
|
||||
void pause_state::handle_input(const egn::input_event& event){
|
||||
@@ -45,11 +53,12 @@ void pause_state::handle_input(const egn::input_event& event){
|
||||
}else if(event.ev_type == egn::input_event::type::RESIZE){
|
||||
m_under_state->handle_input(event);
|
||||
m_screen_renderer.resize_viewport(event.x, event.y);
|
||||
m_font_renderer.resize_viewport(event.x, event.y);
|
||||
}
|
||||
}
|
||||
void pause_state::update(double){}
|
||||
void pause_state::render(){
|
||||
scene tmp;
|
||||
m_under_state->render();
|
||||
m_screen_renderer.render(tmp);
|
||||
//m_screen_renderer.render(tmp);
|
||||
m_font_renderer.render(m_text);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user