Skip to content

Commit

Permalink
Custom drawable layer: Simple Fill (#1810)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Alex Cristici <[email protected]>
  • Loading branch information
3 people authored Nov 6, 2023
1 parent ef69472 commit a73e88f
Show file tree
Hide file tree
Showing 11 changed files with 552 additions and 222 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions bazel/core.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions include/mbgl/gfx/drawable_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ class DrawableBuilder {
/// 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;
Expand Down
25 changes: 25 additions & 0 deletions include/mbgl/gfx/fill_generator.hpp
Original file line number Diff line number Diff line change
@@ -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
29 changes: 23 additions & 6 deletions include/mbgl/style/layers/custom_drawable_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class CustomDrawableLayerHost::Interface {
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,
Expand Down Expand Up @@ -72,6 +77,13 @@ class CustomDrawableLayerHost::Interface {
*/
void setLineOptions(const LineOptions& options);

/**
* @brief Set the fill options
*
* @param options
*/
void setFillOptions(const FillOptions& options);

/**
* @brief Add a polyline
*
Expand All @@ -80,17 +92,14 @@ class CustomDrawableLayerHost::Interface {
*/
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;
Expand All @@ -102,9 +111,17 @@ class CustomDrawableLayerHost::Interface {
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 {
Expand Down
94 changes: 63 additions & 31 deletions platform/darwin/app/ExampleCustomDrawableStyleLayer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ - (instancetype)initWithIdentifier:(NSString *)identifier {
return self = [super initWithPendingLayer:std::move(layer)];
}


@end

class ExampleCustomDrawableStyleLayerHost : public mbgl::style::CustomDrawableLayerHost {
Expand All @@ -47,43 +46,76 @@ void update(Interface& interface) override {

// 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();
}
Expand Down
151 changes: 151 additions & 0 deletions src/mbgl/gfx/fill_generator.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit a73e88f

Please sign in to comment.