147 lines
5.3 KiB
C++
147 lines
5.3 KiB
C++
#include <llmr/renderer/text_bucket.hpp>
|
|
#include <llmr/geometry/text_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/text/placement.hpp>
|
|
#include <llmr/text/glyph_store.hpp>
|
|
#include <llmr/util/constants.hpp>
|
|
|
|
#include <llmr/util/math.hpp>
|
|
#include <llmr/platform/gl.hpp>
|
|
|
|
#include <iostream>
|
|
|
|
#include <cassert>
|
|
|
|
using namespace llmr;
|
|
|
|
TextBucket::TextBucket(
|
|
TextVertexBuffer &vertexBuffer,
|
|
TriangleElementsBuffer &triangleElementsBuffer,
|
|
const BucketDescription &bucket_desc, Placement &placement)
|
|
: geom_desc(bucket_desc.geometry),
|
|
vertexBuffer(vertexBuffer),
|
|
triangleElementsBuffer(triangleElementsBuffer),
|
|
placement(placement),
|
|
vertex_start(vertexBuffer.index()),
|
|
triangle_elements_start(triangleElementsBuffer.index()) {}
|
|
|
|
void TextBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
|
|
PlacementRange placementRange, float zoom) {
|
|
placementZoom += zoom;
|
|
|
|
for (const PlacedGlyph &glyph : glyphs) {
|
|
const auto &tl = glyph.tl;
|
|
const auto &tr = glyph.tr;
|
|
const auto &bl = glyph.bl;
|
|
const auto &br = glyph.br;
|
|
const auto &tex = glyph.tex;
|
|
const auto &angle = glyph.angle;
|
|
|
|
float minZoom = util::max(
|
|
static_cast<float>(zoom + log(glyph.glyphBox.minScale) / log(2)),
|
|
placementZoom);
|
|
float maxZoom = util::min(
|
|
static_cast<float>(zoom + log(glyph.glyphBox.maxScale) / log(2)),
|
|
25.0f);
|
|
const auto &glyphAnchor = glyph.glyphBox.anchor;
|
|
|
|
if (maxZoom <= minZoom)
|
|
continue;
|
|
|
|
// Lower min zoom so that while fading out the label
|
|
// it can be shown outside of collision-free zoom levels
|
|
if (minZoom == placementZoom) {
|
|
minZoom = 0;
|
|
}
|
|
|
|
const int glyph_vertex_length = 4;
|
|
|
|
if (!triangleGroups.size() ||
|
|
(triangleGroups.back().vertex_length + glyph_vertex_length >
|
|
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;
|
|
|
|
// coordinates (2 triangles)
|
|
vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x,
|
|
tex.y, angle, minZoom, placementRange, maxZoom,
|
|
placementZoom);
|
|
vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y,
|
|
tex.x + tex.w, tex.y, angle, minZoom, placementRange,
|
|
maxZoom, placementZoom);
|
|
vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x,
|
|
tex.y + tex.h, angle, minZoom, placementRange,
|
|
maxZoom, placementZoom);
|
|
vertexBuffer.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y,
|
|
tex.x + tex.w, tex.y + tex.h, angle, minZoom,
|
|
placementRange, maxZoom, placementZoom);
|
|
|
|
// add the two triangles, referencing the four coordinates we just
|
|
// inserted.
|
|
triangleElementsBuffer.add(triangleIndex + 0, triangleIndex + 1,
|
|
triangleIndex + 2);
|
|
triangleElementsBuffer.add(triangleIndex + 1, triangleIndex + 2,
|
|
triangleIndex + 3);
|
|
|
|
triangleGroup.vertex_length += glyph_vertex_length;
|
|
triangleGroup.elements_length += 2;
|
|
}
|
|
};
|
|
|
|
void TextBucket::addFeature(const pbf &geom_pbf,
|
|
const GlyphPositions &face,
|
|
const Shaping &shaping) {
|
|
// Decode all lines.
|
|
std::vector<Coordinate> line;
|
|
Geometry::command cmd;
|
|
|
|
Coordinate coord;
|
|
pbf geom(geom_pbf);
|
|
Geometry geometry(geom);
|
|
int32_t x, y;
|
|
while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
|
if (cmd == Geometry::move_to) {
|
|
if (!line.empty()) {
|
|
placement.addFeature(*this, line, geom_desc, face, shaping);
|
|
line.clear();
|
|
}
|
|
}
|
|
line.emplace_back(x, y);
|
|
}
|
|
if (line.size()) {
|
|
placement.addFeature(*this, line, geom_desc, face, shaping);
|
|
}
|
|
}
|
|
|
|
void TextBucket::render(Painter &painter, const std::string &layer_name,
|
|
const Tile::ID &id) {
|
|
painter.renderText(*this, layer_name, id);
|
|
}
|
|
|
|
bool TextBucket::hasData() const {
|
|
return !triangleGroups.empty();
|
|
}
|
|
|
|
void TextBucket::drawGlyphs(TextShader &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;
|
|
}
|
|
}
|