diff options
author | Mikko Pulkki <mikko.pulkki@mapbox.com> | 2020-04-27 13:26:49 +0300 |
---|---|---|
committer | Mikko Pulkki <55925868+mpulkki-mapbox@users.noreply.github.com> | 2020-05-02 17:07:02 +0300 |
commit | 57b4b2829e8033d6cf3f7bd48c1fe511e00b830c (patch) | |
tree | 0a7956b22946f4855547fc9016c35434aee96659 /src/mbgl/util | |
parent | 9d391d7edd0e5c2bb5c7162deebfd22337588fec (diff) | |
download | qtlocation-mapboxgl-57b4b2829e8033d6cf3f7bd48c1fe511e00b830c.tar.gz |
Refactor TransformState to use internal 3d camera
Diffstat (limited to 'src/mbgl/util')
-rw-r--r-- | src/mbgl/util/camera.cpp | 215 | ||||
-rw-r--r-- | src/mbgl/util/camera.hpp | 42 |
2 files changed, 257 insertions, 0 deletions
diff --git a/src/mbgl/util/camera.cpp b/src/mbgl/util/camera.cpp new file mode 100644 index 0000000000..f64e24ab2d --- /dev/null +++ b/src/mbgl/util/camera.cpp @@ -0,0 +1,215 @@ +#include "camera.hpp" +#include <cassert> +#include <cmath> +#include <mbgl/map/camera.hpp> +#include <mbgl/math/log2.hpp> +#include <mbgl/util/constants.hpp> +#include <mbgl/util/geo.hpp> +#include <mbgl/util/projection.hpp> + +namespace mbgl { + +namespace { +double vec2Len(const vec2& v) { + return std::sqrt(v[0] * v[0] + v[1] * v[1]); +}; + +double vec2Dot(const vec2& a, const vec2& b) { + return a[0] * b[0] + a[1] * b[1]; +}; + +vec2 vec2Scale(const vec2& v, double s) { + return vec2{{v[0] * s, v[1] * s}}; +}; +} // namespace + +namespace util { + +static double mercatorXfromLng(double lng) { + return (180.0 + lng) / 360.0; +} + +static double mercatorYfromLat(double lat) { + return (180.0 - (180.0 / M_PI * std::log(std::tan(M_PI_4 + lat * M_PI / 360.0)))) / 360.0; +} + +static double latFromMercatorY(double y) { + return util::RAD2DEG * (2.0 * std::atan(std::exp(M_PI - y * util::M2PI)) - M_PI_2); +} + +static double lngFromMercatorX(double x) { + return x * 360.0 - 180.0; +} + +static double* getColumn(mat4& matrix, int col) { + assert(col >= 0 && col < 4); + return &matrix[col * 4]; +} + +static const double* getColumn(const mat4& matrix, int col) { + assert(col >= 0 && col < 4); + return &matrix[col * 4]; +} + +static vec3 toMercator(const LatLng& location, double altitudeMeters) { + const double pixelsPerMeter = 1.0 / Projection::getMetersPerPixelAtLatitude(location.latitude(), 0.0); + const double worldSize = Projection::worldSize(std::pow(2.0, 0.0)); + + return {{mercatorXfromLng(location.longitude()), + mercatorYfromLat(location.latitude()), + altitudeMeters * pixelsPerMeter / worldSize}}; +} + +static Quaternion orientationFromPitchBearing(double pitch, double bearing) { + // Both angles have to be negated to achieve CW rotation around the axis of rotation + Quaternion rotBearing = Quaternion::fromAxisAngle({{0.0, 0.0, 1.0}}, -bearing); + Quaternion rotPitch = Quaternion::fromAxisAngle({{1.0, 0.0, 0.0}}, -pitch); + + return rotBearing.multiply(rotPitch); +} + +static void updateTransform(mat4& transform, const Quaternion& orientation) { + // Construct rotation matrix from orientation + mat4 m = orientation.toRotationMatrix(); + + // Apply translation to the matrix + double* col = getColumn(m, 3); + + col[0] = getColumn(transform, 3)[0]; + col[1] = getColumn(transform, 3)[1]; + col[2] = getColumn(transform, 3)[2]; + + transform = m; +} + +static void updateTransform(mat4& transform, const vec3& position) { + getColumn(transform, 3)[0] = position[0]; + getColumn(transform, 3)[1] = position[1]; + getColumn(transform, 3)[2] = position[2]; +} + +Camera::Camera() : orientation(Quaternion::identity) { + matrix::identity(transform); +} + +vec3 Camera::getPosition() const { + const double* p = getColumn(transform, 3); + return {{p[0], p[1], p[2]}}; +} + +mat4 Camera::getCameraToWorld(double scale, bool flippedY) const { + mat4 cameraToWorld; + matrix::invert(cameraToWorld, getWorldToCamera(scale, flippedY)); + return cameraToWorld; +} + +mat4 Camera::getWorldToCamera(double scale, bool flippedY) const { + // transformation chain from world space to camera space: + // 1. Height value (z) of renderables is in meters. Scale z coordinate by pixelsPerMeter + // 2. Transform from pixel coordinates to camera space with cameraMatrix^-1 + // 3. flip Y if required + + // worldToCamera: flip * cam^-1 * zScale + // cameraToWorld: (flip * cam^-1 * zScale)^-1 => (zScale^-1 * cam * flip^-1) + const double worldSize = Projection::worldSize(scale); + const double latitude = latFromMercatorY(getColumn(transform, 3)[1]); + const double pixelsPerMeter = worldSize / (std::cos(latitude * util::DEG2RAD) * util::M2PI * util::EARTH_RADIUS_M); + + // Compute inverse of the camera matrix + mat4 result = orientation.conjugate().toRotationMatrix(); + + matrix::translate( + result, result, -transform[12] * worldSize, -transform[13] * worldSize, -transform[14] * worldSize); + + if (!flippedY) { + // Pre-multiply y + result[1] *= -1.0; + result[5] *= -1.0; + result[9] *= -1.0; + result[13] *= -1.0; + } + + // Post-multiply z + result[8] *= pixelsPerMeter; + result[9] *= pixelsPerMeter; + result[10] *= pixelsPerMeter; + result[11] *= pixelsPerMeter; + + return result; +} + +mat4 Camera::getCameraToClipPerspective(double fovy, double aspectRatio, double nearZ, double farZ) const { + mat4 projection; + matrix::perspective(projection, fovy, aspectRatio, nearZ, farZ); + return projection; +} + +vec3 Camera::forward() const { + const double* column = getColumn(transform, 2); + // The forward direction is towards the map, [0, 0, -1] + return {{-column[0], -column[1], -column[2]}}; +} + +vec3 Camera::right() const { + const double* column = getColumn(transform, 0); + return {{column[0], column[1], column[2]}}; +} + +vec3 Camera::up() const { + const double* column = getColumn(transform, 1); + // Up direction has to be flipped due to y-axis pointing towards south + return {{-column[0], -column[1], -column[2]}}; +} + +void Camera::getOrientation(double& pitch, double& bearing) const { + const vec3 f = forward(); + const vec3 r = right(); + + bearing = std::atan2(-r[1], r[0]); + pitch = std::atan2(std::sqrt(f[0] * f[0] + f[1] * f[1]), -f[2]); +} + +void Camera::setOrientation(double pitch, double bearing) { + orientation = orientationFromPitchBearing(pitch, bearing); + updateTransform(transform, orientation); +} + +void Camera::setOrientation(const Quaternion& orientation_) { + orientation = orientation_; + updateTransform(transform, orientation); +} + +void Camera::setPosition(const vec3& position) { + updateTransform(transform, position); +} + +optional<Quaternion> Camera::orientationFromFrame(const vec3& forward, const vec3& up) { + vec3 upVector = up; + + vec2 xyForward = {{forward[0], forward[1]}}; + vec2 xyUp = {{up[0], up[1]}}; + const double epsilon = 1e-15; + + // Remove roll-component of the resulting orientation by projecting + // the up-vector to the forward vector on xy-plane + if (vec2Len(xyForward) >= epsilon) { + const vec2 xyDir = vec2Scale(xyForward, 1.0 / vec2Len(xyForward)); + + xyUp = vec2Scale(xyDir, vec2Dot(xyUp, xyDir)); + upVector[0] = xyUp[0]; + upVector[1] = xyUp[1]; + } + + const vec3 right = vec3Cross(upVector, forward); + + if (vec3Length(right) < epsilon) { + return nullopt; + } + + const double bearing = std::atan2(-right[1], right[0]); + const double pitch = std::atan2(std::sqrt(forward[0] * forward[0] + forward[1] * forward[1]), -forward[2]); + + return util::orientationFromPitchBearing(pitch, bearing); +} +} // namespace util +} // namespace mbgl
\ No newline at end of file diff --git a/src/mbgl/util/camera.hpp b/src/mbgl/util/camera.hpp new file mode 100644 index 0000000000..030d7cfaac --- /dev/null +++ b/src/mbgl/util/camera.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include <mbgl/util/optional.hpp> +#include <mbgl/util/size.hpp> +#include "quaternion.hpp" + +namespace mbgl { + +class LatLng; + +namespace util { + +class Camera { +public: + Camera(); + + vec3 getPosition() const; + mat4 getCameraToWorld(double scale, bool flippedY) const; + mat4 getWorldToCamera(double scale, bool flippedY) const; + mat4 getCameraToClipPerspective(double fovy, double aspectRatio, double nearZ, double farZ) const; + + vec3 forward() const; + vec3 right() const; + vec3 up() const; + + const Quaternion& getOrientation() const { return orientation; } + void getOrientation(double& pitch, double& bearing) const; + void setOrientation(double pitch, double bearing); + void setOrientation(const Quaternion& orientation_); + void setPosition(const vec3& position); + + // Computes orientation using forward and up vectors of the provided coordinate frame. + // Only bearing and pitch components will be used. Does not return a value if input is invalid + static optional<Quaternion> orientationFromFrame(const vec3& forward, const vec3& up); + +private: + Quaternion orientation; + mat4 transform; // Position (mercator) and orientation of the camera +}; + +} // namespace util +} // namespace mbgl
\ No newline at end of file |