1 #include <mbgl/renderer/buckets/fill_extrusion_bucket.hpp>
2 #include <mbgl/programs/fill_extrusion_program.hpp>
3 #include <mbgl/renderer/bucket_parameters.hpp>
4 #include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
5 #include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
6 #include <mbgl/util/math.hpp>
7 #include <mbgl/util/constants.hpp>
8
9 #include <mapbox/earcut.hpp>
10
11 #include <cassert>
12
13 namespace mapbox {
14 namespace util {
15 template <>
16 struct nth<0, mbgl::GeometryCoordinate> {
getmapbox::util::nth17 static int64_t get(const mbgl::GeometryCoordinate& t) {
18 return t.x;
19 };
20 };
21
22 template <>
23 struct nth<1, mbgl::GeometryCoordinate> {
getmapbox::util::nth24 static int64_t get(const mbgl::GeometryCoordinate& t) {
25 return t.y;
26 };
27 };
28 } // namespace util
29 } // namespace mapbox
30
31 namespace mbgl {
32
33 using namespace style;
34
35 struct GeometryTooLongException : std::exception {};
36
FillExtrusionBucket(const BucketParameters & parameters,const std::vector<const RenderLayer * > & layers)37 FillExtrusionBucket::FillExtrusionBucket(const BucketParameters& parameters, const std::vector<const RenderLayer*>& layers)
38 : Bucket(LayerType::FillExtrusion) {
39 for (const auto& layer : layers) {
40 paintPropertyBinders.emplace(std::piecewise_construct,
41 std::forward_as_tuple(layer->getID()),
42 std::forward_as_tuple(
43 layer->as<RenderFillExtrusionLayer>()->evaluated,
44 parameters.tileID.overscaledZ));
45 }
46 }
47
addFeature(const GeometryTileFeature & feature,const GeometryCollection & geometry)48 void FillExtrusionBucket::addFeature(const GeometryTileFeature& feature,
49 const GeometryCollection& geometry) {
50 for (auto& polygon : classifyRings(geometry)) {
51 // Optimize polygons with many interior rings for earcut tesselation.
52 limitHoles(polygon, 500);
53
54 std::size_t totalVertices = 0;
55
56 for (const auto& ring : polygon) {
57 totalVertices += ring.size();
58 if (totalVertices > std::numeric_limits<uint16_t>::max())
59 throw GeometryTooLongException();
60 }
61
62 if (totalVertices == 0) continue;
63
64 std::vector<uint32_t> flatIndices;
65 flatIndices.reserve(totalVertices);
66
67 std::size_t startVertices = vertices.vertexSize();
68
69 if (triangleSegments.empty() ||
70 triangleSegments.back().vertexLength + (5 * (totalVertices - 1) + 1) >
71 std::numeric_limits<uint16_t>::max()) {
72 triangleSegments.emplace_back(startVertices, triangles.indexSize());
73 }
74
75 auto& triangleSegment = triangleSegments.back();
76 assert(triangleSegment.vertexLength <= std::numeric_limits<uint16_t>::max());
77 uint16_t triangleIndex = triangleSegment.vertexLength;
78
79 assert(triangleIndex + (5 * (totalVertices - 1) + 1) <=
80 std::numeric_limits<uint16_t>::max());
81
82 for (const auto& ring : polygon) {
83 std::size_t nVertices = ring.size();
84
85 if (nVertices == 0)
86 continue;
87
88 std::size_t edgeDistance = 0;
89
90 for (uint32_t i = 0; i < nVertices; i++) {
91 const auto& p1 = ring[i];
92
93 vertices.emplace_back(
94 FillExtrusionProgram::layoutVertex(p1, 0, 0, 1, 1, edgeDistance));
95 flatIndices.emplace_back(triangleIndex);
96 triangleIndex++;
97
98 if (i != 0) {
99 const auto& p2 = ring[i - 1];
100
101 const auto d1 = convertPoint<double>(p1);
102 const auto d2 = convertPoint<double>(p2);
103
104 const Point<double> perp = util::unit(util::perp(d1 - d2));
105 const auto dist = util::dist<int16_t>(d1, d2);
106 if (edgeDistance + dist > std::numeric_limits<int16_t>::max()) {
107 edgeDistance = 0;
108 }
109
110 vertices.emplace_back(
111 FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 0, edgeDistance));
112 vertices.emplace_back(
113 FillExtrusionProgram::layoutVertex(p1, perp.x, perp.y, 0, 1, edgeDistance));
114
115 edgeDistance += dist;
116
117 vertices.emplace_back(
118 FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 0, edgeDistance));
119 vertices.emplace_back(
120 FillExtrusionProgram::layoutVertex(p2, perp.x, perp.y, 0, 1, edgeDistance));
121
122 triangles.emplace_back(triangleIndex, triangleIndex + 1, triangleIndex + 2);
123 triangles.emplace_back(triangleIndex + 1, triangleIndex + 2, triangleIndex + 3);
124 triangleIndex += 4;
125 triangleSegment.vertexLength += 4;
126 triangleSegment.indexLength += 6;
127 }
128 }
129 }
130
131 std::vector<uint32_t> indices = mapbox::earcut(polygon);
132
133 std::size_t nIndices = indices.size();
134 assert(nIndices % 3 == 0);
135
136 for (uint32_t i = 0; i < nIndices; i += 3) {
137 triangles.emplace_back(flatIndices[indices[i]], flatIndices[indices[i + 1]],
138 flatIndices[indices[i + 2]]);
139 }
140
141 triangleSegment.vertexLength += totalVertices;
142 triangleSegment.indexLength += nIndices;
143 }
144
145 for (auto& pair : paintPropertyBinders) {
146 pair.second.populateVertexVectors(feature, vertices.vertexSize());
147 }
148 }
149
upload(gl::Context & context)150 void FillExtrusionBucket::upload(gl::Context& context) {
151 vertexBuffer = context.createVertexBuffer(std::move(vertices));
152 indexBuffer = context.createIndexBuffer(std::move(triangles));
153
154 for (auto& pair : paintPropertyBinders) {
155 pair.second.upload(context);
156 }
157
158 uploaded = true;
159 }
160
hasData() const161 bool FillExtrusionBucket::hasData() const {
162 return !triangleSegments.empty();
163 }
164
getQueryRadius(const RenderLayer & layer) const165 float FillExtrusionBucket::getQueryRadius(const RenderLayer& layer) const {
166 if (!layer.is<RenderFillExtrusionLayer>()) {
167 return 0;
168 }
169
170 const std::array<float, 2>& translate = layer.as<RenderFillExtrusionLayer>()->evaluated.get<FillExtrusionTranslate>();
171 return util::length(translate[0], translate[1]);
172 }
173
174 } // namespace mbgl
175