diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2014-08-04 12:59:22 +0200 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2014-08-04 12:59:22 +0200 |
commit | 9dd50a29e1e8b975e09172b19b303063a96ba20f (patch) | |
tree | 0f35c1d304aafcf465762484848d33e58342c391 /src | |
parent | 19e51e8bd89f0e627045b623a2a57b6702bca896 (diff) | |
download | qtlocation-mapboxgl-9dd50a29e1e8b975e09172b19b303063a96ba20f.tar.gz |
update shaders and backport some collision changes
Diffstat (limited to 'src')
-rw-r--r-- | src/geometry/icon_buffer.cpp | 33 | ||||
-rw-r--r-- | src/renderer/symbol_bucket.cpp | 267 | ||||
-rw-r--r-- | src/shader/icon.fragment.glsl | 11 | ||||
-rw-r--r-- | src/shader/icon.vertex.glsl | 71 | ||||
-rw-r--r-- | src/shader/icon_shader.cpp | 130 | ||||
-rw-r--r-- | src/text/collision.cpp | 68 | ||||
-rw-r--r-- | src/text/placement.cpp | 9 |
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); } |