emscripten works :evil:

This commit is contained in:
MallocNull 2018-09-13 17:11:09 -05:00
parent c880a085ae
commit 2f2a134777
14 changed files with 818 additions and 27 deletions

View file

@ -10,10 +10,18 @@ endif()
set(CMAKE_CXX_STANDARD 11)
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
#if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s USE_SDL=2 --preload-file ../resources/client")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS='[\"bmp\"]'")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -std=c++11 -s USE_SDL=2")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} --preload-file ../resources/client")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} --shell-file ../src/client/shell.html")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS='[\"bmp\"]'")
#endif()
set(CMAKE_EXECUTABLE_SUFFIX ".html")
## CLIENT BUILD ##
#find_package(OpenGL REQUIRED)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 768 KiB

View file

@ -1,14 +1,14 @@
#version 330 core
in vec2 texCoords;
out vec4 fragColor;
precision mediump float;
varying vec2 texCoords;
uniform vec4 fontColor;
uniform sampler2D fontBitmap;
void main() {
vec4 outColor = texture(fontBitmap, texCoords);
vec4 outColor = texture2D(fontBitmap, texCoords);
if(outColor.xyz == vec3(0.0, 0.0, 0.0))
discard;
fragColor = fontColor * outColor;
gl_FragColor = fontColor * outColor;
}

View file

@ -1,8 +1,7 @@
#version 330 core
layout (location = 0) in vec2 aScreenCoords;
layout (location = 1) in vec2 aTexCoords;
attribute vec2 aScreenCoords;
attribute vec2 aTexCoords;
out vec2 texCoords;
varying vec2 texCoords;
uniform mat4 transMatrix;
uniform mat4 orthoMatrix;

View file

@ -1,9 +1,5 @@
#version 330 core
in vec2 texCoords;
out vec4 fragColor;
uniform sampler2D fontBitmap;
precision mediump float;
void main() {
fragColor = texture(fontBitmap, texCoords);
gl_FragColor = vec4(0.5, 0, 0, 1);
}

View file

@ -1,12 +1,5 @@
#version 330 core
layout (location = 0) in vec4 aScreenCoords;
layout (location = 1) in vec2 aTexCoords;
out vec2 texCoords;
uniform mat4 orthoMatrix;
attribute vec2 aPosition;
void main() {
gl_Position = orthoMatrix * aScreenCoords;
texCoords = aTexCoords;
gl_Position = vec4(aPosition, 0, 1);
}

14
src/client/common.hpp Normal file
View file

@ -0,0 +1,14 @@
#ifndef SOSC_CLIENT_COMMON_H
#define SOSC_CLIENT_COMMON_H
#include <string>
#ifdef SOSC_DEBUG
#define SOSC_RESOURCE_PATH (std::string("../resources/client/"))
#else
#define SOSC_RESOURCE_PATH (std::string("resources/"))
#endif
#define SOSC_RESC(X) (SOSC_RESOURCE_PATH + std::string(X))
#endif

View file

@ -2,15 +2,33 @@
#include <GL/glew.h>
#include <emscripten.h>
#include <iostream>
#include <string>
#include "ui/font.hpp"
static struct {
SDL_Window* window;
SDL_GLContext gl_ctx;
sosc::ui::Font* font;
sosc::ui::Text* text;
} _ctx;
void draw();
int main(int argc, char** argv) {
using namespace sosc;
/*if(argc != 3)
return -1;*/
int client_width = std::stoi(argv[1]),
client_height = std::stoi(argv[2]);
std::cout << "VERSION 9: " << argc << std::endl
<< argv[0] << std::endl
<< argv[1] << std::endl
<< argv[2] << std::endl;
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
std::cout << SDL_GetError() << std::endl;
return -1;
@ -21,7 +39,7 @@ int main(int argc, char** argv) {
_ctx.window = SDL_CreateWindow(
"SockScape Client",
0, 0,
640, 480,
client_width, client_height,
SDL_WINDOW_OPENGL
);
@ -32,12 +50,28 @@ int main(int argc, char** argv) {
if(glewInit() != GLEW_OK)
return -1;
ui::font_init_subsystem(_ctx.window);
_ctx.font = new ui::Font(
SOSC_RESC("fonts/scape.bmp"),
SOSC_RESC("fonts/scape.dat")
);
ui::font_set_default(_ctx.font);
_ctx.text = new ui::Text(
75, glm::vec4(1, 0, 0, 1), "test text",
100, 100, 400
);
emscripten_set_main_loop(draw, 0, 1);
std::cout << "end test" << std::endl;
}
void draw() {
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(.25, .25, .25, 1);
_ctx.text->Render();
SDL_GL_SwapWindow(_ctx.window);
}

View file

@ -0,0 +1,109 @@
#include "_shader.hpp"
sosc::shdr::Shader::Shader() : loaded(false) {}
bool sosc::shdr::Shader::Load() {
if(this->loaded)
return true;
this->program = glCreateProgram();
this->PrepareLoad();
this->loaded = true;
return true;
}
void sosc::shdr::Shader::Start() const {
if(!this->loaded)
return;
glUseProgram(this->program);
}
void sosc::shdr::Shader::Stop() const {
if(!this->loaded)
return;
glUseProgram(0);
}
void sosc::shdr::Shader::Unload() {
if(!this->loaded)
return;
PrepareUnload();
glDeleteProgram(this->program);
this->loaded = false;
}
bool sosc::shdr::Shader::AttachSource
(const std::string& fileName, GLuint shaderType)
{
if(this->loaded)
return false;
GLuint shader = glCreateShader(shaderType);
std::ifstream file(fileName);
std::stringstream ss;
ss << file.rdbuf();
std::string source = ss.str();
const char* src = source.c_str();
glShaderSource(shader, 1, &src, nullptr);
glCompileShader(shader);
GLint err_chk;
glGetShaderiv(shader, GL_COMPILE_STATUS, &err_chk);
if(err_chk == GL_FALSE) {
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &err_chk);
auto msg = new char[err_chk];
glGetShaderInfoLog(shader, err_chk, nullptr, msg);
std::cerr << "Error in '"<< fileName << "':" << msg << std::endl;
delete[] msg;
return false;
}
glAttachShader(this->program, shader);
this->shaders.push_back(shader);
return true;
}
bool sosc::shdr::Shader::LinkProgram() {
GLint err_chk;
glLinkProgram(this->program);
glGetProgramiv(this->program, GL_LINK_STATUS, &err_chk);
if(err_chk == GL_FALSE) {
glGetProgramiv(this->program, GL_INFO_LOG_LENGTH, &err_chk);
auto msg = new char[err_chk];
glGetProgramInfoLog(this->program, err_chk, nullptr, msg);
std::cerr << "Error linking shader: " << msg << std::endl;
delete[] msg;
return false;
}
for(const auto& shader : this->shaders)
glDeleteShader(shader);
return true;
}
bool sosc::shdr::Shader::LoadUniforms(std::vector<std::string> names) {
if(this->loaded)
return false;
GLint id;
this->locations.clear();
for(const auto& name : names) {
if((id = glGetUniformLocation(this->program, name.c_str())) == -1)
0; //return false;
this->locations.push_back(id);
}
return true;
}

View file

@ -0,0 +1,52 @@
#ifndef SOSC_SHADER_CORE_H
#define SOSC_SHADER_CORE_H
#include "common.hpp"
#include <GL/glew.h>
#include <exception>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
namespace sosc {
namespace shdr {
class Shader {
public:
Shader();
bool Load();
void Start() const;
inline GLint operator[] (std::vector<GLint>::size_type index) {
if(index >= locations.size())
return -1;
return locations[index];
}
inline GLint Uniform(std::vector<GLint>::size_type index) {
if(index >= locations.size())
return -1;
return locations[index];
}
void Stop() const;
void Unload();
protected:
virtual void PrepareLoad() = 0;
virtual void PrepareUnload() {};
bool AttachSource(const std::string& fileName, GLuint shaderType);
bool LinkProgram();
bool LoadUniforms(std::vector<std::string> names);
private:
std::vector<GLint> locations;
std::vector<GLuint> shaders;
GLuint program;
bool loaded;
};
}}
#endif

View file

@ -0,0 +1,20 @@
#ifndef SOSC_SHADER_TEST_H
#define SOSC_SHADER_TEST_H
#include <SDL.h>
#include "common.hpp"
#include "_shader.hpp"
namespace sosc {
namespace shdr {
class TestShader : public Shader {
protected:
void PrepareLoad() override {
this->AttachSource(SOSC_RESC("shaders/test.vert"), GL_VERTEX_SHADER);
this->AttachSource(SOSC_RESC("shaders/test.frag"), GL_FRAGMENT_SHADER);
this->LinkProgram();
}
};
}}
#endif

54
src/client/shell.html Normal file
View file

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SockScape Client</title>
<style type="text/css">
body {
overflow: hidden;
}
#wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 0;
margin: 0;
}
#wrapper #canvas {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div id="wrapper">
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
</div>
<script type="text/javascript">
var Module = {
preRun: [],
postRun: [],
print: function(text) {
console.log(text);
},
printErr: function(text) {
console.error(text)
},
arguments: [
window.innerWidth.toString(),
window.innerHeight.toString()
],
canvas: document.getElementById("canvas")
};
</script>
{{{ SCRIPT }}}
</body>
</html>

412
src/client/ui/font.cpp Normal file
View file

@ -0,0 +1,412 @@
#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))
namespace sosc {
namespace ui {
class FontShader;
}}
// STATE STRUCT //
static struct {
sosc::ui::FontShader* shader;
sosc::ui::Font* default_font;
} _font_ctx;
// FONT SHADER CLASS //
namespace sosc {
namespace ui {
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.f, (float)width, (float)height, 0.f);
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"
});
}
};
}}
// SUBSYSTEM FUNCS //
void sosc::ui::font_init_subsystem(SDL_Window* window) {
_font_ctx.shader = new FontShader();
_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();
delete _font_ctx.shader;
}
// FONT CLASS //
sosc::ui::Font::Font
(const std::string& bitmapPath, const std::string& dataPath,
bool useNearest)
{
this->loaded = false;
this->Load(bitmapPath, dataPath);
}
bool sosc::ui::Font::Load
(const std::string& bitmapPath, const std::string& dataPath,
bool useNearest)
{
if(this->loaded)
this->Unload();
SDL_RWops* rwop = SDL_RWFromFile(bitmapPath.c_str(), "rb");
SDL_Surface* image = SDL_LoadBMP_RW(rwop, 1);
if(!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)image->w;
this->height = (uint32_t)image->h;
this->cell_width = LILEND_UNPACK(buffer, 0x08);
this->cell_height = 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_RGB,
this->width, this->height, 0,
GL_RGB,
GL_UNSIGNED_BYTE, image->pixels
);
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
useNearest ? GL_NEAREST : GL_LINEAR
);
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
useNearest ? GL_NEAREST : GL_LINEAR
);
glBindTexture(GL_TEXTURE_2D, 0);
SDL_FreeSurface(image);
this->loaded = true;
return true;
}
void sosc::ui::Font::BindBitmap() const {
if(!this->loaded)
return;
glBindTexture(GL_TEXTURE_2D, this->texture);
}
void sosc::ui::Font::UnbindBitmap() const {
if(!this->loaded)
return;
glBindTexture(GL_TEXTURE_2D, 0);
}
void sosc::ui::Font::Unload() {
glDeleteTextures(1, &this->texture);
this->loaded = false;
}
// TEXT CLASS //
sosc::ui::Text::Text() {
this->font = _font_ctx.default_font;
this->font_size = 0;
this->vertices = nullptr;
this->tex_coords = nullptr;
glGenVertexArrays(1, &this->vao);
glGenBuffers(2, this->vbos);
}
sosc::ui::Text::Text
(sosc::ui::Font *font, uint32_t size, const glm::vec4& color) : Text()
{
this->font = font;
this->font_size = size;
this->font_color = color;
}
sosc::ui::Text::Text(uint32_t size, const glm::vec4& color,
const std::string &text, uint32_t x, uint32_t y, uint32_t w) : Text()
{
this->Set(size, color, text, x, y, w);
}
sosc::ui::Text::Text
(sosc::ui::Font *font, uint32_t size, const glm::vec4& color,
const std::string &text, uint32_t x, uint32_t y, uint32_t w) : Text()
{
this->font = font;
this->Set(size, color, text, x, y, w);
}
void sosc::ui::Text::Set(uint32_t size, const glm::vec4& color,
const std::string &text, uint32_t x, uint32_t y, uint32_t w)
{
this->font_size = size;
this->font_color = color;
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::SetFontColor(const glm::vec4 &color) {
this->font_color = color;
}
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();
}
uint32_t sosc::ui::Text::GetHeight() const {
return this->GetLineCount() * this->font_size;
}
uint32_t sosc::ui::Text::GetLineCount() const {
if(this->wrap_width == 0)
return 1;
else {
uint32_t count = 1, topleft_x = 0;
for(const auto c : this->text) {
auto width = (uint32_t)((*this->font)[c].width * this->font_size);
if(topleft_x + width > this->wrap_width) {
++count;
topleft_x = 0;
}
topleft_x += width;
}
return count;
}
}
void sosc::ui::Text::Render() {
auto shdr = _font_ctx.shader;
shdr->Start();
glUniformMatrix4fv(
(*shdr)[shdr->TRANSLATION_MATRIX],
1, GL_FALSE,
glm::value_ptr(this->trans_matrix)
);
glUniform4f(
(*shdr)[shdr->FONT_COLOR],
this->font_color.r, this->font_color.g,
this->font_color.b, this->font_color.a
);
glActiveTexture(GL_TEXTURE0);
this->font->BindBitmap();
glBindVertexArray(this->vao);
glDrawArrays(GL_TRIANGLES, 0, this->vertex_count);
glBindVertexArray(0);
this->font->UnbindBitmap();
shdr->Stop();
}
void sosc::ui::Text::Destroy() {
glDeleteBuffers(2, this->vbos);
glDeleteVertexArrays(1, &this->vao);
delete[] this->vertices;
delete[] this->tex_coords;
}
void sosc::ui::Text::Redraw() {
this->vertex_count = (GLuint)(6 * this->text.length());
delete[] this->vertices;
this->vertices = new float[this->vertex_count * 2];
delete[]this->tex_coords;
this->tex_coords = new float[this->vertex_count * 2];
uint32_t top_x = 0, top_y = 0;
for(int i = 0; i < this->text.length(); ++i) {
auto glyph = (*this->font)[this->text[i]];
uint32_t width = (uint32_t)(this->font_size * glyph.width),
height = this->font_size;
if(top_x + width > this->wrap_width && this->wrap_width != 0) {
top_x = 0;
top_y += height;
}
/// TRIANGLE 1 ///
// TOP LEFT
this->vertices[i*12] = top_x;
this->vertices[i*12 + 1] = top_y;
this->tex_coords[i*12] = glyph.top_left.x;
this->tex_coords[i*12 + 1] = glyph.top_left.y;
// TOP RIGHT
this->vertices[i*12 + 2] = top_x + width;
this->vertices[i*12 + 3] = top_y;
this->tex_coords[i*12 + 2] = glyph.top_right.x;
this->tex_coords[i*12 + 3] = glyph.top_right.y;
// BOTTOM LEFT
this->vertices[i*12 + 4] = top_x;
this->vertices[i*12 + 5] = top_y + height;
this->tex_coords[i*12 + 4] = glyph.bottom_left.x;
this->tex_coords[i*12 + 5] = glyph.bottom_left.y;
/// TRIANGLE 2 ///
// BOTTOM LEFT
this->vertices[i*12 + 6] = top_x;
this->vertices[i*12 + 7] = top_y + height;
this->tex_coords[i*12 + 6] = glyph.bottom_left.x;
this->tex_coords[i*12 + 7] = glyph.bottom_left.y;
// TOP RIGHT
this->vertices[i*12 + 8] = top_x + width;
this->vertices[i*12 + 9] = top_y;
this->tex_coords[i*12 + 8] = glyph.top_right.x;
this->tex_coords[i*12 + 9] = glyph.top_right.y;
// BOTTOM RIGHT
this->vertices[i*12 + 10] = top_x + width;
this->vertices[i*12 + 11] = top_y + height;
this->tex_coords[i*12 + 10] = glyph.bottom_right.x;
this->tex_coords[i*12 + 11] = glyph.bottom_right.y;
top_x += width;
}
glBindVertexArray(this->vao);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, this->vbos[0]);
glBufferData(
GL_ARRAY_BUFFER,
this->vertex_count * 2 * sizeof(float),
this->vertices,
GL_STATIC_DRAW
);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, this->vbos[1]);
glBufferData(
GL_ARRAY_BUFFER,
this->vertex_count * 2 * sizeof(float),
this->tex_coords,
GL_STATIC_DRAW
);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindVertexArray(0);
}

100
src/client/ui/font.hpp Normal file
View file

@ -0,0 +1,100 @@
#ifndef SOSC_UI_FONT_H
#define SOSC_UI_FONT_H
#include <SDL.h>
#include <SDL_image.h>
#include <GL/glew.h>
#include <GLM/glm.hpp>
#include <GLM/gtc/matrix_transform.hpp>
#include <GLM/gtc/type_ptr.hpp>
#include <string>
#include <fstream>
#include "shaders/_shader.hpp"
namespace sosc {
namespace ui {
class Font;
void 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 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 useNearest = true);
bool Load(const std::string& bitmapPath,
const std::string& dataPath, bool useNearest = true);
inline const glyph_t& operator[] (char c) const {
return this->glyphs[(uint8_t)c];
}
void Unload();
private:
void BindBitmap() const;
void UnbindBitmap() const;
bool loaded;
GLuint texture;
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, const glm::vec4& color);
Text(uint32_t size, const glm::vec4& color, const std::string& text,
uint32_t x, uint32_t y, uint32_t w = 0);
Text(Font* font, uint32_t size, const glm::vec4& color,
const std::string& text, uint32_t x, uint32_t y, uint32_t w = 0);
void Set(uint32_t size, const glm::vec4& color, 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 SetFontColor(const glm::vec4& color);
void SetText(const std::string& text);
void SetPosition(uint32_t x, uint32_t y);
void SetWrapWidth(uint32_t w);
uint32_t GetHeight() const;
uint32_t GetLineCount() const;
void Render();
void Destroy();
private:
void Redraw();
Font* font;
glm::vec4 font_color;
uint32_t font_size,
wrap_width;
std::string text;
glm::mat4 trans_matrix;
GLsizei vertex_count;
float* vertices;
float* tex_coords;
GLuint vao, vbos[2];
};
}}
#endif