Files
MapLibreParticleLab/src/mbgl/renderer/painter_fill.cpp
2016-09-29 10:17:47 -07:00

177 lines
8.1 KiB
C++

#include <mbgl/renderer/painter.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/gl/gl.hpp>
#include <mbgl/renderer/fill_bucket.hpp>
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/style/layers/fill_layer.hpp>
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/shader/shaders.hpp>
#include <mbgl/util/convert.hpp>
namespace mbgl {
using namespace style;
void Painter::renderFill(PaintParameters& parameters,
FillBucket& bucket,
const FillLayer& layer,
const RenderTile& tile) {
const FillPaintProperties& properties = layer.impl->paint;
mat4 vertexMatrix = tile.translatedMatrix(properties.fillTranslate,
properties.fillTranslateAnchor,
state);
Color fillColor = properties.fillColor;
float opacity = properties.fillOpacity;
const bool isOutlineColorDefined = !properties.fillOutlineColor.isUndefined();
Color strokeColor = isOutlineColorDefined? properties.fillOutlineColor : fillColor;
auto worldSize = util::convert<GLfloat>(frame.framebufferSize);
bool pattern = !properties.fillPattern.value.from.empty();
bool outline = properties.fillAntialias && !pattern && isOutlineColorDefined;
bool fringeline = properties.fillAntialias && !pattern && !isOutlineColorDefined;
context.stencilOp = { gl::StencilTestOperation::Keep, gl::StencilTestOperation::Keep,
gl::StencilTestOperation::Replace };
context.stencilTest = true;
context.depthFunc = gl::DepthTestFunction::LessEqual;
context.depthTest = true;
context.depthMask = true;
context.lineWidth = 2.0f; // This is always fixed and does not depend on the pixelRatio!
auto& outlineShader = parameters.shaders.outline;
auto& patternShader = parameters.shaders.pattern;
auto& outlinePatternShader = parameters.shaders.outlinePattern;
auto& plainShader = parameters.shaders.plain;
// Because we're drawing top-to-bottom, and we update the stencil mask
// befrom, we have to draw the outline first (!)
if (outline && pass == RenderPass::Translucent) {
context.program = outlineShader.getID();
outlineShader.u_matrix = vertexMatrix;
outlineShader.u_outline_color = strokeColor;
outlineShader.u_opacity = opacity;
// Draw the entire line
outlineShader.u_world = worldSize;
if (isOutlineColorDefined) {
// If we defined a different color for the fill outline, we are
// going to ignore the bits in 0x07 and just care about the global
// clipping mask.
setDepthSublayer(2); // OK
} else {
// Otherwise, we only want to drawFill the antialiased parts that are
// *outside* the current shape. This is important in case the fill
// or stroke color is translucent. If we wouldn't clip to outside
// the current shape, some pixels from the outline stroke overlapped
// the (non-antialiased) fill.
setDepthSublayer(0); // OK
}
bucket.drawVertices(outlineShader, context, paintMode());
}
if (pattern) {
optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(
properties.fillPattern.value.from, SpritePatternMode::Repeating);
optional<SpriteAtlasPosition> imagePosB =
spriteAtlas->getPosition(properties.fillPattern.value.to, SpritePatternMode::Repeating);
// Image fill.
if (pass == RenderPass::Translucent && imagePosA && imagePosB) {
context.program = patternShader.getID();
patternShader.u_matrix = vertexMatrix;
patternShader.u_pattern_tl_a = imagePosA->tl;
patternShader.u_pattern_br_a = imagePosA->br;
patternShader.u_pattern_tl_b = imagePosB->tl;
patternShader.u_pattern_br_b = imagePosB->br;
patternShader.u_opacity = properties.fillOpacity;
patternShader.u_image = 0;
patternShader.u_mix = properties.fillPattern.value.t;
patternShader.u_pattern_size_a = imagePosA->size;
patternShader.u_pattern_size_b = imagePosB->size;
patternShader.u_scale_a = properties.fillPattern.value.fromScale;
patternShader.u_scale_b = properties.fillPattern.value.toScale;
patternShader.u_tile_units_to_pixels = 1.0f / tile.id.pixelsToTileUnits(1.0f, state.getIntegerZoom());
GLint tileSizeAtNearestZoom = util::tileSize * state.zoomScale(state.getIntegerZoom() - tile.id.canonical.z);
GLint pixelX = tileSizeAtNearestZoom * (tile.id.canonical.x + tile.id.wrap * state.zoomScale(tile.id.canonical.z));
GLint pixelY = tileSizeAtNearestZoom * tile.id.canonical.y;
patternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }};
patternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }};
spriteAtlas->bind(true, context, 0);
// Draw the actual triangles into the color & stencil buffer.
setDepthSublayer(0);
bucket.drawElements(patternShader, context, paintMode());
if (properties.fillAntialias && !isOutlineColorDefined) {
context.program = outlinePatternShader.getID();
outlinePatternShader.u_matrix = vertexMatrix;
outlinePatternShader.u_pattern_tl_a = imagePosA->tl;
outlinePatternShader.u_pattern_br_a = imagePosA->br;
outlinePatternShader.u_pattern_tl_b = imagePosB->tl;
outlinePatternShader.u_pattern_br_b = imagePosB->br;
outlinePatternShader.u_opacity = properties.fillOpacity;
outlinePatternShader.u_image = 0;
outlinePatternShader.u_mix = properties.fillPattern.value.t;
outlinePatternShader.u_pattern_size_a = imagePosA->size;
outlinePatternShader.u_pattern_size_b = imagePosB->size;
outlinePatternShader.u_scale_a = properties.fillPattern.value.fromScale;
outlinePatternShader.u_scale_b = properties.fillPattern.value.toScale;
outlinePatternShader.u_tile_units_to_pixels = 1.0f / tile.id.pixelsToTileUnits(1.0f, state.getIntegerZoom());
outlinePatternShader.u_pixel_coord_upper = {{ float(pixelX >> 16), float(pixelY >> 16) }};
outlinePatternShader.u_pixel_coord_lower = {{ float(pixelX & 0xFFFF), float(pixelY & 0xFFFF) }};
// Draw the entire line
outlinePatternShader.u_world = worldSize;
spriteAtlas->bind(true, context, 0);
setDepthSublayer(2);
bucket.drawVertices(outlinePatternShader, context, paintMode());
}
}
} else {
// No image fill.
if ((fillColor.a >= 1.0f && opacity >= 1.0f) == (pass == RenderPass::Opaque)) {
// Only draw the fill when it's either opaque and we're drawing opaque
// fragments or when it's translucent and we're drawing translucent
// fragments
// Draw filling rectangle.
context.program = plainShader.getID();
plainShader.u_matrix = vertexMatrix;
plainShader.u_color = fillColor;
plainShader.u_opacity = opacity;
// Draw the actual triangles into the color & stencil buffer.
setDepthSublayer(1);
bucket.drawElements(plainShader, context, paintMode());
}
}
// Because we're drawing top-to-bottom, and we update the stencil mask
// below, we have to draw the outline first (!)
if (fringeline && pass == RenderPass::Translucent) {
context.program = outlineShader.getID();
outlineShader.u_matrix = vertexMatrix;
outlineShader.u_outline_color = fillColor;
outlineShader.u_opacity = opacity;
// Draw the entire line
outlineShader.u_world = worldSize;
setDepthSublayer(2);
bucket.drawVertices(outlineShader, context, paintMode());
}
}
} // namespace mbgl