load correct covering tiles in perspective view

port pointCoordinate from -js to convert screen points to tile
coordinates correctly in perspective view.
This commit is contained in:
Ansis Brammanis
2015-08-17 16:45:20 -04:00
parent cef1b25444
commit 0d68c4fedf
8 changed files with 145 additions and 59 deletions

View File

@@ -32,6 +32,7 @@ typedef std::array<double, 16> mat4;
namespace matrix {
void identity(mat4& out);
bool invert(mat4& out, mat4& a);
void ortho(mat4& out, double left, double right, double bottom, double top, double near, double far);
void perspective(mat4& out, double fovy, double aspect, double near, double far);
void copy(mat4& out, const mat4& a);

View File

@@ -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>
@@ -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.pointCoordinate({ 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([&center](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;

View File

@@ -3,6 +3,8 @@
#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;
@@ -17,37 +19,37 @@ void TransformState::matrixFor(mat4& matrix, const TileID& id, const int8_t z) c
matrix::scale(matrix, matrix, s / 4096.0f, s / 4096.0f, 1);
}
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();
matrix::perspective(projMatrix, 2.0f * std::atan((getHeight() / 2.0f) / getAltitude()),
double(getWidth()) / getHeight(), 0.1, farZ);
matrix::translate(projMatrix, projMatrix, 0, 0, -getAltitude());
// 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());
matrix::rotate_x(projMatrix, projMatrix, getPitch());
matrix::rotate_z(projMatrix, projMatrix, getAngle());
matrix::translate(projMatrix, projMatrix, pixel_x() - getWidth() / 2.0f,
pixel_y() - getWidth() / 2.0f, 0);
}
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;
// Calculate the corners of the map view. The resulting coordinates will be
// in fractional tile coordinates.
box b;
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;
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;
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;
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;
double w = width;
double h = height;
box b(
pointCoordinate({ 0, 0 }).zoomTo(z),
pointCoordinate({ w, 0 }).zoomTo(z),
pointCoordinate({ w, h }).zoomTo(z),
pointCoordinate({ 0, h }).zoomTo(z));
return b;
}
@@ -240,6 +242,61 @@ const LatLng TransformState::latLngForPixel(const vec2<double> pixel) const {
return Projection::latLngForProjectedMeters(givenMeters);
}
TileCoordinate TransformState::pointCoordinate(const vec2<double> point) const {
float targetZ = 0;
float tileZoom = std::floor(getZoom());
mat4 mat = coordinatePointMatrix(tileZoom);
mat4 inverted;
bool err = matrix::invert(inverted, mat);
if (err) throw std::runtime_error("failed to invert coordinatePointMatrix");
// 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
matrix::vec4 coord0;
matrix::vec4 coord1;
matrix::vec4 point0 = {{ point.x, point.y, 0, 1 }};
matrix::vec4 point1 = {{ point.x, point.y, 1, 1 }};
matrix::transformMat4(coord0, point0, inverted);
matrix::transformMat4(coord1, point1, inverted);
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;
float t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0);
return { util::interpolate(x0, x1, t), util::interpolate(y0, y1, t), tileZoom };
}
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;
}
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;
}
#pragma mark - Changing

View File

@@ -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
@@ -62,6 +65,9 @@ public:
double pixel_x() const;
double pixel_y() const;
// Conversions
TileCoordinate pointCoordinate(const vec2<double> point) const;
private:
void constrain(double& scale, double& y) const;
@@ -72,6 +78,9 @@ private:
// logical dimensions
uint16_t width = 0, height = 0;
mat4 coordinatePointMatrix(float z) const;
mat4 getPixelMatrix() const;
private:
// animation state
bool rotating = false;

View File

@@ -133,26 +133,7 @@ void Painter::lineWidth(float line_width) {
void Painter::changeMatrix() {
double halfFov = std::atan(0.5 / state.getAltitude());
double topHalfSurfaceDistance = std::sin(halfFov) * state.getAltitude() /
std::sin(M_PI / 2.0f - state.getPitch() - halfFov);
// Calculate z value of the farthest fragment that should be rendered.
double farZ = std::cos(M_PI / 2.0f - state.getPitch()) * topHalfSurfaceDistance + state.getAltitude();
matrix::perspective(projMatrix, 2.0f * std::atan((state.getHeight() / 2.0f) / state.getAltitude()),
double(state.getWidth()) / state.getHeight(), 0.1, farZ);
matrix::translate(projMatrix, projMatrix, 0, 0, -state.getAltitude());
// 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 / state.getHeight());
matrix::rotate_x(projMatrix, projMatrix, state.getPitch());
matrix::rotate_z(projMatrix, projMatrix, state.getAngle());
matrix::translate(projMatrix, projMatrix, state.pixel_x() - state.getWidth() / 2.0f,
state.pixel_y() - state.getWidth() / 2.0f, 0);
state.getProjMatrix(projMatrix);
// The extrusion matrix.
matrix::ortho(extrudeMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, -1);

View File

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

View File

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

View File

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