#include "txt.hpp"
#define GLSL(src) "#version 150 core\n" #src
const char* TextRenderer::vert_glsl =
"#version 150\n"
"///shaderType:vertex\n"
"\n"
"in vec4 position;\n"
"out vec2 texPosition;\n"
"\n"
"void main(void) {\n"
" gl_Position = vec4(position.xy, 0.0, 1.0);\n"
" texPosition = position.zw;\n"
"}\n";
const char* TextRenderer::frag_glsl =
"#version 150\n"
"///shaderType:fragment\n"
"\n"
"in vec2 texPosition;\n"
"uniform sampler2D tex;\n"
"const vec4 color = vec4(1, 1, 0, 1);\n"
"out vec4 outColor;\n"
"\n"
"void main(void) {\n"
" outColor = vec4(1, 1, 1, texture2D(tex, texPosition).r) * color;\n"
"}\n";
Program* TextRenderer::get_prog() {
Shader* vert_sh = Shader::getInst(vert_glsl, strlen(vert_glsl));
if (!vert_sh) {
return NULL;
}
Shader* frag_sh = Shader::getInst(frag_glsl, strlen(frag_glsl));
if (!frag_sh) {
delete vert_sh;
return NULL;
}
Program* program = Program::getInst();
if (!program) {
delete vert_sh;
delete frag_sh;
return NULL;
}
if (!program->attach(vert_sh)) {
delete vert_sh;
delete frag_sh;
delete program;
return NULL;
}
if (!program->attach(frag_sh)) {
delete frag_sh;
delete program;
return NULL;
}
glLinkProgram(*program);
GLint rv;
glGetProgramiv(*program, GL_LINK_STATUS, &rv);
if (rv != GL_TRUE) {
delete program;
return NULL;
}
return program;
}
unsigned TextRenderer::max_height() const {
unsigned w, h;
font.get_glyph_max(w, h);
return h;
}
TextRenderer::TextRenderer(): vbo(4), ebo(GL_ELEMENT_ARRAY_BUFFER), program(NULL), texture(NULL) {
assert(!glerr() || !glerr());
program = get_prog();
if (!program) return;
glUseProgram(*program); // GL_INVALID_OPERATION for glUniform1i
vbo.setAttrib(program, "position", 0, 4);
GLint texloc = glGetUniformLocation(*program, "tex");
if (texloc == -1) {
delete program;
program = NULL;
return;
}
texture = new Texture("TextRenderer");
glUniform1i(texloc, texture->index);
GLuint elements[] = {
0, 1, 2,
2, 3, 0
};
ebo.bind(elements, sizeof(elements), GL_STATIC_DRAW);
assert(!glerr());
}
TextRenderer::~TextRenderer() {
if (program) delete program;
if (texture) delete texture;
}
bool TextRenderer::draw(const char* str, unsigned x, unsigned y, const Window* win, unsigned scale) { // resets current program, array buffer, and element array buffer
if (!program) {
return false;
}
glUseProgram(*program);
ebo.bind();
for (const char* p=str; *p; p++) {
// TODO: color, transparency
// TODO: use a single texture with all glyphs?
const unsigned char* g;
unsigned w, h;
font.get_glyph(*p, g, w, h);
if (!texture->set(g, w, h)) {
return false;
}
w *= scale;
h *= scale;
GLfloat x_start = win->px2iso_x(x);
GLfloat y_start = win->px2iso_y(y);
GLfloat x_end = win->px2iso_x(x+w);
GLfloat y_end = win->px2iso_y(y+h);
GLfloat vertices[] = {
x_start, y_end, 0.0f, 0.0f,
x_end, y_end, 1.0f, 0.0f,
x_end, y_start, 1.0f, 1.0f,
x_start, y_start, 0.0f, 1.0f
};
vbo.bind(vertices, 4*4, GL_DYNAMIC_DRAW);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
x += w+1;
}
return true;
}
Logger* Logger::inst = NULL;
const size_t Logger::max_lines = 5;
Logger::~Logger() {
while (!lines.empty()) {
LOG("%s", lines.front());
free(lines.front());
lines.erase(lines.begin());
}
inst = NULL;
}
void Logger::log(const char *fmt, ...) {
char* s;
va_list vl;
va_start(vl, fmt);
if (vasprintf(&s, fmt, vl) <= 0) {
va_end(vl);
return;
}
va_end(vl);
if (!inst || inst->blocked) {
LOG("%s", s);
free(s);
} else {
while (inst->lines.size() >= max_lines) {
free(inst->lines.front());
inst->lines.erase(inst->lines.begin());
}
inst->lines.push_back(s);
inst->dirty = true;
}
}
bool Logger::logall(const Window* win, bool redraw) {
if (blocked || lines.empty()) {
return false;
}
if (!dirty && !redraw) {
return false;
}
static const unsigned scale = 2;
int num = 0;
for (std::vector<char*>::const_iterator it=lines.begin(); it!=lines.end(); ++it) {
int y = win->h - 10 - ((renderer.max_height()*scale + 1) * ++num);
(void)renderer.draw(*it, 10, y, win, scale);
}
dirty = false;
return true; // might have changed some state
}