diff --git a/CMakeLists.txt b/CMakeLists.txt index 09d71e917..d837dfb82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,7 @@ list(APPEND INCLUDE_FILES ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/backend_scope.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/gfx_types.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/polyline_generator.hpp + ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/fill_generator.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderable.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/renderer_backend.hpp ${PROJECT_SOURCE_DIR}/include/mbgl/gfx/rendering_stats.hpp @@ -470,6 +471,7 @@ list(APPEND SRC_FILES ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/index_vector.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/offscreen_texture.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/polyline_generator.cpp + ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/fill_generator.cpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/program.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/render_pass.hpp ${PROJECT_SOURCE_DIR}/src/mbgl/gfx/renderbuffer.hpp diff --git a/bazel/core.bzl b/bazel/core.bzl index d8bab43ec..018afb979 100644 --- a/bazel/core.bzl +++ b/bazel/core.bzl @@ -613,6 +613,7 @@ MLN_CORE_SOURCE = [ "src/mbgl/util/quaternion.cpp", "src/mbgl/util/quaternion.hpp", "src/mbgl/gfx/polyline_generator.cpp", + "src/mbgl/gfx/fill_generator.cpp", "src/mbgl/util/rapidjson.cpp", "src/mbgl/util/rapidjson.hpp", "src/mbgl/util/rect.hpp", @@ -820,6 +821,7 @@ MLN_CORE_HEADERS = [ "include/mbgl/util/noncopyable.hpp", "include/mbgl/util/platform.hpp", "include/mbgl/gfx/polyline_generator.hpp", + "include/mbgl/gfx/fill_generator.hpp", "include/mbgl/util/premultiply.hpp", "include/mbgl/util/projection.hpp", "include/mbgl/util/range.hpp", diff --git a/include/mbgl/gfx/drawable_builder.hpp b/include/mbgl/gfx/drawable_builder.hpp index d5cca13c4..5013842ea 100644 --- a/include/mbgl/gfx/drawable_builder.hpp +++ b/include/mbgl/gfx/drawable_builder.hpp @@ -204,6 +204,8 @@ public: /// return the curent vertex count std::size_t curVertexCount() const; + bool empty() const { return !(drawables.size() || curVertexCount()); } + protected: /// Create an instance of the appropriate drawable type virtual UniqueDrawable createDrawable() const = 0; diff --git a/include/mbgl/gfx/fill_generator.hpp b/include/mbgl/gfx/fill_generator.hpp new file mode 100644 index 000000000..c273b1ee0 --- /dev/null +++ b/include/mbgl/gfx/fill_generator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace mbgl { +namespace gfx { + +void generateFillBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles); + +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& lineSegments, + gfx::IndexVector& lines, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles); + +} // namespace gfx +} // namespace mbgl diff --git a/include/mbgl/style/layers/custom_drawable_layer.hpp b/include/mbgl/style/layers/custom_drawable_layer.hpp index 561a922d0..7deb77057 100644 --- a/include/mbgl/style/layers/custom_drawable_layer.hpp +++ b/include/mbgl/style/layers/custom_drawable_layer.hpp @@ -41,6 +41,11 @@ public: gfx::PolylineGeneratorOptions geometry; }; + struct FillOptions { + Color color; + float opacity = 1.f; + }; + public: /// @brief Construct a new Interface object (internal core use only) Interface(RenderLayer& layer, @@ -72,6 +77,13 @@ public: */ void setLineOptions(const LineOptions& options); + /** + * @brief Set the fill options + * + * @param options + */ + void setFillOptions(const FillOptions& options); + /** * @brief Add a polyline * @@ -80,17 +92,14 @@ public: */ void addPolyline(const GeometryCoordinates& coordinates); + void addFill(const GeometryCollection& geometry); + /** - * @brief Finishe the current drawable building session + * @brief Finish the current drawable building session * */ void finish(); -protected: - gfx::ShaderPtr lineShaderDefault() const; - - std::unique_ptr createBuilder(const std::string& name, gfx::ShaderPtr shader) const; - public: RenderLayer& layer; LayerGroupBasePtr& layerGroup; @@ -102,9 +111,17 @@ public: UniqueChangeRequestVec& changes; private: + gfx::ShaderPtr lineShaderDefault() const; + gfx::ShaderPtr fillShaderDefault() const; + + std::unique_ptr createBuilder(const std::string& name, gfx::ShaderPtr shader) const; + + gfx::ShaderPtr lineShader; + gfx::ShaderPtr fillShader; std::unique_ptr builder; std::optional tileID; LineOptions lineOptions; + FillOptions fillOptions; }; class CustomDrawableLayer final : public Layer { diff --git a/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm b/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm index 2c4d88429..a3756dce8 100644 --- a/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm +++ b/platform/darwin/app/ExampleCustomDrawableStyleLayer.mm @@ -24,7 +24,6 @@ class ExampleCustomDrawableStyleLayerHost; return self = [super initWithPendingLayer:std::move(layer)]; } - @end class ExampleCustomDrawableStyleLayerHost : public mbgl::style::CustomDrawableLayerHost { @@ -47,43 +46,76 @@ public: // set tile interface.setTileID({11, 327, 791}); + constexpr float extent = mbgl::util::EXTENT; // add polylines - using namespace mbgl; - - constexpr auto numLines = 6; - Interface::LineOptions options[numLines] { - {/*color=*/Color::red(), /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, {} }, - {/*color=*/Color::blue(), /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, {} }, - {/*color=*/Color(1.f, 0.5f, 0, 0.5f), /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, {} }, - {/*color=*/Color(1.f, 1.f, 0, 0.3f), /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, {} }, - {/*color=*/Color::black(), /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, {} }, - {/*color=*/Color(1.f, 0, 1.f, 0.2f), /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, {} }, - }; - for(auto& opt: options) { - opt.geometry.beginCap = style::LineCapType::Round; - opt.geometry.endCap = style::LineCapType::Round; - opt.geometry.joinType = style::LineJoinType::Round; - } - - constexpr auto numPoints = 100; - GeometryCoordinates polyline; - for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { - polyline.emplace_back(ipoint * util::EXTENT / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * util::EXTENT / numLines / 2.f); - } - - for (auto index {0}; index < numLines; ++index) { - for(auto &p : polyline) { - p.y += util::EXTENT / numLines; + { + using namespace mbgl; + + constexpr auto numLines = 6; + Interface::LineOptions options[numLines] { + {/*color=*/Color::red(), /*blur=*/0.0f, /*opacity=*/1.0f, /*gapWidth=*/0.0f, /*offset=*/0.0f, /*width=*/8.0f, {} }, + {/*color=*/Color::blue(), /*blur=*/4.0f, /*opacity=*/1.0f, /*gapWidth=*/2.0f, /*offset=*/-1.0f, /*width=*/4.0f, {} }, + {/*color=*/Color(1.f, 0.5f, 0, 0.5f), /*blur=*/16.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/2.0f, /*width=*/16.0f, {} }, + {/*color=*/Color(1.f, 1.f, 0, 0.3f), /*blur=*/2.0f, /*opacity=*/1.0f, /*gapWidth=*/1.0f, /*offset=*/-2.0f, /*width=*/2.0f, {} }, + {/*color=*/Color::black(), /*blur=*/0.5f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/0.5f, /*width=*/0.5f, {} }, + {/*color=*/Color(1.f, 0, 1.f, 0.2f), /*blur=*/24.0f, /*opacity=*/0.5f, /*gapWidth=*/1.0f, /*offset=*/-5.0f, /*width=*/24.0f, {} }, + }; + for(auto& opt: options) { + opt.geometry.beginCap = style::LineCapType::Round; + opt.geometry.endCap = style::LineCapType::Round; + opt.geometry.joinType = style::LineJoinType::Round; } - // set property values - interface.setLineOptions(options[index]); + constexpr auto numPoints = 100; + GeometryCoordinates polyline; + for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { + polyline.emplace_back(ipoint * extent / numPoints, std::sin(ipoint * 2 * M_PI / numPoints) * extent / numLines / 2.f); + } - // add polyline - interface.addPolyline(polyline); + for (auto index {0}; index < numLines; ++index) { + for(auto &p : polyline) { + p.y += extent / numLines; + } + + // set property values + interface.setLineOptions(options[index]); + + // add polyline + interface.addPolyline(polyline); + } } + // add fill polygon + { + using namespace mbgl; + + GeometryCollection geometry{ + { + // ring 1 + {static_cast(extent* 0.1f), static_cast(extent* 0.2f)}, + {static_cast(extent* 0.5f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.7f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.5f), static_cast(extent* 1.0f)}, + {static_cast(extent* 0.0f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.1f), static_cast(extent* 0.2f)}, + }, + { + // ring 2 + {static_cast(extent* 0.1f), static_cast(extent* 0.25f)}, + {static_cast(extent* 0.15f), static_cast(extent* 0.5f)}, + {static_cast(extent* 0.25f), static_cast(extent* 0.45f)}, + {static_cast(extent* 0.1f), static_cast(extent* 0.25f)}, + }, + }; + + // set properties + interface.setFillOptions({/*color=*/Color::green(), /*opacity=*/0.5f}); + + // add fill + interface.addFill(geometry); + } + // finish interface.finish(); } diff --git a/src/mbgl/gfx/fill_generator.cpp b/src/mbgl/gfx/fill_generator.cpp new file mode 100644 index 000000000..f275ccbd7 --- /dev/null +++ b/src/mbgl/gfx/fill_generator.cpp @@ -0,0 +1,151 @@ +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include +#include + +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 { +namespace gfx { + +struct GeometryTooLongException : std::exception {}; + +namespace { + +std::size_t addRingVertices(gfx::VertexVector& vertices, const GeometryCoordinates& ring) { + for (auto& point : ring) { + vertices.emplace_back(FillProgram::layoutVertex(point)); + } + return ring.size(); +} + +std::size_t totalVerticesCheck(const GeometryCollection& polygon) { + std::size_t totalVertices = 0; + for (const auto& ring : polygon) { + totalVertices += ring.size(); + } + if (totalVertices > std::numeric_limits::max()) throw GeometryTooLongException(); + return totalVertices; +} + +void addFillIndices(SegmentVector& triangleSegments, + gfx::IndexVector& triangles, + std::vector& indices, + std::size_t startVertices, + std::size_t totalVertices) { + std::size_t nIndices = indices.size(); + assert(nIndices % 3 == 0); + + if (triangleSegments.empty() || + triangleSegments.back().vertexLength + totalVertices > std::numeric_limits::max()) { + triangleSegments.emplace_back(startVertices, triangles.elements()); + } + + auto& triangleSegment = triangleSegments.back(); + assert(triangleSegment.vertexLength <= std::numeric_limits::max()); + const auto triangleIndex = static_cast(triangleSegment.vertexLength); + + for (std::size_t i = 0; i < nIndices; i += 3) { + triangles.emplace_back( + triangleIndex + indices[i], triangleIndex + indices[i + 1], triangleIndex + indices[i + 2]); + } + + triangleSegment.vertexLength += totalVertices; + triangleSegment.indexLength += nIndices; +} + +void addOutlineIndices(std::size_t base, + std::size_t nVertices, + SegmentVector& lineSegments, + gfx::IndexVector& lines) { + if (nVertices == 0) return; + + if (lineSegments.empty() || lineSegments.back().vertexLength + nVertices > std::numeric_limits::max()) { + lineSegments.emplace_back(base, lines.elements()); + } + + auto& lineSegment = lineSegments.back(); + assert(lineSegment.vertexLength <= std::numeric_limits::max()); + const auto lineIndex = static_cast(lineSegment.vertexLength); + + lines.emplace_back(static_cast(lineIndex + nVertices - 1), lineIndex); + for (std::size_t i = 1; i < nVertices; i++) { + lines.emplace_back(static_cast(lineIndex + i - 1), static_cast(lineIndex + i)); + } + + lineSegment.vertexLength += nVertices; + lineSegment.indexLength += nVertices * 2; +} + +} // namespace + +void generateFillBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = totalVerticesCheck(polygon); + std::size_t startVertices = vertices.elements(); + + for (const auto& ring : polygon) { + addRingVertices(vertices, ring); + } + + std::vector indices = mapbox::earcut(polygon); + addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices); + } +} + +void generateFillAndOutineBuffers(const GeometryCollection& geometry, + gfx::VertexVector& vertices, + SegmentVector& lineSegments, + gfx::IndexVector& lines, + SegmentVector& triangleSegments, + gfx::IndexVector& triangles) { + for (auto& polygon : classifyRings(geometry)) { + // Optimize polygons with many interior rings for earcut tesselation. + limitHoles(polygon, 500); + + std::size_t totalVertices = totalVerticesCheck(polygon); + std::size_t startVertices = vertices.elements(); + + for (const auto& ring : polygon) { + std::size_t base = vertices.elements(); + std::size_t nVertices = addRingVertices(vertices, ring); + addOutlineIndices(base, nVertices, lineSegments, lines); + } + + std::vector indices = mapbox::earcut(polygon); + addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices); + } +} + +} // namespace gfx + +} // namespace mbgl diff --git a/src/mbgl/renderer/buckets/fill_bucket.cpp b/src/mbgl/renderer/buckets/fill_bucket.cpp index 87e2d8a27..805c78257 100644 --- a/src/mbgl/renderer/buckets/fill_bucket.cpp +++ b/src/mbgl/renderer/buckets/fill_bucket.cpp @@ -4,44 +4,15 @@ #include #include #include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - -#include - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#include - -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 +#include namespace mbgl { -using namespace style; - -struct GeometryTooLongException : std::exception {}; - FillBucket::FillBucket(const FillBucket::PossiblyEvaluatedLayoutProperties&, const std::map>& layerPaintProperties, const float zoom, const uint32_t) { + using namespace style; for (const auto& pair : layerPaintProperties) { paintPropertyBinders.emplace(std::piecewise_construct, std::forward_as_tuple(pair.first), @@ -59,68 +30,8 @@ void FillBucket::addFeature(const GeometryTileFeature& feature, const PatternLayerMap& patternDependencies, std::size_t index, const CanonicalTileID& canonical) { - 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::max()) throw GeometryTooLongException(); - } - - std::size_t startVertices = vertices.elements(); - - 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::max()) { - lineSegments.emplace_back(vertices.elements(), lines.elements()); - } - - auto& lineSegment = lineSegments.back(); - assert(lineSegment.vertexLength <= std::numeric_limits::max()); - const auto lineIndex = static_cast(lineSegment.vertexLength); - - vertices.emplace_back(FillProgram::layoutVertex(ring[0])); - lines.emplace_back(static_cast(lineIndex + nVertices - 1), lineIndex); - - for (std::size_t i = 1; i < nVertices; i++) { - vertices.emplace_back(FillProgram::layoutVertex(ring[i])); - lines.emplace_back(static_cast(lineIndex + i - 1), static_cast(lineIndex + i)); - } - - lineSegment.vertexLength += nVertices; - lineSegment.indexLength += nVertices * 2; - } - - std::vector indices = mapbox::earcut(polygon); - - std::size_t nIndicies = indices.size(); - assert(nIndicies % 3 == 0); - - if (triangleSegments.empty() || - triangleSegments.back().vertexLength + totalVertices > std::numeric_limits::max()) { - triangleSegments.emplace_back(startVertices, triangles.elements()); - } - - auto& triangleSegment = triangleSegments.back(); - assert(triangleSegment.vertexLength <= std::numeric_limits::max()); - const auto triangleIndex = static_cast(triangleSegment.vertexLength); - - for (std::size_t i = 0; i < nIndicies; i += 3) { - triangles.emplace_back(static_cast(triangleIndex + indices[i]), - static_cast(triangleIndex + indices[i + 1]), - static_cast(triangleIndex + indices[i + 2])); - } - - triangleSegment.vertexLength += totalVertices; - triangleSegment.indexLength += nIndicies; - } + // generate buffers + gfx::generateFillAndOutineBuffers(geometry, vertices, lineSegments, lines, triangleSegments, triangles); for (auto& pair : paintPropertyBinders) { const auto it = patternDependencies.find(pair.first); @@ -155,6 +66,7 @@ bool FillBucket::hasData() const { } float FillBucket::getQueryRadius(const RenderLayer& layer) const { + using namespace style; const auto& evaluated = getEvaluated(layer.evaluatedProperties); const std::array& translate = evaluated.get(); return util::length(translate[0], translate[1]); diff --git a/src/mbgl/style/layers/custom_drawable_layer.cpp b/src/mbgl/style/layers/custom_drawable_layer.cpp index 17e16921f..bd08cdaf0 100644 --- a/src/mbgl/style/layers/custom_drawable_layer.cpp +++ b/src/mbgl/style/layers/custom_drawable_layer.cpp @@ -15,8 +15,15 @@ #include #include #include +#include +#include +#include +#include + +#include namespace mbgl { + namespace style { namespace { @@ -141,6 +148,78 @@ private: shaders::LinePropertiesUBO linePropertiesUBO; }; +class FillDrawableTweaker : public gfx::DrawableTweaker { +public: + FillDrawableTweaker(const Color& color_, float opacity_) + : color(color_), + opacity(opacity_) {} + ~FillDrawableTweaker() override = default; + + void init(gfx::Drawable&) override{}; + + void execute(gfx::Drawable& drawable, const PaintParameters& parameters) override { + if (!drawable.getTileID().has_value()) { + return; + } + + const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped(); + mat4 tileMatrix; + parameters.state.matrixFor(/*out*/ tileMatrix, tileID); + + const auto matrix = LayerTweaker::getTileMatrix( + tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, false); + + static const StringIdentity idFillDrawableUBOName = stringIndexer().get("FillDrawableUBO"); + const shaders::FillDrawableUBO fillUBO{/*matrix = */ util::cast(matrix)}; + + static const StringIdentity idFillEvaluatedPropsUBOName = stringIndexer().get("FillEvaluatedPropsUBO"); + const shaders::FillEvaluatedPropsUBO fillPropertiesUBO{ + /* .color = */ color, + /* .opacity = */ opacity, + 0, + 0, + 0, + }; + + static const StringIdentity idFillInterpolateUBOName = stringIndexer().get("FillInterpolateUBO"); + const shaders::FillInterpolateUBO fillInterpolateUBO{ + /* .color_t = */ 0.f, + /* .opacity_t = */ 0.f, + 0, + 0, + }; + auto& uniforms = drawable.mutableUniformBuffers(); + uniforms.createOrUpdate(idFillDrawableUBOName, &fillUBO, parameters.context); + uniforms.createOrUpdate(idFillEvaluatedPropsUBOName, &fillPropertiesUBO, parameters.context); + uniforms.createOrUpdate(idFillInterpolateUBOName, &fillInterpolateUBO, parameters.context); + +#if MLN_RENDER_BACKEND_METAL + const auto zoom = parameters.state.getZoom(); + static const StringIdentity idExpressionInputsUBOName = stringIndexer().get("ExpressionInputsUBO"); + const auto expressionUBO = LayerTweaker::buildExpressionUBO(zoom, parameters.frameCount); + uniforms.createOrUpdate(idExpressionInputsUBOName, &expressionUBO, parameters.context); + + static const StringIdentity idFillPermutationUBOName = stringIndexer().get("FillPermutationUBO"); + const shaders::FillPermutationUBO permutationUBO = { + /* .color = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, + /* .opacity = */ {/*.source=*/shaders::AttributeSource::Constant, /*.expression=*/{}}, + /* .overdrawInspector = */ false, + 0, + 0, + 0, + 0, + 0, + 0, + }; + uniforms.createOrUpdate(idFillPermutationUBOName, &permutationUBO, parameters.context); +#endif // MLN_RENDER_BACKEND_METAL + }; + +private: + Color color; + float opacity; +}; + CustomDrawableLayerHost::Interface::Interface(RenderLayer& layer_, LayerGroupBasePtr& layerGroup_, gfx::ShaderRegistry& shaders_, @@ -180,39 +259,104 @@ void CustomDrawableLayerHost::Interface::setLineOptions(const LineOptions& optio lineOptions = options; } +void CustomDrawableLayerHost::Interface::setFillOptions(const FillOptions& options) { + finish(); + fillOptions = options; +} + void CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates) { - if (!builder) { - builder = createBuilder("thick-lines", lineShaderDefault()); - } else { - // TODO: check builder + if (!lineShader) lineShader = lineShaderDefault(); + assert(lineShader); + if (!builder || builder->getShader() != lineShader) { + builder = createBuilder("lines", lineShader); } + assert(builder); + assert(builder->getShader() == lineShader); builder->addPolyline(coordinates, lineOptions.geometry); } +void CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geometry) { + if (!fillShader) fillShader = fillShaderDefault(); + assert(fillShader); + if (!builder || builder->getShader() != fillShader) { + builder = createBuilder("fill", fillShader); + } + assert(builder); + assert(builder->getShader() == fillShader); + + // provision buffers for fill vertices, indexes and segments + using VertexVector = gfx::VertexVector; + const std::shared_ptr sharedVertices = std::make_shared(); + VertexVector& vertices = *sharedVertices; + + using TriangleIndexVector = gfx::IndexVector; + const std::shared_ptr sharedTriangles = std::make_shared(); + TriangleIndexVector& triangles = *sharedTriangles; + + SegmentVector triangleSegments; + + // generate fill geometry into buffers + gfx::generateFillBuffers(geometry, vertices, triangleSegments, triangles); + + // add to builder + static const StringIdentity idVertexAttribName = stringIndexer().get("a_pos"); + builder->setVertexAttrNameId(idVertexAttribName); + gfx::VertexAttributeArray attrs; + if (const auto& attr = attrs.add(idVertexAttribName)) { + attr->setSharedRawData(sharedVertices, + offsetof(FillLayoutVertex, a1), + /*vertexOffset=*/0, + sizeof(FillLayoutVertex), + gfx::AttributeDataType::Short2); + } + builder->setVertexAttributes(std::move(attrs)); + builder->setRawVertices({}, vertices.elements(), gfx::AttributeDataType::Short2); + builder->setSegments(gfx::Triangles(), sharedTriangles, triangleSegments.data(), triangleSegments.size()); + + // flush current builder drawable + builder->flush(); +} + void CustomDrawableLayerHost::Interface::finish() { - if (builder && builder->curVertexCount()) { - // create tweaker - const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, - lineOptions.blur, - lineOptions.opacity, - lineOptions.gapWidth, - lineOptions.offset, - lineOptions.width, - 0, - 0, - 0}; - - auto tweaker = std::make_shared(linePropertiesUBO); - + if (builder && !builder->empty()) { // finish - builder->flush(); - for (auto& drawable : builder->clearDrawables()) { - assert(tileID.has_value()); - drawable->setTileID(tileID.value()); - drawable->addTweaker(tweaker); + const auto finish_ = [this](auto& tweaker) { + builder->flush(); + for (auto& drawable : builder->clearDrawables()) { + assert(tileID.has_value()); + drawable->setTileID(tileID.value()); + drawable->addTweaker(tweaker); - TileLayerGroup* tileLayerGroup = static_cast(layerGroup.get()); - tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable)); + TileLayerGroup* tileLayerGroup = static_cast(layerGroup.get()); + tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable)); + } + }; + + if (builder->getShader() == lineShader) { + // finish building lines + + // create line tweaker + const shaders::LinePropertiesUBO linePropertiesUBO{lineOptions.color, + lineOptions.blur, + lineOptions.opacity, + lineOptions.gapWidth, + lineOptions.offset, + lineOptions.width, + 0, + 0, + 0}; + auto tweaker = std::make_shared(linePropertiesUBO); + + // finish drawables + finish_(tweaker); + } else if (builder->getShader() == fillShader) { + // finish building fills + + // create fill tweaker + auto tweaker = std::make_shared(fillOptions.color, fillOptions.opacity); + + // finish drawables + finish_(tweaker); } } } @@ -232,6 +376,17 @@ gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const { return lineShaderGroup->getOrCreateShader(context, propertiesAsUniforms); } +gfx::ShaderPtr CustomDrawableLayerHost::Interface::fillShaderDefault() const { + gfx::ShaderGroupPtr fillShaderGroup = shaders.getShaderGroup("FillShader"); + + const std::unordered_set propertiesAsUniforms{ + stringIndexer().get("a_color"), + stringIndexer().get("a_opacity"), + }; + + return fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms); +} + std::unique_ptr CustomDrawableLayerHost::Interface::createBuilder(const std::string& name, gfx::ShaderPtr shader) const { std::unique_ptr builder_ = context.createDrawableBuilder(name); diff --git a/test/api/custom_drawable_layer.test.cpp b/test/api/custom_drawable_layer.test.cpp index 5673cbe14..d592c5831 100644 --- a/test/api/custom_drawable_layer.test.cpp +++ b/test/api/custom_drawable_layer.test.cpp @@ -30,76 +30,108 @@ public: interface.setTileID({11, 327, 791}); // add polylines - using namespace mbgl; + { + using namespace mbgl; - constexpr auto numLines = 6; - Interface::LineOptions options[numLines]{ - {/*color=*/Color::red(), - /*blur=*/0.0f, - /*opacity=*/1.0f, - /*gapWidth=*/0.0f, - /*offset=*/0.0f, - /*width=*/8.0f, - {}}, - {/*color=*/Color::blue(), - /*blur=*/4.0f, - /*opacity=*/1.0f, - /*gapWidth=*/2.0f, - /*offset=*/-1.0f, - /*width=*/4.0f, - {}}, - {/*color=*/Color(1.f, 0.5f, 0, 0.5f), - /*blur=*/16.0f, - /*opacity=*/1.0f, - /*gapWidth=*/1.0f, - /*offset=*/2.0f, - /*width=*/16.0f, - {}}, - {/*color=*/Color(1.f, 1.f, 0, 0.3f), - /*blur=*/2.0f, - /*opacity=*/1.0f, - /*gapWidth=*/1.0f, - /*offset=*/-2.0f, - /*width=*/2.0f, - {}}, - {/*color=*/Color::black(), - /*blur=*/0.5f, - /*opacity=*/0.5f, - /*gapWidth=*/1.0f, - /*offset=*/0.5f, - /*width=*/0.5f, - {}}, - {/*color=*/Color(1.f, 0, 1.f, 0.2f), - /*blur=*/24.0f, - /*opacity=*/0.5f, - /*gapWidth=*/1.0f, - /*offset=*/-5.0f, - /*width=*/24.0f, - {}}, - }; - for (auto& opt : options) { - opt.geometry.beginCap = style::LineCapType::Round; - opt.geometry.endCap = style::LineCapType::Round; - opt.geometry.joinType = style::LineJoinType::Round; - } - - constexpr auto numPoints = 100; - GeometryCoordinates polyline; - for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { - polyline.emplace_back(ipoint * util::EXTENT / numPoints, - std::sin(ipoint * 2 * M_PI / numPoints) * util::EXTENT / numLines / 2.f); - } - - for (auto index{0}; index < numLines; ++index) { - for (auto& p : polyline) { - p.y += util::EXTENT / numLines; + constexpr auto numLines = 6; + Interface::LineOptions options[numLines]{ + {/*color=*/Color::red(), + /*blur=*/0.0f, + /*opacity=*/1.0f, + /*gapWidth=*/0.0f, + /*offset=*/0.0f, + /*width=*/8.0f, + {}}, + {/*color=*/Color::blue(), + /*blur=*/4.0f, + /*opacity=*/1.0f, + /*gapWidth=*/2.0f, + /*offset=*/-1.0f, + /*width=*/4.0f, + {}}, + {/*color=*/Color(1.f, 0.5f, 0, 0.5f), + /*blur=*/16.0f, + /*opacity=*/1.0f, + /*gapWidth=*/1.0f, + /*offset=*/2.0f, + /*width=*/16.0f, + {}}, + {/*color=*/Color(1.f, 1.f, 0, 0.3f), + /*blur=*/2.0f, + /*opacity=*/1.0f, + /*gapWidth=*/1.0f, + /*offset=*/-2.0f, + /*width=*/2.0f, + {}}, + {/*color=*/Color::black(), + /*blur=*/0.5f, + /*opacity=*/0.5f, + /*gapWidth=*/1.0f, + /*offset=*/0.5f, + /*width=*/0.5f, + {}}, + {/*color=*/Color(1.f, 0, 1.f, 0.2f), + /*blur=*/24.0f, + /*opacity=*/0.5f, + /*gapWidth=*/1.0f, + /*offset=*/-5.0f, + /*width=*/24.0f, + {}}, + }; + for (auto& opt : options) { + opt.geometry.beginCap = style::LineCapType::Round; + opt.geometry.endCap = style::LineCapType::Round; + opt.geometry.joinType = style::LineJoinType::Round; } - // set property values - interface.setLineOptions(options[index]); + constexpr auto numPoints = 100; + GeometryCoordinates polyline; + for (auto ipoint{0}; ipoint < numPoints; ++ipoint) { + polyline.emplace_back(ipoint * util::EXTENT / numPoints, + std::sin(ipoint * 2 * M_PI / numPoints) * util::EXTENT / numLines / 2.f); + } - // add polyline - interface.addPolyline(polyline); + for (auto index{0}; index < numLines; ++index) { + for (auto& p : polyline) { + p.y += util::EXTENT / numLines; + } + + // set property values + interface.setLineOptions(options[index]); + + // add polyline + interface.addPolyline(polyline); + } + } + + // add fill polygon + { + using namespace mbgl; + + GeometryCollection geometry{ + { + // ring 1 + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.2f)}, + {static_cast(util::EXTENT * 0.5f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.7f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.5f), static_cast(util::EXTENT * 1.0f)}, + {static_cast(util::EXTENT * 0.0f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.2f)}, + }, + { + // ring 2 + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.25f)}, + {static_cast(util::EXTENT * 0.15f), static_cast(util::EXTENT * 0.5f)}, + {static_cast(util::EXTENT * 0.25f), static_cast(util::EXTENT * 0.45f)}, + {static_cast(util::EXTENT * 0.1f), static_cast(util::EXTENT * 0.25f)}, + }, + }; + + // set properties + interface.setFillOptions({/*color=*/Color::green(), /*opacity=*/0.5f}); + + // add fill + interface.addFill(geometry); } // finish diff --git a/test/fixtures/custom_drawable_layer/basic/expected.png b/test/fixtures/custom_drawable_layer/basic/expected.png index 4aa9e67a4..10572d271 100644 Binary files a/test/fixtures/custom_drawable_layer/basic/expected.png and b/test/fixtures/custom_drawable_layer/basic/expected.png differ