diff options
Diffstat (limited to 'src/mbgl/renderer/layers')
-rw-r--r-- | src/mbgl/renderer/layers/render_background_layer.cpp | 115 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_background_layer.hpp | 33 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_circle_layer.cpp | 120 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_circle_layer.hpp | 40 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_custom_layer.cpp | 81 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_custom_layer.hpp | 36 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp | 167 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp | 44 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_fill_layer.cpp | 205 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_fill_layer.hpp | 40 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_line_layer.cpp | 204 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_line_layer.hpp | 52 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_raster_layer.cpp | 155 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_raster_layer.hpp | 34 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_symbol_layer.cpp | 325 | ||||
-rw-r--r-- | src/mbgl/renderer/layers/render_symbol_layer.hpp | 99 |
16 files changed, 1750 insertions, 0 deletions
diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp new file mode 100644 index 0000000000..9fddba3f74 --- /dev/null +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -0,0 +1,115 @@ +#include <mbgl/renderer/layers/render_background_layer.hpp> +#include <mbgl/style/layers/background_layer_impl.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/util/tile_cover.hpp> + +namespace mbgl { + +using namespace style; + +RenderBackgroundLayer::RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl> _impl) + : RenderLayer(style::LayerType::Background, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::BackgroundLayer::Impl& RenderBackgroundLayer::impl() const { + return static_cast<const style::BackgroundLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderBackgroundLayer::createBucket(const BucketParameters &, + const std::vector<const RenderLayer *> &) const { + assert(false); + return nullptr; +} + +void RenderBackgroundLayer::transition(const TransitionParameters ¶meters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderBackgroundLayer::evaluate(const PropertyEvaluationParameters ¶meters) { + evaluated = unevaluated.evaluate(parameters); + + passes = evaluated.get<style::BackgroundOpacity>() > 0 ? RenderPass::Translucent + : RenderPass::None; +} + +bool RenderBackgroundLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderBackgroundLayer::render(PaintParameters& parameters, RenderSource*) { + // Note that for bottommost layers without a pattern, the background color is drawn with + // glClear rather than this method. + + style::FillPaintProperties::PossiblyEvaluated properties; + properties.get<FillPattern>() = evaluated.get<BackgroundPattern>(); + properties.get<FillOpacity>() = { evaluated.get<BackgroundOpacity>() }; + properties.get<FillColor>() = { evaluated.get<BackgroundColor>() }; + + const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0); + + if (!evaluated.get<BackgroundPattern>().to.empty()) { + optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from); + optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to); + + if (!imagePosA || !imagePosB) + return; + + parameters.imageManager.bind(parameters.context, 0); + + for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { + parameters.programs.fillPattern.get(properties).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + FillPatternUniforms::values( + parameters.matrixForTile(tileID), + parameters.context.viewport.getCurrentValue().size, + parameters.imageManager.getPixelSize(), + *imagePosA, + *imagePosB, + evaluated.get<BackgroundPattern>(), + tileID, + parameters.state + ), + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } else { + for (const auto& tileID : util::tileCover(parameters.state, parameters.state.getIntegerZoom())) { + parameters.programs.fill.get(properties).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ parameters.matrixForTile(tileID) }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + parameters.staticData.tileVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.tileTriangleSegments, + paintAttibuteData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_background_layer.hpp b/src/mbgl/renderer/layers/render_background_layer.hpp new file mode 100644 index 0000000000..a619670ee4 --- /dev/null +++ b/src/mbgl/renderer/layers/render_background_layer.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/background_layer_impl.hpp> +#include <mbgl/style/layers/background_layer_properties.hpp> + +namespace mbgl { + +class RenderBackgroundLayer: public RenderLayer { +public: + RenderBackgroundLayer(Immutable<style::BackgroundLayer::Impl>); + ~RenderBackgroundLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::BackgroundPaintProperties::Unevaluated unevaluated; + style::BackgroundPaintProperties::PossiblyEvaluated evaluated; + + const style::BackgroundLayer::Impl& impl() const; +}; + +template <> +inline bool RenderLayer::is<RenderBackgroundLayer>() const { + return type == style::LayerType::Background; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.cpp b/src/mbgl/renderer/layers/render_circle_layer.cpp new file mode 100644 index 0000000000..e7b022f3ee --- /dev/null +++ b/src/mbgl/renderer/layers/render_circle_layer.cpp @@ -0,0 +1,120 @@ +#include <mbgl/renderer/layers/render_circle_layer.hpp> +#include <mbgl/renderer/buckets/circle_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/circle_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderCircleLayer::RenderCircleLayer(Immutable<style::CircleLayer::Impl> _impl) + : RenderLayer(style::LayerType::Circle, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::CircleLayer::Impl& RenderCircleLayer::impl() const { + return static_cast<const style::CircleLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderCircleLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<CircleBucket>(parameters, layers); +} + +void RenderCircleLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderCircleLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = ((evaluated.get<style::CircleRadius>().constantOr(1) > 0 || + evaluated.get<style::CircleStrokeWidth>().constantOr(1) > 0) + && (evaluated.get<style::CircleColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::CircleStrokeColor>().constantOr(Color::black()).a > 0) + && (evaluated.get<style::CircleOpacity>().constantOr(1) > 0 || + evaluated.get<style::CircleStrokeOpacity>().constantOr(1) > 0)) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderCircleLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + const bool scaleWithMap = evaluated.get<CirclePitchScale>() == CirclePitchScaleType::Map; + const bool pitchWithMap = evaluated.get<CirclePitchAlignment>() == AlignmentType::Map; + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl))); + CircleBucket& bucket = *reinterpret_cast<CircleBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.circle.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.mapMode == MapMode::Still + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + CircleProgram::UniformValues { + uniforms::u_matrix::Value{ + tile.translatedMatrix(evaluated.get<CircleTranslate>(), + evaluated.get<CircleTranslateAnchor>(), + parameters.state) + }, + uniforms::u_scale_with_map::Value{ scaleWithMap }, + uniforms::u_extrude_scale::Value{ pitchWithMap + ? std::array<float, 2> {{ + tile.id.pixelsToTileUnits(1, parameters.state.getZoom()), + tile.id.pixelsToTileUnits(1, parameters.state.getZoom()) }} + : parameters.pixelsToGLUnits }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, + uniforms::u_pitch_with_map::Value{ pitchWithMap } + }, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + } +} + +bool RenderCircleLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + + // Translate query geometry + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::CircleTranslate>(), + evaluated.get<style::CircleTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + // Evaluate function + auto circleRadius = evaluated.get<style::CircleRadius>() + .evaluate(feature, zoom, style::CircleRadius::defaultValue()) + * pixelsToTileUnits; + + // Test intersection + return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp new file mode 100644 index 0000000000..f31715f98f --- /dev/null +++ b/src/mbgl/renderer/layers/render_circle_layer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> +#include <mbgl/style/layers/circle_layer_properties.hpp> + +namespace mbgl { + +class RenderCircleLayer: public RenderLayer { +public: + RenderCircleLayer(Immutable<style::CircleLayer::Impl>); + ~RenderCircleLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::CirclePaintProperties::Unevaluated unevaluated; + style::CirclePaintProperties::PossiblyEvaluated evaluated; + + const style::CircleLayer::Impl& impl() const; +}; + +template <> +inline bool RenderLayer::is<RenderCircleLayer>() const { + return type == style::LayerType::Circle; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp new file mode 100644 index 0000000000..7ece3970da --- /dev/null +++ b/src/mbgl/renderer/layers/render_custom_layer.cpp @@ -0,0 +1,81 @@ +#include <mbgl/renderer/layers/render_custom_layer.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/backend_scope.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/style/layers/custom_layer_impl.hpp> +#include <mbgl/map/transform_state.hpp> + +namespace mbgl { + +using namespace style; + +RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl) + : RenderLayer(LayerType::Custom, _impl) { +} + +RenderCustomLayer::~RenderCustomLayer() { + assert(BackendScope::exists()); + if (initialized) { + if (contextDestroyed && impl().contextLostFn ) { + impl().contextLostFn(impl().context); + } else if (!contextDestroyed && impl().deinitializeFn) { + impl().deinitializeFn(impl().context); + } + } +} + +const CustomLayer::Impl& RenderCustomLayer::impl() const { + return static_cast<const CustomLayer::Impl&>(*baseImpl); +} + +void RenderCustomLayer::evaluate(const PropertyEvaluationParameters&) { + passes = RenderPass::Translucent; +} + +bool RenderCustomLayer::hasTransition() const { + return false; +} + +std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) { + if (!initialized) { + assert(impl().initializeFn); + impl().initializeFn(impl().context); + initialized = true; + } + + gl::Context& context = paintParameters.context; + const TransformState& state = paintParameters.state; + + // Reset GL state to a known state so the CustomLayer always has a clean slate. + context.bindVertexArray = 0; + context.setDepthMode(paintParameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly)); + context.setStencilMode(gl::StencilMode::disabled()); + context.setColorMode(paintParameters.colorModeForRenderPass()); + + CustomLayerRenderParameters parameters; + + parameters.width = state.getSize().width; + parameters.height = state.getSize().height; + parameters.latitude = state.getLatLng().latitude(); + parameters.longitude = state.getLatLng().longitude(); + parameters.zoom = state.getZoom(); + parameters.bearing = -state.getAngle() * util::RAD2DEG; + parameters.pitch = state.getPitch(); + parameters.fieldOfView = state.getFieldOfView(); + + assert(impl().renderFn); + impl().renderFn(impl().context, parameters); + + // Reset the view back to our original one, just in case the CustomLayer changed + // the viewport or Framebuffer. + paintParameters.backend.bind(); + context.setDirtyState(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp new file mode 100644 index 0000000000..32ed9da8da --- /dev/null +++ b/src/mbgl/renderer/layers/render_custom_layer.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/custom_layer_impl.hpp> + +namespace mbgl { + +class RenderCustomLayer: public RenderLayer { +public: + RenderCustomLayer(Immutable<style::CustomLayer::Impl>); + ~RenderCustomLayer() final; + + void transition(const TransitionParameters&) final {} + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const final; + void render(PaintParameters&, RenderSource*) final; + + const style::CustomLayer::Impl& impl() const; + + void markContextDestroyed() { + contextDestroyed = true; + }; + +private: + bool initialized = false; + bool contextDestroyed = false; +}; + +template <> +inline bool RenderLayer::is<RenderCustomLayer>() const { + return type == style::LayerType::Custom; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp new file mode 100644 index 0000000000..fbd6160e8a --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp @@ -0,0 +1,167 @@ +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/renderer/renderer_backend.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderFillExtrusionLayer::RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl> _impl) + : RenderLayer(style::LayerType::FillExtrusion, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::FillExtrusionLayer::Impl& RenderFillExtrusionLayer::impl() const { + return static_cast<const style::FillExtrusionLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderFillExtrusionLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillExtrusionBucket>(parameters, layers); +} + +void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) + ? (RenderPass::Translucent | RenderPass::Pass3D) + : RenderPass::None; +} + +bool RenderFillExtrusionLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + if (parameters.pass == RenderPass::Pass3D) { + const auto& size = parameters.staticData.backendSize; + + if (!renderTexture || renderTexture->getSize() != size) { + renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer); + } + + renderTexture->bind(); + + optional<float> depthClearValue = {}; + if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0; + // Flag the depth buffer as no longer needing to be cleared for the remainder of this pass. + parameters.staticData.depthRenderbuffer->shouldClear(false); + + parameters.context.setStencilMode(gl::StencilMode::disabled()); + parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {}); + + if (evaluated.get<FillExtrusionPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusion.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.state, parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } else { + optional<ImagePosition> imagePosA = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from); + optional<ImagePosition> imagePosB = + parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl))); + FillExtrusionBucket& bucket = + *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)); + + parameters.programs.fillExtrusionPattern.get(evaluated).draw( + parameters.context, gl::Triangles(), + parameters.depthModeFor3D(gl::DepthMode::ReadWrite), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + FillExtrusionPatternUniforms::values( + tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(), + evaluated.get<FillExtrusionTranslateAnchor>(), + parameters.state), + parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB, + evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state, + -std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f, + parameters.evaluatedLight), + *bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments, + bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(), + getID()); + } + } + + } else if (parameters.pass == RenderPass::Translucent) { + parameters.context.bindTexture(renderTexture->getTexture()); + + const auto& size = parameters.staticData.backendSize; + + mat4 viewportMat; + matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1); + + const Properties<>::PossiblyEvaluated properties; + + parameters.programs.extrusionTexture.draw( + parameters.context, gl::Triangles(), gl::DepthMode::disabled(), + gl::StencilMode::disabled(), parameters.colorModeForRenderPass(), + ExtrusionTextureProgram::UniformValues{ + uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size }, + uniforms::u_image::Value{ 0 }, + uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } }, + parameters.staticData.extrusionTextureVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.extrusionTextureSegments, + ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties, + parameters.state.getZoom(), getID()); + } +} + +bool RenderFillExtrusionLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillExtrusionTranslate>(), + evaluated.get<style::FillExtrusionTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp new file mode 100644 index 0000000000..838494cf91 --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/offscreen_texture.hpp> + +namespace mbgl { + +class RenderFillExtrusionLayer: public RenderLayer { +public: + RenderFillExtrusionLayer(Immutable<style::FillExtrusionLayer::Impl>); + ~RenderFillExtrusionLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::FillExtrusionPaintProperties::Unevaluated unevaluated; + style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated; + + const style::FillExtrusionLayer::Impl& impl() const; + + optional<OffscreenTexture> renderTexture; +}; + +template <> +inline bool RenderLayer::is<RenderFillExtrusionLayer>() const { + return type == style::LayerType::FillExtrusion; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp new file mode 100644 index 0000000000..22cb9563c1 --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_layer.cpp @@ -0,0 +1,205 @@ +#include <mbgl/renderer/layers/render_fill_layer.hpp> +#include <mbgl/renderer/buckets/fill_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderFillLayer::RenderFillLayer(Immutable<style::FillLayer::Impl> _impl) + : RenderLayer(style::LayerType::Fill, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::FillLayer::Impl& RenderFillLayer::impl() const { + return static_cast<const style::FillLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderFillLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<FillBucket>(parameters, layers); +} + +void RenderFillLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderFillLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + if (unevaluated.get<style::FillOutlineColor>().isUndefined()) { + evaluated.get<style::FillOutlineColor>() = evaluated.get<style::FillColor>(); + } + + passes = RenderPass::None; + + if (evaluated.get<style::FillAntialias>()) { + passes |= RenderPass::Translucent; + } + + if (!unevaluated.get<style::FillPattern>().isUndefined() + || evaluated.get<style::FillColor>().constantOr(Color()).a < 1.0f + || evaluated.get<style::FillOpacity>().constantOr(0) < 1.0f) { + passes |= RenderPass::Translucent; + } else { + passes |= RenderPass::Opaque; + } +} + +bool RenderFillLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderFillLayer::render(PaintParameters& parameters, RenderSource*) { + if (evaluated.get<FillPattern>().from.empty()) { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl))); + FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, + const auto& drawMode, + const auto& depthMode, + const auto& indexBuffer, + const auto& segments) { + program.get(evaluated).draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + FillProgram::UniformValues { + uniforms::u_matrix::Value{ + tile.translatedMatrix(evaluated.get<FillTranslate>(), + evaluated.get<FillTranslateAnchor>(), + parameters.state) + }, + uniforms::u_world::Value{ parameters.context.viewport.getCurrentValue().size }, + }, + *bucket.vertexBuffer, + indexBuffer, + segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + // Only draw the fill when it's opaque and we're drawing opaque fragments, + // or when it's translucent and we're drawing translucent fragments. + if ((evaluated.get<FillColor>().constantOr(Color()).a >= 1.0f + && evaluated.get<FillOpacity>().constantOr(0) >= 1.0f) == (parameters.pass == RenderPass::Opaque)) { + draw(parameters.programs.fill, + gl::Triangles(), + parameters.depthModeForSublayer(1, parameters.pass == RenderPass::Opaque + ? gl::DepthMode::ReadWrite + : gl::DepthMode::ReadOnly), + *bucket.triangleIndexBuffer, + bucket.triangleSegments); + } + + if (evaluated.get<FillAntialias>() && parameters.pass == RenderPass::Translucent) { + draw(parameters.programs.fillOutline, + gl::Lines{ 2.0f }, + parameters.depthModeForSublayer( + unevaluated.get<FillOutlineColor>().isUndefined() ? 2 : 0, + gl::DepthMode::ReadOnly), + *bucket.lineIndexBuffer, + bucket.lineSegments); + } + } + } else { + if (parameters.pass != RenderPass::Translucent) { + return; + } + + optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillPattern>().from); + optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillPattern>().to); + + if (!imagePosA || !imagePosB) { + return; + } + + parameters.imageManager.bind(parameters.context, 0); + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<FillBucket*>(tile.tile.getBucket(*baseImpl))); + FillBucket& bucket = *reinterpret_cast<FillBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, + const auto& drawMode, + const auto& depthMode, + const auto& indexBuffer, + const auto& segments) { + program.get(evaluated).draw( + parameters.context, + drawMode, + depthMode, + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + FillPatternUniforms::values( + tile.translatedMatrix(evaluated.get<FillTranslate>(), + evaluated.get<FillTranslateAnchor>(), + parameters.state), + parameters.context.viewport.getCurrentValue().size, + parameters.imageManager.getPixelSize(), + *imagePosA, + *imagePosB, + evaluated.get<FillPattern>(), + tile.id, + parameters.state + ), + *bucket.vertexBuffer, + indexBuffer, + segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + draw(parameters.programs.fillPattern, + gl::Triangles(), + parameters.depthModeForSublayer(1, gl::DepthMode::ReadWrite), + *bucket.triangleIndexBuffer, + bucket.triangleSegments); + + if (evaluated.get<FillAntialias>() && unevaluated.get<FillOutlineColor>().isUndefined()) { + draw(parameters.programs.fillOutlinePattern, + gl::Lines { 2.0f }, + parameters.depthModeForSublayer(2, gl::DepthMode::ReadOnly), + *bucket.lineIndexBuffer, + bucket.lineSegments); + } + } + } +} + +bool RenderFillLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float, + const float bearing, + const float pixelsToTileUnits) const { + + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::FillTranslate>(), + evaluated.get<style::FillTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries()); +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp new file mode 100644 index 0000000000..a51865698f --- /dev/null +++ b/src/mbgl/renderer/layers/render_fill_layer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> +#include <mbgl/style/layers/fill_layer_properties.hpp> + +namespace mbgl { + +class RenderFillLayer: public RenderLayer { +public: + RenderFillLayer(Immutable<style::FillLayer::Impl>); + ~RenderFillLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::FillPaintProperties::Unevaluated unevaluated; + style::FillPaintProperties::PossiblyEvaluated evaluated; + + const style::FillLayer::Impl& impl() const; +}; + +template <> +inline bool RenderLayer::is<RenderFillLayer>() const { + return type == style::LayerType::Fill; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp new file mode 100644 index 0000000000..1b4a1c0ff7 --- /dev/null +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -0,0 +1,204 @@ +#include <mbgl/renderer/layers/render_line_layer.hpp> +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/image_manager.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/line_program.hpp> +#include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> +#include <mbgl/geometry/feature_index.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/intersection_tests.hpp> + +namespace mbgl { + +using namespace style; + +RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl) + : RenderLayer(style::LayerType::Line, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::LineLayer::Impl& RenderLineLayer::impl() const { + return static_cast<const style::LineLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderLineLayer::createBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) const { + return std::make_unique<LineBucket>(parameters, layers, impl().layout); +} + +void RenderLineLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderLineLayer::evaluate(const PropertyEvaluationParameters& parameters) { + style::Properties<LineFloorwidth>::Unevaluated extra(unevaluated.get<style::LineWidth>()); + + auto dashArrayParams = parameters; + dashArrayParams.useIntegerZoom = true; + + evaluated = RenderLinePaintProperties::PossiblyEvaluated( + unevaluated.evaluate(parameters).concat(extra.evaluate(dashArrayParams))); + + passes = (evaluated.get<style::LineOpacity>().constantOr(1.0) > 0 + && evaluated.get<style::LineColor>().constantOr(Color::black()).a > 0 + && evaluated.get<style::LineWidth>().constantOr(1.0) > 0) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderLineLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl))); + LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)); + + auto draw = [&] (auto& program, auto&& uniformValues) { + program.get(evaluated).draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments, + bucket.paintPropertyBinders.at(getID()), + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + if (!evaluated.get<LineDasharray>().from.empty()) { + const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round + ? LinePatternCap::Round : LinePatternCap::Square; + LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap); + LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap); + + parameters.lineAtlas.bind(parameters.context, 0); + + draw(parameters.programs.lineSDF, + LineSDFProgram::uniformValues( + evaluated, + parameters.pixelRatio, + tile, + parameters.state, + parameters.pixelsToGLUnits, + posA, + posB, + parameters.lineAtlas.getSize().width)); + + } else if (!evaluated.get<LinePattern>().from.empty()) { + optional<ImagePosition> posA = parameters.imageManager.getPattern(evaluated.get<LinePattern>().from); + optional<ImagePosition> posB = parameters.imageManager.getPattern(evaluated.get<LinePattern>().to); + + if (!posA || !posB) + return; + + parameters.imageManager.bind(parameters.context, 0); + + draw(parameters.programs.linePattern, + LinePatternProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits, + parameters.imageManager.getPixelSize(), + *posA, + *posB)); + + } else { + draw(parameters.programs.line, + LineProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits)); + } + } +} + +optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const double offset) { + if (offset == 0) return {}; + + GeometryCollection newRings; + Point<double> zero(0, 0); + for (const auto& ring : rings) { + newRings.emplace_back(); + auto& newRing = newRings.back(); + + for (auto i = ring.begin(); i != ring.end(); i++) { + auto& p = *i; + + Point<double> aToB = i == ring.begin() ? + zero : + util::perp(util::unit(convertPoint<double>(p - *(i - 1)))); + Point<double> bToC = i + 1 == ring.end() ? + zero : + util::perp(util::unit(convertPoint<double>(*(i + 1) - p))); + Point<double> extrude = util::unit(aToB + bToC); + + const double cosHalfAngle = extrude.x * bToC.x + extrude.y * bToC.y; + extrude *= (1.0 / cosHalfAngle); + + newRing.push_back(convertPoint<int16_t>(extrude * offset) + p); + } + } + + return newRings; +} + +bool RenderLineLayer::queryIntersectsFeature( + const GeometryCoordinates& queryGeometry, + const GeometryTileFeature& feature, + const float zoom, + const float bearing, + const float pixelsToTileUnits) const { + + // Translate query geometry + auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry( + queryGeometry, + evaluated.get<style::LineTranslate>(), + evaluated.get<style::LineTranslateAnchor>(), + bearing, + pixelsToTileUnits); + + // Evaluate function + auto offset = evaluated.get<style::LineOffset>() + .evaluate(feature, zoom, style::LineOffset::defaultValue()) * pixelsToTileUnits; + + // Apply offset to geometry + auto offsetGeometry = offsetLine(feature.getGeometries(), offset); + + // Test intersection + const float halfWidth = getLineWidth(feature, zoom) / 2.0 * pixelsToTileUnits; + return util::polygonIntersectsBufferedMultiLine( + translatedQueryGeometry.value_or(queryGeometry), + offsetGeometry.value_or(feature.getGeometries()), + halfWidth); +} + +float RenderLineLayer::getLineWidth(const GeometryTileFeature& feature, const float zoom) const { + float lineWidth = evaluated.get<style::LineWidth>() + .evaluate(feature, zoom, style::LineWidth::defaultValue()); + float gapWidth = evaluated.get<style::LineGapWidth>() + .evaluate(feature, zoom, style::LineGapWidth::defaultValue()); + if (gapWidth) { + return gapWidth + 2 * lineWidth; + } else { + return lineWidth; + } +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp new file mode 100644 index 0000000000..8bf7e2329d --- /dev/null +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> +#include <mbgl/style/layers/line_layer_properties.hpp> +#include <mbgl/programs/uniforms.hpp> + +namespace mbgl { + +struct LineFloorwidth : style::DataDrivenPaintProperty<float, attributes::a_floorwidth, uniforms::u_floorwidth> { + static float defaultValue() { return 1; } +}; + +class RenderLinePaintProperties : public style::ConcatenateProperties< + style::LinePaintProperties::PropertyTypes, + TypeList<LineFloorwidth>>::Type {}; + +class RenderLineLayer: public RenderLayer { +public: + RenderLineLayer(Immutable<style::LineLayer::Impl>); + ~RenderLineLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + bool queryIntersectsFeature( + const GeometryCoordinates&, + const GeometryTileFeature&, + const float, + const float, + const float) const override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::LinePaintProperties::Unevaluated unevaluated; + RenderLinePaintProperties::PossiblyEvaluated evaluated; + + const style::LineLayer::Impl& impl() const; + +private: + float getLineWidth(const GeometryTileFeature&, const float) const; +}; + +template <> +inline bool RenderLayer::is<RenderLineLayer>() const { + return type == style::LayerType::Line; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_raster_layer.cpp b/src/mbgl/renderer/layers/render_raster_layer.cpp new file mode 100644 index 0000000000..06616d90e5 --- /dev/null +++ b/src/mbgl/renderer/layers/render_raster_layer.cpp @@ -0,0 +1,155 @@ +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/sources/render_image_source.hpp> +#include <mbgl/renderer/render_static_data.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/style/layers/raster_layer_impl.hpp> + +namespace mbgl { + +using namespace style; + +RenderRasterLayer::RenderRasterLayer(Immutable<style::RasterLayer::Impl> _impl) + : RenderLayer(style::LayerType::Raster, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::RasterLayer::Impl& RenderRasterLayer::impl() const { + return static_cast<const style::RasterLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderRasterLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); + return nullptr; +} + +void RenderRasterLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderRasterLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + passes = evaluated.get<style::RasterOpacity>() > 0 ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderRasterLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +static float saturationFactor(float saturation) { + if (saturation > 0) { + return 1 - 1 / (1.001 - saturation); + } else { + return -saturation; + } +} + +static float contrastFactor(float contrast) { + if (contrast > 0) { + return 1 / (1 - contrast); + } else { + return 1 + contrast; + } +} + +static std::array<float, 3> spinWeights(float spin) { + spin *= util::DEG2RAD; + float s = std::sin(spin); + float c = std::cos(spin); + std::array<float, 3> spin_weights = {{ + (2 * c + 1) / 3, + (-std::sqrt(3.0f) * s - c + 1) / 3, + (std::sqrt(3.0f) * s - c + 1) / 3 + }}; + return spin_weights; +} + +void RenderRasterLayer::render(PaintParameters& parameters, RenderSource* source) { + if (parameters.pass != RenderPass::Translucent) + return; + + auto draw = [&] (const mat4& matrix, + const auto& vertexBuffer, + const auto& indexBuffer, + const auto& segments) { + parameters.programs.raster.draw( + parameters.context, + gl::Triangles(), + parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), + gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + RasterProgram::UniformValues { + uniforms::u_matrix::Value{ matrix }, + uniforms::u_image0::Value{ 0 }, + uniforms::u_image1::Value{ 1 }, + uniforms::u_opacity::Value{ evaluated.get<RasterOpacity>() }, + uniforms::u_fade_t::Value{ 1 }, + uniforms::u_brightness_low::Value{ evaluated.get<RasterBrightnessMin>() }, + uniforms::u_brightness_high::Value{ evaluated.get<RasterBrightnessMax>() }, + uniforms::u_saturation_factor::Value{ saturationFactor(evaluated.get<RasterSaturation>()) }, + uniforms::u_contrast_factor::Value{ contrastFactor(evaluated.get<RasterContrast>()) }, + uniforms::u_spin_weights::Value{ spinWeights(evaluated.get<RasterHueRotate>()) }, + uniforms::u_buffer_scale::Value{ 1.0f }, + uniforms::u_scale_parent::Value{ 1.0f }, + uniforms::u_tl_parent::Value{ std::array<float, 2> {{ 0.0f, 0.0f }} }, + }, + vertexBuffer, + indexBuffer, + segments, + RasterProgram::PaintPropertyBinders { evaluated, 0 }, + evaluated, + parameters.state.getZoom(), + getID() + ); + }; + + if (RenderImageSource* imageSource = source->as<RenderImageSource>()) { + if (imageSource->isEnabled() && imageSource->isLoaded() && !imageSource->bucket->needsUpload()) { + RasterBucket& bucket = *imageSource->bucket; + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); + + for (auto matrix_ : imageSource->matrices) { + draw(matrix_, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } + } + } else { + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl))); + RasterBucket& bucket = *reinterpret_cast<RasterBucket*>(tile.tile.getBucket(*baseImpl)); + + if (!bucket.hasData()) + continue; + + assert(bucket.texture); + parameters.context.bindTexture(*bucket.texture, 0, gl::TextureFilter::Linear); + parameters.context.bindTexture(*bucket.texture, 1, gl::TextureFilter::Linear); + + if (bucket.vertexBuffer && bucket.indexBuffer && !bucket.segments.empty()) { + // Draw only the parts of the tile that aren't drawn by another tile in the layer. + draw(tile.matrix, + *bucket.vertexBuffer, + *bucket.indexBuffer, + bucket.segments); + } else { + // Draw the full tile. + draw(tile.matrix, + parameters.staticData.rasterVertexBuffer, + parameters.staticData.quadTriangleIndexBuffer, + parameters.staticData.rasterSegments); + } + } + } +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_raster_layer.hpp b/src/mbgl/renderer/layers/render_raster_layer.hpp new file mode 100644 index 0000000000..87de316f7c --- /dev/null +++ b/src/mbgl/renderer/layers/render_raster_layer.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/layers/raster_layer_impl.hpp> +#include <mbgl/style/layers/raster_layer_properties.hpp> + +namespace mbgl { + +class RenderRasterLayer: public RenderLayer { +public: + RenderRasterLayer(Immutable<style::RasterLayer::Impl>); + ~RenderRasterLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + + void render(PaintParameters&, RenderSource*) override; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + + // Paint properties + style::RasterPaintProperties::Unevaluated unevaluated; + style::RasterPaintProperties::PossiblyEvaluated evaluated; + + const style::RasterLayer::Impl& impl() const; +}; + +template <> +inline bool RenderLayer::is<RenderRasterLayer>() const { + return type == style::LayerType::Raster; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp new file mode 100644 index 0000000000..1376e8a3d8 --- /dev/null +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -0,0 +1,325 @@ +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/renderer/property_evaluation_parameters.hpp> +#include <mbgl/renderer/render_tile.hpp> +#include <mbgl/renderer/paint_parameters.hpp> +#include <mbgl/renderer/frame_history.hpp> +#include <mbgl/text/glyph_atlas.hpp> +#include <mbgl/programs/programs.hpp> +#include <mbgl/programs/symbol_program.hpp> +#include <mbgl/programs/collision_box_program.hpp> +#include <mbgl/tile/tile.hpp> +#include <mbgl/tile/geometry_tile.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/layout/symbol_layout.hpp> +#include <mbgl/layout/symbol_projection.hpp> +#include <mbgl/util/math.hpp> + +#include <cmath> + +namespace mbgl { + +using namespace style; + +RenderSymbolLayer::RenderSymbolLayer(Immutable<style::SymbolLayer::Impl> _impl) + : RenderLayer(style::LayerType::Symbol, _impl), + unevaluated(impl().paint.untransitioned()) { +} + +const style::SymbolLayer::Impl& RenderSymbolLayer::impl() const { + return static_cast<const style::SymbolLayer::Impl&>(*baseImpl); +} + +std::unique_ptr<Bucket> RenderSymbolLayer::createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const { + assert(false); // Should be calling createLayout() instead. + return nullptr; +} + +std::unique_ptr<SymbolLayout> RenderSymbolLayer::createLayout(const BucketParameters& parameters, + const std::vector<const RenderLayer*>& group, + std::unique_ptr<GeometryTileLayer> layer, + GlyphDependencies& glyphDependencies, + ImageDependencies& imageDependencies) const { + return std::make_unique<SymbolLayout>(parameters, + group, + std::move(layer), + imageDependencies, + glyphDependencies); +} + +void RenderSymbolLayer::transition(const TransitionParameters& parameters) { + unevaluated = impl().paint.transitioned(parameters, std::move(unevaluated)); +} + +void RenderSymbolLayer::evaluate(const PropertyEvaluationParameters& parameters) { + evaluated = unevaluated.evaluate(parameters); + + auto hasIconOpacity = evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0; + auto hasTextOpacity = evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0 || + evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0; + + passes = ((evaluated.get<style::IconOpacity>().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (evaluated.get<style::TextOpacity>().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) + ? RenderPass::Translucent : RenderPass::None; +} + +bool RenderSymbolLayer::hasTransition() const { + return unevaluated.hasTransition(); +} + +void RenderSymbolLayer::render(PaintParameters& parameters, RenderSource*) { + if (parameters.pass == RenderPass::Opaque) { + return; + } + + for (const RenderTile& tile : renderTiles) { + assert(dynamic_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl))); + SymbolBucket& bucket = *reinterpret_cast<SymbolBucket*>(tile.tile.getBucket(*baseImpl)); + + const auto& layout = bucket.layout; + + parameters.frameHistory.bind(parameters.context, 1); + + auto draw = [&] (auto& program, + auto&& uniformValues, + const auto& buffers, + const auto& symbolSizeBinder, + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) + { + // We clip symbols to their tile extent in still mode. + const bool needsClipping = parameters.mapMode == MapMode::Still; + + program.get(paintProperties).draw( + parameters.context, + gl::Triangles(), + values_.pitchAlignment == AlignmentType::Map + ? parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly) + : gl::DepthMode::disabled(), + needsClipping + ? parameters.stencilModeForClipping(tile.clip) + : gl::StencilMode::disabled(), + parameters.colorModeForRenderPass(), + std::move(uniformValues), + *buffers.vertexBuffer, + *buffers.dynamicVertexBuffer, + *symbolSizeBinder, + *buffers.indexBuffer, + buffers.segments, + binders, + paintProperties, + parameters.state.getZoom(), + getID() + ); + }; + + assert(dynamic_cast<GeometryTile*>(&tile.tile)); + GeometryTile& geometryTile = static_cast<GeometryTile&>(tile.tile); + + if (bucket.hasIconData()) { + auto values = iconPropertyValues(layout); + auto paintPropertyValues = iconPaintProperties(); + + const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line && + layout.get<IconRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.icon.dynamicVertices, + bucket.icon.placedSymbols, + tile.matrix, + values, + tile, + *bucket.iconSizeBinder, + parameters.state, + parameters.frameHistory); + + parameters.context.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer, std::move(bucket.icon.dynamicVertices)); + } + + const bool iconScaled = layout.get<IconSize>().constantOr(1.0) != 1.0 || bucket.iconsNeedLinear; + const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || parameters.state.getPitch() != 0; + + parameters.context.bindTexture(*geometryTile.iconAtlasTexture, 0, + bucket.sdfIcons || parameters.state.isChanging() || iconScaled || iconTransformed + ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); + + const Size texsize = geometryTile.iconAtlasTexture->size; + + if (bucket.sdfIcons) { + if (values.hasHalo) { + draw(parameters.programs.symbolIconSDF, + SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + + if (values.hasFill) { + draw(parameters.programs.symbolIconSDF, + SymbolSDFIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + } else { + draw(parameters.programs.symbolIcon, + SymbolIconProgram::uniformValues(false, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state), + bucket.icon, + bucket.iconSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).first, + paintPropertyValues); + } + } + + if (bucket.hasTextData()) { + parameters.context.bindTexture(*geometryTile.glyphAtlasTexture, 0, gl::TextureFilter::Linear); + + auto values = textPropertyValues(layout); + auto paintPropertyValues = textPaintProperties(); + + const bool alongLine = layout.get<SymbolPlacement>() == SymbolPlacementType::Line && + layout.get<TextRotationAlignment>() == AlignmentType::Map; + + if (alongLine) { + reprojectLineLabels(bucket.text.dynamicVertices, + bucket.text.placedSymbols, + tile.matrix, + values, + tile, + *bucket.textSizeBinder, + parameters.state, + parameters.frameHistory); + + parameters.context.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices)); + } + + const Size texsize = geometryTile.glyphAtlasTexture->size; + + if (values.hasHalo) { + draw(parameters.programs.symbolGlyph, + SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Halo), + bucket.text, + bucket.textSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).second, + paintPropertyValues); + } + + if (values.hasFill) { + draw(parameters.programs.symbolGlyph, + SymbolSDFTextProgram::uniformValues(true, values, texsize, parameters.pixelsToGLUnits, alongLine, tile, parameters.state, SymbolSDFPart::Fill), + bucket.text, + bucket.textSizeBinder, + values, + bucket.paintPropertyBinders.at(getID()).second, + paintPropertyValues); + } + } + + if (bucket.hasCollisionBoxData()) { + static const style::Properties<>::PossiblyEvaluated properties {}; + static const CollisionBoxProgram::PaintPropertyBinders paintAttributeData(properties, 0); + + parameters.programs.collisionBox.draw( + parameters.context, + gl::Lines { 1.0f }, + gl::DepthMode::disabled(), + parameters.stencilModeForClipping(tile.clip), + parameters.colorModeForRenderPass(), + CollisionBoxProgram::UniformValues { + uniforms::u_matrix::Value{ tile.matrix }, + uniforms::u_scale::Value{ std::pow(2.0f, float(parameters.state.getZoom() - tile.tile.id.overscaledZ)) }, + uniforms::u_zoom::Value{ float(parameters.state.getZoom() * 10) }, + uniforms::u_maxzoom::Value{ float((tile.id.canonical.z + 1) * 10) }, + uniforms::u_collision_y_stretch::Value{ tile.tile.yStretch() }, + uniforms::u_camera_to_center_distance::Value{ parameters.state.getCameraToCenterDistance() }, + uniforms::u_pitch::Value{ parameters.state.getPitch() }, + uniforms::u_fadetexture::Value{ 1 } + }, + *bucket.collisionBox.vertexBuffer, + *bucket.collisionBox.indexBuffer, + bucket.collisionBox.segments, + paintAttributeData, + properties, + parameters.state.getZoom(), + getID() + ); + } + } +} + +style::IconPaintProperties::PossiblyEvaluated RenderSymbolLayer::iconPaintProperties() const { + return style::IconPaintProperties::PossiblyEvaluated { + evaluated.get<style::IconOpacity>(), + evaluated.get<style::IconColor>(), + evaluated.get<style::IconHaloColor>(), + evaluated.get<style::IconHaloWidth>(), + evaluated.get<style::IconHaloBlur>(), + evaluated.get<style::IconTranslate>(), + evaluated.get<style::IconTranslateAnchor>() + }; +} + +style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProperties() const { + return style::TextPaintProperties::PossiblyEvaluated { + evaluated.get<style::TextOpacity>(), + evaluated.get<style::TextColor>(), + evaluated.get<style::TextHaloColor>(), + evaluated.get<style::TextHaloWidth>(), + evaluated.get<style::TextHaloBlur>(), + evaluated.get<style::TextTranslate>(), + evaluated.get<style::TextTranslateAnchor>() + }; +} + + +style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { + return style::SymbolPropertyValues { + layout_.get<style::IconPitchAlignment>(), + layout_.get<style::IconRotationAlignment>(), + layout_.get<style::IconKeepUpright>(), + evaluated.get<style::IconTranslate>(), + evaluated.get<style::IconTranslateAnchor>(), + evaluated.get<style::IconHaloColor>().constantOr(Color::black()).a > 0 && + evaluated.get<style::IconHaloWidth>().constantOr(1), + evaluated.get<style::IconColor>().constantOr(Color::black()).a > 0, + 10.0f + }; +} + +style::SymbolPropertyValues RenderSymbolLayer::textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { + // We hide line labels with viewport alignment as they move into the distance + // because the approximations we use for drawing their glyphs get progressively worse + // The "1.5" here means we start hiding them when the distance from the label + // to the camera is 50% greater than the distance from the center of the map + // to the camera. Depending on viewport properties, you might expect this to filter + // the top third of the screen at pitch 60, and do almost nothing at pitch 45 + // "10" is effectively infinite at any pitch we support + const bool limitMaxDistance = + layout_.get<style::SymbolPlacement>() == style::SymbolPlacementType::Line + && layout_.get<style::TextRotationAlignment>() == style::AlignmentType::Map + && layout_.get<style::TextPitchAlignment>() == style::AlignmentType::Viewport; + + return style::SymbolPropertyValues { + layout_.get<style::TextPitchAlignment>(), + layout_.get<style::TextRotationAlignment>(), + layout_.get<style::TextKeepUpright>(), + evaluated.get<style::TextTranslate>(), + evaluated.get<style::TextTranslateAnchor>(), + evaluated.get<style::TextHaloColor>().constantOr(Color::black()).a > 0 && + evaluated.get<style::TextHaloWidth>().constantOr(1), + evaluated.get<style::TextColor>().constantOr(Color::black()).a > 0, + limitMaxDistance ? 1.5f : 10.0f + }; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/layers/render_symbol_layer.hpp b/src/mbgl/renderer/layers/render_symbol_layer.hpp new file mode 100644 index 0000000000..83709b5122 --- /dev/null +++ b/src/mbgl/renderer/layers/render_symbol_layer.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include <mbgl/text/glyph.hpp> +#include <mbgl/renderer/render_layer.hpp> +#include <mbgl/style/image_impl.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> +#include <mbgl/style/layers/symbol_layer_properties.hpp> + +namespace mbgl { + +namespace style { + +// {icon,text}-specific paint-property packs for use in the symbol Programs. +// Since each program deals either with icons or text, using a smaller property set +// lets us avoid unnecessarily binding attributes for properties the program wouldn't use. +class IconPaintProperties : public Properties< + IconOpacity, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, + IconTranslateAnchor +> {}; + +class TextPaintProperties : public Properties< + TextOpacity, + TextColor, + TextHaloColor, + TextHaloWidth, + TextHaloBlur, + TextTranslate, + TextTranslateAnchor +> {}; + +// Repackaging evaluated values from SymbolLayoutProperties + SymbolPaintProperties +// for genericity over icons vs. text. +class SymbolPropertyValues { +public: + // Layout + AlignmentType pitchAlignment; + AlignmentType rotationAlignment; + bool keepUpright; + + // Paint + std::array<float, 2> translate; + TranslateAnchorType translateAnchor; + + bool hasHalo; + bool hasFill; + + float maxCameraDistance; // 1.5 for road labels, or 10 (essentially infinite) for everything else +}; + +} // namespace style + +class BucketParameters; +class SymbolLayout; +class GeometryTileLayer; + +class RenderSymbolLayer: public RenderLayer { +public: + RenderSymbolLayer(Immutable<style::SymbolLayer::Impl>); + ~RenderSymbolLayer() final = default; + + void transition(const TransitionParameters&) override; + void evaluate(const PropertyEvaluationParameters&) override; + bool hasTransition() const override; + void render(PaintParameters&, RenderSource*) override; + + style::IconPaintProperties::PossiblyEvaluated iconPaintProperties() const; + style::TextPaintProperties::PossiblyEvaluated textPaintProperties() const; + + style::SymbolPropertyValues iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const; + style::SymbolPropertyValues textPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated&) const; + + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; + std::unique_ptr<SymbolLayout> createLayout(const BucketParameters&, + const std::vector<const RenderLayer*>&, + std::unique_ptr<GeometryTileLayer>, + GlyphDependencies&, + ImageDependencies&) const; + + // Paint properties + style::SymbolPaintProperties::Unevaluated unevaluated; + style::SymbolPaintProperties::PossiblyEvaluated evaluated; + + float iconSize = 1.0f; + float textSize = 16.0f; + + const style::SymbolLayer::Impl& impl() const; +}; + +template <> +inline bool RenderLayer::is<RenderSymbolLayer>() const { + return type == style::LayerType::Symbol; +} + +} // namespace mbgl |