diff options
Diffstat (limited to 'src')
24 files changed, 506 insertions, 61 deletions
diff --git a/src/mbgl/programs/line_program.cpp b/src/mbgl/programs/line_program.cpp index faf57ef19b..3c4124e9f4 100644 --- a/src/mbgl/programs/line_program.cpp +++ b/src/mbgl/programs/line_program.cpp @@ -44,6 +44,20 @@ LineProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& p ); } +LineGradientProgram::UniformValues +LineGradientProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, + const RenderTile& tile, + const TransformState& state, + const std::array<float, 2>& pixelsToGLUnits) { + return makeValues<LineGradientProgram::UniformValues>( + properties, + tile, + state, + pixelsToGLUnits, + uniforms::u_image::Value{ 0 } + ); +} + LineSDFProgram::UniformValues LineSDFProgram::uniformValues(const RenderLinePaintProperties::PossiblyEvaluated& properties, float pixelRatio, diff --git a/src/mbgl/programs/line_program.hpp b/src/mbgl/programs/line_program.hpp index da9964e623..c07176fa44 100644 --- a/src/mbgl/programs/line_program.hpp +++ b/src/mbgl/programs/line_program.hpp @@ -4,6 +4,7 @@ #include <mbgl/programs/attributes.hpp> #include <mbgl/programs/uniforms.hpp> #include <mbgl/shaders/line.hpp> +#include <mbgl/shaders/line_gradient.hpp> #include <mbgl/shaders/line_pattern.hpp> #include <mbgl/shaders/line_sdf.hpp> #include <mbgl/util/geometry.hpp> @@ -98,6 +99,26 @@ public: const std::array<float, 2>& pixelsToGLUnits); }; +class LineGradientProgram : public Program< + shaders::line_gradient, + gl::Triangle, + LineLayoutAttributes, + gl::Uniforms< + uniforms::u_matrix, + uniforms::u_ratio, + uniforms::u_gl_units_to_pixels, + uniforms::u_image>, + RenderLinePaintProperties> +{ +public: + using Program::Program; + + static UniformValues uniformValues(const RenderLinePaintProperties::PossiblyEvaluated&, + const RenderTile&, + const TransformState&, + const std::array<float, 2>& pixelsToGLUnits); +}; + class LinePatternProgram : public Program< shaders::line_pattern, gl::Triangle, diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index 4bb23455e8..6c9802ab4b 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -37,6 +37,7 @@ public: hillshade(context, programParameters), hillshadePrepare(context, programParameters), line(context, programParameters), + lineGradient(context, programParameters), lineSDF(context, programParameters), linePattern(context, programParameters), raster(context, programParameters), @@ -64,6 +65,7 @@ public: HillshadeProgram hillshade; HillshadePrepareProgram hillshadePrepare; ProgramMap<LineProgram> line; + ProgramMap<LineGradientProgram> lineGradient; ProgramMap<LineSDFProgram> lineSDF; ProgramMap<LinePatternProgram> linePattern; RasterProgram raster; diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp index a96518df38..248e0a235d 100644 --- a/src/mbgl/renderer/buckets/line_bucket.cpp +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -88,6 +88,16 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome return; } + const auto &props = feature.getProperties(); + optional<double> totalDistance; + optional<double> startDistance; + auto total_it = props.find("$distance_total"); + auto start_it = props.find("$distance_start"); + if (total_it != props.end() && start_it != props.end()) { + totalDistance = numericValue<double>(total_it->second); + startDistance = numericValue<double>(start_it->second); + } + const LineJoinType joinType = layout.evaluate<LineJoin>(zoom, feature); const float miterLimit = joinType == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); @@ -190,7 +200,8 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (prevSegmentLength > 2.0 * sharpCornerOffset) { GeometryCoordinate newPrevVertex = *currentCoordinate - convertPoint<int16_t>(util::round(convertPoint<double>(*currentCoordinate - *prevCoordinate) * (sharpCornerOffset / prevSegmentLength))); distance += util::dist<double>(newPrevVertex, *prevCoordinate); - addCurrentVertex(newPrevVertex, distance, *prevNormal, 0, 0, false, startVertex, triangleStore); + addCurrentVertex(newPrevVertex, distance, *prevNormal, 0, 0, false, startVertex, + triangleStore, totalDistance, startDistance); prevCoordinate = newPrevVertex; } } @@ -235,7 +246,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (middleVertex && currentJoin == LineJoinType::Miter) { joinNormal = joinNormal * miterLength; addCurrentVertex(*currentCoordinate, distance, joinNormal, 0, 0, false, startVertex, - triangleStore); + triangleStore, totalDistance, startDistance); } else if (middleVertex && currentJoin == LineJoinType::FlipBevel) { // miter is too big, flip the direction to make a beveled join @@ -251,10 +262,10 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome } addCurrentVertex(*currentCoordinate, distance, joinNormal, 0, 0, false, startVertex, - triangleStore); + triangleStore, totalDistance, startDistance); addCurrentVertex(*currentCoordinate, distance, joinNormal * -1.0, 0, 0, false, startVertex, - triangleStore); + triangleStore, totalDistance, startDistance); } else if (middleVertex && (currentJoin == LineJoinType::Bevel || currentJoin == LineJoinType::FakeRound)) { const bool lineTurnsLeft = (prevNormal->x * nextNormal->y - prevNormal->y * nextNormal->x) > 0; const float offset = -std::sqrt(miterLength * miterLength - 1); @@ -272,7 +283,7 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome // Close previous segement with bevel if (!startOfLine) { addCurrentVertex(*currentCoordinate, distance, *prevNormal, offsetA, offsetB, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); } if (currentJoin == LineJoinType::FakeRound) { @@ -287,41 +298,44 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome for (int m = 0; m < n; m++) { auto approxFractionalJoinNormal = util::unit(*nextNormal * ((m + 1.0) / (n + 1.0)) + *prevNormal); - addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore); + addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, + startVertex, triangleStore, totalDistance, startDistance); } - addPieSliceVertex(*currentCoordinate, distance, joinNormal, lineTurnsLeft, startVertex, triangleStore); + addPieSliceVertex(*currentCoordinate, distance, joinNormal, lineTurnsLeft, startVertex, + triangleStore, totalDistance, startDistance); for (int k = n - 1; k >= 0; k--) { auto approxFractionalJoinNormal = util::unit(*prevNormal * ((k + 1.0) / (n + 1.0)) + *nextNormal); - addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore); + addPieSliceVertex(*currentCoordinate, distance, approxFractionalJoinNormal, lineTurnsLeft, + startVertex, triangleStore, totalDistance, startDistance); } } // Start next segment if (nextCoordinate) { addCurrentVertex(*currentCoordinate, distance, *nextNormal, -offsetA, -offsetB, - false, startVertex, triangleStore); + false, startVertex, triangleStore, totalDistance, startDistance); } } else if (!middleVertex && currentCap == LineCapType::Butt) { if (!startOfLine) { // Close previous segment with a butt addCurrentVertex(*currentCoordinate, distance, *prevNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); } // Start next segment with a butt if (nextCoordinate) { addCurrentVertex(*currentCoordinate, distance, *nextNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); } } else if (!middleVertex && currentCap == LineCapType::Square) { if (!startOfLine) { // Close previous segment with a square cap addCurrentVertex(*currentCoordinate, distance, *prevNormal, 1, 1, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); // The segment is done. Unset vertices to disconnect segments. e1 = e2 = -1; @@ -330,18 +344,18 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome // Start next segment if (nextCoordinate) { addCurrentVertex(*currentCoordinate, distance, *nextNormal, -1, -1, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); } } else if (middleVertex ? currentJoin == LineJoinType::Round : currentCap == LineCapType::Round) { if (!startOfLine) { // Close previous segment with a butt addCurrentVertex(*currentCoordinate, distance, *prevNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); // Add round cap or linejoin at end of segment addCurrentVertex(*currentCoordinate, distance, *prevNormal, 1, 1, true, startVertex, - triangleStore); + triangleStore, totalDistance, startDistance); // The segment is done. Unset vertices to disconnect segments. e1 = e2 = -1; @@ -351,10 +365,10 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (nextCoordinate) { // Add round cap before first segment addCurrentVertex(*currentCoordinate, distance, *nextNormal, -1, -1, true, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); addCurrentVertex(*currentCoordinate, distance, *nextNormal, 0, 0, false, - startVertex, triangleStore); + startVertex, triangleStore, totalDistance, startDistance); } } @@ -363,7 +377,8 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome if (nextSegmentLength > 2 * sharpCornerOffset) { GeometryCoordinate newCurrentVertex = *currentCoordinate + convertPoint<int16_t>(util::round(convertPoint<double>(*nextCoordinate - *currentCoordinate) * (sharpCornerOffset / nextSegmentLength))); distance += util::dist<double>(newCurrentVertex, *currentCoordinate); - addCurrentVertex(newCurrentVertex, distance, *nextNormal, 0, 0, false, startVertex, triangleStore); + addCurrentVertex(newCurrentVertex, distance, *nextNormal, 0, 0, false, startVertex, + triangleStore, totalDistance, startDistance); currentCoordinate = newCurrentVertex; } } @@ -391,14 +406,27 @@ void LineBucket::addGeometry(const GeometryCoordinates& coordinates, const Geome } void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, - double &distance, + double distance, const Point<double>& normal, double endLeft, double endRight, bool round, std::size_t startVertex, - std::vector<TriangleElement>& triangleStore) { + std::vector<TriangleElement>& triangleStore, + optional<double> totalDistance, + optional<double> startDistance) { Point<double> extrude = normal; + if (totalDistance && startDistance) { + // First scale line from tile units to [0, 2^15) + distance = scaleDistance(distance, *totalDistance, *startDistance); + // Check to see if this vertex is going to be drawn across an edge, + // and if so, don't add square/round caps: + if (shouldClipAtEdge(currentCoordinate, distance)) { + endLeft = 0.; + endRight = 0.; + round = false; + } + } if (endLeft) extrude = extrude - (util::perp(normal) * endLeft); vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, round, false, endLeft, distance * LINE_DISTANCE_SCALE)); @@ -424,9 +452,10 @@ void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, // When we get close to the distance, reset it to zero and add the vertex again with // a distance of zero. The max distance is determined by the number of bits we allocate // to `linesofar`. - if (distance > MAX_LINE_DISTANCE / 2.0f) { + if (distance > MAX_LINE_DISTANCE / 2.0f && !totalDistance && !startDistance) { distance = 0; - addCurrentVertex(currentCoordinate, distance, normal, endLeft, endRight, round, startVertex, triangleStore); + addCurrentVertex(currentCoordinate, distance, normal, endLeft, endRight, round, startVertex, + triangleStore, totalDistance, startDistance); } } @@ -435,8 +464,13 @@ void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex, const Point<double>& extrude, bool lineTurnsLeft, std::size_t startVertex, - std::vector<TriangleElement>& triangleStore) { + std::vector<TriangleElement>& triangleStore, + optional<double> totalDistance, + optional<double> startDistance) { Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0); + if (totalDistance && startDistance) { + distance = scaleDistance(distance, *totalDistance, *startDistance); + } vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, false, lineTurnsLeft, 0, distance * LINE_DISTANCE_SCALE)); e3 = vertices.vertexSize() - 1 - startVertex; if (e1 >= 0 && e2 >= 0) { @@ -465,6 +499,19 @@ bool LineBucket::hasData() const { return !segments.empty(); } +double LineBucket::scaleDistance(double tileDistance, double totalDistance, double startDistance) const { + return (startDistance + tileDistance) * ((MAX_LINE_DISTANCE - 1) / totalDistance); +} + +bool LineBucket::shouldClipAtEdge(const GeometryCoordinate& vertex, double vertexDistance) const { + // We detect non-start/end tile edge vertices for gradient lines because + // we turn clipping off when there's no tile buffer (which we do in + // order to calcuate line distances correctly), and turn these into butt + // ends so they won't overlap with semitransparent neighbors. + if (vertexDistance == 0 || vertexDistance == MAX_LINE_DISTANCE - 1) return false; + return vertex.x == 0 || vertex.x == util::EXTENT || vertex.y == 0 || vertex.y == util::EXTENT; +} + template <class Property> static float get(const RenderLineLayer& layer, const std::map<std::string, LineProgram::PaintPropertyBinders>& paintPropertyBinders) { auto it = paintPropertyBinders.find(layer.getID()); diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp index 4fb77c377e..6f1b3b20c4 100644 --- a/src/mbgl/renderer/buckets/line_bucket.hpp +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -47,12 +47,17 @@ private: TriangleElement(uint16_t a_, uint16_t b_, uint16_t c_) : a(a_), b(b_), c(c_) {} uint16_t a, b, c; }; - void addCurrentVertex(const GeometryCoordinate& currentVertex, double& distance, + void addCurrentVertex(const GeometryCoordinate& currentVertex, double distance, const Point<double>& normal, double endLeft, double endRight, bool round, - std::size_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore); + std::size_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore, + optional<double> totalDistance, optional<double> startDistance); void addPieSliceVertex(const GeometryCoordinate& currentVertex, double distance, const Point<double>& extrude, bool lineTurnsLeft, std::size_t startVertex, - std::vector<TriangleElement>& triangleStore); + std::vector<TriangleElement>& triangleStore, + optional<double> totalDistance, optional<double> startDistance); + + double scaleDistance(double, double, double) const; + bool shouldClipAtEdge(const GeometryCoordinate&, double) const; std::ptrdiff_t e1; std::ptrdiff_t e2; diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp index 1b4a1c0ff7..04d300dd1f 100644 --- a/src/mbgl/renderer/layers/render_line_layer.cpp +++ b/src/mbgl/renderer/layers/render_line_layer.cpp @@ -18,7 +18,7 @@ using namespace style; RenderLineLayer::RenderLineLayer(Immutable<style::LineLayer::Impl> _impl) : RenderLayer(style::LayerType::Line, _impl), - unevaluated(impl().paint.untransitioned()) { + unevaluated(impl().paint.untransitioned()), colorRamp({256, 1}) { } const style::LineLayer::Impl& RenderLineLayer::impl() const { @@ -61,12 +61,12 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { assert(dynamic_cast<LineBucket*>(tile.tile.getBucket(*baseImpl))); LineBucket& bucket = *reinterpret_cast<LineBucket*>(tile.tile.getBucket(*baseImpl)); - auto draw = [&] (auto& program, auto&& uniformValues) { + auto draw = [&] (auto& program, const auto& stencilMode, auto&& uniformValues) { program.get(evaluated).draw( parameters.context, gl::Triangles(), parameters.depthModeForSublayer(0, gl::DepthMode::ReadOnly), - parameters.stencilModeForClipping(tile.clip), + stencilMode, parameters.colorModeForRenderPass(), std::move(uniformValues), *bucket.vertexBuffer, @@ -88,6 +88,7 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { parameters.lineAtlas.bind(parameters.context, 0); draw(parameters.programs.lineSDF, + parameters.stencilModeForClipping(tile.clip), LineSDFProgram::uniformValues( evaluated, parameters.pixelRatio, @@ -108,6 +109,7 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { parameters.imageManager.bind(parameters.context, 0); draw(parameters.programs.linePattern, + parameters.stencilModeForClipping(tile.clip), LinePatternProgram::uniformValues( evaluated, tile, @@ -117,8 +119,22 @@ void RenderLineLayer::render(PaintParameters& parameters, RenderSource*) { *posA, *posB)); + } else if (!unevaluated.get<LineGradient>().getValue().isUndefined()) { + if (!colorRampTexture) { + colorRampTexture = parameters.context.createTexture(colorRamp); + } + + draw(parameters.programs.lineGradient, + gl::StencilMode::disabled(), + LineGradientProgram::uniformValues( + evaluated, + tile, + parameters.state, + parameters.pixelsToGLUnits)); + } else { draw(parameters.programs.line, + parameters.stencilModeForClipping(tile.clip), LineProgram::uniformValues( evaluated, tile, @@ -158,6 +174,27 @@ optional<GeometryCollection> offsetLine(const GeometryCollection& rings, const d return newRings; } +void RenderLineLayer::updateColorRamp() { + auto colorValue = unevaluated.get<LineGradient>().getValue(); + if (colorValue.isUndefined()) { + return; + } + + const auto length = colorRamp.bytes(); + + for (uint32_t i = 0; i < length; i += 4) { + const auto color = colorValue.evaluate(static_cast<double>(i) / length); + colorRamp.data[i + 0] = std::floor(color.r * 255); + colorRamp.data[i + 1] = std::floor(color.g * 255); + colorRamp.data[i + 2] = std::floor(color.b * 255); + colorRamp.data[i + 3] = std::floor(color.a * 255); + } + + if (colorRampTexture) { + colorRampTexture = nullopt; + } +} + bool RenderLineLayer::queryIntersectsFeature( const GeometryCoordinates& queryGeometry, const GeometryTileFeature& feature, diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp index 8bf7e2329d..59333f0404 100644 --- a/src/mbgl/renderer/layers/render_line_layer.hpp +++ b/src/mbgl/renderer/layers/render_line_layer.hpp @@ -4,6 +4,7 @@ #include <mbgl/style/layers/line_layer_impl.hpp> #include <mbgl/style/layers/line_layer_properties.hpp> #include <mbgl/programs/uniforms.hpp> +#include <mbgl/util/image.hpp> namespace mbgl { @@ -32,6 +33,8 @@ public: const float, const float) const override; + void updateColorRamp(); + std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override; // Paint properties @@ -40,6 +43,9 @@ public: const style::LineLayer::Impl& impl() const; + PremultipliedImage colorRamp; + optional<gl::Texture> colorRampTexture; + private: float getLineWidth(const GeometryTileFeature&, const float) const; }; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 627a592d81..8f74e43f42 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -190,6 +190,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) { if (layer.is<RenderHeatmapLayer>()) { layer.as<RenderHeatmapLayer>()->updateColorRamp(); } + + if (layer.is<RenderLineLayer>()) { + layer.as<RenderLineLayer>()->updateColorRamp(); + } } if (layerAdded || layerChanged || zoomChanged || layer.hasTransition()) { diff --git a/src/mbgl/shaders/collision_circle.cpp b/src/mbgl/shaders/collision_circle.cpp index 82ebbf05a0..f220586245 100644 --- a/src/mbgl/shaders/collision_circle.cpp +++ b/src/mbgl/shaders/collision_circle.cpp @@ -26,10 +26,7 @@ varying vec2 v_extrude_scale; void main() { vec4 projectedPoint = u_matrix * vec4(a_anchor_pos, 0, 1); highp float camera_to_anchor_distance = projectedPoint.w; - highp float collision_perspective_ratio = clamp( - 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance), - 0.0, // Prevents oversized near-field circles in pitched/overzoomed tiles - 4.0); + highp float collision_perspective_ratio = 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance); gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0); @@ -46,7 +43,6 @@ void main() { )MBGL_SHADER"; const char* collision_circle::fragmentSource = R"MBGL_SHADER( -uniform float u_overscale_factor; varying float v_placed; varying float v_notUsed; @@ -72,7 +68,7 @@ void main() { float extrude_scale_length = length(v_extrude_scale); float extrude_length = length(v_extrude) * extrude_scale_length; - float stroke_width = 15.0 * extrude_scale_length / u_overscale_factor; + float stroke_width = 15.0 * extrude_scale_length; float radius = v_radius * extrude_scale_length; float distance_to_edge = abs(extrude_length - radius); diff --git a/src/mbgl/shaders/line.cpp b/src/mbgl/shaders/line.cpp index c700295a15..98ca2f4f1b 100644 --- a/src/mbgl/shaders/line.cpp +++ b/src/mbgl/shaders/line.cpp @@ -31,6 +31,7 @@ uniform vec2 u_gl_units_to_pixels; varying vec2 v_normal; varying vec2 v_width2; varying float v_gamma_scale; +varying float v_linesofar; #ifndef HAS_UNIFORM_u_color @@ -131,6 +132,8 @@ void main() { vec2 a_extrude = a_data.xy - 128.0; float a_direction = mod(a_data.z, 4.0) - 1.0; + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + vec2 pos = a_pos_normal.xy; // x is 1 if it's a round cap, 0 otherwise diff --git a/src/mbgl/shaders/line_gradient.cpp b/src/mbgl/shaders/line_gradient.cpp new file mode 100644 index 0000000000..08e59a0d96 --- /dev/null +++ b/src/mbgl/shaders/line_gradient.cpp @@ -0,0 +1,249 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#include <mbgl/shaders/line_gradient.hpp> + +namespace mbgl { +namespace shaders { + +const char* line_gradient::name = "line_gradient"; +const char* line_gradient::vertexSource = R"MBGL_SHADER( + + +// the distance over which the line edge fades out. +// Retina devices need a smaller distance to avoid aliasing. +#define ANTIALIASING 1.0 / DEVICE_PIXEL_RATIO / 2.0 + +// floor(127 / 2) == 63.0 +// the maximum allowed miter limit is 2.0 at the moment. the extrude normal is +// stored in a byte (-128..127). we scale regular normals up to length 63, but +// there are also "special" normals that have a bigger length (of up to 126 in +// this case). +// #define scale 63.0 +#define scale 0.015873016 + +attribute vec4 a_pos_normal; +attribute vec4 a_data; + +uniform mat4 u_matrix; +uniform mediump float u_ratio; +uniform vec2 u_gl_units_to_pixels; + +varying vec2 v_normal; +varying vec2 v_width2; +varying float v_gamma_scale; +varying float v_linesofar; + + +#ifndef HAS_UNIFORM_u_color +uniform lowp float a_color_t; +attribute highp vec4 a_color; +varying highp vec4 color; +#else +uniform highp vec4 u_color; +#endif + + +#ifndef HAS_UNIFORM_u_blur +uniform lowp float a_blur_t; +attribute lowp vec2 a_blur; +varying lowp float blur; +#else +uniform lowp float u_blur; +#endif + + +#ifndef HAS_UNIFORM_u_opacity +uniform lowp float a_opacity_t; +attribute lowp vec2 a_opacity; +varying lowp float opacity; +#else +uniform lowp float u_opacity; +#endif + + +#ifndef HAS_UNIFORM_u_gapwidth +uniform lowp float a_gapwidth_t; +attribute mediump vec2 a_gapwidth; +#else +uniform mediump float u_gapwidth; +#endif + + +#ifndef HAS_UNIFORM_u_offset +uniform lowp float a_offset_t; +attribute lowp vec2 a_offset; +#else +uniform lowp float u_offset; +#endif + + +#ifndef HAS_UNIFORM_u_width +uniform lowp float a_width_t; +attribute mediump vec2 a_width; +#else +uniform mediump float u_width; +#endif + + +void main() { + +#ifndef HAS_UNIFORM_u_color + color = unpack_mix_vec4(a_color, a_color_t); +#else + highp vec4 color = u_color; +#endif + + +#ifndef HAS_UNIFORM_u_blur + blur = unpack_mix_vec2(a_blur, a_blur_t); +#else + lowp float blur = u_blur; +#endif + + +#ifndef HAS_UNIFORM_u_opacity + opacity = unpack_mix_vec2(a_opacity, a_opacity_t); +#else + lowp float opacity = u_opacity; +#endif + + +#ifndef HAS_UNIFORM_u_gapwidth + mediump float gapwidth = unpack_mix_vec2(a_gapwidth, a_gapwidth_t); +#else + mediump float gapwidth = u_gapwidth; +#endif + + +#ifndef HAS_UNIFORM_u_offset + lowp float offset = unpack_mix_vec2(a_offset, a_offset_t); +#else + lowp float offset = u_offset; +#endif + + +#ifndef HAS_UNIFORM_u_width + mediump float width = unpack_mix_vec2(a_width, a_width_t); +#else + mediump float width = u_width; +#endif + + + vec2 a_extrude = a_data.xy - 128.0; + float a_direction = mod(a_data.z, 4.0) - 1.0; + + v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0; + + vec2 pos = a_pos_normal.xy; + + // x is 1 if it's a round cap, 0 otherwise + // y is 1 if the normal points up, and -1 if it points down + mediump vec2 normal = a_pos_normal.zw; + v_normal = normal; + + // these transformations used to be applied in the JS and native code bases. + // moved them into the shader for clarity and simplicity. + gapwidth = gapwidth / 2.0; + float halfwidth = width / 2.0; + offset = -1.0 * offset; + + float inset = gapwidth + (gapwidth > 0.0 ? ANTIALIASING : 0.0); + float outset = gapwidth + halfwidth * (gapwidth > 0.0 ? 2.0 : 1.0) + ANTIALIASING; + + // Scale the extrusion vector down to a normal and then up by the line width + // of this vertex. + mediump vec2 dist = outset * a_extrude * scale; + + // Calculate the offset when drawing a line that is to the side of the actual line. + // We do this by creating a vector that points towards the extrude, but rotate + // it when we're drawing round end points (a_direction = -1 or 1) since their + // extrude vector points in another direction. + mediump float u = 0.5 * a_direction; + mediump float t = 1.0 - abs(u); + mediump vec2 offset2 = offset * a_extrude * scale * normal.y * mat2(t, -u, u, t); + + vec4 projected_extrude = u_matrix * vec4(dist / u_ratio, 0.0, 0.0); + gl_Position = u_matrix * vec4(pos + offset2 / u_ratio, 0.0, 1.0) + projected_extrude; + + // calculate how much the perspective view squishes or stretches the extrude + float extrude_length_without_perspective = length(dist); + float extrude_length_with_perspective = length(projected_extrude.xy / gl_Position.w * u_gl_units_to_pixels); + v_gamma_scale = extrude_length_without_perspective / extrude_length_with_perspective; + + v_width2 = vec2(outset, inset); +} + +)MBGL_SHADER"; +const char* line_gradient::fragmentSource = R"MBGL_SHADER( +#define MAX_LINE_DISTANCE 32767.0 + + +#ifndef HAS_UNIFORM_u_color +varying highp vec4 color; +#else +uniform highp vec4 u_color; +#endif + + +#ifndef HAS_UNIFORM_u_blur +varying lowp float blur; +#else +uniform lowp float u_blur; +#endif + + +#ifndef HAS_UNIFORM_u_opacity +varying lowp float opacity; +#else +uniform lowp float u_opacity; +#endif + + +uniform sampler2D u_image; + +varying vec2 v_width2; +varying vec2 v_normal; +varying float v_gamma_scale; +varying float v_linesofar; + +void main() { + +#ifdef HAS_UNIFORM_u_color + highp vec4 color = u_color; +#endif + + +#ifdef HAS_UNIFORM_u_blur + lowp float blur = u_blur; +#endif + + +#ifdef HAS_UNIFORM_u_opacity + lowp float opacity = u_opacity; +#endif + + + // Calculate the distance of the pixel from the line in pixels. + float dist = length(v_normal) * v_width2.s; + + // Calculate the antialiasing fade factor. This is either when fading in + // the line in case of an offset line (v_width2.t) or when fading out + // (v_width2.s) + float blur2 = (blur + 1.0 / DEVICE_PIXEL_RATIO) * v_gamma_scale; + float alpha = clamp(min(dist - (v_width2.t - blur2), v_width2.s - dist) / blur2, 0.0, 1.0); + + // For gradient lines, v_linesofar is the ratio along the entire line, + // scaled to [0, 2^15), and the gradient ramp is stored in a texture. + color = texture2D(u_image, vec2(v_linesofar / MAX_LINE_DISTANCE, 0.5)); + + gl_FragColor = color * (alpha * opacity); + +#ifdef OVERDRAW_INSPECTOR + gl_FragColor = vec4(1.0); +#endif +} + +)MBGL_SHADER"; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/shaders/line_gradient.hpp b/src/mbgl/shaders/line_gradient.hpp new file mode 100644 index 0000000000..87a9d6d38c --- /dev/null +++ b/src/mbgl/shaders/line_gradient.hpp @@ -0,0 +1,16 @@ +// NOTE: DO NOT CHANGE THIS FILE. IT IS AUTOMATICALLY GENERATED. + +#pragma once + +namespace mbgl { +namespace shaders { + +class line_gradient { +public: + static const char* name; + static const char* vertexSource; + static const char* fragmentSource; +}; + +} // namespace shaders +} // namespace mbgl diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp index 25c8fdb1ca..c3e6da75aa 100644 --- a/src/mbgl/style/conversion/make_property_setters.hpp +++ b/src/mbgl/style/conversion/make_property_setters.hpp @@ -115,6 +115,8 @@ inline auto makePaintPropertySetters() { result["line-dasharray-transition"] = &setTransition<LineLayer, &LineLayer::setLineDasharrayTransition>; result["line-pattern"] = &setProperty<LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>; result["line-pattern-transition"] = &setTransition<LineLayer, &LineLayer::setLinePatternTransition>; + result["line-gradient"] = &setProperty<LineLayer, ColorRampPropertyValue, &LineLayer::setLineGradient>; + result["line-gradient-transition"] = &setTransition<LineLayer, &LineLayer::setLineGradientTransition>; result["icon-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>; result["icon-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconOpacityTransition>; @@ -174,7 +176,7 @@ inline auto makePaintPropertySetters() { result["heatmap-weight-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapWeightTransition>; result["heatmap-intensity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapIntensity>; result["heatmap-intensity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapIntensityTransition>; - result["heatmap-color"] = &setProperty<HeatmapLayer, HeatmapColorPropertyValue, &HeatmapLayer::setHeatmapColor>; + result["heatmap-color"] = &setProperty<HeatmapLayer, ColorRampPropertyValue, &HeatmapLayer::setHeatmapColor>; result["heatmap-color-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapColorTransition>; result["heatmap-opacity"] = &setProperty<HeatmapLayer, PropertyValue<float>, &HeatmapLayer::setHeatmapOpacity>; result["heatmap-opacity-transition"] = &setTransition<HeatmapLayer, &HeatmapLayer::setHeatmapOpacityTransition>; diff --git a/src/mbgl/style/conversion/property_setter.hpp b/src/mbgl/style/conversion/property_setter.hpp index e3716a18dc..ce2cd79b30 100644 --- a/src/mbgl/style/conversion/property_setter.hpp +++ b/src/mbgl/style/conversion/property_setter.hpp @@ -5,7 +5,7 @@ #include <mbgl/style/conversion/constant.hpp> #include <mbgl/style/conversion/property_value.hpp> #include <mbgl/style/conversion/data_driven_property_value.hpp> -#include <mbgl/style/conversion/heatmap_color_property_value.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> #include <mbgl/style/conversion/transition_options.hpp> #include <string> diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp index 42cb655024..e346b49c2f 100644 --- a/src/mbgl/style/expression/compound_expression.cpp +++ b/src/mbgl/style/expression/compound_expression.cpp @@ -231,12 +231,21 @@ std::unordered_map<std::string, CompoundExpressionRegistry::Definition> initiali }); define("heatmap-density", [](const EvaluationContext& params) -> Result<double> { - if (!params.heatmapDensity) { + if (!params.rampEvaluationParameter) { return EvaluationError { "The 'heatmap-density' expression is unavailable in the current evaluation context." }; } - return *(params.heatmapDensity); + return *(params.rampEvaluationParameter); + }); + + define("line-progress", [](const EvaluationContext& params) -> Result<double> { + if (!params.rampEvaluationParameter) { + return EvaluationError { + "The 'line-progress' expression is unavailable in the current evaluation context." + }; + } + return *(params.rampEvaluationParameter); }); define("has", [](const EvaluationContext& params, const std::string& key) -> Result<bool> { diff --git a/src/mbgl/style/expression/parsing_context.cpp b/src/mbgl/style/expression/parsing_context.cpp index 0215982209..1576c39164 100644 --- a/src/mbgl/style/expression/parsing_context.cpp +++ b/src/mbgl/style/expression/parsing_context.cpp @@ -52,7 +52,8 @@ bool isConstant(const Expression& expression) { } return isFeatureConstant(expression) && - isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}}); + isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "heatmap-density"}}) && + isGlobalPropertyConstant(expression, std::array<std::string, 2>{{"zoom", "line-progress"}}); } using namespace mbgl::style::conversion; diff --git a/src/mbgl/style/function/expression.cpp b/src/mbgl/style/function/expression.cpp index d9dbbfa1d3..0c3fb5304c 100644 --- a/src/mbgl/style/function/expression.cpp +++ b/src/mbgl/style/function/expression.cpp @@ -28,9 +28,9 @@ public: }; -EvaluationResult Expression::evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const { +EvaluationResult Expression::evaluate(optional<float> zoom, const Feature& feature, optional<double> rampEvaluationParameter) const { GeoJSONFeature f(feature); - return this->evaluate(EvaluationContext(zoom, &f, heatmapDensity)); + return this->evaluate(EvaluationContext(zoom, &f, rampEvaluationParameter)); } } // namespace expression diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp index 4989ff15f1..dcf85f2ed4 100644 --- a/src/mbgl/style/layers/heatmap_layer.cpp +++ b/src/mbgl/style/layers/heatmap_layer.cpp @@ -6,7 +6,7 @@ // for constructing default heatmap-color ramp expression from style JSON #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> -#include <mbgl/style/conversion/heatmap_color_property_value.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> namespace mbgl { namespace style { @@ -179,17 +179,17 @@ TransitionOptions HeatmapLayer::getHeatmapIntensityTransition() const { return impl().paint.template get<HeatmapIntensity>().options; } -HeatmapColorPropertyValue HeatmapLayer::getDefaultHeatmapColor() { +ColorRampPropertyValue HeatmapLayer::getDefaultHeatmapColor() { conversion::Error error; std::string rawValue = R"JSON(["interpolate",["linear"],["heatmap-density"],0,"rgba(0, 0, 255, 0)",0.1,"royalblue",0.3,"cyan",0.5,"lime",0.7,"yellow",1,"red"])JSON"; - return *conversion::convertJSON<HeatmapColorPropertyValue>(rawValue, error); + return *conversion::convertJSON<ColorRampPropertyValue>(rawValue, error); } -HeatmapColorPropertyValue HeatmapLayer::getHeatmapColor() const { +ColorRampPropertyValue HeatmapLayer::getHeatmapColor() const { return impl().paint.template get<HeatmapColor>().value; } -void HeatmapLayer::setHeatmapColor(HeatmapColorPropertyValue value) { +void HeatmapLayer::setHeatmapColor(ColorRampPropertyValue value) { if (value == getHeatmapColor()) return; auto impl_ = mutableImpl(); diff --git a/src/mbgl/style/layers/heatmap_layer_properties.hpp b/src/mbgl/style/layers/heatmap_layer_properties.hpp index f7afa5fbeb..70bc120184 100644 --- a/src/mbgl/style/layers/heatmap_layer_properties.hpp +++ b/src/mbgl/style/layers/heatmap_layer_properties.hpp @@ -24,6 +24,8 @@ struct HeatmapIntensity : PaintProperty<float> { static float defaultValue() { return 1; } }; +struct HeatmapColor : ColorRampProperty {}; + struct HeatmapOpacity : PaintProperty<float> { static float defaultValue() { return 1; } }; diff --git a/src/mbgl/style/layers/layer.cpp.ejs b/src/mbgl/style/layers/layer.cpp.ejs index 657a7f5a8a..0d11b16a6d 100644 --- a/src/mbgl/style/layers/layer.cpp.ejs +++ b/src/mbgl/style/layers/layer.cpp.ejs @@ -12,7 +12,7 @@ // for constructing default heatmap-color ramp expression from style JSON #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/json.hpp> -#include <mbgl/style/conversion/heatmap_color_property_value.hpp> +#include <mbgl/style/conversion/color_ramp_property_value.hpp> <% } -%> namespace mbgl { @@ -139,16 +139,18 @@ void <%- camelize(type) %>Layer::set<%- camelize(property.name) %>(<%- propertyV // Paint properties <% for (const property of paintProperties) { %> +<% if (property.name !== 'line-gradient') { -%> <%- propertyValueType(property) %> <%- camelize(type) %>Layer::getDefault<%- camelize(property.name) %>() { -<% if (property.name === 'heatmap-color') { -%> +<% if (property.name === 'heatmap-color') { -%> conversion::Error error; std::string rawValue = R"JSON(<%- JSON.stringify(property.default) %>)JSON"; return *conversion::convertJSON<<%- propertyValueType(property)%>>(rawValue, error); -<% } else { -%> +<% } else { -%> return { <%- defaultValue(property) %> }; -<% } -%> +<% } -%> } +<% } -%> <%- propertyValueType(property) %> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>() const { return impl().paint.template get<<%- camelize(property.name) %>>().value; } diff --git a/src/mbgl/style/layers/layer_properties.hpp.ejs b/src/mbgl/style/layers/layer_properties.hpp.ejs index 1bceb84960..58b0f6c569 100644 --- a/src/mbgl/style/layers/layer_properties.hpp.ejs +++ b/src/mbgl/style/layers/layer_properties.hpp.ejs @@ -25,10 +25,13 @@ struct <%- camelize(property.name) %> : <%- layoutPropertyType(property, type) % <% } -%> <% for (const property of paintProperties) { -%> -<% if (property.name === 'heatmap-color') continue; -%> +<% if (property.name === 'heatmap-color' || property.name === 'line-gradient') { -%> +struct <%- camelize(property.name) %> : ColorRampProperty {}; +<% } else { -%> struct <%- camelize(property.name) %> : <%- paintPropertyType(property, type) %> { static <%- evaluatedType(property) %> defaultValue() { return <%- defaultValue(property) %>; } }; +<% } -%> <% } -%> <% if (layoutProperties.length) { -%> diff --git a/src/mbgl/style/layers/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp index 1c7f0d28ee..2d6bf5dd01 100644 --- a/src/mbgl/style/layers/line_layer.cpp +++ b/src/mbgl/style/layers/line_layer.cpp @@ -429,5 +429,28 @@ TransitionOptions LineLayer::getLinePatternTransition() const { return impl().paint.template get<LinePattern>().options; } +ColorRampPropertyValue LineLayer::getLineGradient() const { + return impl().paint.template get<LineGradient>().value; +} + +void LineLayer::setLineGradient(ColorRampPropertyValue value) { + if (value == getLineGradient()) + return; + auto impl_ = mutableImpl(); + impl_->paint.template get<LineGradient>().value = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} + +void LineLayer::setLineGradientTransition(const TransitionOptions& options) { + auto impl_ = mutableImpl(); + impl_->paint.template get<LineGradient>().options = options; + baseImpl = std::move(impl_); +} + +TransitionOptions LineLayer::getLineGradientTransition() const { + return impl().paint.template get<LineGradient>().options; +} + } // namespace style } // namespace mbgl diff --git a/src/mbgl/style/layers/line_layer_properties.hpp b/src/mbgl/style/layers/line_layer_properties.hpp index aeaf51698a..ee6789205c 100644 --- a/src/mbgl/style/layers/line_layer_properties.hpp +++ b/src/mbgl/style/layers/line_layer_properties.hpp @@ -72,6 +72,8 @@ struct LinePattern : CrossFadedPaintProperty<std::string> { static std::string defaultValue() { return ""; } }; +struct LineGradient : ColorRampProperty {}; + class LineLayoutProperties : public Properties< LineCap, LineJoin, @@ -89,7 +91,8 @@ class LinePaintProperties : public Properties< LineOffset, LineBlur, LineDasharray, - LinePattern + LinePattern, + LineGradient > {}; } // namespace style diff --git a/src/mbgl/style/paint_property.hpp b/src/mbgl/style/paint_property.hpp index 195eb645a9..15dbf9205c 100644 --- a/src/mbgl/style/paint_property.hpp +++ b/src/mbgl/style/paint_property.hpp @@ -2,7 +2,7 @@ #include <mbgl/style/properties.hpp> #include <mbgl/style/property_value.hpp> -#include <mbgl/style/heatmap_color_property_value.hpp> +#include <mbgl/style/color_ramp_property_value.hpp> #include <mbgl/style/data_driven_property_value.hpp> #include <mbgl/renderer/property_evaluator.hpp> #include <mbgl/renderer/cross_faded_property_evaluator.hpp> @@ -50,19 +50,19 @@ public: }; /* - * Special-case paint property traits for heatmap-color, needed because - * heatmap-color values do not fit into the + * Special-case paint property traits for heatmap-color and line-gradient, + * needed because these values do not fit into the * Undefined | Value | {Camera,Source,Composite}Function taxonomy that applies * to all other paint properties. * - * These traits are provided here--despite the fact that heatmap-color - * is not used like other paint properties--to allow the parameter-pack-based + * These traits are provided here--despite the fact that color ramps + * are not used like other paint properties--to allow the parameter-pack-based * batch evaluation of paint properties to compile properly. */ -class HeatmapColor { +class ColorRampProperty { public: - using TransitionableType = Transitionable<HeatmapColorPropertyValue>; - using UnevaluatedType = Transitioning<HeatmapColorPropertyValue>; + using TransitionableType = Transitionable<ColorRampPropertyValue>; + using UnevaluatedType = Transitioning<ColorRampPropertyValue>; using EvaluatorType = PropertyEvaluator<Color>; using PossiblyEvaluatedType = Color; using Type = Color; |