892 lines
36 KiB
C++
892 lines
36 KiB
C++
#include <mbgl/style/layers/custom_drawable_layer.hpp>
|
|
#include <mbgl/style/layers/custom_drawable_layer_impl.hpp>
|
|
|
|
#include <mbgl/renderer/layers/render_custom_drawable_layer.hpp>
|
|
#include <mbgl/style/layer_observer.hpp>
|
|
#include <mbgl/gfx/context.hpp>
|
|
#include <mbgl/renderer/paint_parameters.hpp>
|
|
#include <mbgl/renderer/change_request.hpp>
|
|
#include <mbgl/renderer/layer_group.hpp>
|
|
#include <mbgl/gfx/cull_face_mode.hpp>
|
|
#include <mbgl/shaders/shader_program_base.hpp>
|
|
#include <mbgl/renderer/layer_tweaker.hpp>
|
|
#include <mbgl/gfx/drawable.hpp>
|
|
#include <mbgl/gfx/drawable_tweaker.hpp>
|
|
#include <mbgl/shaders/line_layer_ubo.hpp>
|
|
#include <mbgl/util/convert.hpp>
|
|
#include <mbgl/util/geometry.hpp>
|
|
#include <mbgl/shaders/fill_layer_ubo.hpp>
|
|
#include <mbgl/util/math.hpp>
|
|
#include <mbgl/tile/geometry_tile_data.hpp>
|
|
#include <mbgl/util/containers.hpp>
|
|
#include <mbgl/gfx/fill_generator.hpp>
|
|
#include <mbgl/shaders/custom_drawable_layer_ubo.hpp>
|
|
#include <mbgl/shaders/shader_source.hpp>
|
|
#include <mbgl/gfx/renderable.hpp>
|
|
#include <mbgl/gfx/renderer_backend.hpp>
|
|
#include <mbgl/shaders/widevector_ubo.hpp>
|
|
#include <mbgl/shaders/custom_geometry_ubo.hpp>
|
|
#include <mbgl/util/projection.hpp>
|
|
#include <mbgl/util/mat4.hpp>
|
|
#include <mbgl/renderer/render_tile.hpp>
|
|
#include <mbgl/gfx/uniform_buffer.hpp>
|
|
|
|
#include <cmath>
|
|
|
|
namespace mbgl {
|
|
|
|
namespace style {
|
|
|
|
using namespace shaders;
|
|
|
|
namespace {
|
|
const LayerTypeInfo typeInfoCustomDrawable{.type = "custom-drawable",
|
|
.source = LayerTypeInfo::Source::NotRequired,
|
|
.pass3d = LayerTypeInfo::Pass3D::NotRequired,
|
|
.layout = LayerTypeInfo::Layout::NotRequired,
|
|
.fadingTiles = LayerTypeInfo::FadingTiles::NotRequired,
|
|
.crossTileIndex = LayerTypeInfo::CrossTileIndex::NotRequired,
|
|
.tileKind = LayerTypeInfo::TileKind::NotRequired};
|
|
} // namespace
|
|
|
|
CustomDrawableLayer::CustomDrawableLayer(const std::string& layerID, std::unique_ptr<CustomDrawableLayerHost> host)
|
|
: Layer(makeMutable<Impl>(layerID, std::move(host))) {}
|
|
|
|
CustomDrawableLayer::~CustomDrawableLayer() = default;
|
|
|
|
const CustomDrawableLayer::Impl& CustomDrawableLayer::impl() const {
|
|
return static_cast<const Impl&>(*baseImpl);
|
|
}
|
|
|
|
Mutable<CustomDrawableLayer::Impl> CustomDrawableLayer::mutableImpl() const {
|
|
return makeMutable<Impl>(impl());
|
|
}
|
|
|
|
std::unique_ptr<Layer> CustomDrawableLayer::cloneRef(const std::string&) const {
|
|
assert(false);
|
|
return nullptr;
|
|
}
|
|
|
|
using namespace conversion;
|
|
|
|
std::optional<Error> CustomDrawableLayer::setPropertyInternal(const std::string&, const Convertible&) {
|
|
return Error{"layer doesn't support this property"};
|
|
}
|
|
|
|
StyleProperty CustomDrawableLayer::getProperty(const std::string&) const {
|
|
return {};
|
|
}
|
|
|
|
Mutable<Layer::Impl> CustomDrawableLayer::mutableBaseImpl() const {
|
|
return staticMutableCast<Layer::Impl>(mutableImpl());
|
|
}
|
|
|
|
// static
|
|
const LayerTypeInfo* CustomDrawableLayer::Impl::staticTypeInfo() noexcept {
|
|
return &typeInfoCustomDrawable;
|
|
}
|
|
|
|
// CustomDrawableLayerHost::Interface
|
|
|
|
class LineDrawableTweaker : public gfx::DrawableTweaker {
|
|
public:
|
|
LineDrawableTweaker(const CustomDrawableLayerHost::Interface::LineOptions& options_,
|
|
CustomDrawableLayerHost::Interface::LineTweakerCallback&& callback_)
|
|
: options(options_),
|
|
callback(callback_) {}
|
|
|
|
~LineDrawableTweaker() override = default;
|
|
|
|
void init(gfx::Drawable&) override {}
|
|
|
|
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
|
if (!drawable.getTileID().has_value()) {
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback(drawable, parameters, options);
|
|
}
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
if (!layerUniforms) {
|
|
layerUniforms = parameters.context.createLayerUniformBufferArray();
|
|
}
|
|
#endif
|
|
|
|
const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped();
|
|
const auto zoom = parameters.state.getZoom();
|
|
|
|
const auto matrix = LayerTweaker::getTileMatrix(
|
|
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, drawable, false);
|
|
|
|
const shaders::LineEvaluatedPropsUBO propsUBO = {.color = options.color,
|
|
.blur = options.blur,
|
|
.opacity = options.opacity,
|
|
.gapwidth = options.gapWidth,
|
|
.offset = options.offset,
|
|
.width = options.width,
|
|
/*floorwidth=*/.floorwidth = 0.0f,
|
|
.expressionMask = LineExpressionMask::None,
|
|
.pad1 = 0};
|
|
|
|
// We would need to set up `idLineExpressionUBO` if the expression mask isn't empty
|
|
assert(propsUBO.expressionMask == LineExpressionMask::None);
|
|
|
|
if (!expressionUniformBuffer) {
|
|
const LineExpressionUBO exprUBO = {
|
|
/* .color = */ .color = nullptr,
|
|
/* .blur = */ .blur = nullptr,
|
|
/* .opacity = */ .opacity = nullptr,
|
|
/* .gapwidth = */ .gapwidth = nullptr,
|
|
/* .offset = */ .offset = nullptr,
|
|
/* .width = */ .width = nullptr,
|
|
/* .floorWidth = */ .floorWidth = nullptr,
|
|
};
|
|
|
|
expressionUniformBuffer = parameters.context.createUniformBuffer(&exprUBO, sizeof(exprUBO));
|
|
#if MLN_UBO_CONSOLIDATION
|
|
layerUniforms->set(idLineExpressionUBO, expressionUniformBuffer);
|
|
#else
|
|
auto& drawableUniforms = drawable.mutableUniformBuffers();
|
|
drawableUniforms.set(idLineExpressionUBO, expressionUniformBuffer);
|
|
#endif
|
|
}
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
shaders::LineDrawableUnionUBO drawableUBO;
|
|
drawableUBO.lineDrawableUBO = {
|
|
#else
|
|
const shaders::LineDrawableUBO drawableUBO = {
|
|
#endif
|
|
/* .matrix = */ .matrix = util::cast<float>(matrix),
|
|
/* .ratio = */ .ratio = 1.0f / tileID.pixelsToTileUnits(1.0f, zoom),
|
|
|
|
/* .color_t = */ .color_t = 0.f,
|
|
/* .blur_t = */ .blur_t = 0.f,
|
|
/* .opacity_t = */ .opacity_t = 0.f,
|
|
/* .gapwidth_t = */ .gapwidth_t = 0.f,
|
|
/* .offset_t = */ .offset_t = 0.f,
|
|
/* .width_t = */ .width_t = 0.f,
|
|
/* .pad1 = */ .pad1 = 0
|
|
};
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
if (!drawableUniformBuffer) {
|
|
drawableUniformBuffer = parameters.context.createUniformBuffer(
|
|
&drawableUBO, sizeof(drawableUBO), false, true);
|
|
|
|
layerUniforms->set(idLineDrawableUBO, drawableUniformBuffer);
|
|
drawable.setUBOIndex(0);
|
|
} else {
|
|
drawableUniformBuffer->update(&drawableUBO, sizeof(drawableUBO));
|
|
}
|
|
|
|
layerUniforms->createOrUpdate(idLineEvaluatedPropsUBO, &propsUBO, sizeof(propsUBO), parameters.context);
|
|
layerUniforms->bind(*parameters.renderPass);
|
|
#else
|
|
auto& drawableUniforms = drawable.mutableUniformBuffers();
|
|
drawableUniforms.createOrUpdate(idLineDrawableUBO, &drawableUBO, parameters.context);
|
|
drawableUniforms.createOrUpdate(idLineEvaluatedPropsUBO, &propsUBO, parameters.context);
|
|
#endif
|
|
};
|
|
|
|
private:
|
|
CustomDrawableLayerHost::Interface::LineOptions options;
|
|
CustomDrawableLayerHost::Interface::LineTweakerCallback callback;
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
gfx::UniqueUniformBufferArray layerUniforms;
|
|
gfx::UniformBufferPtr drawableUniformBuffer;
|
|
#endif
|
|
|
|
gfx::UniformBufferPtr expressionUniformBuffer;
|
|
};
|
|
|
|
class WideVectorDrawableTweaker : public gfx::DrawableTweaker {
|
|
public:
|
|
WideVectorDrawableTweaker(const CustomDrawableLayerHost::Interface::LineOptions& options_,
|
|
CustomDrawableLayerHost::Interface::LineTweakerCallback&& callback_)
|
|
: options(options_),
|
|
callback(callback_) {}
|
|
|
|
void init(gfx::Drawable&) override {}
|
|
|
|
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
|
if (!drawable.getTileID().has_value()) {
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback(drawable, parameters, options);
|
|
}
|
|
|
|
const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped();
|
|
|
|
mat4 tileMatrix;
|
|
parameters.state.matrixFor(/*out*/ tileMatrix, tileID);
|
|
if (const auto& origin{drawable.getOrigin()}; origin.has_value()) {
|
|
matrix::translate(tileMatrix, tileMatrix, origin->x, origin->y, 0);
|
|
}
|
|
|
|
mat4 projMatrix = parameters.transformParams.projMatrix;
|
|
const auto matrix = LayerTweaker::getTileMatrix(
|
|
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, drawable, false);
|
|
|
|
matf4 mvpMatrix, mvpMatrixDiff, mvMatrix, mvMatrixDiff, pMatrix, pMatrixDiff;
|
|
matrix::diffsplit(mvpMatrix, mvpMatrixDiff, matrix);
|
|
matrix::diffsplit(mvMatrix, mvMatrixDiff, tileMatrix);
|
|
matrix::diffsplit(pMatrix, pMatrixDiff, projMatrix);
|
|
|
|
const auto renderableSize = parameters.backend.getDefaultRenderable().getSize();
|
|
shaders::WideVectorUniformsUBO uniform = {
|
|
/* .mvpMatrix = */ .mvpMatrix = mvpMatrix,
|
|
/* .mvpMatrixDiff = */ .mvpMatrixDiff = mvpMatrixDiff,
|
|
/* .mvMatrix = */ .mvMatrix = mvMatrix,
|
|
/* .mvMatrixDiff = */ .mvMatrixDiff = mvMatrixDiff,
|
|
/* .pMatrix = */ .pMatrix = pMatrix,
|
|
/* .pMatrixDiff = */ .pMatrixDiff = pMatrixDiff,
|
|
/* .frameSize = */ .frameSize = {(float)renderableSize.width, (float)renderableSize.height},
|
|
/* .pad1 = */ .pad1 = 0,
|
|
/* .pad2 = */ .pad2 = 0};
|
|
|
|
shaders::WideVectorUniformWideVecUBO wideVec = {
|
|
/* .color = */ .color = options.color,
|
|
/* .w2 = */ .w2 = options.width,
|
|
/* .offset = */ .offset = options.offset,
|
|
/* .edge = */ .edge = 0.0f, // TODO: MLN does not provide a value. Analyze impact.
|
|
/* .texRepeat = */ .texRepeat = 0.0f, // N/A
|
|
/* .texOffset = */ .texOffset = {}, // N/A
|
|
/* .miterLimit = */ .miterLimit = options.geometry.miterLimit,
|
|
/* .join = */ .join = static_cast<int32_t>(options.geometry.joinType),
|
|
/* .cap = */ .cap = static_cast<int32_t>(options.geometry.beginCap), // TODO: MLN option for endCap to be
|
|
// implemented in the shader!
|
|
/* .hasExp = */ .hasExp = false, // N/A
|
|
/* .interClipLimit = */ .interClipLimit = 0.0f, // N/A
|
|
/* .pad1 = */ .pad1 = 0};
|
|
|
|
auto& drawableUniforms = drawable.mutableUniformBuffers();
|
|
drawableUniforms.createOrUpdate(idWideVectorUniformsUBO, &uniform, parameters.context);
|
|
drawableUniforms.createOrUpdate(idWideVectorUniformWideVecUBO, &wideVec, parameters.context);
|
|
};
|
|
|
|
private:
|
|
CustomDrawableLayerHost::Interface::LineOptions options;
|
|
CustomDrawableLayerHost::Interface::LineTweakerCallback callback;
|
|
};
|
|
|
|
class FillDrawableTweaker : public gfx::DrawableTweaker {
|
|
public:
|
|
FillDrawableTweaker(const CustomDrawableLayerHost::Interface::FillOptions& options_,
|
|
CustomDrawableLayerHost::Interface::FillTweakerCallback&& callback_)
|
|
: options(options_),
|
|
callback(callback_) {}
|
|
|
|
~FillDrawableTweaker() override = default;
|
|
|
|
void init(gfx::Drawable&) override {}
|
|
|
|
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
|
if (!drawable.getTileID().has_value()) {
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback(drawable, parameters, options);
|
|
}
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
if (!layerUniforms) {
|
|
layerUniforms = parameters.context.createLayerUniformBufferArray();
|
|
}
|
|
#endif
|
|
|
|
const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped();
|
|
const auto matrix = LayerTweaker::getTileMatrix(
|
|
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, drawable, false);
|
|
|
|
const shaders::FillEvaluatedPropsUBO propsUBO = {/* .color = */ .color = options.color,
|
|
/* .outline_color = */ .outline_color = Color::white(),
|
|
/* .opacity = */ .opacity = options.opacity,
|
|
/* .fade = */ .fade = 0.f,
|
|
/* .from_scale = */ .from_scale = 0.f,
|
|
/* .to_scale = */ .to_scale = 0.f};
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
FillDrawableUnionUBO drawableUBO;
|
|
drawableUBO.fillDrawableUBO = {
|
|
#else
|
|
const shaders::FillDrawableUBO drawableUBO = {
|
|
#endif
|
|
/* .matrix = */ .matrix = util::cast<float>(matrix),
|
|
|
|
/* .color_t = */ .color_t = 0.f,
|
|
/* .opacity_t = */ .opacity_t = 0.f,
|
|
/* .pad1 = */ .pad1 = 0,
|
|
/* .pad2 = */ .pad2 = 0
|
|
};
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
if (!drawableUniformBuffer) {
|
|
drawableUniformBuffer = parameters.context.createUniformBuffer(
|
|
&drawableUBO, sizeof(drawableUBO), false, true);
|
|
|
|
layerUniforms->set(idFillDrawableUBO, drawableUniformBuffer);
|
|
drawable.setUBOIndex(0);
|
|
} else {
|
|
drawableUniformBuffer->update(&drawableUBO, sizeof(drawableUBO));
|
|
}
|
|
|
|
layerUniforms->createOrUpdate(idFillEvaluatedPropsUBO, &propsUBO, sizeof(propsUBO), parameters.context);
|
|
layerUniforms->bind(*parameters.renderPass);
|
|
#else
|
|
auto& drawableUniforms = drawable.mutableUniformBuffers();
|
|
drawableUniforms.createOrUpdate(idFillDrawableUBO, &drawableUBO, parameters.context);
|
|
drawableUniforms.createOrUpdate(idFillEvaluatedPropsUBO, &propsUBO, parameters.context);
|
|
#endif
|
|
};
|
|
|
|
private:
|
|
CustomDrawableLayerHost::Interface::FillOptions options;
|
|
CustomDrawableLayerHost::Interface::FillTweakerCallback callback;
|
|
|
|
#if MLN_UBO_CONSOLIDATION
|
|
gfx::UniqueUniformBufferArray layerUniforms;
|
|
gfx::UniformBufferPtr drawableUniformBuffer;
|
|
#endif
|
|
};
|
|
|
|
class SymbolDrawableTweaker : public gfx::DrawableTweaker {
|
|
public:
|
|
SymbolDrawableTweaker(const CustomDrawableLayerHost::Interface::SymbolOptions& options_,
|
|
CustomDrawableLayerHost::Interface::SymbolTweakerCallback&& callback_)
|
|
: options(options_),
|
|
callback(callback_) {}
|
|
~SymbolDrawableTweaker() override = default;
|
|
|
|
void init(gfx::Drawable&) override {}
|
|
|
|
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
|
if (!drawable.getTileID().has_value()) {
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback(drawable, parameters, options);
|
|
}
|
|
|
|
const UnwrappedTileID tileID = drawable.getTileID()->toUnwrapped();
|
|
|
|
const auto matrix = LayerTweaker::getTileMatrix(
|
|
tileID, parameters, {{0, 0}}, style::TranslateAnchorType::Viewport, false, false, drawable, false);
|
|
|
|
const auto pixelsToTileUnits = tileID.pixelsToTileUnits(
|
|
1.0f, options.scaleWithMap ? tileID.canonical.z : parameters.state.getZoom());
|
|
const float factor = options.scaleWithMap
|
|
? static_cast<float>(std::pow(2.f, parameters.state.getZoom() - tileID.canonical.z))
|
|
: 1.0f;
|
|
const auto extrudeScale = options.pitchWithMap ? std::array<float, 2>{pixelsToTileUnits, pixelsToTileUnits}
|
|
: std::array<float, 2>{parameters.pixelsToGLUnits[0] * factor,
|
|
parameters.pixelsToGLUnits[1] * factor};
|
|
|
|
const shaders::CustomSymbolIconDrawableUBO drawableUBO = {
|
|
/* .matrix = */ .matrix = util::cast<float>(matrix),
|
|
/* .extrude_scale = */ .extrude_scale = {extrudeScale[0] * options.size.width,
|
|
extrudeScale[1] * options.size.height},
|
|
/* .anchor = */ .anchor = options.anchor,
|
|
/* .angle_degrees = */ .angle_degrees = options.angleDegrees,
|
|
/* .scale_with_map = */ .scale_with_map = options.scaleWithMap,
|
|
/* .pitch_with_map = */ .pitch_with_map = options.pitchWithMap,
|
|
/* .camera_to_center_distance = */ .camera_to_center_distance =
|
|
parameters.state.getCameraToCenterDistance(),
|
|
/* .aspect_ratio = */ .aspect_ratio = parameters.pixelsToGLUnits[0] / parameters.pixelsToGLUnits[1],
|
|
/* .pad1 = */ .pad1 = 0,
|
|
/* .pad2 = */ .pad2 = 0,
|
|
/* .pad3 = */ .pad3 = 0};
|
|
|
|
auto& drawableUniforms = drawable.mutableUniformBuffers();
|
|
drawableUniforms.createOrUpdate(idCustomSymbolDrawableUBO, &drawableUBO, parameters.context);
|
|
};
|
|
|
|
private:
|
|
CustomDrawableLayerHost::Interface::SymbolOptions options;
|
|
CustomDrawableLayerHost::Interface::SymbolTweakerCallback callback;
|
|
};
|
|
|
|
class GeometryDrawableTweaker : public gfx::DrawableTweaker {
|
|
public:
|
|
GeometryDrawableTweaker(const CustomDrawableLayerHost::Interface::GeometryOptions& options_,
|
|
CustomDrawableLayerHost::Interface::GeometryTweakerCallback&& callback_)
|
|
: options(options_),
|
|
callback(callback_) {}
|
|
~GeometryDrawableTweaker() override = default;
|
|
|
|
void init(gfx::Drawable&) override {}
|
|
|
|
void execute(gfx::Drawable& drawable, PaintParameters& parameters) override {
|
|
if (!drawable.getTileID().has_value()) {
|
|
return;
|
|
}
|
|
|
|
if (callback) {
|
|
callback(drawable, parameters, options);
|
|
}
|
|
|
|
CustomGeometryDrawableUBO drawableUBO = {/* .matrix = */ .matrix = util::cast<float>(options.matrix),
|
|
/* .color = */ .color = options.color};
|
|
|
|
auto& drawableUniforms = drawable.mutableUniformBuffers();
|
|
drawableUniforms.createOrUpdate(idCustomGeometryDrawableUBO, &drawableUBO, parameters.context);
|
|
};
|
|
|
|
private:
|
|
CustomDrawableLayerHost::Interface::GeometryOptions options;
|
|
CustomDrawableLayerHost::Interface::GeometryTweakerCallback callback;
|
|
};
|
|
|
|
CustomDrawableLayerHost::Interface::Interface(RenderLayer& layer_,
|
|
LayerGroupBasePtr& layerGroup_,
|
|
gfx::ShaderRegistry& shaders_,
|
|
gfx::Context& context_,
|
|
const TransformState& state_,
|
|
const std::shared_ptr<UpdateParameters>& updateParameters_,
|
|
const RenderTree& renderTree_,
|
|
UniqueChangeRequestVec& changes_)
|
|
: layer(layer_),
|
|
layerGroup(layerGroup_),
|
|
shaders(shaders_),
|
|
context(context_),
|
|
state(state_),
|
|
updateParameters(updateParameters_),
|
|
renderTree(renderTree_),
|
|
changes(changes_) {
|
|
// ensure we have a default layer group set up
|
|
if (!layerGroup) {
|
|
if (auto aLayerGroup = context.createTileLayerGroup(
|
|
/*layerIndex*/ layer.getLayerIndex(), /*initialCapacity=*/64, layer.getID())) {
|
|
changes.emplace_back(std::make_unique<AddLayerGroupRequest>(aLayerGroup));
|
|
layerGroup = std::move(aLayerGroup);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::size_t CustomDrawableLayerHost::Interface::getDrawableCount() const {
|
|
return layerGroup->getDrawableCount();
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::setTileID(OverscaledTileID tileID_) {
|
|
if (tileID != tileID_) finish();
|
|
tileID = tileID_;
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::setLineOptions(const LineOptions& options) {
|
|
finish();
|
|
lineOptions = options;
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::setFillOptions(const FillOptions& options) {
|
|
finish();
|
|
fillOptions = options;
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::setSymbolOptions(const SymbolOptions& options) {
|
|
finish();
|
|
symbolOptions = options;
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::setGeometryOptions(const GeometryOptions& options) {
|
|
finish();
|
|
geometryOptions = options;
|
|
}
|
|
|
|
bool CustomDrawableLayerHost::Interface::updateBuilder(BuilderType type,
|
|
const std::string& name,
|
|
gfx::ShaderPtr shader) {
|
|
if (!shader) return false;
|
|
if (type != builderType || !builder || builder->getShader() != shader) {
|
|
finish();
|
|
builder = createBuilder(name, shader);
|
|
builderType = type;
|
|
}
|
|
assert(builder);
|
|
assert(builder->getShader() == shader);
|
|
return true;
|
|
};
|
|
|
|
util::SimpleIdentity CustomDrawableLayerHost::Interface::addPolyline(const LineString<double>& coordinates,
|
|
LineShaderType shaderType) {
|
|
#if !MLN_RENDER_BACKEND_METAL
|
|
shaderType = LineShaderType::Classic;
|
|
#endif
|
|
|
|
switch (shaderType) {
|
|
case LineShaderType::Classic: {
|
|
// build classic polyline with Geo coordinates
|
|
if (!updateBuilder(BuilderType::LineClassic, "custom-lines", lineShaderDefault())) {
|
|
return util::SimpleIdentity::Empty;
|
|
}
|
|
|
|
// geographic coordinates require tile {0, 0, 0}
|
|
setTileID({0, 0, 0});
|
|
|
|
constexpr int32_t zoom = 0;
|
|
GeometryCoordinates tileCoordinates;
|
|
for (const auto& coord : coordinates) {
|
|
const auto point = Projection::project(LatLng(coord.y, coord.x), zoom);
|
|
tileCoordinates.push_back(Point<int16_t>(static_cast<int16_t>(point.x * mbgl::util::EXTENT),
|
|
static_cast<int16_t>(point.y * mbgl::util::EXTENT)));
|
|
}
|
|
|
|
builder->addPolyline(tileCoordinates, lineOptions.geometry);
|
|
} break;
|
|
|
|
case LineShaderType::WideVector: {
|
|
// build wide vector polyline with Geo coordinates
|
|
if (!updateBuilder(BuilderType::LineWideVector, "custom-lines-widevector", lineShaderWideVector()))
|
|
return util::SimpleIdentity::Empty;
|
|
|
|
// geographic coordinates require tile {0, 0, 0}
|
|
setTileID({0, 0, 0});
|
|
|
|
builder->addWideVectorPolylineGlobal(coordinates, lineOptions.geometry);
|
|
} break;
|
|
}
|
|
|
|
return builder->getCurrentDrawable(true)->getID();
|
|
}
|
|
|
|
util::SimpleIdentity CustomDrawableLayerHost::Interface::addPolyline(const GeometryCoordinates& coordinates,
|
|
LineShaderType shaderType) {
|
|
#if !MLN_RENDER_BACKEND_METAL
|
|
shaderType = LineShaderType::Classic;
|
|
#endif
|
|
|
|
switch (shaderType) {
|
|
case LineShaderType::Classic: {
|
|
// build classic polyline with Tile coordinates
|
|
if (!updateBuilder(BuilderType::LineClassic, "custom-lines", lineShaderDefault())) {
|
|
return util::SimpleIdentity::Empty;
|
|
}
|
|
|
|
builder->addPolyline(coordinates, lineOptions.geometry);
|
|
} break;
|
|
|
|
case LineShaderType::WideVector: {
|
|
// build wide vector polyline
|
|
if (!updateBuilder(BuilderType::LineWideVector, "custom-lines-widevector", lineShaderWideVector()))
|
|
return util::SimpleIdentity::Empty;
|
|
|
|
builder->addWideVectorPolylineLocal(coordinates, lineOptions.geometry);
|
|
} break;
|
|
}
|
|
|
|
return builder->getCurrentDrawable(true)->getID();
|
|
}
|
|
|
|
util::SimpleIdentity CustomDrawableLayerHost::Interface::addFill(const GeometryCollection& geometry) {
|
|
// build fill
|
|
if (!updateBuilder(BuilderType::Fill, "custom-fill", fillShaderDefault())) {
|
|
return util::SimpleIdentity::Empty;
|
|
}
|
|
|
|
// 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 triangleSegments;
|
|
|
|
// generate fill geometry into buffers
|
|
gfx::generateFillBuffers(geometry, vertices, triangles, triangleSegments);
|
|
|
|
// add to builder
|
|
auto attrs = context.createVertexAttributeArray();
|
|
if (const auto& attr = attrs->set(idFillPosVertexAttribute)) {
|
|
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());
|
|
|
|
const auto& id = builder->getCurrentDrawable(true)->getID();
|
|
|
|
// flush current builder drawable
|
|
builder->flush(context);
|
|
|
|
return id;
|
|
}
|
|
|
|
util::SimpleIdentity CustomDrawableLayerHost::Interface::addSymbol(
|
|
const GeometryCoordinate& point, const std::array<std::array<float, 2>, 2>& textureCoordinates) {
|
|
// build symbol
|
|
if (!updateBuilder(BuilderType::Symbol, "custom-symbol", symbolShaderDefault())) {
|
|
return util::SimpleIdentity::Empty;
|
|
}
|
|
|
|
// temporary: buffers
|
|
struct CustomSymbolIcon {
|
|
std::array<float, 2> a_pos;
|
|
std::array<float, 2> a_tex;
|
|
};
|
|
|
|
// vertices
|
|
using VertexVector = gfx::VertexVector<CustomSymbolIcon>;
|
|
const std::shared_ptr<VertexVector> sharedVertices = std::make_shared<VertexVector>();
|
|
VertexVector& vertices = *sharedVertices;
|
|
|
|
// encode center and extrude direction into vertices
|
|
for (int y = 0; y <= 1; ++y) {
|
|
for (int x = 0; x <= 1; ++x) {
|
|
vertices.emplace_back(
|
|
CustomSymbolIcon{.a_pos = {static_cast<float>(point.x * 2 + x), static_cast<float>(point.y * 2 + y)},
|
|
.a_tex = {textureCoordinates[x][0], textureCoordinates[y][1]}});
|
|
}
|
|
}
|
|
|
|
// indexes
|
|
using TriangleIndexVector = gfx::IndexVector<gfx::Triangles>;
|
|
const std::shared_ptr<TriangleIndexVector> sharedTriangles = std::make_shared<TriangleIndexVector>();
|
|
TriangleIndexVector& triangles = *sharedTriangles;
|
|
|
|
triangles.emplace_back(0, 1, 2, 1, 2, 3);
|
|
|
|
SegmentVector triangleSegments;
|
|
triangleSegments.emplace_back(0, 0, 4, 6);
|
|
|
|
// add to builder
|
|
auto attrs = context.createVertexAttributeArray();
|
|
if (const auto& attr = attrs->set(idCustomSymbolPosVertexAttribute)) {
|
|
attr->setSharedRawData(sharedVertices,
|
|
offsetof(CustomSymbolIcon, a_pos),
|
|
/*vertexOffset=*/0,
|
|
sizeof(CustomSymbolIcon),
|
|
gfx::AttributeDataType::Float2);
|
|
}
|
|
if (const auto& attr = attrs->set(idCustomSymbolTexVertexAttribute)) {
|
|
attr->setSharedRawData(sharedVertices,
|
|
offsetof(CustomSymbolIcon, a_tex),
|
|
/*vertexOffset=*/0,
|
|
sizeof(CustomSymbolIcon),
|
|
gfx::AttributeDataType::Float2);
|
|
}
|
|
builder->setVertexAttributes(std::move(attrs));
|
|
builder->setRawVertices({}, vertices.elements(), gfx::AttributeDataType::Float2);
|
|
builder->setSegments(gfx::Triangles(), sharedTriangles, triangleSegments.data(), triangleSegments.size());
|
|
|
|
// texture
|
|
if (symbolOptions.texture) {
|
|
builder->setTexture(symbolOptions.texture, idCustomSymbolImageTexture);
|
|
}
|
|
|
|
// create symbol tweaker
|
|
auto tweaker = std::make_shared<SymbolDrawableTweaker>(symbolOptions, std::move(symbolTweakerCallback));
|
|
builder->addTweaker(tweaker);
|
|
|
|
const auto& id = builder->getCurrentDrawable(true)->getID();
|
|
|
|
// flush current builder drawable
|
|
builder->flush(context);
|
|
|
|
return id;
|
|
}
|
|
|
|
util::SimpleIdentity CustomDrawableLayerHost::Interface::addGeometry(
|
|
std::shared_ptr<gfx::VertexVector<GeometryVertex>> vertices,
|
|
std::shared_ptr<gfx::IndexVector<gfx::Triangles>> indices,
|
|
bool is3D) {
|
|
if (!vertices || !indices) {
|
|
return util::SimpleIdentity::Empty;
|
|
}
|
|
|
|
if (!updateBuilder(BuilderType::Geometry, "custom-geometry", geometryShaderDefault())) {
|
|
return util::SimpleIdentity::Empty;
|
|
}
|
|
|
|
// geographic coordinates require tile {0, 0, 0}
|
|
setTileID({0, 0, 0});
|
|
|
|
SegmentVector triangleSegments;
|
|
triangleSegments.emplace_back(0, 0, vertices->elements(), indices->elements());
|
|
|
|
// add to builder
|
|
auto attrs = context.createVertexAttributeArray();
|
|
if (const auto& attr = attrs->set(idCustomGeometryPosVertexAttribute)) {
|
|
attr->setSharedRawData(vertices,
|
|
offsetof(GeometryVertex, position),
|
|
/*vertexOffset=*/0,
|
|
sizeof(GeometryVertex),
|
|
gfx::AttributeDataType::Float3);
|
|
}
|
|
|
|
if (const auto& attr = attrs->set(idCustomGeometryTexVertexAttribute)) {
|
|
attr->setSharedRawData(vertices,
|
|
offsetof(GeometryVertex, texcoords),
|
|
/*vertexOffset=*/0,
|
|
sizeof(GeometryVertex),
|
|
gfx::AttributeDataType::Float2);
|
|
}
|
|
|
|
builder->setVertexAttributes(std::move(attrs));
|
|
builder->setRawVertices({}, vertices->elements(), gfx::AttributeDataType::Float3);
|
|
builder->setSegments(gfx::Triangles(), indices, triangleSegments.data(), triangleSegments.size());
|
|
|
|
builder->setEnableDepth(true);
|
|
|
|
if (is3D) {
|
|
builder->setDepthType(gfx::DepthMaskType::ReadWrite);
|
|
builder->setIs3D(true);
|
|
}
|
|
|
|
// white texture
|
|
if (!geometryOptions.texture) {
|
|
auto image = std::make_shared<PremultipliedImage>(mbgl::Size(2, 2));
|
|
image->fill(255);
|
|
|
|
geometryOptions.texture = context.createTexture2D();
|
|
geometryOptions.texture->setImage(std::move(image));
|
|
}
|
|
|
|
builder->setTexture(geometryOptions.texture, shaders::idCustomGeometryTexture);
|
|
|
|
builder->addTweaker(std::make_shared<GeometryDrawableTweaker>(geometryOptions, std::move(geometryTweakerCallback)));
|
|
|
|
const auto& id = builder->getCurrentDrawable(true)->getID();
|
|
|
|
// flush current builder drawable
|
|
builder->flush(context);
|
|
|
|
return id;
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::finish() {
|
|
if (builder && !builder->empty()) {
|
|
// flush current builder drawable
|
|
builder->flush(context);
|
|
|
|
// finish function
|
|
const auto finish_ = [this](gfx::DrawableTweakerPtr tweaker) {
|
|
builder->flush(context);
|
|
for (auto& drawable : builder->clearDrawables()) {
|
|
assert(tileID.has_value());
|
|
drawable->setTileID(tileID.value());
|
|
if (tweaker) {
|
|
drawable->addTweaker(tweaker);
|
|
}
|
|
|
|
TileLayerGroup* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
|
|
tileLayerGroup->addDrawable(RenderPass::Translucent, tileID.value(), std::move(drawable));
|
|
}
|
|
};
|
|
|
|
// what were we building?
|
|
switch (builderType) {
|
|
case BuilderType::LineClassic: {
|
|
// finish building classic lines
|
|
|
|
// create line tweaker
|
|
auto tweaker = std::make_shared<LineDrawableTweaker>(lineOptions, std::move(lineTweakerCallback));
|
|
|
|
// finish drawables
|
|
finish_(tweaker);
|
|
} break;
|
|
case BuilderType::LineWideVector: {
|
|
// finish building wide vector lines
|
|
|
|
// create line tweaker
|
|
auto tweaker = std::make_shared<WideVectorDrawableTweaker>(lineOptions, std::move(lineTweakerCallback));
|
|
|
|
// finish drawables
|
|
finish_(tweaker);
|
|
} break;
|
|
case BuilderType::Fill: {
|
|
// finish building fills
|
|
|
|
// create fill tweaker
|
|
auto tweaker = std::make_shared<FillDrawableTweaker>(fillOptions, std::move(fillTweakerCallback));
|
|
|
|
// finish drawables
|
|
finish_(tweaker);
|
|
} break;
|
|
case BuilderType::Symbol:
|
|
// finish building symbols
|
|
finish_(nullptr);
|
|
break;
|
|
|
|
case BuilderType::Geometry:
|
|
finish_(nullptr);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CustomDrawableLayerHost::Interface::removeDrawable(const util::SimpleIdentity& id) {
|
|
TileLayerGroup* tileLayerGroup = static_cast<TileLayerGroup*>(layerGroup.get());
|
|
tileLayerGroup->removeDrawablesIf([&](gfx::Drawable& drawable) { return drawable.getID() == id; });
|
|
}
|
|
|
|
gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderDefault() const {
|
|
gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("LineShader");
|
|
assert(shaderGroup);
|
|
if (!shaderGroup) return gfx::ShaderPtr();
|
|
|
|
const StringIDSetsPair propertiesAsUniforms{{"a_color", "a_blur", "a_opacity", "a_gapwidth", "a_offset", "a_width"},
|
|
{idLineColorVertexAttribute,
|
|
idLineBlurVertexAttribute,
|
|
idLineOpacityVertexAttribute,
|
|
idLineGapWidthVertexAttribute,
|
|
idLineOffsetVertexAttribute,
|
|
idLineWidthVertexAttribute}};
|
|
|
|
return shaderGroup->getOrCreateShader(context, propertiesAsUniforms);
|
|
}
|
|
|
|
gfx::ShaderPtr CustomDrawableLayerHost::Interface::lineShaderWideVector() const {
|
|
gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("WideVectorShader");
|
|
if (!shaderGroup) return gfx::ShaderPtr();
|
|
|
|
return shaderGroup->getOrCreateShader(context, {});
|
|
}
|
|
|
|
gfx::ShaderPtr CustomDrawableLayerHost::Interface::fillShaderDefault() const {
|
|
gfx::ShaderGroupPtr shaderGroup = shaders.getShaderGroup("FillShader");
|
|
|
|
const StringIDSetsPair propertiesAsUniforms{{"a_color", "a_opacity"},
|
|
{idFillColorVertexAttribute, idFillOpacityVertexAttribute}};
|
|
|
|
return shaderGroup->getOrCreateShader(context, propertiesAsUniforms);
|
|
}
|
|
|
|
gfx::ShaderPtr CustomDrawableLayerHost::Interface::symbolShaderDefault() const {
|
|
return context.getGenericShader(shaders, "CustomSymbolIconShader");
|
|
}
|
|
|
|
gfx::ShaderPtr CustomDrawableLayerHost::Interface::geometryShaderDefault() const {
|
|
return context.getGenericShader(shaders, "CustomGeometryShader");
|
|
}
|
|
|
|
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);
|
|
builder_->setShader(std::static_pointer_cast<gfx::ShaderProgramBase>(shader));
|
|
builder_->setSubLayerIndex(0);
|
|
builder_->setEnableDepth(false);
|
|
builder_->setColorMode(gfx::ColorMode::alphaBlended());
|
|
builder_->setCullFaceMode(gfx::CullFaceMode::disabled());
|
|
builder_->setRenderPass(RenderPass::Translucent);
|
|
|
|
return builder_;
|
|
}
|
|
|
|
} // namespace style
|
|
} // namespace mbgl
|