diff options
author | Asheem Mamoowala <asheem.mamoowala@mapbox.com> | 2017-05-12 17:07:48 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-12 17:07:48 -0700 |
commit | 84e39611a096058826dbdb89dccbd1affad05f76 (patch) | |
tree | 97ac706e6a1300ad8d2e2293328e693cff10fbb9 /src/mbgl/renderer/buckets | |
parent | 83d9226e4f23c630cb0a78bd54783e35ed11fa81 (diff) | |
download | qtlocation-mapboxgl-84e39611a096058826dbdb89dccbd1affad05f76.tar.gz |
[core] Move renderer/* files into sub-folders (#8983)
Move renderer/* files into sub-folders
Diffstat (limited to 'src/mbgl/renderer/buckets')
-rw-r--r-- | src/mbgl/renderer/buckets/circle_bucket.cpp | 123 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/circle_bucket.hpp | 42 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/debug_bucket.cpp | 83 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/debug_bucket.hpp | 41 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/fill_bucket.cpp | 145 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/fill_bucket.hpp | 43 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp | 177 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp | 38 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/line_bucket.cpp | 506 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/line_bucket.hpp | 67 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/raster_bucket.cpp | 30 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/raster_bucket.hpp | 22 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/symbol_bucket.cpp | 86 | ||||
-rw-r--r-- | src/mbgl/renderer/buckets/symbol_bucket.hpp | 77 |
14 files changed, 1480 insertions, 0 deletions
diff --git a/src/mbgl/renderer/buckets/circle_bucket.cpp b/src/mbgl/renderer/buckets/circle_bucket.cpp new file mode 100644 index 0000000000..8b5743d500 --- /dev/null +++ b/src/mbgl/renderer/buckets/circle_bucket.cpp @@ -0,0 +1,123 @@ +#include <mbgl/renderer/buckets/circle_bucket.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/programs/circle_program.hpp> +#include <mbgl/style/layers/circle_layer_impl.hpp> +#include <mbgl/renderer/layers/render_circle_layer.hpp> +#include <mbgl/util/constants.hpp> +#include <mbgl/util/math.hpp> + +namespace mbgl { + +using namespace style; + +CircleBucket::CircleBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) + : mode(parameters.mode) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace( + std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as<RenderCircleLayer>()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void CircleBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +void CircleBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderCircle(parameters, *this, *layer.as<RenderCircleLayer>(), tile); +} + +bool CircleBucket::hasData() const { + return !segments.empty(); +} + +void CircleBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { + constexpr const uint16_t vertexLength = 4; + + for (auto& circle : geometry) { + for(auto& point : circle) { + auto x = point.x; + auto y = point.y; + + // Do not include points that are outside the tile boundaries. + // Include all points in Still mode. You need to include points from + // neighbouring tiles so that they are not clipped at tile boundaries. + if ((mode != MapMode::Still) && + (x < 0 || x >= util::EXTENT || y < 0 || y >= util::EXTENT)) continue; + + if (segments.empty() || segments.back().vertexLength + vertexLength > std::numeric_limits<uint16_t>::max()) { + // Move to a new segments because the old one can't hold the geometry. + segments.emplace_back(vertices.vertexSize(), triangles.indexSize()); + } + + // this geometry will be of the Point type, and we'll derive + // two triangles from it. + // + // ┌─────────┐ + // │ 4 3 │ + // │ │ + // │ 1 2 │ + // └─────────┘ + // + vertices.emplace_back(CircleProgram::vertex(point, -1, -1)); // 1 + vertices.emplace_back(CircleProgram::vertex(point, 1, -1)); // 2 + vertices.emplace_back(CircleProgram::vertex(point, 1, 1)); // 3 + vertices.emplace_back(CircleProgram::vertex(point, -1, 1)); // 4 + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t index = segment.vertexLength; + + // 1, 2, 3 + // 1, 4, 3 + triangles.emplace_back(index, index + 1, index + 2); + triangles.emplace_back(index, index + 3, index + 2); + + segment.vertexLength += vertexLength; + segment.indexLength += 6; + } + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +template <class Property> +static float get(const RenderCircleLayer& layer, const std::map<std::string, CircleProgram::PaintPropertyBinders>& paintPropertyBinders) { + auto it = paintPropertyBinders.find(layer.getID()); + if (it == paintPropertyBinders.end() || !it->second.statistics<Property>().max()) { + return layer.evaluated.get<Property>().constantOr(Property::defaultValue()); + } else { + return *it->second.statistics<Property>().max(); + } +} + +float CircleBucket::getQueryRadius(const RenderLayer& layer) const { + if (!layer.is<RenderCircleLayer>()) { + return 0; + } + + auto circleLayer = layer.as<RenderCircleLayer>(); + + float radius = get<CircleRadius>(*circleLayer, paintPropertyBinders); + auto translate = circleLayer->evaluated.get<CircleTranslate>(); + return radius + util::length(translate[0], translate[1]); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/circle_bucket.hpp b/src/mbgl/renderer/buckets/circle_bucket.hpp new file mode 100644 index 0000000000..b048fd7675 --- /dev/null +++ b/src/mbgl/renderer/buckets/circle_bucket.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/circle_program.hpp> +#include <mbgl/style/layers/circle_layer_properties.hpp> + +namespace mbgl { + +class BucketParameters; + +class CircleBucket : public Bucket { +public: + CircleBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + + float getQueryRadius(const RenderLayer&) const override; + + gl::VertexVector<CircleLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<CircleAttributes> segments; + + optional<gl::VertexBuffer<CircleLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + + std::map<std::string, CircleProgram::PaintPropertyBinders> paintPropertyBinders; + + const MapMode mode; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/debug_bucket.cpp b/src/mbgl/renderer/buckets/debug_bucket.cpp new file mode 100644 index 0000000000..acfe15d2fb --- /dev/null +++ b/src/mbgl/renderer/buckets/debug_bucket.cpp @@ -0,0 +1,83 @@ +#include <mbgl/renderer/buckets/debug_bucket.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/geometry/debug_font_data.hpp> +#include <mbgl/util/string.hpp> + +#include <cmath> +#include <string> +#include <vector> + +namespace mbgl { + +DebugBucket::DebugBucket(const OverscaledTileID& id, + const bool renderable_, + const bool complete_, + optional<Timestamp> modified_, + optional<Timestamp> expires_, + MapDebugOptions debugMode_, + gl::Context& context) + : renderable(renderable_), + complete(complete_), + modified(std::move(modified_)), + expires(std::move(expires_)), + debugMode(debugMode_) { + + gl::VertexVector<FillLayoutVertex> vertices; + gl::IndexVector<gl::Lines> indices; + + auto addText = [&] (const std::string& text, double left, double baseline, double scale) { + for (uint8_t c : text) { + if (c < 32 || c >= 127) + continue; + + optional<Point<int16_t>> prev; + + const glyph& glyph = simplex[c - 32]; + for (int32_t j = 0; j < glyph.length; j += 2) { + if (glyph.data[j] == -1 && glyph.data[j + 1] == -1) { + prev = {}; + } else { + Point<int16_t> p { + int16_t(::round(left + glyph.data[j] * scale)), + int16_t(::round(baseline - glyph.data[j + 1] * scale)) + }; + + vertices.emplace_back(FillProgram::layoutVertex(p)); + + if (prev) { + indices.emplace_back(vertices.vertexSize() - 2, + vertices.vertexSize() - 1); + } + + prev = p; + } + } + + left += glyph.width * scale; + } + }; + + double baseline = 200; + if (debugMode & MapDebugOptions::ParseStatus) { + const std::string text = util::toString(id) + " - " + + (complete ? "complete" : renderable ? "renderable" : "pending"); + addText(text, 50, baseline, 5); + baseline += 200; + } + + if (debugMode & MapDebugOptions::Timestamps && modified && expires) { + const std::string modifiedText = "modified: " + util::iso8601(*modified); + addText(modifiedText, 50, baseline, 5); + + const std::string expiresText = "expires: " + util::iso8601(*expires); + addText(expiresText, 50, baseline + 200, 5); + } + + segments.emplace_back(0, 0, vertices.vertexSize(), indices.indexSize()); + + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(indices)); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/debug_bucket.hpp b/src/mbgl/renderer/buckets/debug_bucket.hpp new file mode 100644 index 0000000000..756e58a6de --- /dev/null +++ b/src/mbgl/renderer/buckets/debug_bucket.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <mbgl/map/mode.hpp> +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/noncopyable.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/programs/debug_program.hpp> + +namespace mbgl { + +class OverscaledTileID; + +namespace gl { +class Context; +} // namespace gl + +class DebugBucket : private util::noncopyable { +public: + DebugBucket(const OverscaledTileID& id, + bool renderable, + bool complete, + optional<Timestamp> modified, + optional<Timestamp> expires, + MapDebugOptions, + gl::Context&); + + const bool renderable; + const bool complete; + const optional<Timestamp> modified; + const optional<Timestamp> expires; + const MapDebugOptions debugMode; + + gl::SegmentVector<DebugAttributes> segments; + optional<gl::VertexBuffer<DebugLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Lines>> indexBuffer; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp new file mode 100644 index 0000000000..042d7b7506 --- /dev/null +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -0,0 +1,145 @@ +#include <mbgl/renderer/buckets/fill_bucket.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/style/layers/fill_layer_impl.hpp> +#include <mbgl/renderer/layers/render_fill_layer.hpp> +#include <mbgl/util/math.hpp> + +#include <mapbox/earcut.hpp> + +#include <cassert> + +namespace mapbox { +namespace util { +template <> struct nth<0, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { return t.x; }; +}; + +template <> struct nth<1, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { return t.y; }; +}; +} // namespace util +} // namespace mapbox + +namespace mbgl { + +using namespace style; + +struct GeometryTooLongException : std::exception {}; + +FillBucket::FillBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace( + std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as<RenderFillLayer>()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void FillBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = 0; + + for (const auto& ring : polygon) { + totalVertices += ring.size(); + if (totalVertices > std::numeric_limits<uint16_t>::max()) + throw GeometryTooLongException(); + } + + std::size_t startVertices = vertices.vertexSize(); + + for (const auto& ring : polygon) { + std::size_t nVertices = ring.size(); + + if (nVertices == 0) + continue; + + if (lineSegments.empty() || lineSegments.back().vertexLength + nVertices > std::numeric_limits<uint16_t>::max()) { + lineSegments.emplace_back(vertices.vertexSize(), lines.indexSize()); + } + + auto& lineSegment = lineSegments.back(); + assert(lineSegment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t lineIndex = lineSegment.vertexLength; + + vertices.emplace_back(FillProgram::layoutVertex(ring[0])); + lines.emplace_back(lineIndex + nVertices - 1, lineIndex); + + for (uint32_t i = 1; i < nVertices; i++) { + vertices.emplace_back(FillProgram::layoutVertex(ring[i])); + lines.emplace_back(lineIndex + i - 1, lineIndex + i); + } + + lineSegment.vertexLength += nVertices; + lineSegment.indexLength += nVertices * 2; + } + + std::vector<uint32_t> indices = mapbox::earcut(polygon); + + std::size_t nIndicies = indices.size(); + assert(nIndicies % 3 == 0); + + if (triangleSegments.empty() || triangleSegments.back().vertexLength + totalVertices > std::numeric_limits<uint16_t>::max()) { + triangleSegments.emplace_back(startVertices, triangles.indexSize()); + } + + auto& triangleSegment = triangleSegments.back(); + assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t triangleIndex = triangleSegment.vertexLength; + + for (uint32_t i = 0; i < nIndicies; i += 3) { + triangles.emplace_back(triangleIndex + indices[i], + triangleIndex + indices[i + 1], + triangleIndex + indices[i + 2]); + } + + triangleSegment.vertexLength += totalVertices; + triangleSegment.indexLength += nIndicies; + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +void FillBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + lineIndexBuffer = context.createIndexBuffer(std::move(lines)); + triangleIndexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +void FillBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderFill(parameters, *this, *layer.as<RenderFillLayer>(), tile); +} + +bool FillBucket::hasData() const { + return !triangleSegments.empty() || !lineSegments.empty(); +} + +float FillBucket::getQueryRadius(const RenderLayer& layer) const { + if (!layer.is<RenderFillLayer>()) { + return 0; + } + + const std::array<float, 2>& translate = layer.as<RenderFillLayer>()->evaluated.get<FillTranslate>(); + return util::length(translate[0], translate[1]); + +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_bucket.hpp b/src/mbgl/renderer/buckets/fill_bucket.hpp new file mode 100644 index 0000000000..421d8b332b --- /dev/null +++ b/src/mbgl/renderer/buckets/fill_bucket.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/fill_program.hpp> +#include <mbgl/style/layers/fill_layer_properties.hpp> + +#include <vector> + +namespace mbgl { + +class BucketParameters; + +class FillBucket : public Bucket { +public: + FillBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + + float getQueryRadius(const RenderLayer&) const override; + + gl::VertexVector<FillLayoutVertex> vertices; + gl::IndexVector<gl::Lines> lines; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<FillAttributes> lineSegments; + gl::SegmentVector<FillAttributes> triangleSegments; + + optional<gl::VertexBuffer<FillLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Lines>> lineIndexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> triangleIndexBuffer; + + std::map<std::string, FillProgram::PaintPropertyBinders> paintPropertyBinders; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp new file mode 100644 index 0000000000..f61f1d1549 --- /dev/null +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp @@ -0,0 +1,177 @@ +#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp> +#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/constants.hpp> + +#include <mapbox/earcut.hpp> + +#include <cassert> + +namespace mapbox { +namespace util { +template <> +struct nth<0, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { + return t.x; + }; +}; + +template <> +struct nth<1, mbgl::GeometryCoordinate> { + static int64_t get(const mbgl::GeometryCoordinate& t) { + return t.y; + }; +}; +} // namespace util +} // namespace mapbox + +namespace mbgl { + +using namespace style; + +struct GeometryTooLongException : std::exception {}; + +FillExtrusionBucket::FillExtrusionBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace(std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as<RenderFillExtrusionLayer>()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometry) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = 0; + + for (const auto& ring : polygon) { + totalVertices += ring.size(); + if (totalVertices > std::numeric_limits<uint16_t>::max()) + throw GeometryTooLongException(); + } + + if (totalVertices == 0) continue; + + std::vector<uint32_t> flatIndices; + flatIndices.reserve(totalVertices); + + std::size_t startVertices = vertices.vertexSize(); + + if (triangleSegments.empty() || + triangleSegments.back().vertexLength + (5 * (totalVertices - 1) + 1) > + std::numeric_limits<uint16_t>::max()) { + triangleSegments.emplace_back(startVertices, triangles.indexSize()); + } + + auto& triangleSegment = triangleSegments.back(); + assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t triangleIndex = triangleSegment.vertexLength; + + assert(triangleIndex + (5 * (totalVertices - 1) + 1) <= + std::numeric_limits<uint16_t>::max()); + + for (const auto& ring : polygon) { + std::size_t nVertices = ring.size(); + + if (nVertices == 0) + continue; + + auto edgeDistance = 0; + + for (uint32_t i = 0; i < nVertices; i++) { + const auto& p1 = ring[i]; + + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p1, 0, 0, 1, 1, edgeDistance)); + flatIndices.emplace_back(triangleIndex); + triangleIndex++; + + if (i != 0) { + const auto& p2 = ring[i - 1]; + + const auto d1 = convertPoint<double>(p1); + const auto d2 = convertPoint<double>(p2); + + const Point<double> perp = util::unit(util::perp(d1 - d2)); + + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance)); + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance)); + + edgeDistance += util::dist<int16_t>(d1, d2); + + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance)); + vertices.emplace_back( + FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 1, edgeDistance)); + + triangles.emplace_back(triangleIndex, triangleIndex + 1, triangleIndex + 2); + triangles.emplace_back(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3); + triangleIndex += 4; + triangleSegment.vertexLength += 4; + triangleSegment.indexLength += 6; + } + } + } + + std::vector<uint32_t> indices = mapbox::earcut(polygon); + + std::size_t nIndices = indices.size(); + assert(nIndices % 3 == 0); + + for (uint32_t i = 0; i < nIndices; i += 3) { + triangles.emplace_back(flatIndices[indices[i]], flatIndices[indices[i + 1]], + flatIndices[indices[i + 2]]); + } + + triangleSegment.vertexLength += totalVertices; + triangleSegment.indexLength += nIndices; + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +void FillExtrusionBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +void FillExtrusionBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderFillExtrusion(parameters, *this, *layer.as<RenderFillExtrusionLayer>(), tile); +} + +bool FillExtrusionBucket::hasData() const { + return !triangleSegments.empty(); +} + +float FillExtrusionBucket::getQueryRadius(const RenderLayer& layer) const { + if (!layer.is<RenderFillExtrusionLayer>()) { + return 0; + } + + const std::array<float, 2>& translate = layer.as<RenderFillExtrusionLayer>()->evaluated.get<FillExtrusionTranslate>(); + return util::length(translate[0], translate[1]); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp new file mode 100644 index 0000000000..c54805d743 --- /dev/null +++ b/src/mbgl/renderer/buckets/fill_extrusion_bucket.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/fill_extrusion_program.hpp> +#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp> + +namespace mbgl { + +class BucketParameters; + +class FillExtrusionBucket : public Bucket { +public: + FillExtrusionBucket(const BucketParameters&, const std::vector<const RenderLayer*>&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + + float getQueryRadius(const RenderLayer&) const override; + + gl::VertexVector<FillExtrusionLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<FillExtrusionAttributes> triangleSegments; + + optional<gl::VertexBuffer<FillExtrusionLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + + std::unordered_map<std::string, FillExtrusionProgram::PaintPropertyBinders> paintPropertyBinders; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/line_bucket.cpp b/src/mbgl/renderer/buckets/line_bucket.cpp new file mode 100644 index 0000000000..3c5fa6bc67 --- /dev/null +++ b/src/mbgl/renderer/buckets/line_bucket.cpp @@ -0,0 +1,506 @@ +#include <mbgl/renderer/buckets/line_bucket.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/layers/render_line_layer.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/style/layers/line_layer_impl.hpp> +#include <mbgl/util/math.hpp> +#include <mbgl/util/constants.hpp> + +#include <cassert> + +namespace mbgl { + +using namespace style; + +LineBucket::LineBucket(const BucketParameters& parameters, + const std::vector<const RenderLayer*>& layers, + const style::LineLayoutProperties& layout_) + : layout(layout_.evaluate(PropertyEvaluationParameters(parameters.tileID.overscaledZ))), + overscaling(parameters.tileID.overscaleFactor()) { + for (const auto& layer : layers) { + paintPropertyBinders.emplace( + std::piecewise_construct, + std::forward_as_tuple(layer->getID()), + std::forward_as_tuple( + layer->as<RenderLineLayer>()->evaluated, + parameters.tileID.overscaledZ)); + } +} + +void LineBucket::addFeature(const GeometryTileFeature& feature, + const GeometryCollection& geometryCollection) { + for (auto& line : geometryCollection) { + addGeometry(line, feature.getType()); + } + + for (auto& pair : paintPropertyBinders) { + pair.second.populateVertexVectors(feature, vertices.vertexSize()); + } +} + +/* + * Sharp corners cause dashed lines to tilt because the distance along the line + * is the same at both the inner and outer corners. To improve the appearance of + * dashed lines we add extra points near sharp corners so that a smaller part + * of the line is tilted. + * + * COS_HALF_SHARP_CORNER controls how sharp a corner has to be for us to add an + * extra vertex. The default is 75 degrees. + * + * The newly created vertices are placed SHARP_CORNER_OFFSET pixels from the corner. + */ +const float COS_HALF_SHARP_CORNER = std::cos(75.0 / 2.0 * (M_PI / 180.0)); +const float SHARP_CORNER_OFFSET = 15.0f; + +// The number of bits that is used to store the line distance in the buffer. +const int LINE_DISTANCE_BUFFER_BITS = 14; + +// We don't have enough bits for the line distance as we'd like to have, so +// use this value to scale the line distance (in tile units) down to a smaller +// value. This lets us store longer distances while sacrificing precision. +const float LINE_DISTANCE_SCALE = 1.0 / 2.0; + +// The maximum line distance, in tile units, that fits in the buffer. +const float MAX_LINE_DISTANCE = std::pow(2, LINE_DISTANCE_BUFFER_BITS) / LINE_DISTANCE_SCALE; + +void LineBucket::addGeometry(const GeometryCoordinates& coordinates, FeatureType type) { + const std::size_t len = [&coordinates] { + std::size_t l = coordinates.size(); + // If the line has duplicate vertices at the end, adjust length to remove them. + while (l >= 2 && coordinates[l - 1] == coordinates[l - 2]) { + l--; + } + return l; + }(); + + const std::size_t first = [&coordinates, &len] { + std::size_t i = 0; + // If the line has duplicate vertices at the start, adjust index to remove them. + while (i < len - 1 && coordinates[i] == coordinates[i + 1]) { + i++; + } + return i; + }(); + + // Ignore invalid geometry. + if (len < (type == FeatureType::Polygon ? 3 : 2)) { + return; + } + + const float miterLimit = layout.get<LineJoin>() == LineJoinType::Bevel ? 1.05f : float(layout.get<LineMiterLimit>()); + + const double sharpCornerOffset = SHARP_CORNER_OFFSET * (float(util::EXTENT) / (util::tileSize * overscaling)); + + const GeometryCoordinate firstCoordinate = coordinates[first]; + const LineCapType beginCap = layout.get<LineCap>(); + const LineCapType endCap = type == FeatureType::Polygon ? LineCapType::Butt : LineCapType(layout.get<LineCap>()); + + double distance = 0; + bool startOfLine = true; + optional<GeometryCoordinate> currentCoordinate; + optional<GeometryCoordinate> prevCoordinate; + optional<GeometryCoordinate> nextCoordinate; + optional<Point<double>> prevNormal; + optional<Point<double>> nextNormal; + + // the last three vertices added + e1 = e2 = e3 = -1; + + if (type == FeatureType::Polygon) { + currentCoordinate = coordinates[len - 2]; + nextNormal = util::perp(util::unit(convertPoint<double>(firstCoordinate - *currentCoordinate))); + } + + const std::size_t startVertex = vertices.vertexSize(); + std::vector<TriangleElement> triangleStore; + + for (std::size_t i = first; i < len; ++i) { + if (type == FeatureType::Polygon && i == len - 1) { + // if the line is closed, we treat the last vertex like the first + nextCoordinate = coordinates[first + 1]; + } else if (i + 1 < len) { + // just the next vertex + nextCoordinate = coordinates[i + 1]; + } else { + // there is no next vertex + nextCoordinate = {}; + } + + // if two consecutive vertices exist, skip the current one + if (nextCoordinate && coordinates[i] == *nextCoordinate) { + continue; + } + + if (nextNormal) { + prevNormal = *nextNormal; + } + if (currentCoordinate) { + prevCoordinate = *currentCoordinate; + } + + currentCoordinate = coordinates[i]; + + // Calculate the normal towards the next vertex in this line. In case + // there is no next vertex, pretend that the line is continuing straight, + // meaning that we are just using the previous normal. + nextNormal = nextCoordinate ? util::perp(util::unit(convertPoint<double>(*nextCoordinate - *currentCoordinate))) + : prevNormal; + + // If we still don't have a previous normal, this is the beginning of a + // non-closed line, so we're doing a straight "join". + if (!prevNormal) { + prevNormal = *nextNormal; + } + + // Determine the normal of the join extrusion. It is the angle bisector + // of the segments between the previous line and the next line. + // In the case of 180° angles, the prev and next normals cancel each other out: + // prevNormal + nextNormal = (0, 0), its magnitude is 0, so the unit vector would be + // undefined. In that case, we're keeping the joinNormal at (0, 0), so that the cosHalfAngle + // below will also become 0 and miterLength will become Infinity. + Point<double> joinNormal = *prevNormal + *nextNormal; + if (joinNormal.x != 0 || joinNormal.y != 0) { + joinNormal = util::unit(joinNormal); + } + + /* joinNormal prevNormal + * ↖ ↑ + * .________. prevVertex + * | + * nextNormal ← | currentVertex + * | + * nextVertex ! + * + */ + + // Calculate the length of the miter (the ratio of the miter to the width). + // Find the cosine of the angle between the next and join normals + // using dot product. The inverse of that is the miter length. + const double cosHalfAngle = joinNormal.x * nextNormal->x + joinNormal.y * nextNormal->y; + const double miterLength = + cosHalfAngle != 0 ? 1 / cosHalfAngle : std::numeric_limits<double>::infinity(); + + const bool isSharpCorner = cosHalfAngle < COS_HALF_SHARP_CORNER && prevCoordinate && nextCoordinate; + + if (isSharpCorner && i > first) { + const auto prevSegmentLength = util::dist<double>(*currentCoordinate, *prevCoordinate); + 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); + prevCoordinate = newPrevVertex; + } + } + + // The join if a middle vertex, otherwise the cap + const bool middleVertex = prevCoordinate && nextCoordinate; + LineJoinType currentJoin = layout.get<LineJoin>(); + const LineCapType currentCap = nextCoordinate ? beginCap : endCap; + + if (middleVertex) { + if (currentJoin == LineJoinType::Round) { + if (miterLength < layout.get<LineRoundLimit>()) { + currentJoin = LineJoinType::Miter; + } else if (miterLength <= 2) { + currentJoin = LineJoinType::FakeRound; + } + } + + if (currentJoin == LineJoinType::Miter && miterLength > miterLimit) { + currentJoin = LineJoinType::Bevel; + } + + if (currentJoin == LineJoinType::Bevel) { + // The maximum extrude length is 128 / 63 = 2 times the width of the line + // so if miterLength >= 2 we need to draw a different type of bevel here. + if (miterLength > 2) { + currentJoin = LineJoinType::FlipBevel; + } + + // If the miterLength is really small and the line bevel wouldn't be visible, + // just draw a miter join to save a triangle. + if (miterLength < miterLimit) { + currentJoin = LineJoinType::Miter; + } + } + } + + // Calculate how far along the line the currentVertex is + if (prevCoordinate) + distance += util::dist<double>(*currentCoordinate, *prevCoordinate); + + if (middleVertex && currentJoin == LineJoinType::Miter) { + joinNormal = joinNormal * miterLength; + addCurrentVertex(*currentCoordinate, distance, joinNormal, 0, 0, false, startVertex, + triangleStore); + + } else if (middleVertex && currentJoin == LineJoinType::FlipBevel) { + // miter is too big, flip the direction to make a beveled join + + if (miterLength > 100) { + // Almost parallel lines + joinNormal = *nextNormal * -1.0; + } else { + const double direction = prevNormal->x * nextNormal->y - prevNormal->y * nextNormal->x > 0 ? -1 : 1; + const double bevelLength = miterLength * util::mag(*prevNormal + *nextNormal) / + util::mag(*prevNormal - *nextNormal); + joinNormal = util::perp(joinNormal) * bevelLength * direction; + } + + addCurrentVertex(*currentCoordinate, distance, joinNormal, 0, 0, false, startVertex, + triangleStore); + + addCurrentVertex(*currentCoordinate, distance, joinNormal * -1.0, 0, 0, false, startVertex, + triangleStore); + } 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); + float offsetA; + float offsetB; + + if (lineTurnsLeft) { + offsetB = 0; + offsetA = offset; + } else { + offsetA = 0; + offsetB = offset; + } + + // Close previous segement with bevel + if (!startOfLine) { + addCurrentVertex(*currentCoordinate, distance, *prevNormal, offsetA, offsetB, false, + startVertex, triangleStore); + } + + if (currentJoin == LineJoinType::FakeRound) { + // The join angle is sharp enough that a round join would be visible. + // Bevel joins fill the gap between segments with a single pie slice triangle. + // Create a round join by adding multiple pie slices. The join isn't actually round, but + // it looks like it is at the sizes we render lines at. + + // Add more triangles for sharper angles. + // This math is just a good enough approximation. It isn't "correct". + const int n = std::floor((0.5 - (cosHalfAngle - 0.5)) * 8); + + 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, joinNormal, lineTurnsLeft, startVertex, triangleStore); + + 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); + } + } + + // Start next segment + if (nextCoordinate) { + addCurrentVertex(*currentCoordinate, distance, *nextNormal, -offsetA, -offsetB, + false, startVertex, triangleStore); + } + + } else if (!middleVertex && currentCap == LineCapType::Butt) { + if (!startOfLine) { + // Close previous segment with a butt + addCurrentVertex(*currentCoordinate, distance, *prevNormal, 0, 0, false, + startVertex, triangleStore); + } + + // Start next segment with a butt + if (nextCoordinate) { + addCurrentVertex(*currentCoordinate, distance, *nextNormal, 0, 0, false, + startVertex, triangleStore); + } + + } else if (!middleVertex && currentCap == LineCapType::Square) { + if (!startOfLine) { + // Close previous segment with a square cap + addCurrentVertex(*currentCoordinate, distance, *prevNormal, 1, 1, false, + startVertex, triangleStore); + + // The segment is done. Unset vertices to disconnect segments. + e1 = e2 = -1; + } + + // Start next segment + if (nextCoordinate) { + addCurrentVertex(*currentCoordinate, distance, *nextNormal, -1, -1, false, + startVertex, triangleStore); + } + + } 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); + + // Add round cap or linejoin at end of segment + addCurrentVertex(*currentCoordinate, distance, *prevNormal, 1, 1, true, startVertex, + triangleStore); + + // The segment is done. Unset vertices to disconnect segments. + e1 = e2 = -1; + } + + // Start next segment with a butt + if (nextCoordinate) { + // Add round cap before first segment + addCurrentVertex(*currentCoordinate, distance, *nextNormal, -1, -1, true, + startVertex, triangleStore); + + addCurrentVertex(*currentCoordinate, distance, *nextNormal, 0, 0, false, + startVertex, triangleStore); + } + } + + if (isSharpCorner && i < len - 1) { + const auto nextSegmentLength = util::dist<double>(*currentCoordinate, *nextCoordinate); + 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); + currentCoordinate = newCurrentVertex; + } + } + + startOfLine = false; + } + + const std::size_t endVertex = vertices.vertexSize(); + const std::size_t vertexCount = endVertex - startVertex; + + if (segments.empty() || segments.back().vertexLength + vertexCount > std::numeric_limits<uint16_t>::max()) { + segments.emplace_back(startVertex, triangles.indexSize()); + } + + auto& segment = segments.back(); + assert(segment.vertexLength <= std::numeric_limits<uint16_t>::max()); + uint16_t index = segment.vertexLength; + + for (const auto& triangle : triangleStore) { + triangles.emplace_back(index + triangle.a, index + triangle.b, index + triangle.c); + } + + segment.vertexLength += vertexCount; + segment.indexLength += triangleStore.size() * 3; +} + +void LineBucket::addCurrentVertex(const GeometryCoordinate& currentCoordinate, + double &distance, + const Point<double>& normal, + double endLeft, + double endRight, + bool round, + std::size_t startVertex, + std::vector<TriangleElement>& triangleStore) { + Point<double> extrude = normal; + if (endLeft) + extrude = extrude - (util::perp(normal) * endLeft); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, false }, endLeft, distance * LINE_DISTANCE_SCALE)); + e3 = vertices.vertexSize() - 1 - startVertex; + if (e1 >= 0 && e2 >= 0) { + triangleStore.emplace_back(e1, e2, e3); + } + e1 = e2; + e2 = e3; + + extrude = normal * -1.0; + if (endRight) + extrude = extrude - (util::perp(normal) * endRight); + vertices.emplace_back(LineProgram::layoutVertex(currentCoordinate, extrude, { round, true }, -endRight, distance * LINE_DISTANCE_SCALE)); + e3 = vertices.vertexSize() - 1 - startVertex; + if (e1 >= 0 && e2 >= 0) { + triangleStore.emplace_back(e1, e2, e3); + } + e1 = e2; + e2 = e3; + + // There is a maximum "distance along the line" that we can store in the buffers. + // 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) { + distance = 0; + addCurrentVertex(currentCoordinate, distance, normal, endLeft, endRight, round, startVertex, triangleStore); + } +} + +void LineBucket::addPieSliceVertex(const GeometryCoordinate& currentVertex, + double distance, + const Point<double>& extrude, + bool lineTurnsLeft, + std::size_t startVertex, + std::vector<TriangleElement>& triangleStore) { + Point<double> flippedExtrude = extrude * (lineTurnsLeft ? -1.0 : 1.0); + vertices.emplace_back(LineProgram::layoutVertex(currentVertex, flippedExtrude, { false, lineTurnsLeft }, 0, distance * LINE_DISTANCE_SCALE)); + e3 = vertices.vertexSize() - 1 - startVertex; + if (e1 >= 0 && e2 >= 0) { + triangleStore.emplace_back(e1, e2, e3); + } + + if (lineTurnsLeft) { + e2 = e3; + } else { + e1 = e3; + } +} + +void LineBucket::upload(gl::Context& context) { + vertexBuffer = context.createVertexBuffer(std::move(vertices)); + indexBuffer = context.createIndexBuffer(std::move(triangles)); + + for (auto& pair : paintPropertyBinders) { + pair.second.upload(context); + } + + uploaded = true; +} + +void LineBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderLine(parameters, *this, *layer.as<RenderLineLayer>(), tile); +} + +bool LineBucket::hasData() const { + return !segments.empty(); +} + +template <class Property> +static float get(const RenderLineLayer& layer, const std::map<std::string, LineProgram::PaintPropertyBinders>& paintPropertyBinders) { + auto it = paintPropertyBinders.find(layer.getID()); + if (it == paintPropertyBinders.end() || !it->second.statistics<Property>().max()) { + return layer.evaluated.get<Property>().constantOr(Property::defaultValue()); + } else { + return *it->second.statistics<Property>().max(); + } +} + +float LineBucket::getLineWidth(const RenderLineLayer& layer) const { + float lineWidth = layer.evaluated.get<LineWidth>(); + float gapWidth = get<LineGapWidth>(layer, paintPropertyBinders); + + if (gapWidth) { + return gapWidth + 2 * lineWidth; + } else { + return lineWidth; + } +} + +float LineBucket::getQueryRadius(const RenderLayer& layer) const { + if (!layer.is<RenderLineLayer>()) { + return 0; + } + + auto lineLayer = layer.as<RenderLineLayer>(); + + const std::array<float, 2>& translate = lineLayer->evaluated.get<LineTranslate>(); + float offset = get<LineOffset>(*lineLayer, paintPropertyBinders); + return getLineWidth(*lineLayer) / 2.0 + std::abs(offset) + util::length(translate[0], translate[1]); +} + + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/line_bucket.hpp b/src/mbgl/renderer/buckets/line_bucket.hpp new file mode 100644 index 0000000000..c319548714 --- /dev/null +++ b/src/mbgl/renderer/buckets/line_bucket.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/tile/geometry_tile_data.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/line_program.hpp> +#include <mbgl/style/layers/line_layer_properties.hpp> + +#include <vector> + +namespace mbgl { + +class BucketParameters; +class RenderLineLayer; + +class LineBucket : public Bucket { +public: + LineBucket(const BucketParameters&, + const std::vector<const RenderLayer*>&, + const style::LineLayoutProperties&); + + void addFeature(const GeometryTileFeature&, + const GeometryCollection&) override; + bool hasData() const override; + + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + + float getQueryRadius(const RenderLayer&) const override; + + style::LineLayoutProperties::PossiblyEvaluated layout; + + gl::VertexVector<LineLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<LineAttributes> segments; + + optional<gl::VertexBuffer<LineLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + + std::map<std::string, LineProgram::PaintPropertyBinders> paintPropertyBinders; + +private: + void addGeometry(const GeometryCoordinates&, FeatureType); + + struct TriangleElement { + 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, + const Point<double>& normal, double endLeft, double endRight, bool round, + std::size_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore); + void addPieSliceVertex(const GeometryCoordinate& currentVertex, double distance, + const Point<double>& extrude, bool lineTurnsLeft, std::size_t startVertex, + std::vector<TriangleElement>& triangleStore); + + std::ptrdiff_t e1; + std::ptrdiff_t e2; + std::ptrdiff_t e3; + + const uint32_t overscaling; + + float getLineWidth(const RenderLineLayer& layer) const; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/raster_bucket.cpp b/src/mbgl/renderer/buckets/raster_bucket.cpp new file mode 100644 index 0000000000..4a25959868 --- /dev/null +++ b/src/mbgl/renderer/buckets/raster_bucket.cpp @@ -0,0 +1,30 @@ +#include <mbgl/renderer/buckets/raster_bucket.hpp> +#include <mbgl/renderer/layers/render_raster_layer.hpp> +#include <mbgl/programs/raster_program.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/gl/context.hpp> + +namespace mbgl { + +using namespace style; + +RasterBucket::RasterBucket(UnassociatedImage&& image_) : image(std::move(image_)) { +} + +void RasterBucket::upload(gl::Context& context) { + texture = context.createTexture(std::move(image)); + uploaded = true; +} + +void RasterBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderRaster(parameters, *this, *layer.as<RenderRasterLayer>(), tile); +} + +bool RasterBucket::hasData() const { + return true; +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/raster_bucket.hpp b/src/mbgl/renderer/buckets/raster_bucket.hpp new file mode 100644 index 0000000000..334954e3f4 --- /dev/null +++ b/src/mbgl/renderer/buckets/raster_bucket.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/util/image.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/gl/texture.hpp> + +namespace mbgl { + +class RasterBucket : public Bucket { +public: + RasterBucket(UnassociatedImage&&); + + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + bool hasData() const override; + + UnassociatedImage image; + optional<gl::Texture> texture; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/symbol_bucket.cpp b/src/mbgl/renderer/buckets/symbol_bucket.cpp new file mode 100644 index 0000000000..423161f24c --- /dev/null +++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp @@ -0,0 +1,86 @@ +#include <mbgl/renderer/buckets/symbol_bucket.hpp> +#include <mbgl/renderer/painter.hpp> +#include <mbgl/renderer/layers/render_symbol_layer.hpp> +#include <mbgl/renderer/bucket_parameters.hpp> +#include <mbgl/style/layers/symbol_layer_impl.hpp> + +namespace mbgl { + +using namespace style; + +SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_, + const std::map<std::string, std::pair< + style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>& layerPaintProperties, + const style::DataDrivenPropertyValue<float>& textSize, + const style::DataDrivenPropertyValue<float>& iconSize, + float zoom, + bool sdfIcons_, + bool iconsNeedLinear_) + : layout(std::move(layout_)), + sdfIcons(sdfIcons_), + iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()), + textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())), + iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) { + + for (const auto& pair : layerPaintProperties) { + paintPropertyBinders.emplace( + std::piecewise_construct, + std::forward_as_tuple(pair.first), + std::forward_as_tuple( + std::piecewise_construct, + std::forward_as_tuple(pair.second.first, zoom), + std::forward_as_tuple(pair.second.second, zoom))); + } +} + +void SymbolBucket::upload(gl::Context& context) { + if (hasTextData()) { + text.vertexBuffer = context.createVertexBuffer(std::move(text.vertices)); + text.indexBuffer = context.createIndexBuffer(std::move(text.triangles)); + textSizeBinder->upload(context); + } + + if (hasIconData()) { + icon.vertexBuffer = context.createVertexBuffer(std::move(icon.vertices)); + icon.indexBuffer = context.createIndexBuffer(std::move(icon.triangles)); + iconSizeBinder->upload(context); + } + + if (!collisionBox.vertices.empty()) { + collisionBox.vertexBuffer = context.createVertexBuffer(std::move(collisionBox.vertices)); + collisionBox.indexBuffer = context.createIndexBuffer(std::move(collisionBox.lines)); + } + + for (auto& pair : paintPropertyBinders) { + pair.second.first.upload(context); + pair.second.second.upload(context); + } + + uploaded = true; +} + +void SymbolBucket::render(Painter& painter, + PaintParameters& parameters, + const RenderLayer& layer, + const RenderTile& tile) { + painter.renderSymbol(parameters, *this, *layer.as<RenderSymbolLayer>(), tile); +} + +bool SymbolBucket::hasData() const { + assert(false); // Should be calling SymbolLayout::has{Text,Icon,CollisonBox}Data() instead. + return false; +} + +bool SymbolBucket::hasTextData() const { + return !text.segments.empty(); +} + +bool SymbolBucket::hasIconData() const { + return !icon.segments.empty(); +} + +bool SymbolBucket::hasCollisionBoxData() const { + return !collisionBox.segments.empty(); +} + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/symbol_bucket.hpp b/src/mbgl/renderer/buckets/symbol_bucket.hpp new file mode 100644 index 0000000000..f7e4bcfa20 --- /dev/null +++ b/src/mbgl/renderer/buckets/symbol_bucket.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include <mbgl/renderer/bucket.hpp> +#include <mbgl/map/mode.hpp> +#include <mbgl/gl/vertex_buffer.hpp> +#include <mbgl/gl/index_buffer.hpp> +#include <mbgl/gl/segment.hpp> +#include <mbgl/programs/symbol_program.hpp> +#include <mbgl/programs/collision_box_program.hpp> +#include <mbgl/text/glyph_range.hpp> +#include <mbgl/style/layers/symbol_layer_properties.hpp> +#include <mbgl/layout/symbol_feature.hpp> + +#include <vector> + +namespace mbgl { + +class SymbolBucket : public Bucket { +public: + SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated, + const std::map<std::string, std::pair<style::IconPaintProperties::Evaluated, style::TextPaintProperties::Evaluated>>&, + const style::DataDrivenPropertyValue<float>& textSize, + const style::DataDrivenPropertyValue<float>& iconSize, + float zoom, + bool sdfIcons, + bool iconsNeedLinear); + + void upload(gl::Context&) override; + void render(Painter&, PaintParameters&, const RenderLayer&, const RenderTile&) override; + bool hasData() const override; + bool hasTextData() const; + bool hasIconData() const; + bool hasCollisionBoxData() const; + + const style::SymbolLayoutProperties::PossiblyEvaluated layout; + const bool sdfIcons; + const bool iconsNeedLinear; + + std::map<std::string, std::pair< + SymbolIconProgram::PaintPropertyBinders, + SymbolSDFTextProgram::PaintPropertyBinders>> paintPropertyBinders; + + std::unique_ptr<SymbolSizeBinder> textSizeBinder; + + struct TextBuffer { + gl::VertexVector<SymbolLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<SymbolTextAttributes> segments; + + optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + } text; + + std::unique_ptr<SymbolSizeBinder> iconSizeBinder; + + struct IconBuffer { + gl::VertexVector<SymbolLayoutVertex> vertices; + gl::IndexVector<gl::Triangles> triangles; + gl::SegmentVector<SymbolIconAttributes> segments; + + optional<gl::VertexBuffer<SymbolLayoutVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Triangles>> indexBuffer; + } icon; + + struct CollisionBoxBuffer { + gl::VertexVector<CollisionBoxVertex> vertices; + gl::IndexVector<gl::Lines> lines; + gl::SegmentVector<CollisionBoxAttributes> segments; + + optional<gl::VertexBuffer<CollisionBoxVertex>> vertexBuffer; + optional<gl::IndexBuffer<gl::Lines>> indexBuffer; + } collisionBox; + + SpriteAtlas* spriteAtlas = nullptr; +}; + +} // namespace mbgl |