Custom drawable layer: Simple Fill (#1810)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alex Cristici <alex.cristici@gmail.com>
This commit is contained in:
Stefan Karschti
2023-11-06 20:43:58 +02:00
committed by GitHub
parent ef69472f56
commit a73e88fee2
11 changed files with 551 additions and 221 deletions

View File

@@ -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

View File

@@ -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",

View File

@@ -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;

View File

@@ -0,0 +1,25 @@
#pragma once
#include <mbgl/util/geometry.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gfx/vertex_vector.hpp>
#include <mbgl/gfx/index_vector.hpp>
#include <mbgl/programs/fill_program.hpp>
namespace mbgl {
namespace gfx {
void generateFillBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<Triangles>& triangles);
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& lines,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& triangles);
} // namespace gfx
} // namespace mbgl

View File

@@ -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<gfx::DrawableBuilder> 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<gfx::DrawableBuilder> createBuilder(const std::string& name, gfx::ShaderPtr shader) const;
gfx::ShaderPtr lineShader;
gfx::ShaderPtr fillShader;
std::unique_ptr<gfx::DrawableBuilder> builder;
std::optional<OverscaledTileID> tileID;
LineOptions lineOptions;
FillOptions fillOptions;
};
class CustomDrawableLayer final : public Layer {

View File

@@ -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<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.2f)},
{static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 0.5f)},
{static_cast<int16_t>(extent* 0.7f), static_cast<int16_t>(extent* 0.5f)},
{static_cast<int16_t>(extent* 0.5f), static_cast<int16_t>(extent* 1.0f)},
{static_cast<int16_t>(extent* 0.0f), static_cast<int16_t>(extent* 0.5f)},
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.2f)},
},
{
// ring 2
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.25f)},
{static_cast<int16_t>(extent* 0.15f), static_cast<int16_t>(extent* 0.5f)},
{static_cast<int16_t>(extent* 0.25f), static_cast<int16_t>(extent* 0.45f)},
{static_cast<int16_t>(extent* 0.1f), static_cast<int16_t>(extent* 0.25f)},
},
};
// set properties
interface.setFillOptions({/*color=*/Color::green(), /*opacity=*/0.5f});
// add fill
interface.addFill(geometry);
}
// finish
interface.finish();
}

View File

@@ -0,0 +1,151 @@
#include <mbgl/gfx/fill_generator.hpp>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
#include <mapbox/earcut.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <cassert>
#include <limits>
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<FillLayoutVertex>& 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<uint16_t>::max()) throw GeometryTooLongException();
return totalVertices;
}
void addFillIndices(SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& triangles,
std::vector<uint32_t>& 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<uint16_t>::max()) {
triangleSegments.emplace_back(startVertices, triangles.elements());
}
auto& triangleSegment = triangleSegments.back();
assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto triangleIndex = static_cast<uint16_t>(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<FillAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& lines) {
if (nVertices == 0) return;
if (lineSegments.empty() || lineSegments.back().vertexLength + nVertices > std::numeric_limits<uint16_t>::max()) {
lineSegments.emplace_back(base, lines.elements());
}
auto& lineSegment = lineSegments.back();
assert(lineSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto lineIndex = static_cast<uint16_t>(lineSegment.vertexLength);
lines.emplace_back(static_cast<uint16_t>(lineIndex + nVertices - 1), lineIndex);
for (std::size_t i = 1; i < nVertices; i++) {
lines.emplace_back(static_cast<uint16_t>(lineIndex + i - 1), static_cast<uint16_t>(lineIndex + i));
}
lineSegment.vertexLength += nVertices;
lineSegment.indexLength += nVertices * 2;
}
} // namespace
void generateFillBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& 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<uint32_t> indices = mapbox::earcut(polygon);
addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices);
}
}
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
SegmentVector<FillAttributes>& lineSegments,
gfx::IndexVector<gfx::Lines>& lines,
SegmentVector<FillAttributes>& triangleSegments,
gfx::IndexVector<gfx::Triangles>& 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<uint32_t> indices = mapbox::earcut(polygon);
addFillIndices(triangleSegments, triangles, indices, startVertices, totalVertices);
}
}
} // namespace gfx
} // namespace mbgl

View File

@@ -4,44 +4,15 @@
#include <mbgl/style/layers/fill_layer_impl.hpp>
#include <mbgl/renderer/layers/render_fill_layer.hpp>
#include <mbgl/util/math.hpp>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4244)
#endif
#include <mapbox/earcut.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#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
#include <mbgl/gfx/fill_generator.hpp>
namespace mbgl {
using namespace style;
struct GeometryTooLongException : std::exception {};
FillBucket::FillBucket(const FillBucket::PossiblyEvaluatedLayoutProperties&,
const std::map<std::string, Immutable<style::LayerProperties>>& 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<uint16_t>::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<uint16_t>::max()) {
lineSegments.emplace_back(vertices.elements(), lines.elements());
}
auto& lineSegment = lineSegments.back();
assert(lineSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto lineIndex = static_cast<uint16_t>(lineSegment.vertexLength);
vertices.emplace_back(FillProgram::layoutVertex(ring[0]));
lines.emplace_back(static_cast<uint16_t>(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<uint16_t>(lineIndex + i - 1), static_cast<uint16_t>(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.elements());
}
auto& triangleSegment = triangleSegments.back();
assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
const auto triangleIndex = static_cast<uint16_t>(triangleSegment.vertexLength);
for (std::size_t i = 0; i < nIndicies; i += 3) {
triangles.emplace_back(static_cast<uint16_t>(triangleIndex + indices[i]),
static_cast<uint16_t>(triangleIndex + indices[i + 1]),
static_cast<uint16_t>(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<FillLayerProperties>(layer.evaluatedProperties);
const std::array<float, 2>& translate = evaluated.get<FillTranslate>();
return util::length(translate[0], translate[1]);

View File

@@ -15,8 +15,15 @@
#include <mbgl/util/string_indexer.hpp>
#include <mbgl/util/convert.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/programs/fill_program.hpp>
#include <mbgl/shaders/fill_layer_ubo.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
#include <mbgl/gfx/fill_generator.hpp>
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<float>(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<FillLayoutVertex>;
const std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
VertexVector& vertices = *sharedVertices;
using TriangleIndexVector = gfx::IndexVector<gfx::Triangles>;
const std::shared_ptr<TriangleIndexVector> sharedTriangles = std::make_shared<TriangleIndexVector>();
TriangleIndexVector& triangles = *sharedTriangles;
SegmentVector<FillAttributes> 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<LineDrawableTweaker>(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<TileLayerGroup*>(layerGroup.get());
tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable));
TileLayerGroup* tileLayerGroup = static_cast<TileLayerGroup*>(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<LineDrawableTweaker>(linePropertiesUBO);
// finish drawables
finish_(tweaker);
} else if (builder->getShader() == fillShader) {
// finish building fills
// create fill tweaker
auto tweaker = std::make_shared<FillDrawableTweaker>(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<StringIdentity> propertiesAsUniforms{
stringIndexer().get("a_color"),
stringIndexer().get("a_opacity"),
};
return fillShaderGroup->getOrCreateShader(context, propertiesAsUniforms);
}
std::unique_ptr<gfx::DrawableBuilder> CustomDrawableLayerHost::Interface::createBuilder(const std::string& name,
gfx::ShaderPtr shader) const {
std::unique_ptr<gfx::DrawableBuilder> builder_ = context.createDrawableBuilder(name);

View File

@@ -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<int16_t>(util::EXTENT * 0.1f), static_cast<int16_t>(util::EXTENT * 0.2f)},
{static_cast<int16_t>(util::EXTENT * 0.5f), static_cast<int16_t>(util::EXTENT * 0.5f)},
{static_cast<int16_t>(util::EXTENT * 0.7f), static_cast<int16_t>(util::EXTENT * 0.5f)},
{static_cast<int16_t>(util::EXTENT * 0.5f), static_cast<int16_t>(util::EXTENT * 1.0f)},
{static_cast<int16_t>(util::EXTENT * 0.0f), static_cast<int16_t>(util::EXTENT * 0.5f)},
{static_cast<int16_t>(util::EXTENT * 0.1f), static_cast<int16_t>(util::EXTENT * 0.2f)},
},
{
// ring 2
{static_cast<int16_t>(util::EXTENT * 0.1f), static_cast<int16_t>(util::EXTENT * 0.25f)},
{static_cast<int16_t>(util::EXTENT * 0.15f), static_cast<int16_t>(util::EXTENT * 0.5f)},
{static_cast<int16_t>(util::EXTENT * 0.25f), static_cast<int16_t>(util::EXTENT * 0.45f)},
{static_cast<int16_t>(util::EXTENT * 0.1f), static_cast<int16_t>(util::EXTENT * 0.25f)},
},
};
// set properties
interface.setFillOptions({/*color=*/Color::green(), /*opacity=*/0.5f});
// add fill
interface.addFill(geometry);
}
// finish

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB