Files
MapLibreParticleLab/src/renderer/painter.cpp
Konstantin Käfer 209dd913f9 tile => Tile
2014-01-20 12:03:36 +01:00

235 lines
7.0 KiB
C++

#include <llmr/renderer/painter.hpp>
#include <iostream>
#include <assert.h>
#include <llmr/util/mat4.hpp>
#include <llmr/platform/platform.hpp>
#include <llmr/map/transform.hpp>
#include <llmr/map/tile.hpp>
#include <llmr/platform/gl.hpp>
#include <llmr/map/settings.hpp>
using namespace llmr;
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
GLshort tile_stencil_vertices[] = {
0, 0,
4096, 0,
0, 4096,
4096, 4096
};
GLshort tile_border_vertices[] = {
0, 0,
4096, 0,
4096, 4096,
0, 4096,
0, 0
};
Painter::Painter(Transform& transform, Settings& settings)
: transform(transform),
settings(settings),
currentShader(NULL),
fillShader(NULL),
lineShader(NULL),
outlineShader(NULL) {
}
void Painter::setup() {
setupShaders();
assert(fillShader);
assert(lineShader);
assert(outlineShader);
// Set up the stencil quad we're using to generate the stencil mask.
glGenBuffers(1, &tile_stencil_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(tile_stencil_vertices), tile_stencil_vertices, GL_STATIC_DRAW);
// Set up the tile boundary lines we're using to draw the tile outlines.
glGenBuffers(1, &tile_border_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tile_border_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(tile_border_vertices), tile_border_vertices, GL_STATIC_DRAW);
glEnable(GL_STENCIL_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
void Painter::setupShaders() {
fillShader = new FillShader();
lineShader = new LineShader();
outlineShader = new OutlineShader();
}
void Painter::teardown() {
glDeleteBuffers(1, &tile_stencil_buffer);
if (fillShader) {
delete fillShader;
fillShader = NULL;
}
if (lineShader) {
delete lineShader;
lineShader = NULL;
}
}
void Painter::changeMatrix(const Tile::Ptr& tile) {
// Initialize projection matrix
float projMatrix[16];
mat4::ortho(projMatrix, 0, transform.width, transform.height, 0, 1, 10);
float mvMatrix[16];
transform.matrixFor(mvMatrix, tile->id);
mat4::multiply(matrix, projMatrix, mvMatrix);
}
void Painter::drawClippingMask() {
switchShader(lineShader);
glUniformMatrix4fv(lineShader->u_matrix, 1, GL_FALSE, matrix);
glColorMask(false, false, false, false);
// Clear the entire stencil buffer, except for the 7th bit, which stores
// the global clipping mask that allows us to avoid drawing in regions of
// tiles we've already painted in.
glClearStencil(0x0);
glStencilMask(0xBF);
glClear(GL_STENCIL_BUFFER_BIT);
// The stencil test will fail always, meaning we set all pixels covered
// by this geometry to 0x80. We use the highest bit 0x80 to mark the regions
// we want to draw in. All pixels that have this bit *not* set will never be
// drawn in.
glStencilFunc(GL_EQUAL, 0xC0, 0x40);
glStencilMask(0xC0);
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
// Draw the clipping mask
glBindBuffer(GL_ARRAY_BUFFER, tile_stencil_buffer);
glVertexAttribPointer(lineShader->a_pos, 2, GL_SHORT, false, 0, BUFFER_OFFSET(0));
glUniform4f(lineShader->u_color, 1.0f, 0.0f, 1.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(tile_stencil_vertices));
// glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilMask(0x00);
glColorMask(true, true, true, true);
}
void Painter::clear() {
glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
glClearStencil(0x0);
glStencilMask(0xFF);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void Painter::render(const Tile::Ptr& tile) {
if (tile->state != Tile::ready) {
return;
}
changeMatrix(tile);
drawClippingMask();
switchShader(outlineShader);
glUniformMatrix4fv(outlineShader->u_matrix, 1, GL_FALSE, matrix);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// glDisable(GL_STENCIL_TEST);
tile->lineVertex.bind();
glVertexAttribPointer(outlineShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, BUFFER_OFFSET(0));
glUniform4f(outlineShader->u_color, 0.0f, 0.0f, 0.0f, 1.0f);
glUniform2f(outlineShader->u_world, transform.width, transform.height);
glLineWidth(2.0f);
glDrawArrays(GL_LINE_STRIP, 0, tile->lineVertex.length());
if (settings.debug) {
renderDebug(tile);
}
}
void Painter::renderDebug(const Tile::Ptr& tile) {
// draw tile outline
switchShader(lineShader);
glUniformMatrix4fv(lineShader->u_matrix, 1, GL_FALSE, matrix);
glBindBuffer(GL_ARRAY_BUFFER, tile_border_buffer);
glVertexAttribPointer(lineShader->a_pos, 2, GL_SHORT, false, 0, BUFFER_OFFSET(0));
glUniform4f(lineShader->u_color, 1.0f, 1.0f, 1.0f, 1.0f);
glLineWidth(4.0f);
glDrawArrays(GL_LINE_STRIP, 0, sizeof(tile_border_vertices));
// draw debug info
switchShader(lineShader);
glUniformMatrix4fv(lineShader->u_matrix, 1, GL_FALSE, matrix);
tile->debugFontVertex.bind();
glVertexAttribPointer(lineShader->a_pos, 2, GL_SHORT, GL_FALSE, 0, BUFFER_OFFSET(0));
glUniform4f(lineShader->u_color, 1.0f, 1.0f, 1.0f, 1.0f);
glLineWidth(4.0f);
glDrawArrays(GL_LINES, 0, tile->debugFontVertex.length());
glUniform4f(lineShader->u_color, 0.0f, 0.0f, 0.0f, 1.0f);
glLineWidth(2.0f);
glDrawArrays(GL_LINES, 0, tile->debugFontVertex.length());
}
// Switches to a different shader program.
/**
* @return boolean whether the shader was actually switched
*/
bool Painter::switchShader(Shader *shader) {
if (currentShader != shader) {
glUseProgram(shader->program);
// Disable all attributes from the existing shader that aren't used in
// the new shader. Note: attribute indices are *not* program specific!
if (currentShader) {
const std::forward_list<uint32_t> &hitherto = currentShader->attributes;
const std::forward_list<uint32_t> &henceforth = shader->attributes;
// Find all attribute indices that are used in the old shader,
// but unused in the new one.
for (uint32_t i : hitherto) {
if (std::find(henceforth.begin(), henceforth.end(), i) == henceforth.end()) {
glDisableVertexAttribArray(i);
}
}
// Enable all attributes for the new shader that were not already in
// use in the old one.
for (uint32_t i : henceforth) {
if (std::find(hitherto.begin(), hitherto.end(), i) == hitherto.end()) {
glEnableVertexAttribArray(i);
}
}
currentShader = shader;
} else {
// Enable all attributes for the new shader (== first shader).
for (GLuint index : shader->attributes) {
glEnableVertexAttribArray(index);
}
}
currentShader = shader;
return true;
} else {
return false;
}
}