fix alpha blending + add depth/stencil buffers
This commit is contained in:
@@ -261,6 +261,10 @@ void show_color_debug_image(std::string name, const char *data, size_t logical_w
|
||||
float x_scale = (float)fb_width / (float)width;
|
||||
float y_scale = (float)fb_height / (float)height;
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glPixelZoom(x_scale, -y_scale);
|
||||
glRasterPos2f(-1.0f, 1.0f);
|
||||
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace llmr {
|
||||
|
||||
class Source;
|
||||
|
||||
typedef std::map<std::string, const std::unique_ptr<Source>> Sources;
|
||||
|
||||
class Map : private util::noncopyable {
|
||||
public:
|
||||
explicit Map(View &view);
|
||||
@@ -96,6 +98,7 @@ public:
|
||||
inline uv_loop_t *getLoop() { return loop; }
|
||||
inline time getAnimationTime() const { return animationTime; }
|
||||
inline Texturepool &getTexturepool() { return texturepool; }
|
||||
inline const Sources &getSources() { return sources; }
|
||||
|
||||
private:
|
||||
// uv async callbacks
|
||||
@@ -108,7 +111,7 @@ private:
|
||||
void loadStyle(const uint8_t *const data, uint32_t bytes);
|
||||
|
||||
void updateTiles();
|
||||
void updateClippingIDs();
|
||||
void updateRenderState();
|
||||
|
||||
size_t countLayers(const std::vector<LayerDescription>& layers);
|
||||
|
||||
@@ -123,11 +126,6 @@ private:
|
||||
void renderLayers(const std::vector<LayerDescription>& layers);
|
||||
void renderLayer(const LayerDescription& layer_desc, RenderPass pass);
|
||||
|
||||
void clearFramebuffers();
|
||||
void bindFramebuffer();
|
||||
void pushFramebuffer();
|
||||
GLuint popFramebuffer();
|
||||
|
||||
private:
|
||||
// If cleared, the next time the render thread attempts to render the map, it will *actually*
|
||||
// render the map.
|
||||
@@ -152,17 +150,13 @@ private:
|
||||
SpriteAtlas spriteAtlas;
|
||||
Painter painter;
|
||||
|
||||
std::map<std::string, const std::unique_ptr<Source>> sources;
|
||||
Sources sources;
|
||||
|
||||
bool debug = false;
|
||||
time animationTime = 0;
|
||||
|
||||
int indent = 0;
|
||||
|
||||
std::vector<GLuint> fbos;
|
||||
std::vector<GLuint> fbos_color;
|
||||
int fbo_level = -1;
|
||||
|
||||
private:
|
||||
bool async = false;
|
||||
uv_loop_t *loop = nullptr;
|
||||
|
||||
@@ -36,7 +36,9 @@ public:
|
||||
|
||||
|
||||
bool update();
|
||||
size_t prepareRender(const TransformState &transform);
|
||||
void updateMatrices(const TransformState &transform);
|
||||
void drawClippingMasks();
|
||||
size_t getTileCount() const;
|
||||
void render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc);
|
||||
void finishRender();
|
||||
|
||||
|
||||
@@ -22,12 +22,15 @@
|
||||
|
||||
#include <llmr/map/transform_state.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Transform;
|
||||
class Style;
|
||||
class Tile;
|
||||
class GlyphAtlas;
|
||||
class Source;
|
||||
|
||||
class FillBucket;
|
||||
class LineBucket;
|
||||
@@ -38,9 +41,12 @@ class RasterBucket;
|
||||
class LayerDescription;
|
||||
class RasterTileData;
|
||||
|
||||
typedef std::map<std::string, const std::unique_ptr<Source>> Sources;
|
||||
|
||||
class Painter : private util::noncopyable {
|
||||
public:
|
||||
Painter(Map &map);
|
||||
~Painter();
|
||||
|
||||
|
||||
void setup();
|
||||
@@ -76,17 +82,20 @@ public:
|
||||
void setDebug(bool enabled);
|
||||
|
||||
// Opaque/Translucent pass setting
|
||||
void startOpaquePass();
|
||||
void startTranslucentPass();
|
||||
void endPass();
|
||||
void setOpaque();
|
||||
void setTranslucent();
|
||||
|
||||
// Configures the painter strata that is used for early z-culling of fragments.
|
||||
void setStrata(float strata);
|
||||
|
||||
void prepareClippingMask();
|
||||
void drawClippingMasks(const Sources &sources);
|
||||
void drawClippingMask(const mat4& matrix, const ClipID& clip);
|
||||
void finishClippingMask();
|
||||
|
||||
void clearFramebuffers();
|
||||
void resetFramebuffer();
|
||||
void bindFramebuffer();
|
||||
void pushFramebuffer();
|
||||
GLuint popFramebuffer();
|
||||
void drawComposite(GLuint texture, const CompositeProperties &properties);
|
||||
|
||||
bool needsAnimation() const;
|
||||
@@ -163,6 +172,13 @@ private:
|
||||
|
||||
VertexArrayObject tileBorderArray;
|
||||
|
||||
// Framebuffer management
|
||||
std::vector<GLuint> fbos;
|
||||
std::vector<GLuint> fbos_color;
|
||||
GLuint fbo_depth_stencil;
|
||||
int fbo_level = -1;
|
||||
bool fbo_depth_stencil_valid = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -55,11 +55,23 @@ bool SpriteAtlas::resize(const float newRatio) {
|
||||
void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y,
|
||||
uint32_t *dst, const int dst_stride, const int dst_x, const int dst_y,
|
||||
const int width, const int height) {
|
||||
const int stride = width * sizeof(uint32_t);
|
||||
src += src_y * src_stride + src_x;
|
||||
dst += dst_y * dst_stride + dst_x;
|
||||
for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
|
||||
memcpy(dst, src, stride);
|
||||
for (int x = 0; x < width; x++) {
|
||||
const uint8_t *s = reinterpret_cast<const uint8_t *>(src + x);
|
||||
uint8_t *d = reinterpret_cast<uint8_t *>(dst + x);
|
||||
|
||||
// Premultiply the bitmap.
|
||||
// Note: We don't need to clamp the component values to 0..255, since
|
||||
// the source value is already 0..255 and the operation means they will
|
||||
// stay within the range of 0..255 and won't overflow.
|
||||
const uint8_t a = s[3];
|
||||
d[0] = s[0] * a / 255;
|
||||
d[1] = s[1] * a / 255;
|
||||
d[2] = s[2] * a / 255;
|
||||
d[3] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
133
src/map/map.cpp
133
src/map/map.cpp
@@ -35,8 +35,6 @@ Map::Map(View& view)
|
||||
}
|
||||
|
||||
Map::~Map() {
|
||||
clearFramebuffers();
|
||||
|
||||
uv_loop_delete(loop);
|
||||
loop = nullptr;
|
||||
}
|
||||
@@ -185,10 +183,15 @@ void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width
|
||||
if (transform.resize(width, height, ratio, fb_width, fb_height)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// TODO: Make sure we're not currently rendering anything
|
||||
if (spriteAtlas.resize(ratio)) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// TODO: Make sure we're not currently rendering anything
|
||||
painter.clearFramebuffers();
|
||||
|
||||
if (changed) {
|
||||
update();
|
||||
}
|
||||
@@ -369,24 +372,23 @@ void Map::updateTiles() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Map::updateClippingIDs() {
|
||||
void Map::updateRenderState() {
|
||||
std::forward_list<Tile::ID> ids;
|
||||
|
||||
for (auto &pair : sources) {
|
||||
const std::unique_ptr<Source> &source = pair.second;
|
||||
if (source->enabled) {
|
||||
ids.splice_after(ids.before_begin(), source->getIDs());
|
||||
for (const auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
if (source.enabled) {
|
||||
ids.splice_after(ids.before_begin(), source.getIDs());
|
||||
source.updateMatrices(state);
|
||||
}
|
||||
}
|
||||
|
||||
const std::map<Tile::ID, ClipID> clipIDs = computeClipIDs(ids);
|
||||
|
||||
for (auto &pair : sources) {
|
||||
const std::unique_ptr<Source> &source = pair.second;
|
||||
if (source->enabled) {
|
||||
source->updateClipIDs(clipIDs);
|
||||
for (const auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
if (source.enabled) {
|
||||
source.updateClipIDs(clipIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -413,8 +415,6 @@ void Map::prepare() {
|
||||
}
|
||||
|
||||
updateTiles();
|
||||
|
||||
updateClippingIDs();
|
||||
}
|
||||
|
||||
void Map::render() {
|
||||
@@ -422,26 +422,25 @@ void Map::render() {
|
||||
|
||||
painter.clear();
|
||||
|
||||
painter.resetFramebuffer();
|
||||
|
||||
painter.resize();
|
||||
|
||||
painter.changeMatrix();
|
||||
|
||||
// First, update the sources' tile matrices with the new
|
||||
// transform and render into the stencil buffer, including
|
||||
// drawing the background.
|
||||
updateRenderState();
|
||||
|
||||
painter.prepareClippingMask();
|
||||
painter.drawClippingMasks(sources);
|
||||
|
||||
// Generate debug information
|
||||
size_t source_id = 0;
|
||||
for (auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
size_t count = source.prepareRender(state);
|
||||
for (const auto &pair : sources) {
|
||||
const Source &source = *pair.second;
|
||||
size_t count = source.getTileCount();
|
||||
debug.emplace_back(util::sprintf<100>("source %d: %d\n", source_id, count));
|
||||
source_id++;
|
||||
}
|
||||
|
||||
painter.finishClippingMask();
|
||||
|
||||
// Actually render the layers
|
||||
if (debug::renderTree) { std::cout << "{" << std::endl; indent++; }
|
||||
renderLayers(style.layers);
|
||||
@@ -451,7 +450,7 @@ void Map::render() {
|
||||
// This guarantees that we have at least one function per tile called.
|
||||
// When only rendering layers via the stylesheet, it's possible that we don't
|
||||
// ever visit a tile during rendering.
|
||||
for (auto &pair : sources) {
|
||||
for (const auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
const std::string &name = pair.first;
|
||||
gl::group group(std::string("debug ") + name);
|
||||
@@ -481,14 +480,14 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
|
||||
if (debug::renderTree) {
|
||||
std::cout << std::string(indent++ * 4, ' ') << "OPAQUE {" << std::endl;
|
||||
}
|
||||
painter.startOpaquePass();
|
||||
typedef std::vector<LayerDescription>::const_reverse_iterator riterator;
|
||||
int i = 0;
|
||||
for (riterator it = layers.rbegin(), end = layers.rend(); it != end; ++it, ++i) {
|
||||
painter.setOpaque();
|
||||
painter.setStrata(i * strata_thickness);
|
||||
renderLayer(*it, Opaque);
|
||||
}
|
||||
painter.endPass();
|
||||
// painter.endPass();
|
||||
if (debug::renderTree) {
|
||||
std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
|
||||
}
|
||||
@@ -499,14 +498,14 @@ void Map::renderLayers(const std::vector<LayerDescription>& layers) {
|
||||
if (debug::renderTree) {
|
||||
std::cout << std::string(indent++ * 4, ' ') << "TRANSLUCENT {" << std::endl;
|
||||
}
|
||||
painter.startTranslucentPass();
|
||||
typedef std::vector<LayerDescription>::const_iterator iterator;
|
||||
--i;
|
||||
for (iterator it = layers.begin(), end = layers.end(); it != end; ++it, --i) {
|
||||
painter.setTranslucent();
|
||||
painter.setStrata(i * strata_thickness);
|
||||
renderLayer(*it, Translucent);
|
||||
}
|
||||
painter.endPass();
|
||||
// painter.endPass();
|
||||
if (debug::renderTree) {
|
||||
std::cout << std::string(--indent * 4, ' ') << "}" << std::endl;
|
||||
}
|
||||
@@ -554,13 +553,11 @@ void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
|
||||
<< " (Composite) {" << std::endl;
|
||||
}
|
||||
|
||||
pushFramebuffer();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
painter.pushFramebuffer();
|
||||
|
||||
renderLayers(layer_desc.child_layer);
|
||||
|
||||
GLuint texture = popFramebuffer();
|
||||
GLuint texture = painter.popFramebuffer();
|
||||
|
||||
// Render the previous texture onto the screen.
|
||||
painter.drawComposite(texture, properties);
|
||||
@@ -630,73 +627,3 @@ void Map::renderLayer(const LayerDescription& layer_desc, RenderPass pass) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::clearFramebuffers() {
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
|
||||
// Delete any framebuffers that we might have allocated
|
||||
glDeleteTextures(fbos_color.size(), fbos.data());
|
||||
fbos_color.clear();
|
||||
|
||||
glDeleteFramebuffersEXT(fbos.size(), fbos.data());
|
||||
fbos.clear();
|
||||
}
|
||||
|
||||
void Map::bindFramebuffer() {
|
||||
if (fbo_level < 0) {
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
} else if (fbos.size() > (size_t)fbo_level) {
|
||||
GLuint fbo = fbos[fbo_level];
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
|
||||
} else {
|
||||
// TODO: Convert this to textures so we can composite them and use them in blend operations
|
||||
|
||||
GLuint fbo_color;
|
||||
glGenTextures(1, &fbo_color);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_color);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, state.getFramebufferWidth(), state.getFramebufferHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
fbos_color.emplace_back(fbo_color);
|
||||
|
||||
// TODO: Stencil/Depth buffer
|
||||
|
||||
// Allocate new framebuffer
|
||||
GLuint fbo = 0;
|
||||
glGenFramebuffersEXT(1, &fbo);
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
|
||||
fbos.emplace_back(fbo);
|
||||
|
||||
// Assemble the FBO
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_color, 0);
|
||||
// TODO: Stencil/Depth buffer
|
||||
|
||||
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
|
||||
fprintf(stderr, "Couldn't create framebuffer: ");
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: fprintf(stderr, "incomplete attachment\n"); break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: fprintf(stderr, "incomplete missing attachment\n"); break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: fprintf(stderr, "incomplete draw buffer\n"); break;
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED: fprintf(stderr, "unsupported\n"); break;
|
||||
default: fprintf(stderr, "other\n"); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Map::pushFramebuffer() {
|
||||
fbo_level++;
|
||||
bindFramebuffer();
|
||||
}
|
||||
|
||||
GLuint Map::popFramebuffer() {
|
||||
GLuint texture = fbos_color[fbo_level];
|
||||
fbo_level--;
|
||||
bindFramebuffer();
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -45,27 +45,31 @@ void Source::updateClipIDs(const std::map<Tile::ID, ClipID> &mapping) {
|
||||
});
|
||||
}
|
||||
|
||||
size_t Source::prepareRender(const TransformState &transform) {
|
||||
if (!enabled) return 0;
|
||||
void Source::updateMatrices(const TransformState &transform) {
|
||||
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
Tile &tile = *pair.second;
|
||||
transform.matrixFor(tile.matrix, tile.id);
|
||||
matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
|
||||
}
|
||||
}
|
||||
|
||||
size_t masks = 0;
|
||||
size_t Source::getTileCount() const {
|
||||
return tiles.size();
|
||||
}
|
||||
|
||||
void Source::drawClippingMasks() {
|
||||
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
Tile &tile = *pair.second;
|
||||
gl::group group(util::sprintf<32>("mask %d/%d/%d", tile.id.z, tile.id.y, tile.id.z));
|
||||
transform.matrixFor(tile.matrix, tile.id);
|
||||
matrix::multiply(tile.matrix, painter.projMatrix, tile.matrix);
|
||||
painter.drawClippingMask(tile.matrix, tile.clip);
|
||||
masks++;
|
||||
}
|
||||
|
||||
return masks;
|
||||
}
|
||||
|
||||
void Source::render(const LayerDescription& layer_desc, const BucketDescription &bucket_desc) {
|
||||
if (!enabled) return;
|
||||
|
||||
gl::group group(std::string("layer: ") + layer_desc.name);
|
||||
for (std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
for (const std::pair<const Tile::ID, std::unique_ptr<Tile>> &pair : tiles) {
|
||||
Tile &tile = *pair.second;
|
||||
if (tile.data && tile.data->state == TileData::State::parsed) {
|
||||
painter.renderTileLayer(tile, layer_desc);
|
||||
|
||||
@@ -20,6 +20,10 @@ Painter::Painter(Map &map)
|
||||
: map(map) {
|
||||
}
|
||||
|
||||
Painter::~Painter() {
|
||||
clearFramebuffers();
|
||||
}
|
||||
|
||||
bool Painter::needsAnimation() const {
|
||||
return frameHistory.needsAnimation(300);
|
||||
}
|
||||
@@ -133,24 +137,18 @@ void Painter::clear() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void Painter::startOpaquePass() {
|
||||
gl::start_group("opaque pass");
|
||||
void Painter::setOpaque() {
|
||||
pass = Opaque;
|
||||
glDisable(GL_BLEND);
|
||||
depthMask(true);
|
||||
}
|
||||
|
||||
void Painter::startTranslucentPass() {
|
||||
gl::start_group("translucent pass");
|
||||
void Painter::setTranslucent() {
|
||||
pass = Translucent;
|
||||
glEnable(GL_BLEND);
|
||||
depthMask(false);
|
||||
}
|
||||
|
||||
void Painter::endPass() {
|
||||
gl::end_group();
|
||||
}
|
||||
|
||||
void Painter::setStrata(float value) {
|
||||
strata = value;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/map/source.hpp>
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::prepareClippingMask() {
|
||||
gl::start_group("clipping masks");
|
||||
void Painter::drawClippingMasks(const Sources &sources) {
|
||||
gl::group group("clipping masks");
|
||||
|
||||
useProgram(plainShader->program);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
depthMask(false);
|
||||
glDepthRange(1.0f, 1.0f);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
Color background = map.getStyle().computed.background.color;
|
||||
background[0] *= map.getStyle().computed.background.opacity;
|
||||
background[1] *= map.getStyle().computed.background.opacity;
|
||||
background[2] *= map.getStyle().computed.background.opacity;
|
||||
background[3] *= map.getStyle().computed.background.opacity;
|
||||
plainShader->setColor(background);
|
||||
|
||||
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
|
||||
|
||||
for (const auto &pair : sources) {
|
||||
Source &source = *pair.second;
|
||||
if (source.enabled) {
|
||||
source.drawClippingMasks();
|
||||
}
|
||||
}
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
depthMask(true);
|
||||
glStencilMask(0x0);
|
||||
}
|
||||
|
||||
void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
|
||||
@@ -21,22 +42,6 @@ void Painter::drawClippingMask(const mat4& matrix, const ClipID &clip) {
|
||||
GLint id = clip.mask.to_ulong();
|
||||
GLuint mask = clipMask[clip.length];
|
||||
glStencilFunc(GL_ALWAYS, id, mask);
|
||||
glStencilMask(0xFF);
|
||||
|
||||
Color background = map.getStyle().computed.background.color;
|
||||
background[0] *= map.getStyle().computed.background.opacity;
|
||||
background[1] *= map.getStyle().computed.background.opacity;
|
||||
background[2] *= map.getStyle().computed.background.opacity;
|
||||
background[3] *= map.getStyle().computed.background.opacity;
|
||||
|
||||
plainShader->setColor(background);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
|
||||
}
|
||||
|
||||
void Painter::finishClippingMask() {
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
depthMask(true);
|
||||
glStencilMask(0x0);
|
||||
gl::end_group();
|
||||
}
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
using namespace llmr;
|
||||
|
||||
void Painter::drawComposite(GLuint texture, const CompositeProperties &properties) {
|
||||
// We're doing full-screen framebuffer blending, so no need to do stencil testing here.
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
// Normally, we're using premultiplied alpha, but the resulting image we've
|
||||
// just drawn is non-premultiplied. That means when blending, we have to make
|
||||
// sure that we account for that.
|
||||
glBlendFunc(GL_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
useProgram(compositeShader->program);
|
||||
compositeShader->setMatrix(nativeMatrix);
|
||||
|
||||
@@ -14,12 +20,19 @@ void Painter::drawComposite(GLuint texture, const CompositeProperties &propertie
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
compositeShader->setImage(0);
|
||||
|
||||
// TODO: layerstyle
|
||||
// TODO: translate
|
||||
compositeShader->setOpacity(properties.opacity);
|
||||
|
||||
// Draw the clipping mask
|
||||
// Draw the full screen texture.
|
||||
// TODO: Do more blend effects like multiply + blur
|
||||
compositeArray.bind(*compositeShader, tileStencilBuffer, BUFFER_OFFSET(0));
|
||||
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
// Reset back.
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
// Reset back to use premultiplied alpha blending.
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
111
src/renderer/painter_framebuffers.cpp
Normal file
111
src/renderer/painter_framebuffers.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/map/map.hpp>
|
||||
#include <llmr/util/clip_ids.hpp>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
|
||||
void Painter::clearFramebuffers() {
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
|
||||
// Delete any framebuffers that we might have allocated
|
||||
glDeleteTextures(fbos_color.size(), fbos.data());
|
||||
fbos_color.clear();
|
||||
|
||||
if (fbo_depth_stencil != 0) {
|
||||
glDeleteRenderbuffersEXT(1, &fbo_depth_stencil);
|
||||
fbo_depth_stencil = 0;
|
||||
}
|
||||
|
||||
glDeleteFramebuffersEXT(fbos.size(), fbos.data());
|
||||
fbos.clear();
|
||||
}
|
||||
|
||||
void Painter::bindFramebuffer() {
|
||||
if (fbo_level < 0) {
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
} else if (fbos.size() > (size_t)fbo_level) {
|
||||
GLuint fbo = fbos[fbo_level];
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
|
||||
} else {
|
||||
// TODO: Convert this to textures so we can composite them and use them in blend operations
|
||||
|
||||
GLuint fbo_color;
|
||||
glGenTextures(1, &fbo_color);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_color);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, map.getState().getFramebufferWidth(), map.getState().getFramebufferHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
fbos_color.emplace_back(fbo_color);
|
||||
|
||||
// Use a shared depth/stencil buffer for all framebuffer objects
|
||||
if (fbo_depth_stencil == 0) {
|
||||
// Create depth/stencil buffer
|
||||
glGenRenderbuffersEXT(1, &fbo_depth_stencil);
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo_depth_stencil);
|
||||
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, map.getState().getFramebufferWidth(), map.getState().getFramebufferHeight());
|
||||
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
||||
}
|
||||
|
||||
// Allocate new framebuffer
|
||||
GLuint fbo = 0;
|
||||
glGenFramebuffersEXT(1, &fbo);
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
|
||||
fbos.emplace_back(fbo);
|
||||
|
||||
// Assemble the FBO
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo_color, 0);
|
||||
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER_EXT, fbo_depth_stencil);
|
||||
|
||||
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
|
||||
fprintf(stderr, "Couldn't create framebuffer: ");
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: fprintf(stderr, "incomplete attachment\n"); break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: fprintf(stderr, "incomplete missing attachment\n"); break;
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: fprintf(stderr, "incomplete draw buffer\n"); break;
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED: fprintf(stderr, "unsupported\n"); break;
|
||||
default: fprintf(stderr, "other\n"); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::resetFramebuffer() {
|
||||
if (fbo_level >= 0) {
|
||||
fbo_level = -1;
|
||||
bindFramebuffer();
|
||||
}
|
||||
|
||||
fbo_depth_stencil_valid = false;
|
||||
}
|
||||
|
||||
void Painter::pushFramebuffer() {
|
||||
fbo_level++;
|
||||
bindFramebuffer();
|
||||
|
||||
if (fbo_depth_stencil_valid) {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
} else {
|
||||
depthMask(true);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
glColorMask(false, false, false, false);
|
||||
|
||||
drawClippingMasks(map.getSources());
|
||||
|
||||
glColorMask(true, true, true, true);
|
||||
fbo_depth_stencil_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint Painter::popFramebuffer() {
|
||||
GLuint texture = fbos_color[fbo_level];
|
||||
fbo_level--;
|
||||
bindFramebuffer();
|
||||
return texture;
|
||||
}
|
||||
@@ -4,5 +4,6 @@ uniform float u_opacity;
|
||||
varying vec2 v_pos;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = texture2D(u_image, v_pos / 2.0 + 0.5) * u_opacity;
|
||||
vec4 color = texture2D(u_image, v_pos);
|
||||
gl_FragColor = color * u_opacity;
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ varying vec2 v_pos;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_matrix * vec4(a_pos, 0, 1);
|
||||
v_pos = gl_Position.xy;
|
||||
v_pos = (gl_Position.xy + 1.0) / 2.0;
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ using namespace llmr;
|
||||
|
||||
const shader_source llmr::shaders[SHADER_COUNT] = {
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = tmpvar_2.xy;\n}\n\n",
|
||||
"#version 120\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, ((v_pos / 2.0) + 0.5)) * u_opacity);\n}\n\n",
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvarying vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = ((tmpvar_2.xy + 1.0) / 2.0);\n}\n\n",
|
||||
"#version 120\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n gl_FragColor = (texture2D (u_image, v_pos) * u_opacity);\n}\n\n",
|
||||
},
|
||||
{
|
||||
"#version 120\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
|
||||
|
||||
@@ -7,8 +7,8 @@ using namespace llmr;
|
||||
|
||||
const shader_source llmr::shaders[SHADER_COUNT] = {
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvarying highp vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = tmpvar_2.xy;\n}\n\n",
|
||||
"precision highp float;\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n lowp vec4 tmpvar_1;\n tmpvar_1 = (texture2D (u_image, ((v_pos / 2.0) + 0.5)) * u_opacity);\n gl_FragColor = tmpvar_1;\n}\n\n",
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nvarying highp vec2 v_pos;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n vec4 tmpvar_2;\n tmpvar_2 = (u_matrix * tmpvar_1);\n gl_Position = tmpvar_2;\n v_pos = ((tmpvar_2.xy + 1.0) / 2.0);\n}\n\n",
|
||||
"precision highp float;\nuniform sampler2D u_image;\nuniform float u_opacity;\nvarying vec2 v_pos;\nvoid main ()\n{\n lowp vec4 tmpvar_1;\n tmpvar_1 = (texture2D (u_image, v_pos) * u_opacity);\n gl_FragColor = tmpvar_1;\n}\n\n",
|
||||
},
|
||||
{
|
||||
"precision highp float;\nattribute vec2 a_pos;\nuniform mat4 u_matrix;\nuniform float u_size;\nvoid main ()\n{\n vec4 tmpvar_1;\n tmpvar_1.zw = vec2(0.0, 1.0);\n tmpvar_1.xy = a_pos;\n gl_Position = (u_matrix * tmpvar_1);\n gl_PointSize = u_size;\n}\n\n",
|
||||
|
||||
Reference in New Issue
Block a user