diff options
Diffstat (limited to 'src/mbgl/renderer/fill_bucket.cpp')
-rw-r--r-- | src/mbgl/renderer/fill_bucket.cpp | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/mbgl/renderer/fill_bucket.cpp b/src/mbgl/renderer/fill_bucket.cpp new file mode 100644 index 0000000000..0a7d77935d --- /dev/null +++ b/src/mbgl/renderer/fill_bucket.cpp @@ -0,0 +1,246 @@ +#include <mbgl/renderer/fill_bucket.hpp> +#include <mbgl/geometry/fill_buffer.hpp> +#include <mbgl/geometry/elements_buffer.hpp> +#include <mbgl/geometry/geometry.hpp> + +#include <mbgl/renderer/painter.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/map/vector_tile.hpp> + +#include <mbgl/platform/gl.hpp> + + +#include <cassert> + +struct geometry_too_long_exception : std::exception {}; + +using namespace mbgl; + + + +void *FillBucket::alloc(void *, unsigned int size) { + return ::malloc(size); +} + +void *FillBucket::realloc(void *, void *ptr, unsigned int size) { + return ::realloc(ptr, size); +} + +void FillBucket::free(void *, void *ptr) { + ::free(ptr); +} + +FillBucket::FillBucket(FillVertexBuffer &vertexBuffer_, + TriangleElementsBuffer &triangleElementsBuffer_, + LineElementsBuffer &lineElementsBuffer_, + const StyleBucketFill &properties_) + : properties(properties_), + allocator(new TESSalloc{&alloc, &realloc, &free, nullptr, // userData + 64, // meshEdgeBucketSize + 64, // meshVertexBucketSize + 32, // meshFaceBucketSize + 64, // dictNodeBucketSize + 8, // regionBucketSize + 128, // extraVertices allocated for the priority queue. + }), + tesselator(tessNewTess(allocator)), + vertexBuffer(vertexBuffer_), + triangleElementsBuffer(triangleElementsBuffer_), + lineElementsBuffer(lineElementsBuffer_), + vertex_start(vertexBuffer_.index()), + triangle_elements_start(triangleElementsBuffer_.index()), + line_elements_start(lineElementsBuffer.index()) { + assert(tesselator); +} + +FillBucket::~FillBucket() { + if (tesselator) { + tessDeleteTess(tesselator); + } + if (allocator) { + delete allocator; + } +} + +void FillBucket::addGeometry(pbf& geom) { + Geometry::command cmd; + + Coordinate coord; + Geometry geometry(geom); + int32_t x, y; + while ((cmd = geometry.next(x, y)) != Geometry::end) { + if (cmd == Geometry::move_to) { + if (line.size()) { + clipper.AddPath(line, ClipperLib::ptSubject, true); + line.clear(); + hasVertices = true; + } + } + line.emplace_back(x, y); + } + + if (line.size()) { + clipper.AddPath(line, ClipperLib::ptSubject, true); + line.clear(); + hasVertices = true; + } + + tessellate(); +} + +void FillBucket::tessellate() { + if (!hasVertices) { + return; + } + hasVertices = false; + + std::vector<std::vector<ClipperLib::IntPoint>> polygons; + clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftPositive); + clipper.Clear(); + + if (polygons.size() == 0) { + return; + } + + size_t total_vertex_count = 0; + for (const std::vector<ClipperLib::IntPoint>& polygon : polygons) { + total_vertex_count += polygon.size(); + } + + if (total_vertex_count > 65536) { + throw geometry_too_long_exception(); + } + + if (!lineGroups.size() || (lineGroups.back().vertex_length + total_vertex_count > 65535)) { + // Move to a new group because the old one can't hold the geometry. + lineGroups.emplace_back(); + } + + line_group_type& lineGroup = lineGroups.back(); + uint32_t lineIndex = lineGroup.vertex_length; + + for (const std::vector<ClipperLib::IntPoint>& polygon : polygons) { + const size_t group_count = polygon.size(); + assert(group_count >= 3); + + std::vector<TESSreal> clipped_line; + for (const ClipperLib::IntPoint& pt : polygon) { + clipped_line.push_back(pt.X); + clipped_line.push_back(pt.Y); + vertexBuffer.add(pt.X, pt.Y); + } + + for (size_t i = 0; i < group_count; i++) { + const size_t prev_i = (i == 0 ? group_count : i) - 1; + lineElementsBuffer.add(lineIndex + prev_i, lineIndex + i); + } + + lineIndex += group_count; + + tessAddContour(tesselator, vertexSize, clipped_line.data(), stride, (int)clipped_line.size() / vertexSize); + } + + lineGroup.elements_length += total_vertex_count; + + if (tessTesselate(tesselator, TESS_WINDING_POSITIVE, TESS_POLYGONS, vertices_per_group, vertexSize, 0)) { + const TESSreal *vertices = tessGetVertices(tesselator); + const size_t vertex_count = tessGetVertexCount(tesselator); + TESSindex *vertex_indices = const_cast<TESSindex *>(tessGetVertexIndices(tesselator)); + const TESSindex *elements = tessGetElements(tesselator); + const int triangle_count = tessGetElementCount(tesselator); + + for (size_t i = 0; i < vertex_count; ++i) { + if (vertex_indices[i] == TESS_UNDEF) { + vertexBuffer.add(std::round(vertices[i * 2]), std::round(vertices[i * 2 + 1])); + vertex_indices[i] = (TESSindex)total_vertex_count; + total_vertex_count++; + } + } + + if (!triangleGroups.size() || (triangleGroups.back().vertex_length + total_vertex_count > 65535)) { + // Move to a new group because the old one can't hold the geometry. + triangleGroups.emplace_back(); + } + + // We're generating triangle fans, so we always start with the first + // coordinate in this polygon. + triangle_group_type& triangleGroup = triangleGroups.back(); + uint32_t triangleIndex = triangleGroup.vertex_length; + + for (int i = 0; i < triangle_count; ++i) { + const TESSindex *element_group = &elements[i * vertices_per_group]; + + if (element_group[0] != TESS_UNDEF && element_group[1] != TESS_UNDEF && element_group[2] != TESS_UNDEF) { + const TESSindex a = vertex_indices[element_group[0]]; + const TESSindex b = vertex_indices[element_group[1]]; + const TESSindex c = vertex_indices[element_group[2]]; + + if (a != TESS_UNDEF && b != TESS_UNDEF && c != TESS_UNDEF) { + triangleElementsBuffer.add(triangleIndex + a, triangleIndex + b, triangleIndex + c); + } else { +#if defined(DEBUG) + // TODO: We're missing a vertex that was not part of the line. + fprintf(stderr, "undefined element buffer\n"); +#endif + } + } else { +#if defined(DEBUG) + fprintf(stderr, "undefined element buffer\n"); +#endif + } + } + + triangleGroup.vertex_length += total_vertex_count; + triangleGroup.elements_length += triangle_count; + } else { +#if defined(DEBUG) + fprintf(stderr, "tessellation failed\n"); +#endif + } + + // We're adding the total vertex count *after* we added additional vertices + // in the tessellation step. They won't be part of the actual lines, but + // we need to skip over them anyway if we draw the next group. + lineGroup.vertex_length += total_vertex_count; +} + +void FillBucket::render(Painter& painter, util::ptr<StyleLayer> layer_desc, const Tile::ID& id, const mat4 &matrix) { + painter.renderFill(*this, layer_desc, id, matrix); +} + +bool FillBucket::hasData() const { + return !triangleGroups.empty() || !lineGroups.empty(); +} + +void FillBucket::drawElements(PlainShader& shader) { + char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); + char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); + for (triangle_group_type& group : triangleGroups) { + group.array[0].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * vertexBuffer.itemSize; + elements_index += group.elements_length * triangleElementsBuffer.itemSize; + } +} + +void FillBucket::drawElements(PatternShader& shader) { + char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); + char *elements_index = BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer.itemSize); + for (triangle_group_type& group : triangleGroups) { + group.array[1].bind(shader, vertexBuffer, triangleElementsBuffer, vertex_index); + glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * vertexBuffer.itemSize; + elements_index += group.elements_length * triangleElementsBuffer.itemSize; + } +} + +void FillBucket::drawVertices(OutlineShader& shader) { + char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer.itemSize); + char *elements_index = BUFFER_OFFSET(line_elements_start * lineElementsBuffer.itemSize); + for (line_group_type& group : lineGroups) { + group.array[0].bind(shader, vertexBuffer, lineElementsBuffer, vertex_index); + glDrawElements(GL_LINES, group.elements_length * 2, GL_UNSIGNED_SHORT, elements_index); + vertex_index += group.vertex_length * vertexBuffer.itemSize; + elements_index += group.elements_length * lineElementsBuffer.itemSize; + } +} |