summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-08-04 12:59:22 +0200
committerKonstantin Käfer <mail@kkaefer.com>2014-08-04 12:59:22 +0200
commit9dd50a29e1e8b975e09172b19b303063a96ba20f (patch)
tree0f35c1d304aafcf465762484848d33e58342c391 /src
parent19e51e8bd89f0e627045b623a2a57b6702bca896 (diff)
downloadqtlocation-mapboxgl-9dd50a29e1e8b975e09172b19b303063a96ba20f.tar.gz
update shaders and backport some collision changes
Diffstat (limited to 'src')
-rw-r--r--src/geometry/icon_buffer.cpp33
-rw-r--r--src/renderer/symbol_bucket.cpp267
-rw-r--r--src/shader/icon.fragment.glsl11
-rw-r--r--src/shader/icon.vertex.glsl71
-rw-r--r--src/shader/icon_shader.cpp130
-rw-r--r--src/text/collision.cpp68
-rw-r--r--src/text/placement.cpp9
7 files changed, 434 insertions, 155 deletions
diff --git a/src/geometry/icon_buffer.cpp b/src/geometry/icon_buffer.cpp
index a25b2de068..c571dfa69e 100644
--- a/src/geometry/icon_buffer.cpp
+++ b/src/geometry/icon_buffer.cpp
@@ -1,18 +1,35 @@
#include <mbgl/geometry/icon_buffer.hpp>
#include <mbgl/platform/gl.hpp>
+#include <mbgl/util/math.hpp>
#include <cmath>
-using namespace mbgl;
+namespace mbgl {
-void IconVertexBuffer::add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty) {
+const double IconVertexBuffer::angleFactor = 128.0 / M_PI;
+
+size_t IconVertexBuffer::add(int16_t x, int16_t y, float ox, float oy, int16_t tx, int16_t ty, float angle, float minzoom, std::array<float, 2> range, float maxzoom, float labelminzoom) {
+ const size_t idx = index();
void *data = addElement();
- vertex_type *vertices = static_cast<vertex_type *>(data);
- vertices[0] = x;
- vertices[1] = y;
+ int16_t *shorts = static_cast<int16_t *>(data);
+ shorts[0] /* pos */ = x;
+ shorts[1] /* pos */ = y;
+ shorts[2] /* offset */ = std::round(ox * 64); // use 1/64 pixels for placement
+ shorts[3] /* offset */ = std::round(oy * 64);
+
+ uint8_t *ubytes = static_cast<uint8_t *>(data);
+ ubytes[8] /* labelminzoom */ = labelminzoom * 10;
+ ubytes[9] /* minzoom */ = minzoom * 10; // 1/10 zoom levels: z16 == 160.
+ ubytes[10] /* maxzoom */ = std::fmin(maxzoom, 25) * 10; // 1/10 zoom levels: z16 == 160.
+ ubytes[11] /* angle */ = (int16_t)std::round(angle * angleFactor) % 256;
+ ubytes[12] /* rangeend */ = util::max((int16_t)std::round(range[0] * angleFactor), (int16_t)0) % 256;
+ ubytes[13] /* rangestart */ = util::min((int16_t)std::round(range[1] * angleFactor), (int16_t)255) % 256;
+
+ shorts[8] /* tex */ = tx;
+ shorts[9] /* tex */ = ty;
+
+ return idx;
+}
- uint16_t *texture = static_cast<uint16_t *>(data);
- texture[2] = tx;
- texture[3] = ty;
}
diff --git a/src/renderer/symbol_bucket.cpp b/src/renderer/symbol_bucket.cpp
index f09f87da0a..03e5c91f26 100644
--- a/src/renderer/symbol_bucket.cpp
+++ b/src/renderer/symbol_bucket.cpp
@@ -8,6 +8,7 @@
#include <mbgl/text/glyph_store.hpp>
#include <mbgl/text/placement.hpp>
#include <mbgl/platform/log.hpp>
+#include <mbgl/map/sprite.hpp>
#include <mbgl/util/utf.hpp>
#include <mbgl/util/token.hpp>
@@ -20,13 +21,8 @@ SymbolBucket::SymbolBucket(TextVertexBuffer &textVertexBuffer, IconVertexBuffer
TriangleElementsBuffer &triangleElementsBuffer,
const StyleBucketSymbol &properties, Placement &placement)
: properties(properties),
- textVertexBuffer(textVertexBuffer),
- iconVertexBuffer(iconVertexBuffer),
- triangleElementsBuffer(triangleElementsBuffer),
- placement(placement),
- text_vertex_start(textVertexBuffer.index()),
- icon_vertex_start(iconVertexBuffer.index()),
- triangle_elements_start(triangleElementsBuffer.index()) {}
+ placement(placement)
+ {}
void SymbolBucket::render(Painter &painter, std::shared_ptr<StyleLayer> layer_desc,
const Tile::ID &id) {
@@ -39,11 +35,11 @@ bool SymbolBucket::hasData() const {
bool SymbolBucket::hasTextData() const {
- return !triangleGroups.empty();
+ return !text.groups.empty();
}
bool SymbolBucket::hasIconData() const {
- return icon_vertex_end > 0;
+ return !icon.groups.empty() > 0;
}
void SymbolBucket::addGlyph(uint64_t tileid, const std::string stackname,
@@ -61,74 +57,72 @@ void SymbolBucket::addGlyph(uint64_t tileid, const std::string stackname,
}
}
-void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter,
- const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, GlyphAtlas &glyphAtlas,
- GlyphStore &glyphStore) {
+std::vector<SymbolFeature> SymbolBucket::processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite) {
const bool text = properties.text.field.size();
const bool icon = properties.icon.image.size();
- util::utf8_to_utf32 ucs4conv;
+ std::vector<SymbolFeature> features;
+
+ if (!text && !icon) {
+ return features;
+ }
- std::vector<std::pair<std::u32string, pbf>> labels;
+ util::utf8_to_utf32 ucs4conv;
// Determine and load glyph ranges
- {
- std::set<GlyphRange> ranges;
+ std::set<GlyphRange> ranges;
- FilteredVectorTileLayer filtered_layer(layer, filter);
- for (const pbf &feature_pbf : filtered_layer) {
- VectorTileFeature feature {feature_pbf, layer};
+ FilteredVectorTileLayer filtered_layer(layer, filter);
+ for (const pbf &feature_pbf : filtered_layer) {
+ const VectorTileFeature feature {feature_pbf, layer};
- if (text) {
- std::string u8string = util::replaceTokens(properties.text.field, feature.properties);
+ SymbolFeature ft;
- auto& convert = std::use_facet<std::ctype<char>>(std::locale());
- if (properties.text.transform == TextTransformType::Uppercase) {
- convert.toupper(&u8string[0], &u8string[0] + u8string.size());
- } else if (properties.text.transform == TextTransformType::Lowercase) {
- convert.tolower(&u8string[0], &u8string[0] + u8string.size());
- }
+ if (text) {
+ std::string u8string = util::replaceTokens(properties.text.field, feature.properties);
- std::u32string string = ucs4conv.convert(u8string);
+ auto& convert = std::use_facet<std::ctype<char>>(std::locale());
+ if (properties.text.transform == TextTransformType::Uppercase) {
+ convert.toupper(&u8string[0], &u8string[0] + u8string.size());
+ } else if (properties.text.transform == TextTransformType::Lowercase) {
+ convert.tolower(&u8string[0], &u8string[0] + u8string.size());
+ }
- if (string.size()) {
- // Loop through all characters of this text and collect unique codepoints.
- for (char32_t chr : string) {
- ranges.insert(getGlyphRange(chr));
- }
+ ft.label = ucs4conv.convert(u8string);
- labels.emplace_back(string, feature.geometry);
+ if (ft.label.size()) {
+ // Loop through all characters of this text and collect unique codepoints.
+ for (char32_t chr : ft.label) {
+ ranges.insert(getGlyphRange(chr));
}
}
+ }
- // TODO: refactor for simultaneous placement
- // TODO: refactor to use quads instead of GL_POINTS
- if (icon) {
- std::string field = util::replaceTokens(properties.icon.image, feature.properties);
-
- const Rect<uint16_t> rect = spriteAtlas.waitForImage(field, sprite);
- const uint16_t tx = rect.x + rect.w / 2;
- const uint16_t ty = rect.y + rect.h / 2;
-
- Geometry::command cmd;
- pbf geom = feature.geometry;
- Geometry geometry(geom);
- int32_t x, y;
- while ((cmd = geometry.next(x, y)) != Geometry::end) {
- if (cmd == Geometry::move_to) {
- iconVertexBuffer.add(x, y, tx, ty);
- } else {
- Log::Warning(Event::ParseTile, "other command than move_to in icon geometry");
- }
- }
-
- icon_vertex_end = iconVertexBuffer.index();
- }
+ if (icon) {
+ ft.sprite = util::replaceTokens(properties.icon.image, feature.properties);
}
- glyphStore.waitForGlyphRanges(properties.text.font, ranges);
+ if (ft.label.length() || ft.sprite.length()) {
+ ft.geometry = feature.geometry;
+ features.push_back(std::move(ft));
+ }
}
+ glyphStore.waitForGlyphRanges(properties.text.font, ranges);
+ sprite.waitUntilLoaded();
+
+ return features;
+}
+
+
+
+
+void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpression &filter,
+ const Tile::ID &id, SpriteAtlas &spriteAtlas, Sprite &sprite, GlyphAtlas &glyphAtlas,
+ GlyphStore &glyphStore) {
+
+ const std::vector<SymbolFeature> features = processFeatures(layer, filter, glyphStore, sprite);
+
float horizontalAlign = 0.5;
if (properties.text.horizontal_align == TextHorizontalAlignType::Right) horizontalAlign = 1;
@@ -138,26 +132,84 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress
if (properties.text.vertical_align == TextVerticalAlignType::Bottom) verticalAlign = 1;
else if (properties.text.vertical_align == TextVerticalAlignType::Top) verticalAlign = 0;
-
-
- // Create a copy!
const FontStack &fontStack = glyphStore.getFontStack(properties.text.font);
- GlyphPositions face;
+ for (const SymbolFeature &feature : features) {
+ Shaping shaping;
+ Rect<uint16_t> image;
- // Shape and place all labels.
- for (const std::pair<std::u32string, pbf> &label : labels) {
+ // if feature has text, shape the text
+ if (feature.label.length()) {
+ shaping = fontStack.getShaping(feature.label, properties.text.max_width, properties.text.line_height, horizontalAlign, verticalAlign, properties.text.letter_spacing);
+ }
- // Shape labels.
- const Shaping shaping = fontStack.getShaping(label.first, properties.text.max_width,
- properties.text.line_height, horizontalAlign,
- verticalAlign, properties.text.letter_spacing);
+ // if feature has icon, get sprite atlas position
+ if (feature.sprite.length()) {
+ image = spriteAtlas.waitForImage(feature.sprite, sprite);
+ }
- addGlyph(id.to_uint64(), properties.text.font, label.first, fontStack, glyphAtlas, face);
+ // if either shaping or icon position is present, add the feature
+ if (shaping.size() || image) {
+ addFeature(feature.geometry, shaping, image);
+ }
+ }
+}
- // Place labels.
- addFeature(label.second, face, shaping);
+void SymbolBucket::addFeature(const pbf &geom_pbf, const Shaping &shaping, const Rect<uint16_t> &image) {
+ // 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()) {
+ addFeature(line, shaping, image);
+ line.clear();
+ }
+ }
+ line.emplace_back(x, y);
}
+ if (line.size()) {
+ addFeature(line, shaping, image);
+ }
+}
+
+void SymbolBucket::addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const Rect<uint16_t> &image) {
+ const float minScale = 0.5f;
+ const float glyphSize = 24.0f;
+
+
+ const bool horizontalText = properties.text.rotation_alignment == RotationAlignmentType::Viewport;
+ const bool horizontalIcon = properties.icon.rotation_alignment == RotationAlignmentType::Viewport;
+ const float fontScale = properties.text.max_size / glyphSize;
+ const float textBoxScale = placement.collision.tilePixelRatio * fontScale,
+ iconBoxScale = collision.tilePixelRatio * info['icon-max-size'],
+ iconWithoutText = info['text-optional'] || !shaping,
+ textWithoutIcon = info['icon-optional'] || !image;
+}
+
+
+//
+// GlyphPositions face;
+//
+//
+// // Shape and place all labels.
+// for (const std::pair<std::u32string, pbf> &label : labels) {
+//
+// // Shape labels.
+// const Shaping shaping = fontStack.getShaping(label.first, properties.text.max_width,
+// properties.text.line_height, horizontalAlign,
+// verticalAlign, properties.text.letter_spacing);
+//
+// addGlyph(id.to_uint64(), properties.text.font, label.first, fontStack, glyphAtlas, face);
+//
+// // Place labels.
+// addFeature(label.second, face, shaping);
+// }
}
void SymbolBucket::addFeature(const pbf &geom_pbf, const GlyphPositions &face,
@@ -184,6 +236,79 @@ void SymbolBucket::addFeature(const pbf &geom_pbf, const GlyphPositions &face,
}
}
+
+class Symbol {
+public:
+ vec2<float> tl, tr, bl, br;
+ Rect<uint16_t> tex;
+ float angle;
+ float minScale = 0.0f;
+ float maxScale = std::numeric_limits<float>::infinity();
+ CollisionAnchor anchor;
+};
+
+typedef std::vector<Symbol> Symbols;
+
+template <typename Buffer>
+void SymbolBucket::addSymbols(Buffer &buffer, const Symbols &symbols, float placementZoom, PlacementRange placementRange, float zoom) {
+ placementZoom += zoom;
+
+ for (const Symbol &symbol : symbols) {
+ const auto &tl = symbol.tl;
+ const auto &tr = symbol.tr;
+ const auto &bl = symbol.bl;
+ const auto &br = symbol.br;
+ const auto &tex = symbol.tex;
+ const auto &angle = symbol.angle;
+
+ float minZoom = util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)),
+ placementZoom);
+ float maxZoom =
+ util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
+ const auto &glyphAnchor = symbol.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)
+ buffer.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, angle, minZoom,
+ placementRange, maxZoom, placementZoom);
+ buffer.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, angle,
+ minZoom, placementRange, maxZoom, placementZoom);
+ buffer.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, angle,
+ minZoom, placementRange, maxZoom, placementZoom);
+ buffer.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 SymbolBucket::addGlyphs(const PlacedGlyphs &glyphs, float placementZoom,
PlacementRange placementRange, float zoom) {
placementZoom += zoom;
diff --git a/src/shader/icon.fragment.glsl b/src/shader/icon.fragment.glsl
index 52c9e65aa5..beaee7654e 100644
--- a/src/shader/icon.fragment.glsl
+++ b/src/shader/icon.fragment.glsl
@@ -1,11 +1,10 @@
-uniform sampler2D u_image;
-uniform vec2 u_dimension;
-uniform vec4 u_color;
-uniform float u_size;
+uniform sampler2D u_texture;
varying vec2 v_tex;
+varying float v_alpha;
void main() {
- vec2 pos = (v_tex + (gl_PointCoord - 0.5) * u_size) / u_dimension;
- gl_FragColor = u_color * texture2D(u_image, pos);
+ gl_FragColor = texture2D(u_texture, v_tex);
+ gl_FragColor.a *= v_alpha;
+ gl_FragColor.rgb *= gl_FragColor.a;
}
diff --git a/src/shader/icon.vertex.glsl b/src/shader/icon.vertex.glsl
index d0ffe4b33e..d273b733d7 100644
--- a/src/shader/icon.vertex.glsl
+++ b/src/shader/icon.vertex.glsl
@@ -1,14 +1,73 @@
attribute vec2 a_pos;
+attribute vec2 a_offset;
attribute vec2 a_tex;
+attribute float a_angle;
+attribute float a_minzoom;
+attribute float a_maxzoom;
+attribute float a_rangeend;
+attribute float a_rangestart;
+attribute float a_labelminzoom;
-uniform mat4 u_matrix;
-uniform float u_size;
-uniform float u_ratio;
+
+// posmatrix is for the vertex position, exmatrix is for rotating and projecting
+// the extrusion vector.
+uniform mat4 u_posmatrix;
+uniform mat4 u_exmatrix;
+uniform float u_angle;
+uniform float u_zoom;
+uniform float u_flip;
+uniform float u_fadedist;
+uniform float u_minfadezoom;
+uniform float u_maxfadezoom;
+uniform float u_fadezoom;
+uniform float u_opacity;
+
+uniform vec2 u_texsize;
varying vec2 v_tex;
+varying float v_alpha;
void main() {
- gl_Position = u_matrix * vec4(a_pos, 0, 1);
- gl_PointSize = u_size;
- v_tex = a_tex * u_ratio;
+
+ float a_fadedist = 10.0;
+ float rev = 0.0;
+
+ // u_angle is angle of the map, -128..128 representing 0..2PI
+ // a_angle is angle of the label, 0..256 representing 0..2PI, where 0 is horizontal text
+ float rotated = mod(a_angle + u_angle, 256.0);
+ // if the label rotates with the map, and if the rotated label is upside down, hide it
+ if (u_flip > 0.0 && rotated >= 64.0 && rotated < 192.0) rev = 1.0;
+
+ // If the label should be invisible, we move the vertex outside
+ // of the view plane so that the triangle gets clipped. This makes it easier
+ // for us to create degenerate triangle strips.
+ // u_zoom is the current zoom level adjusted for the change in font size
+ float z = 2.0 - step(a_minzoom, u_zoom) - (1.0 - step(a_maxzoom, u_zoom)) + rev;
+
+ // fade out labels
+ float alpha = clamp((u_fadezoom - a_labelminzoom) / u_fadedist, 0.0, 1.0);
+
+ if (u_fadedist >= 0.0) {
+ v_alpha = alpha;
+ } else {
+ v_alpha = 1.0 - alpha;
+ }
+ if (u_maxfadezoom < a_labelminzoom) {
+ v_alpha = 0.0;
+ }
+ if (u_minfadezoom >= a_labelminzoom) {
+ v_alpha = 1.0;
+ }
+
+ // if label has been faded out, clip it
+ z += step(v_alpha, 0.0);
+
+ // all the angles are 0..256 representing 0..2PI
+ // hide if (angle >= a_rangeend && angle < rangestart)
+ z += step(a_rangeend, u_angle) * (1.0 - step(a_rangestart, u_angle));
+
+ gl_Position = u_posmatrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0);
+ v_tex = a_tex / u_texsize;
+
+ v_alpha *= u_opacity;
}
diff --git a/src/shader/icon_shader.cpp b/src/shader/icon_shader.cpp
index b3ac9272ba..435b813fa4 100644
--- a/src/shader/icon_shader.cpp
+++ b/src/shader/icon_shader.cpp
@@ -17,62 +17,130 @@ IconShader::IconShader()
}
a_pos = glGetAttribLocation(program, "a_pos");
+ a_offset = glGetAttribLocation(program, "a_offset");
a_tex = glGetAttribLocation(program, "a_tex");
+ a_angle = glGetAttribLocation(program, "a_angle");
+ a_minzoom = glGetAttribLocation(program, "a_minzoom");
+ a_maxzoom = glGetAttribLocation(program, "a_maxzoom");
+ a_rangeend = glGetAttribLocation(program, "a_rangeend");
+ a_rangestart = glGetAttribLocation(program, "a_rangestart");
+ a_labelminzoom = glGetAttribLocation(program, "a_labelminzoom");
u_matrix = glGetUniformLocation(program, "u_matrix");
- u_color = glGetUniformLocation(program, "u_color");
- u_size = glGetUniformLocation(program, "u_size");
- u_ratio = glGetUniformLocation(program, "u_ratio");
- u_dimension = glGetUniformLocation(program, "u_dimension");
+ u_exmatrix = glGetUniformLocation(program, "u_exmatrix");
+ u_angle = glGetUniformLocation(program, "u_angle");
+ u_zoom = glGetUniformLocation(program, "u_zoom");
+ u_flip = glGetUniformLocation(program, "u_flip");
+ u_fadedist = glGetUniformLocation(program, "u_fadedist");
+ u_minfadezoom = glGetUniformLocation(program, "u_minfadezoom");
+ u_maxfadezoom = glGetUniformLocation(program, "u_maxfadezoom");
+ u_fadezoom = glGetUniformLocation(program, "u_fadezoom");
+ u_opacity = glGetUniformLocation(program, "u_opacity");
// fprintf(stderr, "IconShader:\n");
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
- // fprintf(stderr, " - u_color: %d\n", u_color);
- // fprintf(stderr, " - u_size: %d\n", u_size);
- // fprintf(stderr, " - u_ratio: %d\n", u_ratio);
- // fprintf(stderr, " - u_dimension: %d\n", u_dimension);
- // fprintf(stderr, " - u_image: %d\n", u_image);
+ // fprintf(stderr, " - u_exmatrix: %d\n", u_exmatrix);
+ // fprintf(stderr, " - u_angle: %d\n", u_angle);
+ // fprintf(stderr, " - u_zoom: %d\n", u_zoom);
+ // fprintf(stderr, " - u_flip: %d\n", u_flip);
+ // fprintf(stderr, " - u_fadedist: %d\n", u_fadedist);
+ // fprintf(stderr, " - u_minfadezoom: %d\n", u_minfadezoom);
+ // fprintf(stderr, " - u_maxfadezoom: %d\n", u_maxfadezoom);
+ // fprintf(stderr, " - u_fadezoom: %d\n", u_fadezoom);
+ // fprintf(stderr, " - u_opacity: %d\n", u_opacity);
}
void IconShader::bind(char *offset) {
+ const int stride = 20;
+
glEnableVertexAttribArray(a_pos);
- glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 8, offset);
+ glVertexAttribPointer(a_pos, 2, GL_SHORT, false, stride, offset + 0);
+
+ glEnableVertexAttribArray(a_offset);
+ glVertexAttribPointer(a_offset, 2, GL_SHORT, false, stride, offset + 4);
+
+ glEnableVertexAttribArray(a_labelminzoom);
+ glVertexAttribPointer(a_labelminzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 8);
+
+ glEnableVertexAttribArray(a_minzoom);
+ glVertexAttribPointer(a_minzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 9);
+
+ glEnableVertexAttribArray(a_maxzoom);
+ glVertexAttribPointer(a_maxzoom, 1, GL_UNSIGNED_BYTE, false, stride, offset + 10);
+
+ glEnableVertexAttribArray(a_angle);
+ glVertexAttribPointer(a_angle, 1, GL_UNSIGNED_BYTE, false, stride, offset + 11);
+
+ glEnableVertexAttribArray(a_rangeend);
+ glVertexAttribPointer(a_rangeend, 1, GL_UNSIGNED_BYTE, false, stride, offset + 12);
+
+ glEnableVertexAttribArray(a_rangestart);
+ glVertexAttribPointer(a_rangestart, 1, GL_UNSIGNED_BYTE, false, stride, offset + 13);
glEnableVertexAttribArray(a_tex);
- glVertexAttribPointer(a_tex, 2, GL_UNSIGNED_SHORT, false, 8, offset + 4);
+ glVertexAttribPointer(a_tex, 2, GL_SHORT, false, stride, offset + 16);
+}
+
+void IconShader::setExtrudeMatrix(const std::array<float, 16>& new_exmatrix) {
+ if (exmatrix != new_exmatrix) {
+ glUniformMatrix4fv(u_exmatrix, 1, GL_FALSE, new_exmatrix.data());
+ exmatrix = new_exmatrix;
+ }
}
-void IconShader::setImage(int32_t new_image) {
- if (image != new_image) {
- glUniform1i(u_image, new_image);
- image = new_image;
+void IconShader::setAngle(float new_angle) {
+ if (angle != new_angle) {
+ glUniform1f(u_angle, new_angle);
+ angle = new_angle;
}
}
-void IconShader::setColor(const std::array<float, 4>& new_color) {
- if (color != new_color) {
- glUniform4fv(u_color, 1, new_color.data());
- color = new_color;
+void IconShader::setZoom(float new_zoom) {
+ if (zoom != new_zoom) {
+ glUniform1f(u_zoom, new_zoom);
+ zoom = new_zoom;
}
}
-void IconShader::setSize(float new_size) {
- if (size != new_size) {
- glUniform1f(u_size, new_size);
- size = new_size;
+void IconShader::setFlip(float new_flip) {
+ if (flip != new_flip) {
+ glUniform1f(u_flip, new_flip);
+ flip = new_flip;
}
}
-void IconShader::setRatio(float new_ratio) {
- if (ratio != new_ratio) {
- glUniform1f(u_ratio, new_ratio);
- ratio = new_ratio;
+void IconShader::setFadeDist(float new_fadedist) {
+ if (fadedist != new_fadedist) {
+ glUniform1f(u_fadedist, new_fadedist);
+ fadedist = new_fadedist;
}
}
-void IconShader::setDimension(const std::array<float, 2>& new_dimension) {
- if (dimension != new_dimension) {
- glUniform2fv(u_dimension, 1, new_dimension.data());
- dimension = new_dimension;
+void IconShader::setMinFadeZoom(float new_minfadezoom) {
+ if (minfadezoom != new_minfadezoom) {
+ glUniform1f(u_minfadezoom, new_minfadezoom);
+ minfadezoom = new_minfadezoom;
}
}
+
+void IconShader::setMaxFadeZoom(float new_maxfadezoom) {
+ if (maxfadezoom != new_maxfadezoom) {
+ glUniform1f(u_maxfadezoom, new_maxfadezoom);
+ maxfadezoom = new_maxfadezoom;
+ }
+}
+
+void IconShader::setFadeZoom(float new_fadezoom) {
+ if (fadezoom != new_fadezoom) {
+ glUniform1f(u_fadezoom, new_fadezoom);
+ fadezoom = new_fadezoom;
+ }
+}
+
+void IconShader::setOpacity(float new_opacity) {
+ if (opacity != new_opacity) {
+ glUniform1f(u_opacity, new_opacity);
+ opacity = new_opacity;
+ }
+}
+
diff --git a/src/text/collision.cpp b/src/text/collision.cpp
index 18cf56ca0c..362ac92e27 100644
--- a/src/text/collision.cpp
+++ b/src/text/collision.cpp
@@ -31,24 +31,40 @@ Collision::~Collision() {
delete ((Tree *)hTree);
}
-Collision::Collision() : cTree(new Tree()), hTree(new Tree()) {
+Collision::Collision(float zoom, float tileExtent, float tileSize, float placementDepth)
+ : hTree(new Tree()), // tree for horizontal labels
+ cTree(new Tree()), // tree for glyphs from curved labels
+
+ // tile pixels per screen pixels at the tile's zoom level
+ tilePixelRatio(tileExtent / tileSize),
+
+ zoom(zoom),
+
+ // Calculate the maximum scale we can go down in our fake-3d rtree so that
+ // placement still makes sense. This is calculated so that the minimum
+ // placement zoom can be at most 25.5 (we use an unsigned integer x10 to
+ // store the minimum zoom).
+ //
+ // We don't want to place labels all the way to 25.5. This lets too many
+ // glyphs be placed, slowing down collision checking. Only place labels if
+ // they will show up within the intended zoom range of the tile.
+ maxPlacementScale(std::exp(std::log(2) * util::min(3.0f, placementDepth, 25.5f - zoom)))
+{
const float m = 4096;
+ const float edge = m * tilePixelRatio * 2;
// Hack to prevent cross-tile labels
insert(
{{GlyphBox{
CollisionRect{CollisionPoint{0, 0}, CollisionPoint{0, m * 8}}, 0},
GlyphBox{
- CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}},
- 0}}},
+ CollisionRect{CollisionPoint{0, 0}, CollisionPoint{m * 8, 0}}, 0}}},
CollisionAnchor(0, 0), 1, {{M_PI * 2, 0}}, false, 2);
insert({{GlyphBox{
- CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}},
- 0},
+ CollisionRect{CollisionPoint{-m * 8, 0}, CollisionPoint{0, 0}}, 0},
GlyphBox{
- CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}},
- 0}}},
+ CollisionRect{CollisionPoint{0, -m * 8}, CollisionPoint{0, 0}}, 0}}},
CollisionAnchor{m, m}, 1, {{M_PI * 2, 0}}, false, 2);
}
@@ -74,7 +90,7 @@ GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor)
PlacementProperty Collision::place(const GlyphBoxes &boxes, const CollisionAnchor &anchor,
float minPlacementScale, float maxPlacementScale, float padding,
- bool horizontal, bool alwaysVisible) {
+ bool horizontal, bool allowOverlap, bool ignorePlacement) {
float minScale = std::numeric_limits<float>::infinity();
for (const GlyphBox &glyphBox : boxes) {
minScale = util::min(minScale, glyphBox.minScale);
@@ -95,17 +111,17 @@ PlacementProperty Collision::place(const GlyphBoxes &boxes, const CollisionAncho
y22 = box.br.y * box.br.y,
diag = std::sqrt(util::max(x12 + y12, x12 + y22, x22 + y12, x22 + y22));
- glyphs.front().hBox = CollisionRect{{-diag, -diag}, {diag, diag}};
-
- } else {
- glyphs = boxes;
+ glyphs.front().hBox = CollisionRect{
+ {-diag, -diag},
+ {diag, diag}
+ };
}
// Calculate the minimum scale the entire label can be shown without
// collisions
- float scale = alwaysVisible ? minPlacementScale
- : getPlacementScale(glyphs, minPlacementScale,
- maxPlacementScale, padding);
+ float scale =
+ allowOverlap ? minPlacementScale : getPlacementScale(glyphs, minPlacementScale,
+ maxPlacementScale, padding);
// Return if the label can never be placed without collision
if (scale < 0 || scale == std::numeric_limits<float>::infinity()) {
@@ -113,8 +129,10 @@ PlacementProperty Collision::place(const GlyphBoxes &boxes, const CollisionAncho
}
// Calculate the range it is safe to rotate all glyphs
- PlacementRange rotationRange = getPlacementRange(glyphs, scale, horizontal);
- insert(glyphs, anchor, scale, rotationRange, horizontal, padding);
+ PlacementRange rotationRange = allowOverlap ? PlacementRange{{2 * M_PI, 0.0f}}
+ : getPlacementRange(glyphs, scale, horizontal);
+
+ if (!ignorePlacement) insert(glyphs, anchor, scale, rotationRange, horizontal, padding);
float zoom = log(scale) / log(2);
@@ -177,18 +195,10 @@ float Collision::getPlacementScale(const GlyphBoxes &glyphs,
float padding = std::fmax(pad, placement.padding) * 8.0f;
// Original algorithm:
- float s1 = (ob.tl.x - nb.br.x - padding) /
- (na.x - oa.x); // scale at which new box is to the
- // left of old box
- float s2 = (ob.br.x - nb.tl.x + padding) /
- (na.x - oa.x); // scale at which new box is to the
- // right of old box
- float s3 = (ob.tl.y - nb.br.y - padding) /
- (na.y - oa.y); // scale at which new box is to the
- // top of old box
- float s4 = (ob.br.y - nb.tl.y + padding) /
- (na.y - oa.y); // scale at which new box is to the
- // bottom of old box
+ float s1 = (ob.tl.x - nb.br.x - padding) / (na.x - oa.x); // scale at which new box is to the left of old box
+ float s2 = (ob.br.x - nb.tl.x + padding) / (na.x - oa.x); // scale at which new box is to the right of old box
+ float s3 = (ob.tl.y - nb.br.y - padding) / (na.y - oa.y); // scale at which new box is to the top of old box
+ float s4 = (ob.br.y - nb.tl.y + padding) / (na.y - oa.y); // scale at which new box is to the bottom of old box
if (isnan(s1) || isnan(s2)) {
s1 = s2 = 1;
diff --git a/src/text/placement.cpp b/src/text/placement.cpp
index ae5bf46bf6..75988cc997 100644
--- a/src/text/placement.cpp
+++ b/src/text/placement.cpp
@@ -272,10 +272,11 @@ void Placement::addFeature(SymbolBucket &bucket, const std::vector<Coordinate> &
PlacedGlyphs glyphs;
GlyphBoxes boxes;
- getGlyphs(glyphs, boxes, anchor, info.text.offset, shaping, face, fontScale, horizontal, line,
- info.text.max_angle_delta * (M_PI/180), info.text.rotate);
- PlacementProperty place = collision.place(boxes, anchor, anchor.scale, maxPlacementScale,
- info.text.padding, horizontal, info.text.ignore_placement);
+ getGlyphs(glyphs, boxes, anchor, info.text.offset, shaping, face, fontScale, horizontal,
+ line, info.text.max_angle_delta * (M_PI / 180), info.text.rotate);
+ PlacementProperty place =
+ collision.place(boxes, anchor, anchor.scale, maxPlacementScale, info.text.padding,
+ horizontal, info.text.allow_overlap, info.text.ignore_placement);
if (place) {
bucket.addGlyphs(glyphs, place.zoom, place.rotationRange, zoom - zOffset);
}