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