summaryrefslogtreecommitdiff
path: root/src/renderer/fill_bucket.cpp
blob: d505fa807965e33fa476e4707b4548b6a6d8c7ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <llmr/renderer/fill_bucket.hpp>
#include <llmr/geometry/fill_buffer.hpp>
#include <llmr/geometry/elements_buffer.hpp>
#include <llmr/geometry/geometry.hpp>

#include <llmr/renderer/painter.hpp>
#include <llmr/style/style.hpp>
#include <llmr/map/vector_tile.hpp>

#include <llmr/platform/gl.hpp>

#include <cassert>

struct geometry_too_long_exception : std::exception {};

using namespace llmr;

FillBucket::FillBucket(const std::shared_ptr<FillVertexBuffer>& vertexBuffer,
                       const std::shared_ptr<TriangleElementsBuffer>& triangleElementsBuffer,
                       const std::shared_ptr<LineElementsBuffer>& lineElementsBuffer,
                       const BucketDescription& bucket_desc)
    : geom_desc(bucket_desc.geometry),
      vertexBuffer(vertexBuffer),
      triangleElementsBuffer(triangleElementsBuffer),
      lineElementsBuffer(lineElementsBuffer),
      vertex_start(vertexBuffer->index()),
      triangle_elements_start(triangleElementsBuffer->index()),
      line_elements_start(lineElementsBuffer->index()) {
}

void FillBucket::addGeometry(pbf& geom) {
    std::vector<Coordinate> line;
    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()) {
                addGeometry(line);
                line.clear();
            }
        }
        line.emplace_back(x, y);
    }
    if (line.size()) {
        addGeometry(line);
    }
}

void FillBucket::addGeometry(const std::vector<Coordinate>& line) {
    uint32_t vertex_start = vertexBuffer->index();
    // vertexBuffer->addDegenerate();
    for (const Coordinate& coord : line) {
        vertexBuffer->add(coord.x, coord.y);
    }

    size_t vertex_end = vertexBuffer->index();

    size_t vertex_count = vertex_end - vertex_start;
    if (vertex_count > 65536) {
        throw geometry_too_long_exception();
    }

    {
        if (!triangleGroups.size() || (triangleGroups.back().vertex_length + 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& group = triangleGroups.back();
        uint32_t index = group.vertex_length;
        for (size_t i = 2; i < vertex_count; ++i) {
            triangleElementsBuffer->add(index, index + i - 1, index + i);
        }

        group.vertex_length += vertex_count;
        group.elements_length += (vertex_count - 2);
    }

    {
        if (!lineGroups.size() || (lineGroups.back().vertex_length + vertex_count > 65535)) {
            // Move to a new group because the old one can't hold the geometry.
            lineGroups.emplace_back();
        }

        // We're generating triangle fans, so we always start with the first
        // coordinate in this polygon.
        line_group_type& group = lineGroups.back();
        uint32_t index = group.vertex_length;
        for (size_t i = 1; i < vertex_count; ++i) {
            lineElementsBuffer->add(index + i - 1, index + i);
        }

        group.vertex_length += vertex_count;
        group.elements_length += (vertex_count - 1);
    }
}

void FillBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
    painter.renderFill(*this, layer_name, id);
}

bool FillBucket::empty() const {
    return triangleGroups.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.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.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;
    }
}