summaryrefslogtreecommitdiff
path: root/src/mbgl/programs/symbol_program.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mbgl/programs/symbol_program.hpp')
-rw-r--r--src/mbgl/programs/symbol_program.hpp356
1 files changed, 337 insertions, 19 deletions
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp
index fdd1aa5c3b..ae50e790be 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -1,6 +1,10 @@
#pragma once
-#include <mbgl/programs/program.hpp>
+#include <mbgl/gl/context.hpp>
+#include <mbgl/gl/program.hpp>
+#include <mbgl/math/clamp.hpp>
+#include <mbgl/util/interpolate.hpp>
+
#include <mbgl/programs/attributes.hpp>
#include <mbgl/programs/uniforms.hpp>
#include <mbgl/shaders/symbol_icon.hpp>
@@ -10,6 +14,7 @@
#include <mbgl/style/layers/symbol_layer_properties.hpp>
#include <mbgl/style/layers/symbol_layer_impl.hpp>
+
#include <cmath>
#include <array>
@@ -30,14 +35,19 @@ MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture);
MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio);
MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo);
-MBGL_DEFINE_UNIFORM_SCALAR(float, u_font_scale);
MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale);
+
+MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_text);
+MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_zoom_constant);
+MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_size);
+MBGL_DEFINE_UNIFORM_SCALAR(float, u_layout_size);
} // namespace uniforms
struct SymbolLayoutAttributes : gl::Attributes<
attributes::a_pos_offset,
- attributes::a_texture_pos,
- attributes::a_data<4>>
+ attributes::a_data<uint16_t, 4>>
{
static Vertex vertex(Point<float> a,
Point<float> o,
@@ -57,19 +67,324 @@ struct SymbolLayoutAttributes : gl::Attributes<
}},
{{
static_cast<uint16_t>(tx / 4),
- static_cast<uint16_t>(ty / 4)
- }},
+ static_cast<uint16_t>(ty / 4),
+ mbgl::attributes::packUint8Pair(
+ static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160
+ static_cast<uint8_t>(labelangle)
+ ),
+ mbgl::attributes::packUint8Pair(
+ static_cast<uint8_t>(minzoom * 10),
+ static_cast<uint8_t>(::fmin(maxzoom, 25) * 10)
+ )
+ }}
+ };
+ }
+};
+
+class SymbolSizeAttributes : public gl::Attributes<attributes::a_size> {
+public:
+ using Attribute = attributes::a_size::Type;
+};
+
+// Mimic the PaintPropertyBinder technique specifically for the {text,icon}-size layout properties
+// in order to provide a 'custom' scheme for encoding the necessary attribute data. As with
+// PaintPropertyBinder, SymbolSizeBinder is an abstract class whose implementations handle the
+// particular attribute & uniform logic needed by each possible type of the {Text,Icon}Size properties.
+class SymbolSizeBinder {
+public:
+ using Uniforms = gl::Uniforms<
+ uniforms::u_is_size_zoom_constant,
+ uniforms::u_is_size_feature_constant,
+ uniforms::u_size_t,
+ uniforms::u_size,
+ uniforms::u_layout_size>;
+ using UniformValues = Uniforms::Values;
+
+ static std::unique_ptr<SymbolSizeBinder> create(const float tileZoom,
+ const style::DataDrivenPropertyValue<float>& sizeProperty,
+ const float defaultValue);
+
+ virtual SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const = 0;
+ virtual void populateVertexVector(const GeometryTileFeature& feature) = 0;
+ virtual UniformValues uniformValues(float currentZoom) const = 0;
+ virtual void upload(gl::Context&) = 0;
+};
+
+// Return the smallest range of stops that covers the interval [lowerZoom, upperZoom]
+template <class Stops>
+Range<float> getCoveringStops(Stops s, float lowerZoom, float upperZoom) {
+ assert(!s.stops.empty());
+ auto minIt = s.stops.lower_bound(lowerZoom);
+ auto maxIt = s.stops.lower_bound(upperZoom);
+
+ // lower_bound yields first element >= lowerZoom, but we want the *last*
+ // element <= lowerZoom, so if we found a stop > lowerZoom, back up by one.
+ if (minIt != s.stops.begin() && minIt->first > lowerZoom) {
+ minIt--;
+ }
+ return Range<float> {
+ minIt == s.stops.end() ? s.stops.rbegin()->first : minIt->first,
+ maxIt == s.stops.end() ? s.stops.rbegin()->first : maxIt->first
+ };
+}
+
+class ConstantSymbolSizeBinder : public SymbolSizeBinder {
+public:
+ using PropertyValue = variant<float, style::CameraFunction<float>>;
+
+ ConstantSymbolSizeBinder(const float /*tileZoom*/, const float& size, const float /*defaultValue*/)
+ : layoutSize(size) {}
+
+ ConstantSymbolSizeBinder(const float /*tileZoom*/, const style::Undefined&, const float defaultValue)
+ : layoutSize(defaultValue) {}
+
+ ConstantSymbolSizeBinder(const float tileZoom, const style::CameraFunction<float>& function_, const float /*defaultValue*/)
+ : layoutSize(function_.evaluate(tileZoom + 1)) {
+ function_.stops.match(
+ [&] (const style::ExponentialStops<float>& stops) {
+ coveringRanges = std::make_tuple(
+ getCoveringStops(stops, tileZoom, tileZoom + 1),
+ Range<float> { function_.evaluate(tileZoom), function_.evaluate(tileZoom + 1) }
+ );
+ functionInterpolationBase = stops.base;
+ },
+ [&] (const style::IntervalStops<float>&) {
+ function = function_;
+ }
+ );
+ }
+
+ SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float>) const override {
+ return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} };
+ }
+ void upload(gl::Context&) override {}
+ void populateVertexVector(const GeometryTileFeature&) override {};
+
+ UniformValues uniformValues(float currentZoom) const override {
+ float size = layoutSize;
+ bool isZoomConstant = !(coveringRanges || function);
+ if (coveringRanges) {
+ // Even though we could get the exact value of the camera function
+ // at z = currentZoom, we intentionally do not: instead, we interpolate
+ // between the camera function values at a pair of zoom stops covering
+ // [tileZoom, tileZoom + 1] in order to be consistent with this
+ // restriction on composite functions.
+ const Range<float>& zoomLevels = std::get<0>(*coveringRanges);
+ const Range<float>& sizeLevels = std::get<1>(*coveringRanges);
+ float t = util::clamp(
+ util::interpolationFactor(*functionInterpolationBase, zoomLevels, currentZoom),
+ 0.0f, 1.0f
+ );
+ size = sizeLevels.min + t * (sizeLevels.max - sizeLevels.min);
+ } else if (function) {
+ size = function->evaluate(currentZoom);
+ }
+
+ return UniformValues {
+ uniforms::u_is_size_zoom_constant::Value{ isZoomConstant },
+ uniforms::u_is_size_feature_constant::Value{ true },
+ uniforms::u_size_t::Value{ 0.0f }, // unused
+ uniforms::u_size::Value{ size },
+ uniforms::u_layout_size::Value{ layoutSize }
+ };
+ }
+
+ float layoutSize;
+ // used for exponential functions
+ optional<std::tuple<Range<float>, Range<float>>> coveringRanges;
+ optional<float> functionInterpolationBase;
+ // used for interval functions
+ optional<style::CameraFunction<float>> function;
+};
+
+class SourceFunctionSymbolSizeBinder : public SymbolSizeBinder {
+public:
+ using Vertex = gl::detail::Vertex<gl::Attribute<uint16_t, 1>>;
+ using VertexVector = gl::VertexVector<Vertex>;
+ using VertexBuffer = gl::VertexBuffer<Vertex>;
+
+ SourceFunctionSymbolSizeBinder(const float /*tileZoom*/, const style::SourceFunction<float>& function_, const float defaultValue_)
+ : function(function_),
+ defaultValue(defaultValue_) {
+ }
+
+ SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const override {
+ if (currentValue.isConstant()) {
+ return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} };
+ }
+
+ return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::variableBinding(*buffer, 0, 1) };
+ }
+
+ void populateVertexVector(const GeometryTileFeature& feature) override {
+ const auto sizeVertex = Vertex {
{{
- static_cast<uint8_t>(labelminzoom * 10), // 1/10 zoom levels: z16 == 160
- static_cast<uint8_t>(labelangle),
- static_cast<uint8_t>(minzoom * 10),
- static_cast<uint8_t>(::fmin(maxzoom, 25) * 10)
+ static_cast<uint16_t>(function.evaluate(feature, defaultValue) * 10)
}}
};
+
+ vertices.emplace_back(sizeVertex);
+ vertices.emplace_back(sizeVertex);
+ vertices.emplace_back(sizeVertex);
+ vertices.emplace_back(sizeVertex);
+ };
+
+ UniformValues uniformValues(float) const override {
+ return UniformValues {
+ uniforms::u_is_size_zoom_constant::Value{ true },
+ uniforms::u_is_size_feature_constant::Value{ false },
+ uniforms::u_size_t::Value{ 0.0f }, // unused
+ uniforms::u_size::Value{ 0.0f }, // unused
+ uniforms::u_layout_size::Value{ 0.0f } // unused
+ };
+ }
+
+ void upload(gl::Context& context) override {
+ buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
+ }
+
+ const style::SourceFunction<float>& function;
+ const float defaultValue;
+
+ VertexVector vertices;
+ optional<VertexBuffer> buffer;
+};
+
+class CompositeFunctionSymbolSizeBinder: public SymbolSizeBinder {
+public:
+ using Vertex = SymbolSizeAttributes::Vertex;
+ using VertexVector = gl::VertexVector<Vertex>;
+ using VertexBuffer = gl::VertexBuffer<Vertex>;
+
+ CompositeFunctionSymbolSizeBinder(const float tileZoom, const style::CompositeFunction<float>& function_, const float defaultValue_)
+ : function(function_),
+ defaultValue(defaultValue_),
+ layoutZoom(tileZoom + 1),
+ coveringZoomStops(function.stops.match(
+ [&] (const auto& stops) {
+ return getCoveringStops(stops, tileZoom, tileZoom + 1); }))
+ {}
+
+ SymbolSizeAttributes::Bindings attributeBindings(const style::PossiblyEvaluatedPropertyValue<float> currentValue) const override {
+ if (currentValue.isConstant()) {
+ return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::ConstantBinding {{{0, 0, 0}}} };
+ }
+
+ return SymbolSizeAttributes::Bindings { SymbolSizeAttributes::Attribute::variableBinding(*buffer, 0) };
+ }
+
+ void populateVertexVector(const GeometryTileFeature& feature) override {
+ const auto sizeVertex = Vertex {
+ {{
+ static_cast<uint16_t>(function.evaluate(coveringZoomStops.min, feature, defaultValue) * 10),
+ static_cast<uint16_t>(function.evaluate(coveringZoomStops.max, feature, defaultValue) * 10),
+ static_cast<uint16_t>(function.evaluate(layoutZoom, feature, defaultValue) * 10)
+ }}
+ };
+
+ vertices.emplace_back(sizeVertex);
+ vertices.emplace_back(sizeVertex);
+ vertices.emplace_back(sizeVertex);
+ vertices.emplace_back(sizeVertex);
+ };
+
+ UniformValues uniformValues(float currentZoom) const override {
+ float sizeInterpolationT = util::clamp(
+ util::interpolationFactor(1.0f, coveringZoomStops, currentZoom),
+ 0.0f, 1.0f
+ );
+
+ return UniformValues {
+ uniforms::u_is_size_zoom_constant::Value{ false },
+ uniforms::u_is_size_feature_constant::Value{ false },
+ uniforms::u_size_t::Value{ sizeInterpolationT },
+ uniforms::u_size::Value{ 0.0f }, // unused
+ uniforms::u_layout_size::Value{ 0.0f } // unused
+ };
+ }
+
+ void upload(gl::Context& context) override {
+ buffer = VertexBuffer { context.createVertexBuffer(std::move(vertices)) };
}
+
+ const style::CompositeFunction<float>& function;
+ const float defaultValue;
+ float layoutZoom;
+ Range<float> coveringZoomStops;
+
+ VertexVector vertices;
+ optional<VertexBuffer> buffer;
};
-class SymbolIconProgram : public Program<
+
+template <class Shaders,
+ class Primitive,
+ class LayoutAttrs,
+ class Uniforms,
+ class PaintProperties>
+class SymbolProgram {
+public:
+ using LayoutAttributes = LayoutAttrs;
+ using LayoutVertex = typename LayoutAttributes::Vertex;
+
+ using LayoutAndSizeAttributes = gl::ConcatenateAttributes<LayoutAttributes, SymbolSizeAttributes>;
+
+ using PaintPropertyBinders = typename PaintProperties::Binders;
+ using PaintAttributes = typename PaintPropertyBinders::Attributes;
+ using Attributes = gl::ConcatenateAttributes<LayoutAndSizeAttributes, PaintAttributes>;
+
+ using UniformValues = typename Uniforms::Values;
+ using SizeUniforms = typename SymbolSizeBinder::Uniforms;
+ using PaintUniforms = typename PaintPropertyBinders::Uniforms;
+ using AllUniforms = gl::ConcatenateUniforms<Uniforms, gl::ConcatenateUniforms<SizeUniforms, PaintUniforms>>;
+
+ using ProgramType = gl::Program<Primitive, Attributes, AllUniforms>;
+
+ ProgramType program;
+
+ SymbolProgram(gl::Context& context, const ProgramParameters& programParameters)
+ : program(ProgramType::createProgram(
+ context,
+ programParameters,
+ Shaders::name,
+ Shaders::vertexSource,
+ Shaders::fragmentSource)) {
+ }
+
+ template <class DrawMode>
+ void draw(gl::Context& context,
+ DrawMode drawMode,
+ gl::DepthMode depthMode,
+ gl::StencilMode stencilMode,
+ gl::ColorMode colorMode,
+ UniformValues&& uniformValues,
+ const gl::VertexBuffer<LayoutVertex>& layoutVertexBuffer,
+ const SymbolSizeBinder& symbolSizeBinder,
+ const style::PossiblyEvaluatedPropertyValue<float>& currentSizeValue,
+ const gl::IndexBuffer<DrawMode>& indexBuffer,
+ const gl::SegmentVector<Attributes>& segments,
+ const PaintPropertyBinders& paintPropertyBinders,
+ const typename PaintProperties::Evaluated& currentProperties,
+ float currentZoom) {
+ program.draw(
+ context,
+ std::move(drawMode),
+ std::move(depthMode),
+ std::move(stencilMode),
+ std::move(colorMode),
+ uniformValues
+ .concat(symbolSizeBinder.uniformValues(currentZoom))
+ .concat(paintPropertyBinders.uniformValues(currentZoom)),
+ LayoutAttributes::allVariableBindings(layoutVertexBuffer)
+ .concat(symbolSizeBinder.attributeBindings(currentSizeValue))
+ .concat(paintPropertyBinders.attributeBindings(currentProperties)),
+ indexBuffer,
+ segments
+ );
+ }
+};
+
+class SymbolIconProgram : public SymbolProgram<
shaders::symbol_icon,
gl::Triangle,
SymbolLayoutAttributes,
@@ -80,13 +395,15 @@ class SymbolIconProgram : public Program<
uniforms::u_zoom,
uniforms::u_rotate_with_map,
uniforms::u_texture,
- uniforms::u_fadetexture>,
+ uniforms::u_fadetexture,
+ uniforms::u_is_text>,
style::IconPaintProperties>
{
public:
- using Program::Program;
+ using SymbolProgram::SymbolProgram;
- static UniformValues uniformValues(const style::SymbolPropertyValues&,
+ static UniformValues uniformValues(const bool isText,
+ const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile&,
@@ -99,7 +416,7 @@ enum class SymbolSDFPart {
};
template <class PaintProperties>
-class SymbolSDFProgram : public Program<
+class SymbolSDFProgram : public SymbolProgram<
shaders::symbol_sdf,
gl::Triangle,
SymbolLayoutAttributes,
@@ -111,7 +428,7 @@ class SymbolSDFProgram : public Program<
uniforms::u_rotate_with_map,
uniforms::u_texture,
uniforms::u_fadetexture,
- uniforms::u_font_scale,
+ uniforms::u_is_text,
uniforms::u_gamma_scale,
uniforms::u_pitch,
uniforms::u_bearing,
@@ -121,7 +438,7 @@ class SymbolSDFProgram : public Program<
PaintProperties>
{
public:
- using BaseProgram = Program<shaders::symbol_sdf,
+ using BaseProgram = SymbolProgram<shaders::symbol_sdf,
gl::Triangle,
SymbolLayoutAttributes,
gl::Uniforms<
@@ -132,7 +449,7 @@ public:
uniforms::u_rotate_with_map,
uniforms::u_texture,
uniforms::u_fadetexture,
- uniforms::u_font_scale,
+ uniforms::u_is_text,
uniforms::u_gamma_scale,
uniforms::u_pitch,
uniforms::u_bearing,
@@ -147,7 +464,8 @@ public:
using BaseProgram::BaseProgram;
- static UniformValues uniformValues(const style::SymbolPropertyValues&,
+ static UniformValues uniformValues(const bool isText,
+ const style::SymbolPropertyValues&,
const Size& texsize,
const std::array<float, 2>& pixelsToGLUnits,
const RenderTile&,