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:
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
25
include/mbgl/gfx/fill_generator.hpp
Normal file
25
include/mbgl/gfx/fill_generator.hpp
Normal 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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
151
src/mbgl/gfx/fill_generator.cpp
Normal file
151
src/mbgl/gfx/fill_generator.cpp
Normal 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
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 |
Reference in New Issue
Block a user