diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2015-08-31 16:45:21 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2015-08-31 16:45:21 -0700 |
commit | abc1c84fb817a8e31dd1226a633dc895fdae602e (patch) | |
tree | 0eef59c9f60a19b722b413f9deef20982cb77b45 /src/mbgl | |
parent | 119a9fbf776fc488b19668f54cc68998ebdfb12a (diff) | |
parent | 831df11ec47cb44f6664490a361ff42ef288ce9b (diff) | |
download | qtlocation-mapboxgl-abc1c84fb817a8e31dd1226a633dc895fdae602e.tar.gz |
Merge branch 'master' into node
Diffstat (limited to 'src/mbgl')
55 files changed, 882 insertions, 362 deletions
diff --git a/src/mbgl/map/live_tile_data.cpp b/src/mbgl/map/live_tile_data.cpp index 8be918d6d4..0b73b52f36 100644 --- a/src/mbgl/map/live_tile_data.cpp +++ b/src/mbgl/map/live_tile_data.cpp @@ -25,9 +25,7 @@ LiveTileData::LiveTileData(const TileID& id_, style_, style_.layers, state, - std::make_unique<CollisionTile>(id_.z, 4096, - source_.tile_size * id.overscaling, - 0, false)) { + std::make_unique<CollisionTile>(0, 0, false)) { state = State::loaded; if (!tile) { diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index b260b228fc..bab1a48225 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -271,6 +271,18 @@ void Map::resetNorth() { } +#pragma mark - Pitch + +void Map::setPitch(double pitch) { + transform->setPitch(std::min(pitch, 60.0) * M_PI / 180); + update(Update::Repaint); +} + +double Map::getPitch() const { + return transform->getPitch() / M_PI * 180; +} + + #pragma mark - Projection void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const { @@ -294,11 +306,11 @@ const LatLng Map::latLngForProjectedMeters(const ProjectedMeters projectedMeters } const vec2<double> Map::pixelForLatLng(const LatLng latLng) const { - return transform->getState().pixelForLatLng(latLng); + return transform->getState().latLngToPoint(latLng); } const LatLng Map::latLngForPixel(const vec2<double> pixel) const { - return transform->getState().latLngForPixel(pixel); + return transform->getState().pointToLatLng(pixel); } #pragma mark - Annotations diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 175f987195..f6cc4ec2b7 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -336,7 +336,6 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame) if (!painter) { painter = std::make_unique<Painter>(data); painter->setup(); - painter->updateRenderOrder(*style); } painter->setDebug(data.getDebug()); @@ -403,10 +402,6 @@ void MapContext::setSprite(const std::string& name, std::shared_ptr<const Sprite void MapContext::onTileDataChanged() { assert(util::ThreadContext::currentlyOn(util::ThreadType::Map)); - if (painter) { - painter->updateRenderOrder(*style); - } - updateFlags |= Update::Repaint; asyncUpdate->send(); } diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 4b2ae2cfbd..5899fea1ec 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -9,6 +9,7 @@ #include <mbgl/storage/response.hpp> #include <mbgl/util/math.hpp> #include <mbgl/util/box.hpp> +#include <mbgl/util/tile_coordinate.hpp> #include <mbgl/util/mapbox.hpp> #include <mbgl/storage/file_source.hpp> #include <mbgl/style/style_layer.hpp> @@ -284,8 +285,8 @@ TileData::State Source::addTile(MapData& data, // If we don't find working tile data, we're just going to load it. if (info.type == SourceType::Vector) { - auto tileData = std::make_shared<VectorTileData>(normalized_id, style, info, - transformState.getAngle(), data.getCollisionDebug()); + auto tileData = std::make_shared<VectorTileData>(normalized_id, style, info, + transformState.getAngle(), transformState.getPitch(), data.getCollisionDebug()); tileData->request(data.pixelRatio, callback); new_tile.data = tileData; } else if (info.type == SourceType::Raster) { @@ -332,14 +333,14 @@ std::forward_list<TileID> Source::coveringTiles(const TransformState& state) con // Map four viewport corners to pixel coordinates box points = state.cornersToBox(z); - const vec2<double>& center = points.center; + const TileCoordinate center = state.pointToCoordinate({ state.getWidth() / 2.0f, state.getHeight()/ 2.0f }).zoomTo(z); std::forward_list<TileID> covering_tiles = tileCover(z, points, reparseOverscaled ? actualZ : z); covering_tiles.sort([¢er](const TileID& a, const TileID& b) { // Sorts by distance from the box center - return std::fabs(a.x - center.x) + std::fabs(a.y - center.y) < - std::fabs(b.x - center.x) + std::fabs(b.y - center.y); + return std::fabs(a.x - center.column) + std::fabs(a.y - center.row) < + std::fabs(b.x - center.column) + std::fabs(b.y - center.row); }); return covering_tiles; @@ -506,7 +507,7 @@ bool Source::update(MapData& data, updateTilePtrs(); for (auto& tilePtr : tilePtrs) { - tilePtr->data->redoPlacement(transformState.getAngle(), data.getCollisionDebug()); + tilePtr->data->redoPlacement(transformState.getAngle(), transformState.getPitch(), data.getCollisionDebug()); } updated = data.getAnimationTime(); @@ -529,14 +530,9 @@ void Source::invalidateTiles(const std::unordered_set<TileID, TileID::Hash>& ids } void Source::updateTilePtrs() { - std::vector<Tile*> newPtrs; + tilePtrs.clear(); for (const auto& pair : tiles) { - newPtrs.push_back(pair.second.get()); - } - - if (tilePtrs != newPtrs) { - tilePtrs = newPtrs; - emitTileLoaded(true); + tilePtrs.push_back(pair.second.get()); } } @@ -568,7 +564,7 @@ void Source::tileLoadingCompleteCallback(const TileID& normalized_id, const Tran return; } - data->redoPlacement(transformState.getAngle(), collisionDebug); + data->redoPlacement(transformState.getAngle(), transformState.getPitch(), collisionDebug); emitTileLoaded(true); } diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp index e78d888415..047ccd5cd0 100644 --- a/src/mbgl/map/tile_data.hpp +++ b/src/mbgl/map/tile_data.hpp @@ -72,7 +72,7 @@ public: virtual Bucket* getBucket(const StyleLayer&) = 0; - virtual void redoPlacement(float, bool) {} + virtual void redoPlacement(float, float, bool) {} bool isReady() const { return isReadyState(state); diff --git a/src/mbgl/map/tile_worker.cpp b/src/mbgl/map/tile_worker.cpp index a555ca7ab0..06de585a3c 100644 --- a/src/mbgl/map/tile_worker.cpp +++ b/src/mbgl/map/tile_worker.cpp @@ -25,7 +25,7 @@ TileWorker::TileWorker(TileID id_, maxZoom(maxZoom_), style(style_), state(state_), - collision(std::move(collision_)) { + collisionTile(std::move(collision_)) { assert(style.sprite); } @@ -55,13 +55,12 @@ TileParseResult TileWorker::parse(const GeometryTile& geometryTile) { return partialParse ? TileData::State::partial : TileData::State::parsed; } -void TileWorker::redoPlacement(float angle, bool collisionDebug) { - collision->reset(angle, 0); - collision->setDebug(collisionDebug); +void TileWorker::redoPlacement(float angle, float pitch, bool collisionDebug) { + collisionTile = std::make_unique<CollisionTile>(angle, pitch, collisionDebug); for (auto i = layers.rbegin(); i != layers.rend(); i++) { auto bucket = getBucket(**i); if (bucket) { - bucket->placeFeatures(); + bucket->placeFeatures(*collisionTile); } } } @@ -224,7 +223,7 @@ std::unique_ptr<Bucket> TileWorker::createCircleBucket(const GeometryTileLayer& std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer& layer, const StyleBucket& bucket_desc) { - auto bucket = std::make_unique<SymbolBucket>(*collision, id.overscaling); + auto bucket = std::make_unique<SymbolBucket>(id.overscaling, id.z); const float z = id.z; auto& layout = bucket->layout; @@ -283,7 +282,7 @@ std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer& } bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas, *style.glyphAtlas, - *style.glyphStore); + *style.glyphStore, *collisionTile); return bucket->hasData() ? std::move(bucket) : nullptr; } diff --git a/src/mbgl/map/tile_worker.hpp b/src/mbgl/map/tile_worker.hpp index 15a5487dc9..62527b9fd9 100644 --- a/src/mbgl/map/tile_worker.hpp +++ b/src/mbgl/map/tile_worker.hpp @@ -44,7 +44,7 @@ public: Bucket* getBucket(const StyleLayer&) const; TileParseResult parse(const GeometryTile&); - void redoPlacement(float angle, bool collisionDebug); + void redoPlacement(float angle, float pitch, bool collisionDebug); std::vector<util::ptr<StyleLayer>> layers; @@ -75,7 +75,7 @@ private: TriangleElementsBuffer triangleElementsBuffer; LineElementsBuffer lineElementsBuffer; - std::unique_ptr<CollisionTile> collision; + std::unique_ptr<CollisionTile> collisionTile; // Contains all the Bucket objects for the tile. Buckets are render // objects and they get added to this map as they get processed. diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index ea8775dbcb..7ccc2ad4cc 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -347,6 +347,15 @@ double Transform::getAngle() const { return state.angle; } +#pragma mark - Pitch + +void Transform::setPitch(double pitch) { + state.pitch = pitch; +} + +double Transform::getPitch() const { + return state.pitch; +} #pragma mark - Transition diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 57dc8c4534..5d5a72d6d3 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -41,6 +41,10 @@ public: void setAngle(double angle, double cx, double cy); double getAngle() const; + // Pitch + void setPitch(double pitch); + double getPitch() const; + // Transitions Update updateTransitions(const TimePoint& now); void cancelTransitions(); diff --git a/src/mbgl/map/transform_state.cpp b/src/mbgl/map/transform_state.cpp index 9a5e530920..c672ca5dea 100644 --- a/src/mbgl/map/transform_state.cpp +++ b/src/mbgl/map/transform_state.cpp @@ -1,8 +1,9 @@ #include <mbgl/map/transform_state.hpp> #include <mbgl/map/tile_id.hpp> -#include <mbgl/util/projection.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/box.hpp> +#include <mbgl/util/tile_coordinate.hpp> +#include <mbgl/util/interpolate.hpp> using namespace mbgl; @@ -10,52 +11,44 @@ using namespace mbgl; void TransformState::matrixFor(mat4& matrix, const TileID& id, const int8_t z) const { const double tile_scale = std::pow(2, z); - const double tile_size = scale * util::tileSize / tile_scale; + double s = util::tileSize * scale / tile_scale; matrix::identity(matrix); - - matrix::translate(matrix, matrix, 0.5f * (float)width, 0.5f * (float)height, 0); - matrix::rotate_z(matrix, matrix, angle); - matrix::translate(matrix, matrix, -0.5f * (float)width, -0.5f * (float)height, 0); - - matrix::translate(matrix, matrix, pixel_x() + id.x * tile_size, pixel_y() + id.y * tile_size, 0); - - // TODO: Get rid of the 8 (scaling from 4096 to tile size); - float factor = scale / tile_scale / (4096.0f / util::tileSize); - matrix::scale(matrix, matrix, factor, factor, 1); + matrix::translate(matrix, matrix, id.x * s, id.y * s, 0); + matrix::scale(matrix, matrix, s / 4096.0f, s / 4096.0f, 1); } -box TransformState::cornersToBox(uint32_t z) const { - const double ref_scale = std::pow(2, z); - - const double angle_sin = std::sin(-angle); - const double angle_cos = std::cos(-angle); - - const double w_2 = static_cast<double>(width) / 2.0; - const double h_2 = static_cast<double>(height) / 2.0; - const double ss_0 = scale * util::tileSize; - const double ss_1 = ref_scale / ss_0; - const double ss_2 = ss_0 / 2.0; +void TransformState::getProjMatrix(mat4& projMatrix) const { + double halfFov = std::atan(0.5 / getAltitude()); + double topHalfSurfaceDistance = std::sin(halfFov) * getAltitude() / + std::sin(M_PI / 2.0f - getPitch() - halfFov); + // Calculate z value of the farthest fragment that should be rendered. + double farZ = std::cos(M_PI / 2.0f - getPitch()) * topHalfSurfaceDistance + getAltitude(); - // Calculate the corners of the map view. The resulting coordinates will be - // in fractional tile coordinates. - box b; + matrix::perspective(projMatrix, 2.0f * std::atan((getHeight() / 2.0f) / getAltitude()), + double(getWidth()) / getHeight(), 0.1, farZ); - b.tl.x = ((-w_2) * angle_cos - (-h_2) * angle_sin + ss_2 - x) * ss_1; - b.tl.y = ((-w_2) * angle_sin + (-h_2) * angle_cos + ss_2 - y) * ss_1; + matrix::translate(projMatrix, projMatrix, 0, 0, -getAltitude()); - b.tr.x = ((+w_2) * angle_cos - (-h_2) * angle_sin + ss_2 - x) * ss_1; - b.tr.y = ((+w_2) * angle_sin + (-h_2) * angle_cos + ss_2 - y) * ss_1; + // After the rotateX, z values are in pixel units. Convert them to + // altitude unites. 1 altitude unit = the screen height. + matrix::scale(projMatrix, projMatrix, 1, -1, 1.0f / getHeight()); - b.bl.x = ((-w_2) * angle_cos - (+h_2) * angle_sin + ss_2 - x) * ss_1; - b.bl.y = ((-w_2) * angle_sin + (+h_2) * angle_cos + ss_2 - y) * ss_1; + matrix::rotate_x(projMatrix, projMatrix, getPitch()); + matrix::rotate_z(projMatrix, projMatrix, getAngle()); - b.br.x = ((+w_2) * angle_cos - (+h_2) * angle_sin + ss_2 - x) * ss_1; - b.br.y = ((+w_2) * angle_sin + (+h_2) * angle_cos + ss_2 - y) * ss_1; - - b.center.x = (ss_2 - x) * ss_1; - b.center.y = (ss_2 - y) * ss_1; + matrix::translate(projMatrix, projMatrix, pixel_x() - getWidth() / 2.0f, + pixel_y() - getHeight() / 2.0f, 0); +} +box TransformState::cornersToBox(uint32_t z) const { + double w = width; + double h = height; + box b( + pointToCoordinate({ 0, 0 }).zoomTo(z), + pointToCoordinate({ w, 0 }).zoomTo(z), + pointToCoordinate({ w, h }).zoomTo(z), + pointToCoordinate({ 0, h }).zoomTo(z)); return b; } @@ -154,91 +147,134 @@ float TransformState::getAngle() const { return angle; } +float TransformState::getAltitude() const { + return altitude; +} -#pragma mark - Projection - -const vec2<double> TransformState::pixelForLatLng(const LatLng latLng) const { - LatLng ll = getLatLng(); - double zoom = getZoom(); - - const double centerX = static_cast<double>(width) / 2.0; - const double centerY = static_cast<double>(height) / 2.0; - - const double m = Projection::getMetersPerPixelAtLatitude(0, zoom); +float TransformState::getPitch() const { + return pitch; +} - const double angle_sin = std::sin(-angle); - const double angle_cos = std::cos(-angle); +#pragma mark - Projection - const ProjectedMeters givenMeters = Projection::projectedMetersForLatLng(latLng); +float TransformState::lngX(float lng) const { + return (180.0f + lng) * worldSize() / 360.0f; +} - const double givenAbsoluteX = givenMeters.easting / m; - const double givenAbsoluteY = givenMeters.northing / m; +float TransformState::latY(float lat) const { + float y_ = 180.0f / M_PI * std::log(std::tan(M_PI / 4 + lat * M_PI / 360.0f)); + return (180.0f - y_) * worldSize() / 360.0f; +} - const ProjectedMeters centerMeters = Projection::projectedMetersForLatLng(ll); +float TransformState::xLng(float x_, float worldSize_) const { + return x_ * 360.0f / worldSize_ - 180.0f; +} - const double centerAbsoluteX = centerMeters.easting / m; - const double centerAbsoluteY = centerMeters.northing / m; +float TransformState::yLat(float y_, float worldSize_) const { + float y2 = 180.0f - y_ * 360.0f / worldSize_; + return 360.0f / M_PI * std::atan(std::exp(y2 * M_PI / 180.0f)) - 90.0f; +} - const double deltaX = givenAbsoluteX - centerAbsoluteX; - const double deltaY = givenAbsoluteY - centerAbsoluteY; +float TransformState::zoomScale(float zoom) const { + return std::pow(2.0f, zoom); +} - const double translatedX = deltaX + centerX; - const double translatedY = deltaY + centerY; +float TransformState::worldSize() const { + return util::tileSize * scale; +} - const double rotatedX = translatedX * angle_cos - translatedY * angle_sin; - const double rotatedY = translatedX * angle_sin + translatedY * angle_cos; +vec2<double> TransformState::latLngToPoint(const LatLng& latLng) const { + return coordinateToPoint(latLngToCoordinate(latLng)); +} - const double rotatedCenterX = centerX * angle_cos - centerY * angle_sin; - const double rotatedCenterY = centerX * angle_sin + centerY * angle_cos; +LatLng TransformState::pointToLatLng(const vec2<double> point) const { + return coordinateToLatLng(pointToCoordinate(point)); +} - double x_ = rotatedX + (centerX - rotatedCenterX); - double y_ = rotatedY + (centerY - rotatedCenterY); +TileCoordinate TransformState::latLngToCoordinate(const LatLng& latLng) const { + const float tileZoom = getIntegerZoom(); + const float k = zoomScale(tileZoom) / worldSize(); + return { + lngX(latLng.longitude) * k, + latY(latLng.latitude) * k, + tileZoom + }; +} - return vec2<double>(x_, y_); +LatLng TransformState::coordinateToLatLng(const TileCoordinate& coord) const { + const float worldSize_ = zoomScale(coord.zoom); + LatLng latLng = { + yLat(coord.row, worldSize_), + xLng(coord.column, worldSize_) + }; + while (latLng.longitude < 180.0f) latLng.longitude += 360.0f; + while (latLng.longitude > 180.0f) latLng.longitude -= 360.0f; + return latLng; } -const LatLng TransformState::latLngForPixel(const vec2<double> pixel) const { - LatLng ll = getLatLng(); - double zoom = getZoom(); +vec2<double> TransformState::coordinateToPoint(const TileCoordinate& coord) const { + mat4 mat = coordinatePointMatrix(coord.zoom); + matrix::vec4 p; + matrix::vec4 c = {{ coord.column, coord.row, 0, 1 }}; + matrix::transformMat4(p, c, mat); + return { p[0] / p[3], height - p[1] / p[3] }; +} - const double centerX = static_cast<double>(width) / 2.0; - const double centerY = static_cast<double>(height) / 2.0; +TileCoordinate TransformState::pointToCoordinate(const vec2<double> point) const { - const double m = Projection::getMetersPerPixelAtLatitude(0, zoom); + float targetZ = 0; + const float tileZoom = getIntegerZoom(); - const double angle_sin = std::sin(angle); - const double angle_cos = std::cos(angle); + mat4 mat = coordinatePointMatrix(tileZoom); - const double unrotatedCenterX = centerX * angle_cos - centerY * angle_sin; - const double unrotatedCenterY = centerX * angle_sin + centerY * angle_cos; + mat4 inverted; + bool err = matrix::invert(inverted, mat); - const double unrotatedX = pixel.x * angle_cos - pixel.y * angle_sin; - const double unrotatedY = pixel.x * angle_sin + pixel.y * angle_cos; + if (err) throw std::runtime_error("failed to invert coordinatePointMatrix"); - const double givenX = unrotatedX + (centerX - unrotatedCenterX); - const double givenY = unrotatedY + (centerY - unrotatedCenterY); + double flippedY = height - point.y; - const ProjectedMeters centerMeters = Projection::projectedMetersForLatLng(ll); + // since we don't know the correct projected z value for the point, + // unproject two points to get a line and then find the point on that + // line with z=0 - const double centerAbsoluteX = centerMeters.easting / m; - const double centerAbsoluteY = centerMeters.northing / m; + matrix::vec4 coord0; + matrix::vec4 coord1; + matrix::vec4 point0 = {{ point.x, flippedY, 0, 1 }}; + matrix::vec4 point1 = {{ point.x, flippedY, 1, 1 }}; + matrix::transformMat4(coord0, point0, inverted); + matrix::transformMat4(coord1, point1, inverted); - const double givenAbsoluteX = givenX + centerAbsoluteX - centerX; - const double givenAbsoluteY = givenY + centerAbsoluteY - centerY; + float w0 = coord0[3]; + float w1 = coord1[3]; + float x0 = coord0[0] / w0; + float x1 = coord1[0] / w1; + float y0 = coord0[1] / w0; + float y1 = coord1[1] / w1; + float z0 = coord0[2] / w0; + float z1 = coord1[2] / w1; - ProjectedMeters givenMeters = ProjectedMeters(givenAbsoluteY * m, givenAbsoluteX * m); - // adjust for date line - ProjectedMeters sw, ne; - Projection::getWorldBoundsMeters(sw, ne); - double d = ne.easting - sw.easting; - if (ll.longitude > 0 && givenMeters.easting > centerMeters.easting) givenMeters.easting -= d; + float t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0); + + return { util::interpolate(x0, x1, t), util::interpolate(y0, y1, t), tileZoom }; +} - // adjust for world wrap - while (givenMeters.easting < sw.easting) givenMeters.easting += d; - while (givenMeters.easting > ne.easting) givenMeters.easting -= d; +mat4 TransformState::coordinatePointMatrix(float z) const { + mat4 proj; + getProjMatrix(proj); + float s = util::tileSize * scale / std::pow(2, z); + matrix::scale(proj, proj, s , s, 1); + matrix::multiply(proj, getPixelMatrix(), proj); + return proj; +} - return Projection::latLngForProjectedMeters(givenMeters); +mat4 TransformState::getPixelMatrix() const { + mat4 m; + matrix::identity(m); + matrix::scale(m, m, width / 2.0f, -height / 2.0f, 1); + matrix::translate(m, m, 1, -1, 0); + return m; } diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp index 16f2de2f79..d4b46f04b1 100644 --- a/src/mbgl/map/transform_state.hpp +++ b/src/mbgl/map/transform_state.hpp @@ -1,10 +1,11 @@ #ifndef MBGL_MAP_TRANSFORM_STATE #define MBGL_MAP_TRANSFORM_STATE -#include <mbgl/util/mat4.hpp> #include <mbgl/util/geo.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/vec.hpp> +#include <mbgl/util/mat4.hpp> +#include <mbgl/util/vec4.hpp> #include <cstdint> #include <array> @@ -14,6 +15,7 @@ namespace mbgl { class TileID; struct box; +struct TileCoordinate; class TransformState { friend class Transform; @@ -21,6 +23,7 @@ class TransformState { public: // Matrix void matrixFor(mat4& matrix, const TileID& id, const int8_t z) const; + void getProjMatrix(mat4& matrix) const; box cornersToBox(uint32_t z) const; // Dimensions @@ -28,9 +31,6 @@ public: uint16_t getWidth() const; uint16_t getHeight() const; - float worldSize() const; - float lngX(float lon) const; - float latY(float lat) const; std::array<float, 2> locationCoordinate(float lon, float lat) const; void getLonLat(double &lon, double &lat) const; @@ -49,13 +49,26 @@ public: // Rotation float getAngle() const; - // Projection - const vec2<double> pixelForLatLng(const LatLng latLng) const; - const LatLng latLngForPixel(const vec2<double> pixel) const; + float getAltitude() const; + float getPitch() const; // Changing bool isChanging() const; + double pixel_x() const; + double pixel_y() const; + + // Conversion and projection + + vec2<double> latLngToPoint(const LatLng& latLng) const; + LatLng pointToLatLng(const vec2<double> point) const; + + TileCoordinate latLngToCoordinate(const LatLng& latLng) const; + LatLng coordinateToLatLng(const TileCoordinate& coord) const; + + vec2<double> coordinateToPoint(const TileCoordinate& coord) const; + TileCoordinate pointToCoordinate(const vec2<double> point) const; + private: void constrain(double& scale, double& y) const; @@ -63,12 +76,19 @@ private: double min_scale = std::pow(2, 0); double max_scale = std::pow(2, 18); - double pixel_x() const; - double pixel_y() const; - // logical dimensions uint16_t width = 0, height = 0; + float xLng(float x, float worldSize) const; + float yLat(float y, float worldSize) const; + float lngX(float lon) const; + float latY(float lat) const; + float zoomScale(float zoom) const; + float worldSize() const; + + mat4 coordinatePointMatrix(float z) const; + mat4 getPixelMatrix() const; + private: // animation state bool rotating = false; @@ -80,6 +100,8 @@ private: double x = 0, y = 0; double angle = 0; double scale = 1; + double altitude = 1.5; + double pitch = 0.0; // cache values for spherical mercator math double Bc = (scale * util::tileSize) / 360; diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp index c9ee33910b..678a3867ca 100644 --- a/src/mbgl/map/vector_tile_data.cpp +++ b/src/mbgl/map/vector_tile_data.cpp @@ -16,6 +16,7 @@ VectorTileData::VectorTileData(const TileID& id_, Style& style_, const SourceInfo& source_, float angle, + float pitch, bool collisionDebug) : TileData(id_), worker(style_.workers), @@ -25,9 +26,7 @@ VectorTileData::VectorTileData(const TileID& id_, style_, style_.layers, state, - std::make_unique<CollisionTile>(id_.z, 4096, - source_.tile_size * id.overscaling, - angle, collisionDebug)), + std::make_unique<CollisionTile>(angle, pitch, collisionDebug)), source(source_), lastAngle(angle), currentAngle(angle) { @@ -98,11 +97,14 @@ Bucket* VectorTileData::getBucket(const StyleLayer& layer) { return tileWorker.getBucket(layer); } -void VectorTileData::redoPlacement(float angle, bool collisionDebug) { - if (angle == currentAngle && collisionDebug == currentCollisionDebug) +void VectorTileData::redoPlacement(float angle, float pitch, bool collisionDebug) { + if (angle == currentAngle && + pitch == currentPitch && + collisionDebug == currentCollisionDebug) return; lastAngle = angle; + lastPitch = pitch; lastCollisionDebug = collisionDebug; if (state != State::parsed || redoingPlacement) @@ -110,9 +112,10 @@ void VectorTileData::redoPlacement(float angle, bool collisionDebug) { redoingPlacement = true; currentAngle = angle; + currentPitch = pitch; currentCollisionDebug = collisionDebug; - workRequest = worker.redoPlacement(tileWorker, angle, collisionDebug, [this] { + workRequest = worker.redoPlacement(tileWorker, angle, pitch, collisionDebug, [this] { for (const auto& layer : tileWorker.layers) { auto bucket = getBucket(*layer); if (bucket) { @@ -120,7 +123,7 @@ void VectorTileData::redoPlacement(float angle, bool collisionDebug) { } } redoingPlacement = false; - redoPlacement(lastAngle, lastCollisionDebug); + redoPlacement(lastAngle, lastPitch, lastCollisionDebug); }); } diff --git a/src/mbgl/map/vector_tile_data.hpp b/src/mbgl/map/vector_tile_data.hpp index e0e8be29bf..4db0cbe004 100644 --- a/src/mbgl/map/vector_tile_data.hpp +++ b/src/mbgl/map/vector_tile_data.hpp @@ -19,6 +19,7 @@ public: Style&, const SourceInfo&, float angle_, + float pitch_, bool collisionDebug_); ~VectorTileData(); @@ -29,7 +30,7 @@ public: bool reparse(std::function<void ()> callback); - void redoPlacement(float angle, bool collisionDebug) override; + void redoPlacement(float angle, float pitch, bool collisionDebug) override; void cancel() override; @@ -43,6 +44,8 @@ private: std::string data; float lastAngle = 0; float currentAngle; + float lastPitch = 0; + float currentPitch; bool lastCollisionDebug = 0; bool currentCollisionDebug = 0; bool redoingPlacement = false; diff --git a/src/mbgl/renderer/bucket.hpp b/src/mbgl/renderer/bucket.hpp index 4bdb766a7c..eb02f32ebc 100644 --- a/src/mbgl/renderer/bucket.hpp +++ b/src/mbgl/renderer/bucket.hpp @@ -5,6 +5,8 @@ #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/mat4.hpp> +#include <atomic> + #define BUFFER_OFFSET(i) ((char*)nullptr + (i)) namespace mbgl { @@ -12,9 +14,12 @@ namespace mbgl { class Painter; class StyleLayer; class TileID; +class CollisionTile; class Bucket : private util::noncopyable { public: + Bucket() : uploaded(false) {} + // As long as this bucket has a Prepare render pass, this function is getting called. Typically, // this only happens once when the bucket is being rendered for the first time. virtual void upload() = 0; @@ -29,11 +34,11 @@ public: return !uploaded; } - virtual void placeFeatures() {} + virtual void placeFeatures(CollisionTile&) {} virtual void swapRenderData() {} protected: - bool uploaded = false; + std::atomic<bool> uploaded; }; diff --git a/src/mbgl/renderer/painter.cpp b/src/mbgl/renderer/painter.cpp index b55b71dcb1..e757d682a6 100644 --- a/src/mbgl/renderer/painter.cpp +++ b/src/mbgl/renderer/painter.cpp @@ -132,13 +132,11 @@ void Painter::lineWidth(float line_width) { } void Painter::changeMatrix() { - // Initialize projection matrix - matrix::ortho(projMatrix, 0, state.getWidth(), state.getHeight(), 0, -1, 1); + + state.getProjMatrix(projMatrix); // The extrusion matrix. - matrix::identity(extrudeMatrix); - matrix::multiply(extrudeMatrix, projMatrix, extrudeMatrix); - matrix::rotate_z(extrudeMatrix, extrudeMatrix, state.getAngle()); + matrix::ortho(extrudeMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, -1); // The native matrix is a 1:1 matrix that paints the coordinates at the // same screen position as the vertex specifies. @@ -156,10 +154,6 @@ void Painter::clear() { MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); } -void Painter::setStrata(float value) { - strata = value; -} - void Painter::prepareTile(const Tile& tile) { const GLint ref = (GLint)tile.clip.reference.to_ulong(); const GLuint mask = (GLuint)tile.clip.mask.to_ulong(); @@ -184,6 +178,9 @@ void Painter::render(const Style& style, TransformState state_, const FrameData& resize(); changeMatrix(); + // Figure out what buckets we have to draw and what order we have to draw them in. + const auto order = determineRenderOrder(style); + // - UPLOAD PASS ------------------------------------------------------------------------------- // Uploads all required buffers and images before we do any actual rendering. { @@ -202,6 +199,7 @@ void Painter::render(const Style& style, TransformState state_, const FrameData& } } + // - CLIPPING MASKS ---------------------------------------------------------------------------- // Draws the clipping masks to the stencil buffer. { @@ -225,19 +223,19 @@ void Painter::render(const Style& style, TransformState state_, const FrameData& if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; } // TODO: Correctly compute the number of layers recursively beforehand. - const float strataThickness = 1.0f / (order.size() + 1); + depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon; // - OPAQUE PASS ------------------------------------------------------------------------------- // Render everything top-to-bottom by using reverse iterators. Render opaque objects first. renderPass(RenderPass::Opaque, order.rbegin(), order.rend(), - 0, 1, strataThickness); + 0, 1); // - TRANSLUCENT PASS -------------------------------------------------------------------------- // Make a second pass, rendering translucent objects. This time, we render bottom-to-top. renderPass(RenderPass::Translucent, order.begin(), order.end(), - order.size() - 1, -1, strataThickness); + order.size() - 1, -1); if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; } @@ -268,12 +266,9 @@ void Painter::render(const Style& style, TransformState state_, const FrameData& template <class Iterator> void Painter::renderPass(RenderPass pass_, Iterator it, Iterator end, - std::size_t i, int8_t increment, - const float strataThickness) { + std::size_t i, int8_t increment) { pass = pass_; - const double zoom = state.getZoom(); - MBGL_DEBUG_GROUP(pass == RenderPass::Opaque ? "opaque" : "translucent"); if (debug::renderTree) { @@ -284,24 +279,16 @@ void Painter::renderPass(RenderPass pass_, config.blend = pass == RenderPass::Translucent; for (; it != end; ++it, i += increment) { + currentLayer = i; const auto& item = *it; if (item.bucket && item.tile) { - // Skip this layer if it's outside the range of min/maxzoom. - // This may occur when there /is/ a bucket created for this layer, but the min/max-zoom - // is set to a fractional value, or value that is larger than the source maxzoom. - if (item.layer.bucket->min_zoom > zoom || - item.layer.bucket->max_zoom <= zoom) { - continue; - } if (item.layer.hasRenderPass(pass)) { MBGL_DEBUG_GROUP(item.layer.id + " - " + std::string(item.tile->id)); - setStrata(i * strataThickness); prepareTile(*item.tile); item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix); } } else { MBGL_DEBUG_GROUP("background"); - setStrata(i * strataThickness); renderBackground(item.layer); } } @@ -311,8 +298,8 @@ void Painter::renderPass(RenderPass pass_, } } -void Painter::updateRenderOrder(const Style& style) { - order.clear(); +std::vector<RenderItem> Painter::determineRenderOrder(const Style& style) { + std::vector<RenderItem> order; for (const auto& layerPtr : style.layers) { const auto& layer = *layerPtr; @@ -349,6 +336,15 @@ void Painter::updateRenderOrder(const Style& style) { continue; } + // Skip this layer if it's outside the range of min/maxzoom. + // This may occur when there /is/ a bucket created for this layer, but the min/max-zoom + // is set to a fractional value, or value that is larger than the source maxzoom. + const double zoom = state.getZoom(); + if (layer.bucket->min_zoom > zoom || + layer.bucket->max_zoom <= zoom) { + continue; + } + const auto& tiles = source->getTiles(); for (auto tile : tiles) { assert(tile); @@ -362,6 +358,8 @@ void Painter::updateRenderOrder(const Style& style) { } } } + + return order; } void Painter::renderBackground(const StyleLayer &layer_desc) { @@ -387,7 +385,7 @@ void Painter::renderBackground(const StyleLayer &layer_desc) { patternShader->u_opacity = properties.opacity; LatLng latLng = state.getLatLng(); - vec2<double> center = state.pixelForLatLng(latLng); + vec2<double> center = state.latLngToPoint(latLng); float scale = 1 / std::pow(2, zoomFraction); std::array<float, 2> sizeA = imagePosA.size; @@ -428,7 +426,8 @@ void Painter::renderBackground(const StyleLayer &layer_desc) { config.stencilTest = false; config.depthTest = true; - config.depthRange = { strata + strata_epsilon, 1.0f }; + config.depthRange = { 1.0f, 1.0f }; + MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); } @@ -457,3 +456,9 @@ mat4 Painter::translatedMatrix(const mat4& matrix, const std::array<float, 2> &t return vtxMatrix; } } + +void Painter::setDepthSublayer(int n) { + float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon; + float farDepth = nearDepth + depthRangeSize; + config.depthRange = { nearDepth, farDepth }; +} diff --git a/src/mbgl/renderer/painter.hpp b/src/mbgl/renderer/painter.hpp index 78cf308b7b..9c0f5db8ca 100644 --- a/src/mbgl/renderer/painter.hpp +++ b/src/mbgl/renderer/painter.hpp @@ -123,9 +123,6 @@ public: // Changes whether debug information is drawn onto the map void setDebug(bool enabled); - // Configures the painter strata that is used for early z-culling of fragments. - void setStrata(float strata); - void drawClippingMasks(const std::set<Source*>&); void drawClippingMask(const mat4& matrix, const ClipID& clip); @@ -137,17 +134,16 @@ public: bool needsAnimation() const; - void updateRenderOrder(const Style& style); - private: void setupShaders(); mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor); + std::vector<RenderItem> determineRenderOrder(const Style& style); + template <class Iterator> void renderPass(RenderPass, Iterator it, Iterator end, - std::size_t i, int8_t iIncrement, - const float strataThickness); + std::size_t i, int8_t increment); void prepareTile(const Tile& tile); @@ -162,6 +158,8 @@ private: SDFShader& sdfShader, void (SymbolBucket::*drawSDF)(SDFShader&)); + void setDepthSublayer(int n); + public: void useProgram(uint32_t program); void lineWidth(float lineWidth); @@ -199,12 +197,13 @@ private: uint32_t gl_program = 0; float gl_lineWidth = 0; std::array<uint16_t, 2> gl_viewport = {{ 0, 0 }}; - float strata = 0; RenderPass pass = RenderPass::Opaque; - const float strata_epsilon = 1.0f / (1 << 16); Color background = {{ 0, 0, 0, 0 }}; - std::vector<RenderItem> order; + int numSublayers = 3; + size_t currentLayer; + float depthRangeSize; + const float depthEpsilon = 1.0f / (1 << 16); public: FrameHistory frameHistory; diff --git a/src/mbgl/renderer/painter_circle.cpp b/src/mbgl/renderer/painter_circle.cpp index 0c0ac15069..538ba9e6ab 100644 --- a/src/mbgl/renderer/painter_circle.cpp +++ b/src/mbgl/renderer/painter_circle.cpp @@ -40,7 +40,7 @@ void Painter::renderCircle(CircleBucket& bucket, useProgram(circleShader->program); circleShader->u_matrix = vtxMatrix; - circleShader->u_exmatrix = projMatrix; + circleShader->u_exmatrix = extrudeMatrix; circleShader->u_color = color; circleShader->u_blur = std::max(properties.blur, antialiasing); circleShader->u_size = properties.radius; diff --git a/src/mbgl/renderer/painter_fill.cpp b/src/mbgl/renderer/painter_fill.cpp index 414ec2bbcb..d27feb4693 100644 --- a/src/mbgl/renderer/painter_fill.cpp +++ b/src/mbgl/renderer/painter_fill.cpp @@ -55,7 +55,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const static_cast<float>(frame.framebufferSize[0]), static_cast<float>(frame.framebufferSize[1]) }}; - config.depthRange = { strata, 1.0f }; + setDepthSublayer(0); bucket.drawVertices(*outlineShader); } @@ -95,7 +95,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const // Draw the actual triangles into the color & stencil buffer. config.depthMask = GL_TRUE; - config.depthRange = { strata, 1.0f }; + setDepthSublayer(0); bucket.drawElements(*patternShader); } } @@ -112,7 +112,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const // Draw the actual triangles into the color & stencil buffer. config.depthMask = GL_TRUE; - config.depthRange = { strata + strata_epsilon, 1.0f }; + setDepthSublayer(1); bucket.drawElements(*plainShader); } } @@ -132,7 +132,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const static_cast<float>(frame.framebufferSize[1]) }}; - config.depthRange = { strata + strata_epsilon + strata_epsilon, 1.0f }; + setDepthSublayer(2); bucket.drawVertices(*outlineShader); } } diff --git a/src/mbgl/renderer/painter_line.cpp b/src/mbgl/renderer/painter_line.cpp index f406c0d8f1..258bab6aef 100644 --- a/src/mbgl/renderer/painter_line.cpp +++ b/src/mbgl/renderer/painter_line.cpp @@ -11,6 +11,7 @@ #include <mbgl/shader/linepattern_shader.hpp> #include <mbgl/geometry/sprite_atlas.hpp> #include <mbgl/geometry/line_atlas.hpp> +#include <mbgl/util/mat2.hpp> using namespace mbgl; @@ -51,9 +52,22 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const color[2] *= properties.opacity; color[3] *= properties.opacity; + float ratio = state.getScale() / std::pow(2, id.z) / 8.0 * id.overscaling; + + mat2 antialiasingMatrix; + matrix::identity(antialiasingMatrix); + matrix::scale(antialiasingMatrix, antialiasingMatrix, 1.0, std::cos(state.getPitch())); + matrix::rotate(antialiasingMatrix, antialiasingMatrix, state.getAngle()); + + // calculate how much longer the real world distance is at the top of the screen + // than at the middle of the screen. + float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4 * (1 + std::pow(state.getAltitude(), 2))); + float x = state.getHeight() / 2.0f * std::tan(state.getPitch()); + float extra = (topedgelength + x) / topedgelength - 1; + mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor); - config.depthRange = { strata, 1.0f }; + setDepthSublayer(0); if (!properties.dash_array.from.empty()) { @@ -62,7 +76,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linesdfShader->u_matrix = vtxMatrix; linesdfShader->u_exmatrix = extrudeMatrix; linesdfShader->u_linewidth = {{ outset, inset }}; - linesdfShader->u_ratio = data.pixelRatio; + linesdfShader->u_ratio = ratio; linesdfShader->u_blur = blur; linesdfShader->u_color = color; @@ -83,6 +97,8 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linesdfShader->u_image = 0; linesdfShader->u_sdfgamma = lineAtlas->width / (properties.dash_line_width * std::min(posA.width, posB.width) * 256.0 * data.pixelRatio) / 2; linesdfShader->u_mix = properties.dash_array.t; + linesdfShader->u_extra = extra; + linesdfShader->u_antialiasingmatrix = antialiasingMatrix; bucket.drawLineSDF(*linesdfShader); @@ -97,7 +113,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linepatternShader->u_matrix = vtxMatrix; linepatternShader->u_exmatrix = extrudeMatrix; linepatternShader->u_linewidth = {{ outset, inset }}; - linepatternShader->u_ratio = data.pixelRatio; + linepatternShader->u_ratio = ratio; linepatternShader->u_blur = blur; linepatternShader->u_pattern_size_a = {{imagePosA.size[0] * factor * properties.image.fromScale, imagePosA.size[1]}}; @@ -108,10 +124,11 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const linepatternShader->u_pattern_br_b = imagePosB.br; linepatternShader->u_fade = properties.image.t; linepatternShader->u_opacity = properties.opacity; + linepatternShader->u_extra = extra; + linepatternShader->u_antialiasingmatrix = antialiasingMatrix; MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); spriteAtlas->bind(true); - config.depthRange = { strata + strata_epsilon, 1.0f }; // may or may not matter bucket.drawLinePatterns(*linepatternShader); @@ -121,8 +138,10 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const lineShader->u_matrix = vtxMatrix; lineShader->u_exmatrix = extrudeMatrix; lineShader->u_linewidth = {{ outset, inset }}; - lineShader->u_ratio = data.pixelRatio; + lineShader->u_ratio = ratio; lineShader->u_blur = blur; + lineShader->u_extra = extra; + lineShader->u_antialiasingmatrix = antialiasingMatrix; lineShader->u_color = color; diff --git a/src/mbgl/renderer/painter_raster.cpp b/src/mbgl/renderer/painter_raster.cpp index 2a8f8e7078..70cbcb4ca5 100644 --- a/src/mbgl/renderer/painter_raster.cpp +++ b/src/mbgl/renderer/painter_raster.cpp @@ -24,7 +24,7 @@ void Painter::renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, c config.stencilTest = true; config.depthTest = true; - config.depthRange = { strata + strata_epsilon, 1.0f }; + setDepthSublayer(0); bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray); } } diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 5c2db77096..c6ea372398 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -28,25 +28,40 @@ void Painter::renderSDF(SymbolBucket &bucket, { mat4 vtxMatrix = translatedMatrix(matrix, styleProperties.translate, id, styleProperties.translate_anchor); - mat4 exMatrix; - matrix::copy(exMatrix, projMatrix); - bool aligned_with_map = (bucketProperties.rotation_alignment == RotationAlignmentType::Map); - const float angleOffset = aligned_with_map ? state.getAngle() : 0; - - if (angleOffset) { - matrix::rotate_z(exMatrix, exMatrix, angleOffset); + bool skewed = aligned_with_map; + mat4 exMatrix; + float s; + float gammaScale; + + if (skewed) { + matrix::identity(exMatrix); + s = 4096.0f / util::tileSize / id.overscaling / std::pow(2, state.getZoom() - id.z); + gammaScale = 1.0f / std::cos(state.getPitch()); + } else { + exMatrix = extrudeMatrix; + s = state.getAltitude(); + gammaScale = 1.0f; } + matrix::scale(exMatrix, exMatrix, s, s, 1); // If layerStyle.size > bucket.info.fontSize then labels may collide float fontSize = styleProperties.size; float fontScale = fontSize / sdfFontSize; matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f); + // calculate how much longer the real world distance is at the top of the screen + // than at the middle of the screen. + float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2))); + float x = state.getHeight() / 2.0f * std::tan(state.getPitch()); + float extra = (topedgelength + x) / topedgelength - 1; + useProgram(sdfShader.program); sdfShader.u_matrix = vtxMatrix; sdfShader.u_exmatrix = exMatrix; sdfShader.u_texsize = texsize; + sdfShader.u_skewed = skewed; + sdfShader.u_extra = extra; // adjust min/max zooms for variable font sies float zoomAdjust = std::log(fontSize / bucketProperties.size) / std::log(2); @@ -70,7 +85,7 @@ void Painter::renderSDF(SymbolBucket &bucket, // We're drawing in the translucent pass which is bottom-to-top, so we need // to draw the halo first. if (styleProperties.halo_color[3] > 0.0f && styleProperties.halo_width > 0.0f) { - sdfShader.u_gamma = styleProperties.halo_blur * blurOffset / fontScale / sdfPx + gamma; + sdfShader.u_gamma = (styleProperties.halo_blur * blurOffset / fontScale / sdfPx + gamma) * gammaScale; if (styleProperties.opacity < 1.0f) { Color color = styleProperties.halo_color; @@ -85,13 +100,13 @@ void Painter::renderSDF(SymbolBucket &bucket, sdfShader.u_buffer = (haloOffset - styleProperties.halo_width / fontScale) / sdfPx; - config.depthRange = { strata, 1.0f }; + setDepthSublayer(0); (bucket.*drawSDF)(sdfShader); } // Then, we draw the text/icon over the halo if (styleProperties.color[3] > 0.0f) { - sdfShader.u_gamma = gamma; + sdfShader.u_gamma = gamma * gammaScale; if (styleProperties.opacity < 1.0f) { Color color = styleProperties.color; @@ -106,7 +121,7 @@ void Painter::renderSDF(SymbolBucket &bucket, sdfShader.u_buffer = (256.0f - 64.0f) / 256.0f; - config.depthRange = { strata + strata_epsilon, 1.0f }; + setDepthSublayer(1); (bucket.*drawSDF)(sdfShader); } } @@ -120,7 +135,6 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c const auto &properties = layer_desc.getProperties<SymbolProperties>(); const auto &layout = bucket.layout; - config.depthTest = true; config.depthMask = GL_FALSE; if (bucket.hasCollisionBoxData()) { @@ -133,7 +147,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c collisionBoxShader->u_maxzoom = (id.z + 1) * 10; lineWidth(1.0f); - config.depthRange = { strata, 1.0f }; + setDepthSublayer(0); bucket.drawCollisionBoxes(*collisionBoxShader); } @@ -150,6 +164,8 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c config.stencilTest = drawAcrossEdges ? false : true; if (bucket.hasIconData()) { + config.depthTest = layout.icon.rotation_alignment == RotationAlignmentType::Map; + bool sdf = bucket.sdfIcons; const float angleOffset = @@ -160,7 +176,8 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c const float fontSize = properties.icon.size; const float fontScale = fontSize / 1.0f; - spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1 || sdf); + spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line + || angleOffset != 0 || fontScale != 1 || sdf || state.getPitch() != 0); if (sdf) { renderSDF(bucket, @@ -175,19 +192,33 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c } else { mat4 vtxMatrix = translatedMatrix(matrix, properties.icon.translate, id, properties.icon.translate_anchor); + bool skewed = layout.icon.rotation_alignment == RotationAlignmentType::Map; mat4 exMatrix; - matrix::copy(exMatrix, projMatrix); - - if (angleOffset) { - matrix::rotate_z(exMatrix, exMatrix, angleOffset); + float s; + + if (skewed) { + matrix::identity(exMatrix); + s = 4096.0f / util::tileSize / id.overscaling / std::pow(2, state.getZoom() - id.z); + } else { + exMatrix = extrudeMatrix; + s = state.getAltitude(); } + matrix::scale(exMatrix, exMatrix, s, s, 1); matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f); + // calculate how much longer the real world distance is at the top of the screen + // than at the middle of the screen. + float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2))); + float x = state.getHeight() / 2.0f * std::tan(state.getPitch()); + float extra = (topedgelength + x) / topedgelength - 1; + useProgram(iconShader->program); iconShader->u_matrix = vtxMatrix; iconShader->u_exmatrix = exMatrix; iconShader->u_texsize = {{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }}; + iconShader->u_skewed = skewed; + iconShader->u_extra = extra; // adjust min/max zooms for variable font sies float zoomAdjust = std::log(fontSize / layout.icon.size) / std::log(2); @@ -199,12 +230,14 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c iconShader->u_fadezoom = state.getNormalizedZoom() * 10; iconShader->u_opacity = properties.icon.opacity; - config.depthRange = { strata, 1.0f }; + setDepthSublayer(0); bucket.drawIcons(*iconShader); } } if (bucket.hasTextData()) { + config.depthTest = layout.text.rotation_alignment == RotationAlignmentType::Map; + glyphAtlas->bind(); renderSDF(bucket, diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index e8ad132c51..7665fd9a6d 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -53,8 +53,8 @@ SymbolInstance::SymbolInstance(Anchor &anchor, const std::vector<Coordinate> &li iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconAlongLine) {}; -SymbolBucket::SymbolBucket(CollisionTile &collision_, float overscaling_) - : collision(collision_), overscaling(overscaling_) { +SymbolBucket::SymbolBucket(float overscaling_, float zoom_) + : overscaling(overscaling_), zoom(zoom_), tileSize(512 * overscaling_), tilePixelRatio(tileExtent / tileSize) { } SymbolBucket::~SymbolBucket() { @@ -174,7 +174,8 @@ bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer, void SymbolBucket::addFeatures(uintptr_t tileUID, SpriteAtlas& spriteAtlas, GlyphAtlas& glyphAtlas, - GlyphStore& glyphStore) { + GlyphStore& glyphStore, + CollisionTile& collisionTile) { float horizontalAlign = 0.5; float verticalAlign = 0.5; @@ -264,7 +265,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID, features.clear(); - placeFeatures(true); + placeFeatures(collisionTile, true); } @@ -275,13 +276,13 @@ void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines, const float glyphSize = 24.0f; const float fontScale = layout.text.size / glyphSize; - const float textBoxScale = collision.tilePixelRatio * fontScale; - const float textMaxBoxScale = collision.tilePixelRatio * layout.text.max_size / glyphSize; - const float iconBoxScale = collision.tilePixelRatio * layout.icon.size; - const float symbolSpacing = collision.tilePixelRatio * layout.spacing; + const float textBoxScale = tilePixelRatio * fontScale; + const float textMaxBoxScale = tilePixelRatio * layout.text.max_size / glyphSize; + const float iconBoxScale = tilePixelRatio * layout.icon.size; + const float symbolSpacing = tilePixelRatio * layout.spacing; const bool avoidEdges = layout.avoid_edges && layout.placement != PlacementType::Line; - const float textPadding = layout.text.padding * collision.tilePixelRatio; - const float iconPadding = layout.icon.padding * collision.tilePixelRatio; + const float textPadding = layout.text.padding * tilePixelRatio; + const float iconPadding = layout.icon.padding * tilePixelRatio; const float textMaxAngle = layout.text.max_angle * M_PI / 180; const bool textAlongLine = layout.text.rotation_alignment == RotationAlignmentType::Map && @@ -354,11 +355,11 @@ bool SymbolBucket::anchorIsTooClose(const std::u32string &text, const float repe return false; } -void SymbolBucket::placeFeatures() { - placeFeatures(false); +void SymbolBucket::placeFeatures(CollisionTile& collisionTile) { + placeFeatures(collisionTile, false); } -void SymbolBucket::placeFeatures(bool swapImmediately) { +void SymbolBucket::placeFeatures(CollisionTile& collisionTile, bool swapImmediately) { renderDataInProgress = std::make_unique<SymbolRenderData>(); @@ -380,8 +381,8 @@ void SymbolBucket::placeFeatures(bool swapImmediately) { // Don't sort symbols that won't overlap because it isn't necessary and // because it causes more labels to pop in and out when rotating. if (mayOverlap) { - float sin = std::sin(collision.angle); - float cos = std::cos(collision.angle); + float sin = std::sin(collisionTile.angle); + float cos = std::cos(collisionTile.angle); std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) { const float aRotated = sin * a.x + cos * a.y; @@ -401,9 +402,9 @@ void SymbolBucket::placeFeatures(bool swapImmediately) { // Calculate the scales at which the text and icon can be placed without collision. float glyphScale = hasText && !layout.text.allow_overlap ? - collision.placeFeature(symbolInstance.textCollisionFeature) : collision.minScale; + collisionTile.placeFeature(symbolInstance.textCollisionFeature) : collisionTile.minScale; float iconScale = hasIcon && !layout.icon.allow_overlap ? - collision.placeFeature(symbolInstance.iconCollisionFeature) : collision.minScale; + collisionTile.placeFeature(symbolInstance.iconCollisionFeature) : collisionTile.minScale; // Combine the scales for icons and text. @@ -421,33 +422,32 @@ void SymbolBucket::placeFeatures(bool swapImmediately) { if (hasText) { if (!layout.text.ignore_placement) { - collision.insertFeature(symbolInstance.textCollisionFeature, glyphScale); + collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale); } - if (glyphScale < collision.maxScale) { + if (glyphScale < collisionTile.maxScale) { addSymbols<SymbolRenderData::TextBuffer, TextElementGroup>(renderDataInProgress->text, - symbolInstance.glyphQuads, glyphScale, layout.text.keep_upright, textAlongLine); + symbolInstance.glyphQuads, glyphScale, layout.text.keep_upright, textAlongLine, collisionTile.angle); } } if (hasIcon) { if (!layout.icon.ignore_placement) { - collision.insertFeature(symbolInstance.iconCollisionFeature, iconScale); + collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale); } - if (iconScale < collision.maxScale) { + if (iconScale < collisionTile.maxScale) { addSymbols<SymbolRenderData::IconBuffer, IconElementGroup>(renderDataInProgress->icon, - symbolInstance.iconQuads, iconScale, layout.icon.keep_upright, iconAlongLine); + symbolInstance.iconQuads, iconScale, layout.icon.keep_upright, iconAlongLine, collisionTile.angle); } } } - if (collision.getDebug()) addToDebugBuffers(); + if (collisionTile.getDebug()) addToDebugBuffers(collisionTile); if (swapImmediately) swapRenderData(); } template <typename Buffer, typename GroupType> -void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const bool alongLine) { - const float zoom = collision.zoom; +void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const bool alongLine, const float placementAngle) { const float placementZoom = ::fmax(std::log(scale) / std::log(2) + zoom, 0); @@ -461,10 +461,10 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float 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; + const auto &anchorPoint = symbol.anchorPoint; // drop upside down versions of glyphs - const float a = std::fmod(symbol.angle + collision.angle + M_PI, M_PI * 2); + const float a = std::fmod(symbol.angle + placementAngle + M_PI, M_PI * 2); if (keepUpright && alongLine && (a <= M_PI / 2 || a > M_PI * 3 / 2)) continue; @@ -491,13 +491,13 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float uint32_t triangleIndex = triangleGroup.vertex_length; // coordinates (2 triangles) - buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, minZoom, + buffer.vertices.add(anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom, maxZoom, placementZoom); - buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y, + buffer.vertices.add(anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y, minZoom, maxZoom, placementZoom); - buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h, + buffer.vertices.add(anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h, minZoom, maxZoom, placementZoom); - buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, + buffer.vertices.add(anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h, minZoom, maxZoom, placementZoom); // add the two triangles, referencing the four coordinates we just inserted. @@ -509,11 +509,10 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float } } -void SymbolBucket::addToDebugBuffers() { +void SymbolBucket::addToDebugBuffers(CollisionTile &collisionTile) { - const float yStretch = 1.0f; - const float angle = collision.angle; - const float zoom = collision.zoom; + const float yStretch = collisionTile.yStretch; + const float angle = collisionTile.angle; float angle_sin = std::sin(-angle); float angle_cos = std::cos(-angle); std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index 3a181be55d..c8264bc438 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -64,7 +64,7 @@ class SymbolBucket : public Bucket { typedef ElementGroup<1> CollisionBoxElementGroup; public: - SymbolBucket(CollisionTile &collision, float overscaling); + SymbolBucket(float overscaling, float zoom); ~SymbolBucket() override; void upload() override; @@ -77,7 +77,8 @@ public: void addFeatures(uintptr_t tileUID, SpriteAtlas&, GlyphAtlas&, - GlyphStore&); + GlyphStore&, + CollisionTile&); void drawGlyphs(SDFShader& shader); void drawIcons(SDFShader& shader); @@ -88,7 +89,7 @@ public: const FilterExpression&, GlyphStore&, Sprite&); - void placeFeatures() override; + void placeFeatures(CollisionTile&) override; private: void addFeature(const std::vector<std::vector<Coordinate>> &lines, @@ -97,22 +98,28 @@ private: bool anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor); std::map<std::u32string, std::vector<Anchor>> compareText; - void addToDebugBuffers(); + void addToDebugBuffers(CollisionTile &collisionTile); - void placeFeatures(bool swapImmediately); + void placeFeatures(CollisionTile& collisionTile, bool swapImmediately); void swapRenderData() override; // Adds placed items to the buffer. template <typename Buffer, typename GroupType> - void addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const bool alongLine); + void addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, + const bool keepUpright, const bool alongLine, const float placementAngle); public: StyleLayoutSymbol layout; bool sdfIcons = false; private: - CollisionTile &collision; + const float overscaling; + const float zoom; + const float tileSize; + const float tileExtent = 4096.0f; + const float tilePixelRatio; + std::vector<SymbolInstance> symbolInstances; std::vector<SymbolFeature> features; diff --git a/src/mbgl/shader/icon.vertex.glsl b/src/mbgl/shader/icon.vertex.glsl index 549570021d..3850e01ab7 100644 --- a/src/mbgl/shader/icon.vertex.glsl +++ b/src/mbgl/shader/icon.vertex.glsl @@ -14,6 +14,8 @@ uniform float u_minfadezoom; uniform float u_maxfadezoom; uniform float u_fadezoom; uniform float u_opacity; +uniform bool u_skewed; +uniform float u_extra; uniform vec2 u_texsize; @@ -51,7 +53,15 @@ void main() { // if label has been faded out, clip it z += step(v_alpha, 0.0); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0); + if (u_skewed) { + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0); + gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1); + gl_Position.z += z * gl_Position.w; + } else { + vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, z, 0); + gl_Position = u_matrix * vec4(a_pos, 0, 1) + extrude; + } + v_tex = a_tex / u_texsize; v_alpha *= u_opacity; diff --git a/src/mbgl/shader/icon_shader.hpp b/src/mbgl/shader/icon_shader.hpp index b6156ae9a0..18b0fce37b 100644 --- a/src/mbgl/shader/icon_shader.hpp +++ b/src/mbgl/shader/icon_shader.hpp @@ -21,6 +21,8 @@ public: Uniform<float> u_fadezoom = {"u_fadezoom", *this}; Uniform<float> u_opacity = {"u_opacity", *this}; Uniform<std::array<float, 2>> u_texsize = {"u_texsize", *this}; + Uniform<int32_t> u_skewed = {"u_skewed", *this}; + Uniform<float> u_extra = {"u_extra", *this}; private: int32_t a_pos = -1; diff --git a/src/mbgl/shader/line.fragment.glsl b/src/mbgl/shader/line.fragment.glsl index 717c46e10d..e0ef649965 100644 --- a/src/mbgl/shader/line.fragment.glsl +++ b/src/mbgl/shader/line.fragment.glsl @@ -3,6 +3,7 @@ uniform vec4 u_color; uniform float u_blur; varying vec2 v_normal; +varying float v_gamma_scale; void main() { // Calculate the distance of the pixel from the line in pixels. @@ -11,7 +12,8 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_linewidth.t) or when fading out // (v_linewidth.s) - float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0); + float blur = u_blur * v_gamma_scale; + float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0); gl_FragColor = u_color * alpha; } diff --git a/src/mbgl/shader/line.vertex.glsl b/src/mbgl/shader/line.vertex.glsl index 1f5432991c..980212384b 100644 --- a/src/mbgl/shader/line.vertex.glsl +++ b/src/mbgl/shader/line.vertex.glsl @@ -9,17 +9,17 @@ attribute vec2 a_pos; attribute vec4 a_data; -// matrix is for the vertex position, exmatrix is for rotating and projecting -// the extrusion vector. uniform mat4 u_matrix; -uniform mat4 u_exmatrix; // shared uniform float u_ratio; uniform vec2 u_linewidth; -uniform vec4 u_color; + +uniform float u_extra; +uniform mat2 u_antialiasingmatrix; varying vec2 v_normal; +varying float v_gamma_scale; void main() { vec2 a_extrude = a_data.xy; @@ -34,11 +34,22 @@ void main() { // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. - vec4 dist = vec4(u_linewidth.s * a_extrude * scale, 0.0, 0.0); + vec2 dist = u_linewidth.s * a_extrude * scale; // Remove the texture normal bit of the position before scaling it with the // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist; + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist / u_ratio, 0.0, 1.0); + + // position of y on the screen + float y = gl_Position.y / gl_Position.w; + + // how much features are squished in the y direction by the tilt + float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); + + // how much features are squished in all directions by the perspectiveness + float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.90)); + + v_gamma_scale = perspective_scale * squish_scale; } diff --git a/src/mbgl/shader/line_shader.hpp b/src/mbgl/shader/line_shader.hpp index 85152d6e0c..0572cac88d 100644 --- a/src/mbgl/shader/line_shader.hpp +++ b/src/mbgl/shader/line_shader.hpp @@ -18,6 +18,8 @@ public: Uniform<std::array<float, 2>> u_linewidth = {"u_linewidth", *this}; Uniform<float> u_ratio = {"u_ratio", *this}; Uniform<float> u_blur = {"u_blur", *this}; + Uniform<float> u_extra = {"u_extra", *this}; + UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this}; private: int32_t a_pos = -1; diff --git a/src/mbgl/shader/linepattern.fragment.glsl b/src/mbgl/shader/linepattern.fragment.glsl index 08feff4396..4110a99ead 100644 --- a/src/mbgl/shader/linepattern.fragment.glsl +++ b/src/mbgl/shader/linepattern.fragment.glsl @@ -1,5 +1,4 @@ uniform vec2 u_linewidth; -uniform float u_point; uniform float u_blur; uniform vec2 u_pattern_size_a; @@ -15,17 +14,17 @@ uniform sampler2D u_image; varying vec2 v_normal; varying float v_linesofar; +varying float v_gamma_scale; void main() { // Calculate the distance of the pixel from the line in pixels. - float dist = length(v_normal) * (1.0 - u_point) + u_point * length(gl_PointCoord * 2.0 - 1.0); - - dist *= u_linewidth.s; + float dist = length(v_normal) * u_linewidth.s; // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_linewidth.t) or when fading out // (v_linewidth.s) - float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0); + float blur = u_blur * v_gamma_scale; + float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0); float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0); float y_a = 0.5 + (v_normal.y * u_linewidth.s / u_pattern_size_a.y); diff --git a/src/mbgl/shader/linepattern.vertex.glsl b/src/mbgl/shader/linepattern.vertex.glsl index ada384a3d1..541c9fc3a7 100644 --- a/src/mbgl/shader/linepattern.vertex.glsl +++ b/src/mbgl/shader/linepattern.vertex.glsl @@ -18,10 +18,13 @@ uniform mat4 u_exmatrix; uniform float u_ratio; uniform vec2 u_linewidth; uniform vec4 u_color; -uniform float u_point; + +uniform float u_extra; +uniform mat2 u_antialiasingmatrix; varying vec2 v_normal; varying float v_linesofar; +varying float v_gamma_scale; void main() { vec2 a_extrude = a_data.xy; @@ -37,24 +40,23 @@ void main() { // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. - vec2 extrude = a_extrude * scale; - vec2 dist = u_linewidth.s * extrude * (1.0 - u_point); - - // If the x coordinate is the maximum integer, we move the z coordinates out - // of the view plane so that the triangle gets clipped. This makes it easier - // for us to create degenerate triangle strips. - float z = step(32767.0, a_pos.x); - - // When drawing points, skip every other vertex - z += u_point * step(1.0, v_normal.y); + vec2 dist = u_linewidth.s * a_extrude * scale; // Remove the texture normal bit of the position before scaling it with the // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0) + u_exmatrix * vec4(dist, z, 0.0); - v_linesofar = a_linesofar;// * u_ratio; + gl_Position = u_matrix * vec4(floor(a_pos / 2.0) + dist.xy / u_ratio, 0.0, 1.0); + v_linesofar = a_linesofar; + + // position of y on the screen + float y = gl_Position.y / gl_Position.w; + + // how much features are squished in the y direction by the tilt + float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); + // how much features are squished in all directions by the perspectiveness + float perspective_scale = 1.0 / (1.0 - y * u_extra); - gl_PointSize = 2.0 * u_linewidth.s - 1.0; + v_gamma_scale = perspective_scale * squish_scale; } diff --git a/src/mbgl/shader/linepattern_shader.hpp b/src/mbgl/shader/linepattern_shader.hpp index 16d9ff8be7..dce065a0d5 100644 --- a/src/mbgl/shader/linepattern_shader.hpp +++ b/src/mbgl/shader/linepattern_shader.hpp @@ -26,6 +26,8 @@ public: Uniform<float> u_blur = {"u_blur", *this}; Uniform<float> u_fade = {"u_fade", *this}; Uniform<float> u_opacity = {"u_opacity", *this}; + Uniform<float> u_extra = {"u_extra", *this}; + UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this}; private: int32_t a_pos = -1; diff --git a/src/mbgl/shader/linesdf.fragment.glsl b/src/mbgl/shader/linesdf.fragment.glsl index 5d53dbe883..f8962fa570 100644 --- a/src/mbgl/shader/linesdf.fragment.glsl +++ b/src/mbgl/shader/linesdf.fragment.glsl @@ -8,6 +8,7 @@ uniform float u_mix; varying vec2 v_normal; varying vec2 v_tex_a; varying vec2 v_tex_b; +varying float v_gamma_scale; void main() { // Calculate the distance of the pixel from the line in pixels. @@ -16,7 +17,8 @@ void main() { // Calculate the antialiasing fade factor. This is either when fading in // the line in case of an offset line (v_linewidth.t) or when fading out // (v_linewidth.s) - float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0); + float blur = u_blur * v_gamma_scale; + float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0); float sdfdist_a = texture2D(u_image, v_tex_a).a; float sdfdist_b = texture2D(u_image, v_tex_b).a; diff --git a/src/mbgl/shader/linesdf.vertex.glsl b/src/mbgl/shader/linesdf.vertex.glsl index 7f246ced67..03851ed533 100644 --- a/src/mbgl/shader/linesdf.vertex.glsl +++ b/src/mbgl/shader/linesdf.vertex.glsl @@ -22,9 +22,13 @@ uniform float u_tex_y_a; uniform vec2 u_patternscale_b; uniform float u_tex_y_b; +uniform float u_extra; +uniform mat2 u_antialiasingmatrix; + varying vec2 v_normal; varying vec2 v_tex_a; varying vec2 v_tex_b; +varying float v_gamma_scale; void main() { vec2 a_extrude = a_data.xy; @@ -40,14 +44,25 @@ void main() { // Scale the extrusion vector down to a normal and then up by the line width // of this vertex. - vec4 dist = vec4(u_linewidth.s * a_extrude * scale, 0.0, 0.0); + vec2 dist = u_linewidth.s * a_extrude * scale; // Remove the texture normal bit of the position before scaling it with the // model/view matrix. Add the extrusion vector *after* the model/view matrix // because we're extruding the line in pixel space, regardless of the current // tile's zoom level. - gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist; + gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist / u_ratio, 0.0, 1.0); v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a); v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b); + + // position of y on the screen + float y = gl_Position.y / gl_Position.w; + + // how much features are squished in the y direction by the tilt + float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude); + + // how much features are squished in all directions by the perspectiveness + float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.9)); + + v_gamma_scale = perspective_scale * squish_scale; } diff --git a/src/mbgl/shader/linesdf_shader.hpp b/src/mbgl/shader/linesdf_shader.hpp index 16b7f3cb1b..9928d5b304 100644 --- a/src/mbgl/shader/linesdf_shader.hpp +++ b/src/mbgl/shader/linesdf_shader.hpp @@ -25,6 +25,8 @@ public: Uniform<int32_t> u_image = {"u_image", *this}; Uniform<float> u_sdfgamma = {"u_sdfgamma", *this}; Uniform<float> u_mix = {"u_mix", *this}; + Uniform<float> u_extra = {"u_extra", *this}; + UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this}; private: int32_t a_pos = -1; diff --git a/src/mbgl/shader/outline.vertex.glsl b/src/mbgl/shader/outline.vertex.glsl index 29c16e3ded..f720b8d629 100644 --- a/src/mbgl/shader/outline.vertex.glsl +++ b/src/mbgl/shader/outline.vertex.glsl @@ -6,5 +6,5 @@ varying vec2 v_pos; void main() { gl_Position = u_matrix * vec4(a_pos, 0, 1); - v_pos = (gl_Position.xy + 1.0) / 2.0 * u_world; + v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world; } diff --git a/src/mbgl/shader/sdf.fragment.glsl b/src/mbgl/shader/sdf.fragment.glsl index d72d61dab1..a77e959e53 100644 --- a/src/mbgl/shader/sdf.fragment.glsl +++ b/src/mbgl/shader/sdf.fragment.glsl @@ -5,9 +5,11 @@ uniform float u_gamma; varying vec2 v_tex; varying float v_alpha; +varying float v_gamma_scale; void main() { float dist = texture2D(u_texture, v_tex).a; - float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist) * v_alpha; + float gamma = u_gamma * v_gamma_scale; + float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * v_alpha; gl_FragColor = u_color * alpha; } diff --git a/src/mbgl/shader/sdf.vertex.glsl b/src/mbgl/shader/sdf.vertex.glsl index 192459f9da..5d2e1ce249 100644 --- a/src/mbgl/shader/sdf.vertex.glsl +++ b/src/mbgl/shader/sdf.vertex.glsl @@ -13,11 +13,14 @@ uniform float u_fadedist; uniform float u_minfadezoom; uniform float u_maxfadezoom; uniform float u_fadezoom; +uniform bool u_skewed; +uniform float u_extra; uniform vec2 u_texsize; varying vec2 v_tex; varying float v_alpha; +varying float v_gamma_scale; void main() { vec2 a_tex = a_data1.xy; @@ -48,6 +51,19 @@ void main() { // if label has been faded out, clip it show *= (1.0 - step(v_alpha, 0.0)); - gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset * show / 64.0, 0, 0); + if (u_skewed) { + vec4 extrude = u_exmatrix * vec4(a_offset * show / 64.0, 0, 0); + gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1); + } else { + vec4 extrude = u_exmatrix * vec4(a_offset * show / 64.0, 0, 0); + gl_Position = u_matrix * vec4(a_pos, 0, 1) + extrude; + } + + // position of y on the screen + float y = gl_Position.y / gl_Position.w; + // how much features are squished in all directions by the perspectiveness + float perspective_scale = 1.0 / (1.0 - y * u_extra); + v_gamma_scale = perspective_scale; + v_tex = a_tex / u_texsize; } diff --git a/src/mbgl/shader/sdf_shader.hpp b/src/mbgl/shader/sdf_shader.hpp index 36dfa30d3e..181788ab16 100644 --- a/src/mbgl/shader/sdf_shader.hpp +++ b/src/mbgl/shader/sdf_shader.hpp @@ -23,6 +23,8 @@ public: Uniform<float> u_minfadezoom = {"u_minfadezoom", *this}; Uniform<float> u_maxfadezoom = {"u_maxfadezoom", *this}; Uniform<float> u_fadezoom = {"u_fadezoom", *this}; + Uniform<int32_t> u_skewed = {"u_skewed", *this}; + Uniform<float> u_extra = {"u_extra", *this}; protected: int32_t a_pos = -1; diff --git a/src/mbgl/shader/uniform.hpp b/src/mbgl/shader/uniform.hpp index d2a248c609..55646f7d87 100644 --- a/src/mbgl/shader/uniform.hpp +++ b/src/mbgl/shader/uniform.hpp @@ -36,10 +36,16 @@ public: location = MBGL_CHECK_ERROR(glGetUniformLocation(shader.program, name)); } - void operator=(const T& t) { - if (current != t) { - current = t; - bind(t); + void operator=(const std::array<double, C*R>& t) { + bool dirty = false; + for (unsigned int i = 0; i < C*R; i++) { + if (current[i] != t[i]) { + current[i] = t[i]; + dirty = true; + } + } + if (dirty) { + bind(current); } } diff --git a/src/mbgl/text/collision_tile.cpp b/src/mbgl/text/collision_tile.cpp index 1c7806a4e0..d18860ccf5 100644 --- a/src/mbgl/text/collision_tile.cpp +++ b/src/mbgl/text/collision_tile.cpp @@ -3,17 +3,17 @@ namespace mbgl { -void CollisionTile::reset(const float _angle, const float pitch) { +CollisionTile::CollisionTile(const float angle_, const float pitch, bool debug_) : + angle(angle_), debug(debug_) { tree.clear(); - angle = _angle; // Compute the transformation matrix. - float angle_sin = std::sin(_angle); - float angle_cos = std::cos(_angle); + float angle_sin = std::sin(angle); + float angle_cos = std::cos(angle); rotationMatrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}}; // Stretch boxes in y direction to account for the map tilt. - const float _yStretch = 1.0f / std::cos(pitch / 180 * M_PI); + const float _yStretch = 1.0f / std::cos(pitch); // The amount the map is squished depends on the y position. // Sort of account for this by making all boxes a bit bigger. diff --git a/src/mbgl/text/collision_tile.hpp b/src/mbgl/text/collision_tile.hpp index aed9b59f32..3fd1b0a4c8 100644 --- a/src/mbgl/text/collision_tile.hpp +++ b/src/mbgl/text/collision_tile.hpp @@ -35,22 +35,18 @@ namespace mbgl { class CollisionTile { public: - inline explicit CollisionTile(float _zoom, float tileExtent, float tileSize, float angle_, bool debug_) : - zoom(_zoom), tilePixelRatio(tileExtent / tileSize), debug(debug_) { reset(angle_, 0); } + explicit CollisionTile(float angle_, float pitch_, bool debug_); - void reset(const float angle, const float pitch); float placeFeature(const CollisionFeature &feature); void insertFeature(CollisionFeature &feature, const float minPlacementScale); - void setDebug(bool debug_) { debug = debug_; } bool getDebug() { return debug; } - const float zoom; - const float tilePixelRatio; - float angle = 0; + const float angle = 0; const float minScale = 0.5f; const float maxScale = 2.0f; + float yStretch; private: @@ -58,7 +54,6 @@ class CollisionTile { Tree tree; std::array<float, 4> rotationMatrix; - float yStretch; bool debug; }; diff --git a/src/mbgl/text/quads.cpp b/src/mbgl/text/quads.cpp index 9434396156..19c48d7315 100644 --- a/src/mbgl/text/quads.cpp +++ b/src/mbgl/text/quads.cpp @@ -50,12 +50,12 @@ SymbolQuads getIconQuads(Anchor &anchor, const PositionedIcon &shapedIcon, } struct GlyphInstance { - explicit GlyphInstance(const vec2<float> &anchor_) : anchor(anchor_) {} - explicit GlyphInstance(const vec2<float> &anchor_, float offset_, float minScale_, float maxScale_, + explicit GlyphInstance(const vec2<float> &anchorPoint_) : anchorPoint(anchorPoint_) {} + explicit GlyphInstance(const vec2<float> &anchorPoint_, float offset_, float minScale_, float maxScale_, float angle_) - : anchor(anchor_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {} + : anchorPoint(anchorPoint_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {} - const vec2<float> anchor; + const vec2<float> anchorPoint; const float offset = 0.0f; const float minScale = globalMinScale; const float maxScale = std::numeric_limits<float>::infinity(); @@ -65,19 +65,19 @@ struct GlyphInstance { typedef std::vector<GlyphInstance> GlyphInstances; void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &anchor, - float offset, const std::vector<Coordinate> &line, int segment, int8_t direction) { + float offset, const std::vector<Coordinate> &line, int segment, bool forward) { - const bool upsideDown = direction < 0; + const bool upsideDown = !forward; if (offset < 0) - direction *= -1; + forward = !forward; - if (direction > 0) + if (forward) segment++; assert((int)line.size() > segment); vec2<float> end = line[segment]; - vec2<float> newAnchor = anchor; + vec2<float> newAnchorPoint = anchor; float prevscale = std::numeric_limits<float>::infinity(); offset = std::fabs(offset); @@ -85,16 +85,16 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor & const float placementScale = anchor.scale; while (true) { - const float dist = util::dist<float>(newAnchor, end); + const float dist = util::dist<float>(newAnchorPoint, end); const float scale = offset / dist; - float angle = std::atan2(end.y - newAnchor.y, end.x - newAnchor.x); - if (direction < 0) + float angle = std::atan2(end.y - newAnchorPoint.y, end.x - newAnchorPoint.x); + if (!forward) angle += M_PI; if (upsideDown) angle += M_PI; glyphs = GlyphInstance{ - /* anchor */ newAnchor, + /* anchor */ newAnchorPoint, /* offset */ static_cast<float>(upsideDown ? M_PI : 0.0), /* minScale */ scale, /* maxScale */ prevscale, @@ -103,11 +103,11 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor & if (scale <= placementScale) break; - newAnchor = end; + newAnchorPoint = end; // skip duplicate nodes - while (newAnchor == end) { - segment += direction; + while (newAnchorPoint == end) { + segment += forward ? 1 : -1; if ((int)line.size() <= segment || segment < 0) { anchor.scale = scale; return; @@ -115,8 +115,8 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor & end = line[segment]; } - vec2<float> normal = util::normal<float>(newAnchor, end) * dist; - newAnchor = newAnchor - normal; + vec2<float> normal = util::normal<float>(newAnchorPoint, end) * dist; + newAnchorPoint = newAnchorPoint - normal; prevscale = scale; } @@ -148,9 +148,9 @@ SymbolQuads getGlyphQuads(Anchor &anchor, const Shaping &shapedText, GlyphInstances glyphInstances; if (alongLine) { - getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, 1); + getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true); if (keepUpright) - getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, -1); + getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false); } else { glyphInstances.emplace_back(GlyphInstance{anchor}); @@ -194,7 +194,7 @@ SymbolQuads getGlyphQuads(Anchor &anchor, const Shaping &shapedText, const float glyphMinScale = std::max(instance.minScale, anchor.scale); const float glyphAngle = std::fmod((anchor.angle + textRotate + instance.offset + 2 * M_PI), (2 * M_PI)); - quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchor, glyphMinScale, instance.maxScale); + quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale); } diff --git a/src/mbgl/text/quads.hpp b/src/mbgl/text/quads.hpp index b47cc718b6..97fdb6a1fc 100644 --- a/src/mbgl/text/quads.hpp +++ b/src/mbgl/text/quads.hpp @@ -11,7 +11,7 @@ namespace mbgl { struct SymbolQuad { explicit SymbolQuad(const vec2<float> &tl_, const vec2<float> &tr_, const vec2<float> &bl_, const vec2<float> &br_, - const Rect<uint16_t> &tex_, float angle_, const vec2<float> &anchor_, + const Rect<uint16_t> &tex_, float angle_, const vec2<float> &anchorPoint_, float minScale_, float maxScale_) : tl(tl_), tr(tr_), @@ -19,14 +19,14 @@ namespace mbgl { br(br_), tex(tex_), angle(angle_), - anchor(anchor_), + anchorPoint(anchorPoint_), minScale(minScale_), maxScale(maxScale_) {} vec2<float> tl, tr, bl, br; Rect<uint16_t> tex; float angle; - vec2<float> anchor; + vec2<float> anchorPoint; float minScale, maxScale; }; diff --git a/src/mbgl/util/box.hpp b/src/mbgl/util/box.hpp index 55a5d46fbc..f03adc2e50 100644 --- a/src/mbgl/util/box.hpp +++ b/src/mbgl/util/box.hpp @@ -1,13 +1,14 @@ #ifndef MBGL_UTIL_BOX #define MBGL_UTIL_BOX -#include <mbgl/util/vec.hpp> +#include <mbgl/util/tile_coordinate.hpp> namespace mbgl { struct box { - vec2<double> tl, tr, bl, br; - vec2<double> center; + box(TileCoordinate tl_, TileCoordinate tr_, TileCoordinate br_, TileCoordinate bl_) : + tl(tl_), tr(tr_), br(br_), bl(bl_) {} + TileCoordinate tl, tr, br, bl; }; } diff --git a/src/mbgl/util/mat2.cpp b/src/mbgl/util/mat2.cpp new file mode 100644 index 0000000000..6910244541 --- /dev/null +++ b/src/mbgl/util/mat2.cpp @@ -0,0 +1,52 @@ +// This is an incomplete port of http://glmatrix.net/ +// +// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not claim +// that you wrote the original software. If you use this software in a +// product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +#include <mbgl/util/mat2.hpp> + +#include <cmath> + +using namespace mbgl; + +void matrix::identity(mat2& out) { + out[0] = 1.0f; + out[1] = 0.0f; + out[2] = 0.0f; + out[3] = 1.0f; +} + +void matrix::rotate(mat2& out, const mat2& a, double rad) { + double a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], + s = std::sin(rad), + c = std::cos(rad); + out[0] = a0 * c + a2 * s; + out[1] = a1 * c + a3 * s; + out[2] = a0 * -s + a2 * c; + out[3] = a1 * -s + a3 * c; +}; + +void matrix::scale(mat2& out, const mat2& a, double v0, double v1) { + double a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3]; + out[0] = a0 * v0; + out[1] = a1 * v0; + out[2] = a2 * v1; + out[3] = a3 * v1; +} diff --git a/src/mbgl/util/mat2.hpp b/src/mbgl/util/mat2.hpp new file mode 100644 index 0000000000..b5cec821f9 --- /dev/null +++ b/src/mbgl/util/mat2.hpp @@ -0,0 +1,41 @@ +// This is an incomplete port of http://glmatrix.net/ +// +// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not claim +// that you wrote the original software. If you use this software in a +// product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +#ifndef MBGL_UTIL_MAT2 +#define MBGL_UTIL_MAT2 + +#include <array> + +namespace mbgl { + +typedef std::array<double, 4> mat2; + +namespace matrix { + +void identity(mat2& out); +void rotate(mat2& out, const mat2& a, double rad); +void scale(mat2& out, const mat2& a, double v0, double v1); + +} +} + +#endif diff --git a/src/mbgl/util/mat3.cpp b/src/mbgl/util/mat3.cpp index 263768ee41..f4ae8841d5 100644 --- a/src/mbgl/util/mat3.cpp +++ b/src/mbgl/util/mat3.cpp @@ -38,8 +38,8 @@ void matrix::identity(mat3& out) { out[8] = 1.0f; } -void matrix::translate(mat3& out, const mat3& a, float x, float y) { - float a00 = a[0], a01 = a[1], a02 = a[2], +void matrix::translate(mat3& out, const mat3& a, double x, double y) { + double a00 = a[0], a01 = a[1], a02 = a[2], a10 = a[3], a11 = a[4], a12 = a[5], a20 = a[6], a21 = a[7], a22 = a[8]; @@ -56,8 +56,8 @@ void matrix::translate(mat3& out, const mat3& a, float x, float y) { out[8] = x * a02 + y * a12 + a22; } -void matrix::rotate(mat3& out, const mat3& a, float rad) { - float s = std::sin(rad), +void matrix::rotate(mat3& out, const mat3& a, double rad) { + double s = std::sin(rad), c = std::cos(rad), a00 = a[0], a01 = a[1], @@ -82,7 +82,7 @@ void matrix::rotate(mat3& out, const mat3& a, float rad) { out[8] = a22; }; -void matrix::scale(mat3& out, const mat3& a, float x, float y) { +void matrix::scale(mat3& out, const mat3& a, double x, double y) { out[0] = x * a[0]; out[1] = x * a[1]; out[2] = x * a[2]; diff --git a/src/mbgl/util/mat3.hpp b/src/mbgl/util/mat3.hpp index fa40751764..3a6aba5a11 100644 --- a/src/mbgl/util/mat3.hpp +++ b/src/mbgl/util/mat3.hpp @@ -27,14 +27,14 @@ namespace mbgl { -typedef std::array<float, 9> mat3; +typedef std::array<double, 9> mat3; namespace matrix { void identity(mat3& out); -void translate(mat3& out, const mat3& a, float x, float y); -void rotate(mat3& out, const mat3& a, float rad); -void scale(mat3& out, const mat3& a, float x, float y); +void translate(mat3& out, const mat3& a, double x, double y); +void rotate(mat3& out, const mat3& a, double rad); +void scale(mat3& out, const mat3& a, double x, double y); } } diff --git a/src/mbgl/util/mat4.cpp b/src/mbgl/util/mat4.cpp index cabd8e2842..123c8464fd 100644 --- a/src/mbgl/util/mat4.cpp +++ b/src/mbgl/util/mat4.cpp @@ -45,8 +45,55 @@ void matrix::identity(mat4& out) { out[15] = 1.0f; } -void matrix::ortho(mat4& out, float left, float right, float bottom, float top, float near, float far) { - float lr = 1.0f / (left - right), +bool matrix::invert(mat4& out, mat4& a) { + float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], + a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], + a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], + + b00 = a00 * a11 - a01 * a10, + b01 = a00 * a12 - a02 * a10, + b02 = a00 * a13 - a03 * a10, + b03 = a01 * a12 - a02 * a11, + b04 = a01 * a13 - a03 * a11, + b05 = a02 * a13 - a03 * a12, + b06 = a20 * a31 - a21 * a30, + b07 = a20 * a32 - a22 * a30, + b08 = a20 * a33 - a23 * a30, + b09 = a21 * a32 - a22 * a31, + b10 = a21 * a33 - a23 * a31, + b11 = a22 * a33 - a23 * a32, + + // Calculate the determinant + det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + + if (!det) { + return true; + } + det = 1.0 / det; + + out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return false; +} + +void matrix::ortho(mat4& out, double left, double right, double bottom, double top, double near, double far) { + double lr = 1.0f / (left - right), bt = 1.0f / (bottom - top), nf = 1.0f / (near - far); out[0] = -2.0f * lr; @@ -67,6 +114,27 @@ void matrix::ortho(mat4& out, float left, float right, float bottom, float top, out[15] = 1.0f; } +void matrix::perspective(mat4& out, double fovy, double aspect, double near, double far) { + double f = 1.0f / std::tan(fovy / 2.0f), + nf = 1.0f / (near - far); + out[0] = f / aspect; + out[1] = 0.0f; + out[2] = 0.0f; + out[3] = 0.0f; + out[4] = 0.0f; + out[5] = f; + out[6] = 0.0f; + out[7] = 0.0f; + out[8] = 0.0f; + out[9] = 0.0f; + out[10] = (far + near) * nf; + out[11] = -1.0f; + out[12] = 0.0f; + out[13] = 0.0f; + out[14] = (2.0f * far * near) * nf; + out[15] = 0.0f; +} + void matrix::copy(mat4& out, const mat4& a) { out[0] = a[0]; out[1] = a[1]; @@ -86,14 +154,14 @@ void matrix::copy(mat4& out, const mat4& a) { out[15] = a[15]; } -void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) { +void matrix::translate(mat4& out, const mat4& a, double x, double y, double z) { if (&a == &out) { out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; } else { - float a00, a01, a02, a03, + double a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23; @@ -112,8 +180,42 @@ void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) { } } -void matrix::rotate_z(mat4& out, const mat4& a, float rad) { - float s = std::sin(rad), +void matrix::rotate_x(mat4& out, const mat4& a, double rad) { + double s = std::sin(rad), + c = std::cos(rad), + a10 = a[4], + a11 = a[5], + a12 = a[6], + a13 = a[7], + a20 = a[8], + a21 = a[9], + a22 = a[10], + a23 = a[11]; + + if (&a != &out) { // If the source and destination differ, copy the unchanged rows + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[12] = a[12]; + out[13] = a[13]; + out[14] = a[14]; + out[15] = a[15]; + } + + // Perform axis-specific matrix multiplication + out[4] = a10 * c + a20 * s; + out[5] = a11 * c + a21 * s; + out[6] = a12 * c + a22 * s; + out[7] = a13 * c + a23 * s; + out[8] = a20 * c - a10 * s; + out[9] = a21 * c - a11 * s; + out[10] = a22 * c - a12 * s; + out[11] = a23 * c - a13 * s; +} + +void matrix::rotate_z(mat4& out, const mat4& a, double rad) { + double s = std::sin(rad), c = std::cos(rad), a00 = a[0], a01 = a[1], @@ -146,7 +248,7 @@ void matrix::rotate_z(mat4& out, const mat4& a, float rad) { out[7] = a13 * c - a03 * s; } -void matrix::scale(mat4& out, const mat4& a, float x, float y, float z) { +void matrix::scale(mat4& out, const mat4& a, double x, double y, double z) { out[0] = a[0] * x; out[1] = a[1] * x; out[2] = a[2] * x; @@ -166,13 +268,13 @@ void matrix::scale(mat4& out, const mat4& a, float x, float y, float z) { } void matrix::multiply(mat4& out, const mat4& a, const mat4& b) { - float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], + double a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15]; // Cache only the current line of the second matrix - float b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; + double b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3]; out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; diff --git a/src/mbgl/util/tile_coordinate.hpp b/src/mbgl/util/tile_coordinate.hpp new file mode 100644 index 0000000000..0f5f0dcb8b --- /dev/null +++ b/src/mbgl/util/tile_coordinate.hpp @@ -0,0 +1,29 @@ +#ifndef MBGL_UTIL_TILE_COORDINATE +#define MBGL_UTIL_TILE_COORDINATE + +#include <mbgl/util/vec.hpp> + +namespace mbgl { + +struct TileCoordinate { + float column; + float row; + float zoom; + + TileCoordinate(float column_, float row_, float zoom_) : + column(column_), row(row_), zoom(zoom_) {} + + TileCoordinate zoomTo(float targetZoom) { + float scale = std::pow(2, targetZoom - zoom); + return { column * scale, row * scale, targetZoom }; + } + + TileCoordinate operator-(TileCoordinate c) { + c = c.zoomTo(zoom); + return { column - c.column, row - c.row, zoom }; + }; +}; + +} + +#endif diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp index 9e444fec12..b7937bf849 100644 --- a/src/mbgl/util/tile_cover.cpp +++ b/src/mbgl/util/tile_cover.cpp @@ -1,6 +1,7 @@ #include <mbgl/util/tile_cover.hpp> #include <mbgl/util/vec.hpp> #include <mbgl/util/box.hpp> +#include <mbgl/util/tile_coordinate.hpp> namespace mbgl { @@ -78,13 +79,19 @@ std::forward_list<TileID> tileCover(int8_t z, const mbgl::box &bounds, int8_t ac } }; + mbgl::vec2<double> tl = { bounds.tl.column, bounds.tl.row }; + mbgl::vec2<double> tr = { bounds.tr.column, bounds.tr.row }; + mbgl::vec2<double> br = { bounds.br.column, bounds.br.row }; + mbgl::vec2<double> bl = { bounds.bl.column, bounds.bl.row }; + // Divide the screen up in two triangles and scan each of them: // \---+ // | \ | // +---\. - scanTriangle(bounds.tl, bounds.tr, bounds.br, 0, tiles, scanLine); - scanTriangle(bounds.br, bounds.bl, bounds.tl, 0, tiles, scanLine); + scanTriangle(tl, tr, br, 0, tiles, scanLine); + scanTriangle(br, bl, tl, 0, tiles, scanLine); + t.sort(); t.unique(); return t; diff --git a/src/mbgl/util/vec4.cpp b/src/mbgl/util/vec4.cpp new file mode 100644 index 0000000000..415b15d806 --- /dev/null +++ b/src/mbgl/util/vec4.cpp @@ -0,0 +1,33 @@ +// This is an incomplete port of http://glmatrix.net/ +// +// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not claim +// that you wrote the original software. If you use this software in a +// product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +#include <mbgl/util/vec4.hpp> + +using namespace mbgl; + +void matrix::transformMat4(vec4& out, vec4& a, mat4& m) { + float x = a[0], y = a[1], z = a[2], w = a[3]; + out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; + out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; + out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; + out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; +} diff --git a/src/mbgl/util/vec4.hpp b/src/mbgl/util/vec4.hpp new file mode 100644 index 0000000000..c5b4ab1d9e --- /dev/null +++ b/src/mbgl/util/vec4.hpp @@ -0,0 +1,40 @@ +// This is an incomplete port of http://glmatrix.net/ +// +// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the +// use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not claim +// that you wrote the original software. If you use this software in a +// product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +#ifndef MBGL_UTIL_VEC4 +#define MBGL_UTIL_VEC4 + +#include <array> +#include <mbgl/util/mat4.hpp> + +namespace mbgl { + +namespace matrix { + +typedef std::array<double, 4> vec4; + +void transformMat4(vec4& out, vec4& a, mat4& m); + +} +} + +#endif diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp index 516d2f5dba..b238071bc5 100644 --- a/src/mbgl/util/worker.cpp +++ b/src/mbgl/util/worker.cpp @@ -46,8 +46,8 @@ public: } } - void redoPlacement(TileWorker* worker, float angle, bool collisionDebug, std::function<void ()> callback) { - worker->redoPlacement(angle, collisionDebug); + void redoPlacement(TileWorker* worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) { + worker->redoPlacement(angle, pitch, collisionDebug); callback(); } }; @@ -76,9 +76,9 @@ std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker, const Liv return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker, &tile); } -std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, bool collisionDebug, std::function<void ()> callback) { +std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) { current = (current + 1) % threads.size(); - return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, collisionDebug); + return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, pitch, collisionDebug); } } // end namespace mbgl diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp index 2d78338db2..3ecc402ad2 100644 --- a/src/mbgl/util/worker.hpp +++ b/src/mbgl/util/worker.hpp @@ -49,6 +49,7 @@ public: Request redoPlacement( TileWorker&, float angle, + float pitch, bool collisionDebug, std::function<void ()> callback); |