Files
MapLibreParticleLab/src/mbgl/renderer/buckets/fill_extrusion_bucket.cpp
Mike Taves fba652eb72 fix: typos (#4202)
Co-authored-by: Bart Louwers <bart@emeel.net>
2026-07-13 15:08:27 +00:00

242 lines
9.0 KiB
C++

#include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
#include <mbgl/renderer/bucket_parameters.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/constants.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
namespace mbgl {
using namespace style;
struct GeometryTooLongException : std::exception {};
FillExtrusionBucket::FillExtrusionBucket(
const FillExtrusionBucket::PossiblyEvaluatedLayoutProperties&,
const std::map<std::string, Immutable<style::LayerProperties>>& layerPaintProperties,
const float zoom,
const uint32_t) {
for (const auto& pair : layerPaintProperties) {
paintPropertyBinders.emplace(
std::piecewise_construct,
std::forward_as_tuple(pair.first),
std::forward_as_tuple(getEvaluated<FillExtrusionLayerProperties>(pair.second), zoom));
}
}
FillExtrusionBucket::~FillExtrusionBucket() {
sharedVertices->release();
}
void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
const GeometryCollection& geometry,
const ImagePositions& patternPositions,
const PatternLayerMap& patternDependencies,
std::size_t index,
const CanonicalTileID& canonical) {
for (auto& polygon : classifyRings(geometry)) {
// Optimize polygons with many interior rings for earcut tessellation.
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();
}
if (totalVertices == 0) continue;
std::vector<uint32_t> flatIndices;
flatIndices.reserve(totalVertices);
std::size_t startVertices = vertices.elements();
if (triangleSegments.empty() || triangleSegments.back().vertexLength + (5 * (totalVertices - 1) + 1) >
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());
auto triangleIndex = static_cast<uint16_t>(triangleSegment.vertexLength);
assert(triangleIndex + (5 * (totalVertices - 1) + 1) <= std::numeric_limits<uint16_t>::max());
for (const auto& ring : polygon) {
std::size_t nVertices = ring.size();
if (nVertices == 0) continue;
std::size_t edgeDistance = 0;
for (std::size_t i = 0; i < nVertices; i++) {
const auto& p1 = ring[i];
#if MLN_USE_FILL_EXTRUSION_INSTANCING
vertices.emplace_back(layoutVertex(p1, edgeDistance, i == nVertices - 1));
flatIndices.emplace_back(triangleIndex);
triangleIndex++;
if (i < nVertices - 1) {
const auto& p2 = ring[i + 1];
const auto d1 = convertPoint<double>(p1);
const auto d2 = convertPoint<double>(p2);
const size_t dist = util::dist<uint16_t>(d1, d2);
if (edgeDistance + dist > static_cast<size_t>(std::numeric_limits<uint16_t>::max())) {
edgeDistance = 0;
}
edgeDistance += dist;
}
#else
vertices.emplace_back(
FillExtrusionBucket::layoutVertex(p1, 0, 0, 1, 1, static_cast<uint16_t>(edgeDistance)));
flatIndices.emplace_back(triangleIndex);
triangleIndex++;
if (i != 0) {
const auto& p2 = ring[i - 1];
const auto d1 = convertPoint<double>(p1);
const auto d2 = convertPoint<double>(p2);
const Point<double> perp = util::unit(util::perp(d1 - d2));
const size_t dist = util::dist<int16_t>(d1, d2);
if (edgeDistance + dist > static_cast<size_t>(std::numeric_limits<int16_t>::max())) {
edgeDistance = 0;
}
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p1, perp.x, perp.y, 0, 0, static_cast<uint16_t>(edgeDistance)));
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p1, perp.x, perp.y, 0, 1, static_cast<uint16_t>(edgeDistance)));
edgeDistance += dist;
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p2, perp.x, perp.y, 0, 0, static_cast<uint16_t>(edgeDistance)));
vertices.emplace_back(FillExtrusionBucket::layoutVertex(
p2, perp.x, perp.y, 0, 1, static_cast<uint16_t>(edgeDistance)));
// ┌──────┐
// │ 0 1 │ Counter-Clockwise winding order.
// │ │ Triangle 1: 0 => 2 => 1
// │ 2 3 │ Triangle 2: 1 => 2 => 3
// └──────┘
triangles.emplace_back(triangleIndex, triangleIndex + 2, triangleIndex + 1);
triangles.emplace_back(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
triangleIndex += 4;
triangleSegment.vertexLength += 4;
triangleSegment.indexLength += 6;
}
#endif
}
}
std::vector<uint32_t> indices = mapbox::earcut(polygon);
std::size_t nIndices = indices.size();
assert(nIndices % 3 == 0);
for (std::size_t i = 0; i < nIndices; i += 3) {
// Counter-Clockwise winding order.
triangles.emplace_back(static_cast<uint16_t>(flatIndices[indices[i]]),
static_cast<uint16_t>(flatIndices[indices[i + 2]]),
static_cast<uint16_t>(flatIndices[indices[i + 1]]));
}
triangleSegment.vertexLength += totalVertices;
triangleSegment.indexLength += nIndices;
}
for (auto& pair : paintPropertyBinders) {
const auto it = patternDependencies.find(pair.first);
if (it != patternDependencies.end()) {
pair.second.populateVertexVectors(
feature, vertices.elements(), index, patternPositions, it->second, canonical);
} else {
pair.second.populateVertexVectors(feature, vertices.elements(), index, patternPositions, {}, canonical);
}
}
}
void FillExtrusionBucket::upload([[maybe_unused]] gfx::UploadPass& uploadPass) {
uploaded = true;
}
bool FillExtrusionBucket::hasData() const {
return !triangleSegments.empty();
}
float FillExtrusionBucket::getQueryRadius(const RenderLayer& layer) const {
const auto& evaluated = getEvaluated<FillExtrusionLayerProperties>(layer.evaluatedProperties);
const std::array<float, 2>& translate = evaluated.get<FillExtrusionTranslate>();
return util::length(translate[0], translate[1]);
}
void FillExtrusionBucket::update(const FeatureStates& states,
const GeometryTileLayer& layer,
const std::string& layerID,
const ImagePositions& imagePositions) {
auto it = paintPropertyBinders.find(layerID);
if (it != paintPropertyBinders.end()) {
it->second.updateVertexVectors(states, layer, imagePositions);
uploaded = false;
sharedVertices->updateModified();
}
}
std::array<float, 3> FillExtrusionBucket::lightColor(const EvaluatedLight& light) {
const auto color = light.get<LightColor>();
return {{color.r, color.g, color.b}};
}
std::array<float, 3> FillExtrusionBucket::lightPosition(const EvaluatedLight& light, const TransformState& state) {
auto lightPos = light.get<LightPosition>().getCartesian();
mat3 lightMat;
matrix::identity(lightMat);
if (light.get<LightAnchor>() == LightAnchorType::Viewport) {
matrix::rotate(lightMat, lightMat, -state.getBearing());
}
matrix::transformMat3f(lightPos, lightPos, lightMat);
return lightPos;
}
float FillExtrusionBucket::lightIntensity(const EvaluatedLight& light) {
return light.get<LightIntensity>();
}
} // namespace mbgl