update shaders and backport some collision changes

This commit is contained in:
Konstantin Käfer
2014-08-04 12:59:22 +02:00
parent 19e51e8bd8
commit 9dd50a29e1
12 changed files with 515 additions and 196 deletions

View File

@@ -6,13 +6,13 @@
namespace mbgl {
class IconVertexBuffer : public Buffer<
4 + // int16 x/y coordinates per vertex (== 4 bytes)
4 // uint16 x/y coordinates of icon in sprite (== 4 bytes)
20
> {
public:
typedef int16_t vertex_type;
static const double angleFactor;
size_t 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);
void add(vertex_type x, vertex_type y, uint16_t tx, uint16_t ty);
};
}

View File

@@ -4,6 +4,8 @@
#include "bucket.hpp"
#include <mbgl/geometry/vao.hpp>
#include <mbgl/geometry/elements_buffer.hpp>
#include <mbgl/geometry/text_buffer.hpp>
#include <mbgl/geometry/icon_buffer.hpp>
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/text/types.hpp>
#include <mbgl/text/glyph.hpp>
@@ -16,9 +18,6 @@
namespace mbgl {
class Style;
class TextVertexBuffer;
class IconVertexBuffer;
class TriangleElementsBuffer;
class TextShader;
class IconShader;
class DotShader;
@@ -29,9 +28,14 @@ class GlyphAtlas;
class GlyphStore;
class FontStack;
class SymbolBucket : public Bucket {
typedef ElementGroup triangle_group_type;
class SymbolFeature {
public:
pbf geometry;
std::u32string label;
std::string sprite;
};
class SymbolBucket : public Bucket {
public:
SymbolBucket(
TextVertexBuffer &textVertexBuffer,
@@ -57,6 +61,14 @@ public:
void drawIcons(DotShader& shader);
private:
std::vector<SymbolFeature> processFeatures(const VectorTileLayer &layer, const FilterExpression &filter, GlyphStore &glyphStore, const Sprite &sprite);
void addFeature(const pbf &geom_pbf, const Shaping &shaping, const Rect<uint16_t> &image);
void addFeature(const std::vector<Coordinate> &line, const Shaping &shaping, const Rect<uint16_t> &image);
void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string,
const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
void addFeature(const pbf &geom_pbf, const GlyphPositions &face, const Shaping &shaping);
@@ -65,18 +77,20 @@ public:
const StyleBucketSymbol &properties;
private:
TextVertexBuffer& textVertexBuffer;
IconVertexBuffer& iconVertexBuffer;
TriangleElementsBuffer& triangleElementsBuffer;
Placement &placement;
const size_t text_vertex_start;
const size_t icon_vertex_start;
const size_t triangle_elements_start;
size_t icon_vertex_end = 0;
VertexArrayObject array;
struct {
TextVertexBuffer vertices;
TriangleElementsBuffer triangles;
std::vector<ElementGroup> groups;
} text;
struct {
IconVertexBuffer vertices;
TriangleElementsBuffer triangles;
std::vector<ElementGroup> groups;
} icon;
std::vector<triangle_group_type> triangleGroups;
};
}

View File

@@ -11,30 +11,53 @@ public:
void bind(char *offset);
void setImage(int32_t image);
void setColor(const std::array<float, 4>& color);
void setDimension(const std::array<float, 2>& dimension);
void setSize(float size);
void setRatio(float ratio);
void setExtrudeMatrix(const std::array<float, 16>& exmatrix);
void setAngle(float angle);
void setZoom(float zoom);
void setFlip(float flip);
void setFadeDist(float fadedist);
void setMinFadeZoom(float minfadezoom);
void setMaxFadeZoom(float maxfadezoom);
void setFadeZoom(float fadezoom);
void setOpacity(float opacity);
private:
int32_t a_pos = -1;
int32_t a_offset = -1;
int32_t a_tex = -1;
int32_t a_angle = -1;
int32_t a_minzoom = -1;
int32_t a_maxzoom = -1;
int32_t a_rangeend = -1;
int32_t a_rangestart = -1;
int32_t a_labelminzoom = -1;
int32_t image = -1;
int32_t u_image = -1;
std::array<float, 16> exmatrix = {{}};
int32_t u_exmatrix = -1;
std::array<float, 4> color = {{}};
int32_t u_color = -1;
float angle = 0;
int32_t u_angle = -1;
std::array<float, 2> dimension = {{}};
int32_t u_dimension = -1;
float zoom = 0;
int32_t u_zoom = -1;
float size = 0;
int32_t u_size = -1;
float flip = 0;
int32_t u_flip = -1;
float ratio = 0;
int32_t u_ratio = -1;
float fadedist = 0;
int32_t u_fadedist = -1;
float minfadezoom = 0;
int32_t u_minfadezoom = -1;
float maxfadezoom = 0;
int32_t u_maxfadezoom = -1;
float fadezoom = 0;
int32_t u_fadezoom = -1;
float opacity = 0;
int32_t u_opacity = -1;
};
}

View File

@@ -8,13 +8,12 @@ namespace mbgl {
class Collision {
public:
Collision();
Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1);
~Collision();
PlacementProperty place(const GlyphBoxes &boxes,
const CollisionAnchor &anchor,
float minPlacementScale, float maxPlacementScale,
float padding, bool horizontal, bool alwaysVisible);
PlacementProperty place(const GlyphBoxes &boxes, const CollisionAnchor &anchor,
float minPlacementScale, float maxPlacementScale, float padding,
bool horizontal, bool allowOverlap, bool ignorePlacement);
float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale,
float maxPlacementScale, float pad);
PlacementRange getPlacementRange(const GlyphBoxes &glyphs,
@@ -24,8 +23,11 @@ public:
bool horizontal, float padding);
private:
void *cTree;
void *hTree;
void *cTree;
const float tilePixelRatio;
const float zoom;
const float maxPlacementScale;
};
}

View File

@@ -5,7 +5,8 @@ namespace mbgl {
template <typename T>
struct Rect {
explicit Rect(T x, T y, T w, T h) : x(x), y(y), w(w), h(h) {}
inline Rect() {}
inline Rect(T x, T y, T w, T h) : x(x), y(y), w(w), h(h) {}
T x = 0, y = 0;
T w = 0, h = 0;

View File

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

View File

@@ -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();
std::vector<SymbolFeature> features;
if (!text && !icon) {
return features;
}
util::utf8_to_utf32 ucs4conv;
std::vector<std::pair<std::u32string, pbf>> labels;
// 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);
if (string.size()) {
// Loop through all characters of this text and collect unique codepoints.
for (char32_t chr : string) {
ranges.insert(getGlyphRange(chr));
}
labels.emplace_back(string, feature.geometry);
}
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());
}
// 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);
ft.label = ucs4conv.convert(u8string);
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");
}
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));
}
icon_vertex_end = iconVertexBuffer.index();
}
}
glyphStore.waitForGlyphRanges(properties.text.font, ranges);
if (icon) {
ft.sprite = util::replaceTokens(properties.icon.image, feature.properties);
}
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,28 +132,86 @@ 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);
// Place labels.
addFeature(label.second, face, shaping);
// if either shaping or icon position is present, add the feature
if (shaping.size() || image) {
addFeature(feature.geometry, shaping, image);
}
}
}
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,
const Shaping &shaping) {
// Decode all lines.
@@ -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;

View File

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

View File

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

View File

@@ -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::setImage(int32_t new_image) {
if (image != new_image) {
glUniform1i(u_image, new_image);
image = new_image;
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::setColor(const std::array<float, 4>& new_color) {
if (color != new_color) {
glUniform4fv(u_color, 1, new_color.data());
color = new_color;
void IconShader::setAngle(float new_angle) {
if (angle != new_angle) {
glUniform1f(u_angle, new_angle);
angle = new_angle;
}
}
void IconShader::setSize(float new_size) {
if (size != new_size) {
glUniform1f(u_size, new_size);
size = new_size;
void IconShader::setZoom(float new_zoom) {
if (zoom != new_zoom) {
glUniform1f(u_zoom, new_zoom);
zoom = new_zoom;
}
}
void IconShader::setRatio(float new_ratio) {
if (ratio != new_ratio) {
glUniform1f(u_ratio, new_ratio);
ratio = new_ratio;
void IconShader::setFlip(float new_flip) {
if (flip != new_flip) {
glUniform1f(u_flip, new_flip);
flip = new_flip;
}
}
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::setFadeDist(float new_fadedist) {
if (fadedist != new_fadedist) {
glUniform1f(u_fadedist, new_fadedist);
fadedist = new_fadedist;
}
}
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;
}
}

View File

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

View File

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