Files
MapLibreParticleLab/src/renderer/text_bucket.cpp
2014-05-30 14:01:53 +02:00

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;
}
}