diff --git a/resources/client/shaders/font/font.frag b/resources/client/shaders/font/font.frag new file mode 100644 index 0000000..c4ba77d --- /dev/null +++ b/resources/client/shaders/font/font.frag @@ -0,0 +1,19 @@ +#version 330 core +in vec2 texCoords; +out vec4 fragColor; + +uniform vec4 fontColor; +uniform sampler2D fontBitmap; + +void main() { + vec4 outColor = texture(fontBitmap, texCoords); + if(outColor.xyz == vec3(0.0, 0.0, 0.0)) + discard; + + fragColor = vec4( + fontColor.x * outColor.x, + fontColor.y * outColor.y, + fontColor.z * outColor.z, + 1.0 + ); +} \ No newline at end of file diff --git a/resources/client/shaders/font/font.vert b/resources/client/shaders/font/font.vert new file mode 100644 index 0000000..a93513c --- /dev/null +++ b/resources/client/shaders/font/font.vert @@ -0,0 +1,13 @@ +#version 330 core +layout (location = 0) in vec4 aScreenCoords; +layout (location = 1) in vec2 aTexCoords; + +out vec2 texCoords; + +uniform mat4 transMatrix; +uniform mat4 orthoMatrix; + +void main() { + gl_Position = orthoMatrix * aScreenCoords; + texCoords = aTexCoords; +} \ No newline at end of file diff --git a/resources/client/shaders/test.frag b/resources/client/shaders/test.frag index c90338d..1eff33f 100644 --- a/resources/client/shaders/test.frag +++ b/resources/client/shaders/test.frag @@ -1,9 +1,9 @@ #version 330 core in vec2 texCoords; -uniform sampler2D graphicSampler; - out vec4 fragColor; +uniform sampler2D fontBitmap; + void main() { - fragColor = texture(graphicSampler, texCoords); + fragColor = texture(fontBitmap, texCoords); } \ No newline at end of file diff --git a/src/client/main.cpp b/src/client/main.cpp index cd220a4..32ed027 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -4,6 +4,7 @@ #define GL3_PROTOTYPES 1 #include +#include "ui/font.hpp" #include "shaders/test.hpp" void setColor(float r, float g, float b, SDL_Window* window) { @@ -44,15 +45,18 @@ int main(int argc, char* argv[]) { SDL_GL_SetSwapInterval(1); - shdr::TestShader test; - test.Load(); - test.UpdateWindow(window); - #ifndef __APPLE__ glewExperimental = GL_TRUE; glewInit(); #endif + ui::font_init_subsystem(window); + ui::Font scapeFont( + SOSC_RESC("fonts/scape.bmp"), + SOSC_RESC("fonts/scape.dat") + ); + ui::font_set_default(&scapeFont); + bool running = true; while(running) { glClear(GL_COLOR_BUFFER_BIT); @@ -70,7 +74,7 @@ int main(int argc, char* argv[]) { event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { glViewport(0, 0, event.window.data1, event.window.data2); - test.UpdateWindow(window); + ui::font_window_changed(window); } if(event.type == SDL_KEYDOWN) { @@ -85,8 +89,7 @@ int main(int argc, char* argv[]) { } } - test.Unload(); - + ui::font_deinit_subsystem(); SDL_GL_DeleteContext(ctx); SDL_DestroyWindow(window); diff --git a/src/client/ui/font.cpp b/src/client/ui/font.cpp new file mode 100644 index 0000000..f654dda --- /dev/null +++ b/src/client/ui/font.cpp @@ -0,0 +1,267 @@ +#include "font.hpp" + +#define LILEND_UNPACK(X,N) \ + (((uint32_t)(X)[N]) | \ + ((((uint32_t)(X)[(N)+1])) << 8u) | \ + ((((uint32_t)(X)[(N)+2])) << 16u) | \ + ((((uint32_t)(X)[(N)+3])) << 24u)) + +// FONT SHADER CLASS // + +namespace sosc { +namespace ui { +static class FontShader : public sosc::shdr::Shader { +public: + enum Uniforms { + ORTHO_MATRIX = 0, + TRANSLATION_MATRIX, + FONT_BITMAP, + FONT_COLOR + }; + + void UpdateWindow(SDL_Window *window) { + this->Start(); + + int width, height; + SDL_GetWindowSize(window, &width, &height); + glm::mat4 orthoMatrix = glm::ortho(0, width, height, 0); + glUniformMatrix4fv( + (*this)[ORTHO_MATRIX], 1, GL_FALSE, glm::value_ptr(orthoMatrix) + ); + + this->Stop(); + } + +protected: + void PrepareLoad() override { + this->AttachSource( + SOSC_RESC("shaders/font/font.vert"), GL_VERTEX_SHADER + ); + this->AttachSource( + SOSC_RESC("shaders/font/font.frag"), GL_FRAGMENT_SHADER + ); + + this->LinkProgram(); + this->LoadUniforms({ + "orthoMatrix", + "transMatrix" + "fontBitmap", + "fontColor" + }); + } +}; +}} + +// STATE STRUCT // + +static struct { + sosc::ui::FontShader shader; + sosc::ui::Font* default_font; +} _font_ctx; + +// SUBSYSTEM FUNCS // + +bool sosc::ui::font_init_subsystem(SDL_Window* window) { + _font_ctx.shader.Load(); + _font_ctx.shader.UpdateWindow(window); + _font_ctx.default_font = nullptr; +} + +void sosc::ui::font_set_default(Font *font) { + _font_ctx.default_font = font; +} + +void sosc::ui::font_window_changed(SDL_Window* window) { + _font_ctx.shader.UpdateWindow(window); +} + +void sosc::ui::font_deinit_subsystem() { + _font_ctx.shader.Unload(); +} + +// FONT CLASS // + +sosc::ui::Font::Font + (const std::string& bitmapPath, const std::string& dataPath) +{ + if(!this->Load(bitmapPath, dataPath)) + throw new FontException(bitmapPath, dataPath); +} + +bool sosc::ui::Font::Load + (const std::string& filePath, const std::string& dataPath) +{ + if(this->loaded) + this->Unload(); + + SDL_RWops* rwop = SDL_RWFromFile(filePath.c_str(), "rb"); + this->image = SDL_LoadBMP_RW(rwop, 1); + if(!this->image) + return false; + + char buffer[0x111]; + std::ifstream dataFile(dataPath, std::ios::in | std::ios::binary); + if(!dataFile.is_open()) + return false; + dataFile.read(buffer, 0x111); + dataFile.close(); + std::string data(buffer, 0x111); + if(buffer[0x10] != 0) + return false; + + this->width = (uint32_t)this->image->w; + this->height = (uint32_t)this->image->h; + + this->cell_width = LILEND_UNPACK(buffer, 0x08); + this->cell_width = LILEND_UNPACK(buffer, 0x0C); + + for(int i = 0; i < 256; ++i) { + auto glyph = &this->glyphs[i]; + auto width = (uint8_t)buffer[0x11 + i]; + + glyph->width = (double)width / (double)this->cell_width; + + int x = (this->cell_width * i) % this->width; + int y = ((this->cell_width * i) / this->width) * this->cell_height; + + glyph->top_left = glm::vec2( + (double)x / (double)this->width, + 1 - (double)y / (double)this->height + ); + glyph->top_right = glm::vec2( + (double)(x + width) / (double)this->width, + 1 - (double)y / (double)this->height + ); + glyph->bottom_left = glm::vec2( + (double)x / (double)this->width, + 1 - (double)(y + this->cell_height) / (double)this->height + ); + glyph->bottom_right = glm::vec2( + (double)(x + width) / (double)this->width, + 1 - (double)(y + this->cell_height) / (double)this->height + ); + } + + glGenTextures(1, &this->texture); + glBindTexture(GL_TEXTURE_2D, this->texture); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA, + this->width, this->height, 0, + (this->image->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB), + GL_UNSIGNED_BYTE, this->image->pixels + ); + glBindTexture(GL_TEXTURE_2D, 0); + + this->loaded = true; +} + +void sosc::ui::Font::BindBitmap() { + if(!this->loaded) + return; + + glBindTexture(GL_TEXTURE_2D, this->texture); +} + +void sosc::ui::Font::UnbindBitmap() { + if(!this->loaded) + return; + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void sosc::ui::Font::Unload() { + glDeleteTextures(1, &this->texture); + SDL_FreeSurface(this->image); + + this->loaded = false; +} + +// TEXT CLASS // + +sosc::ui::Text::Text() { + this->font = _font_ctx.default_font; + this->font_size = 0; + + glGenVertexArrays(1, &this->vao); + glBindVertexArray(this->vao); + glGenBuffers(2, this->vbos); + glBindVertexArray(0); +} + +sosc::ui::Text::Text(sosc::ui::Font *font, uint32_t size) : Text() { + this->font = font; + this->font_size = size; +} + +sosc::ui::Text::Text(uint32_t size, const std::string &text, + uint32_t x, uint32_t y, uint32_t w) : Text() +{ + this->Set(size, text, x, y, w); +} + +sosc::ui::Text::Text(sosc::ui::Font *font, uint32_t size, + const std::string &text, uint32_t x, uint32_t y, uint32_t w) : Text() +{ + this->font = font; + this->Set(size, text, x, y, w); +} + +void sosc::ui::Text::Set + (uint32_t size, const std::string &text, uint32_t x, uint32_t y, uint32_t w) +{ + this->font_size = size; + this->text = text; + if(w != 0) + this->wrap_width = w; + this->trans_matrix = glm::translate(glm::mat4(1.f), glm::vec3(x, y, 0.f)); + this->Redraw(); +} + +void sosc::ui::Text::SetFont(Font *font, uint32_t size) { + this->font = font; + if(size != 0) + this->font_size = size; + this->Redraw(); +} + +void sosc::ui::Text::SetFontSize(uint32_t size) { + this->font_size = size; + this->Redraw(); +} + +void sosc::ui::Text::SetText(const std::string &text) { + this->text = text; + this->Redraw(); +} + +void sosc::ui::Text::SetPosition(uint32_t x, uint32_t y) { + this->trans_matrix = glm::translate(glm::mat4(1.f), glm::vec3(x, y, 0.f)); +} + +void sosc::ui::Text::SetWrapWidth(uint32_t w) { + this->wrap_width = w; + this->Redraw(); +} + +void sosc::ui::Text::Render() { + glBindVertexArray(this->vao); + glDrawArrays(GL_TRIANGLES, 0, this->vertex_count); + glBindVertexArray(0); +} + +void sosc::ui::Text::Destroy() { + glDeleteBuffers(2, this->vbos); + glDeleteVertexArrays(1, &this->vao); +} + +void sosc::ui::Text::Redraw() { + this->vertex_count = ; + uint32_t line_width = 0, top_x = 0, top_y = 0; + for(const auto c : this->text) { + auto glyph = (*this->font)[c]; + uint32_t width = (uint32_t)(this->font_size * glyph.width), + height = this->font_size; + + + } +} \ No newline at end of file diff --git a/src/client/ui/font.hpp b/src/client/ui/font.hpp new file mode 100644 index 0000000..c233f71 --- /dev/null +++ b/src/client/ui/font.hpp @@ -0,0 +1,111 @@ +#ifndef SOSC_UI_FONT_H +#define SOSC_UI_FONT_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "utils/net.hpp" +#include "shaders/_shader.hpp" + +namespace sosc { +namespace ui { +class Font; +bool font_init_subsystem(SDL_Window* window); +void font_set_default(sosc::ui::Font* font); +void font_window_changed(SDL_Window* window); +void font_deinit_subsystem(); + +class FontException : public std::exception { +public: + explicit FontException + (const std::string& bitmapPath, const std::string& dataPath) + { + std::stringstream ss; + ss << "Could not load bitmap file '" << bitmapPath << "' " + << "and/or data file '" << dataPath << "'."; + + this->errorInfo = ss.str(); + } + + const std::string& what() noexcept { + return this->errorInfo; + } +private: + std::string errorInfo; +}; + +class Font { +public: + struct glyph_t { + double width; + glm::vec2 + top_left, + top_right, + bottom_left, + bottom_right; + }; + + Font() : loaded(false) {} + Font(const std::string& bitmapPath, const std::string& dataPath); + bool Load(const std::string& bitmapPath, const std::string& dataPath); + + inline const glyph_t& operator[] (char c) const { + return this->glyphs[(uint8_t)c]; + } + + void Unload(); +private: + void BindBitmap(); + void UnbindBitmap(); + + bool loaded; + GLuint texture; + SDL_Surface* image; + uint32_t + width, height, + cell_width, cell_height; + glyph_t glyphs[256]; + + friend class Text; +}; + +class Text { +public: + Text(); + Text(Font* font, uint32_t size); + Text(uint32_t size, const std::string& text, + uint32_t x, uint32_t y, uint32_t w = 0); + Text(Font* font, uint32_t size, const std::string& text, + uint32_t x, uint32_t y, uint32_t w = 0); + + void Set(uint32_t size, const std::string& text, + uint32_t x, uint32_t y, uint32_t w = 0); + void SetFont(Font* font, uint32_t size = 0); + void SetFontSize(uint32_t size); + void SetText(const std::string& text); + void SetPosition(uint32_t x, uint32_t y); + void SetWrapWidth(uint32_t w); + + void Render(); + + void Destroy(); +private: + void Redraw(); + + Font* font; + uint32_t font_size, + wrap_width; + std::string text; + glm::mat4 trans_matrix; + GLsizei vertex_count; + GLuint vao, vbos[2]; +}; +}} + +#endif diff --git a/src/client/ui/text.cpp b/src/client/ui/text.cpp deleted file mode 100644 index f511576..0000000 --- a/src/client/ui/text.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "text.hpp" - -// FONT CLASS // - -bool sosc::ui::Font::Load - (const std::string& filePath, const std::string& dataPath) -{ - if(this->loaded) - this->Unload(); - - SDL_RWops* rwop = SDL_RWFromFile(filePath.c_str(), "rb"); - this->image = SDL_LoadBMP_RW(rwop, 1); - if(!this->image) - return false; - - char buffer[0x111]; - std::ifstream dataFile(dataPath, std::ios::in | std::ios::binary); - if(!dataFile.is_open()) - return false; - dataFile.read(buffer, 0x111); - dataFile.close(); - std::string data(buffer, 0x111); - if(buffer[0x10] != 0) - return false; - - this->width = (uint32_t)this->image->w; - this->height = (uint32_t)this->image->h; - this->cell_width = sosc::net::ntohv(data, 0x08); - this->cell_height = sosc::net::ntohv(data, 0x0C); - - for(int i = 0; i < 256; ++i) { - auto glyph = &this->glyphs[i]; - auto width = (uint8_t)buffer[0x11 + i]; - - glyph->width = (double)width / (double)this->cell_width; - - int x = (this->cell_width * i) % this->width; - int y = ((this->cell_width * i) / this->width) * this->cell_height; - - glyph->top_left = glm::vec2( - (double)x / (double)this->width, - 1 - (double)y / (double)this->height - ); - glyph->top_right = glm::vec2( - (double)(x + width) / (double)this->width, - 1 - (double)y / (double)this->height - ); - glyph->bottom_left = glm::vec2( - (double)x / (double)this->width, - 1 - (double)(y + this->cell_height) / (double)this->height - ); - glyph->bottom_right = glm::vec2( - (double)(x + width) / (double)this->width, - 1 - (double)(y + this->cell_height) / (double)this->height - ); - } - - glGenTextures(1, &this->texture); - glBindTexture(GL_TEXTURE_2D, this->texture); - glTexImage2D( - GL_TEXTURE_2D, 0, GL_RGBA, - this->width, this->height, 0, - (this->image->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB), - GL_UNSIGNED_BYTE, this->image->pixels - ); - glBindTexture(GL_TEXTURE_2D, 0); - - this->loaded = true; -} - -void sosc::ui::Font::Unload() { - glDeleteTextures(1, &this->texture); - SDL_FreeSurface(this->image); - - this->loaded = false; -} - -// TEXT CLASS // - diff --git a/src/client/ui/text.hpp b/src/client/ui/text.hpp deleted file mode 100644 index 593fdd8..0000000 --- a/src/client/ui/text.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef SOSC_UI_TEXT_H -#define SOSC_UI_TEXT_H - -#include -#include -#include -#include - -#include -#include -#include "utils/net.hpp" - -namespace sosc { -namespace ui { -class Font { -public: - struct glyph_t { - double width; - glm::vec2 - top_left, - top_right, - bottom_left, - bottom_right; - }; - - Font() : loaded(false) {} - bool Load(const std::string& bitmapPath, const std::string& dataPath); - - inline const glyph_t& operator[] (char c) { - return this->glyphs[(uint8_t)c]; - } - - - - void Unload(); -private: - bool loaded; - GLuint texture; - SDL_Surface* image; - uint32_t - width, height, - cell_width, cell_height; - glyph_t glyphs[256]; -}; - -class Text { -public: - Text(Font* font) : loaded(false) {} - - -private: - Font* font; - GLuint vao, vbo[2]; - bool loaded; -}; -}} - -#endif