Add FreeCameraOptions to the Map class

This commit is contained in:
Mikko Pulkki
2020-04-27 13:28:46 +03:00
committed by Mikko Pulkki
parent 57b4b2829e
commit 83c06c1f99
9 changed files with 210 additions and 4 deletions

View File

@@ -500,4 +500,14 @@ void Map::dumpDebugLogs() const {
Log::Info(Event::General, "--------------------------------------------------------------------------------");
}
void Map::setFreeCameraOptions(const FreeCameraOptions& camera) {
impl->transform.setFreeCameraOptions(camera);
impl->cameraMutated = true;
impl->onUpdate();
}
FreeCameraOptions Map::getFreeCameraOptions() const {
return impl->transform.getFreeCameraOptions();
}
} // namespace mbgl

View File

@@ -650,4 +650,13 @@ double Transform::getMaxPitchForEdgeInsets(const EdgeInsets& insets) const {
// e.g. Maximum pitch of 60 degrees is when perspective center's offset from the top is 84% of screen height.
}
FreeCameraOptions Transform::getFreeCameraOptions() const {
return state.getFreeCameraOptions();
}
void Transform::setFreeCameraOptions(const FreeCameraOptions& options) {
cancelTransitions();
state.setFreeCameraOptions(options);
}
} // namespace mbgl

View File

@@ -113,6 +113,9 @@ public:
ScreenCoordinate latLngToScreenCoordinate(const LatLng&) const;
LatLng screenCoordinateToLatLng(const ScreenCoordinate&, LatLng::WrapMode = LatLng::Wrapped) const;
FreeCameraOptions getFreeCameraOptions() const;
void setFreeCameraOptions(const FreeCameraOptions& options);
private:
MapObserver& observer;
TransformState state;

View File

@@ -177,8 +177,8 @@ void TransformState::updateCameraState() const {
// x & y tracks the center of the map in pixels. However as rendering is done in pixel coordinates the rendering
// origo is actually in the middle of the map (0.5 * worldSize). x&y positions have to be negated because it defines
// position of the map, not the camera. Moving map 10 units left has the same effect as moving camera 10 units to the
// right.
// position of the map, not the camera. Moving map 10 units left has the same effect as moving camera 10 units to
// the right.
const double dx = 0.5 * worldSize - x;
const double dy = 0.5 * worldSize - y;
@@ -226,6 +226,83 @@ void TransformState::updateStateFromCamera() {
setBearing(newBearing);
setPitch(newPitch);
}
FreeCameraOptions TransformState::getFreeCameraOptions() const {
updateCameraState();
FreeCameraOptions options;
options.position = camera.getPosition();
options.orientation = camera.getOrientation().m;
return options;
}
bool TransformState::setCameraPosition(const vec3& position) {
if (std::isnan(position[0]) || std::isnan(position[1]) || std::isnan(position[2])) return false;
const double maxWorldSize = Projection::worldSize(std::pow(2.0, getMaxZoom()));
const double minWorldSize = Projection::worldSize(std::pow(2.0, getMinZoom()));
const double distToCenter = getCameraToCenterDistance();
const vec3 updatedPos = vec3{
{position[0], position[1], util::clamp(position[2], distToCenter / maxWorldSize, distToCenter / minWorldSize)}};
camera.setPosition(updatedPos);
return true;
}
bool TransformState::setCameraOrientation(const Quaternion& orientation_) {
const vec4& c = orientation_.m;
if (std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(c[2]) || std::isnan(c[3])) {
return false;
}
// Zero-length quaternions are not valid
if (orientation_.length() == 0.0) {
return false;
}
Quaternion unitQuat = orientation_.normalized();
const vec3 forward = unitQuat.transform({{0.0, 0.0, -1.0}});
const vec3 up = unitQuat.transform({{0.0, -1.0, 0.0}});
if (up[2] < 0.0) {
// Camera is upside down and not recoverable
return false;
}
const optional<Quaternion> updatedOrientation = util::Camera::orientationFromFrame(forward, up);
if (!updatedOrientation) return false;
camera.setOrientation(updatedOrientation.value());
return true;
}
void TransformState::setFreeCameraOptions(const FreeCameraOptions& options) {
if (!valid()) {
return;
}
if (!options.position && !options.orientation) return;
// Check if the state is dirty and camera needs to be synchronized
updateMatricesIfNeeded();
bool changed = false;
if (options.orientation && options.orientation.value() != camera.getOrientation().m) {
changed |= setCameraOrientation(options.orientation.value());
}
if (options.position && options.position.value() != camera.getPosition()) {
changed |= setCameraPosition(options.position.value());
}
if (changed) {
updateStateFromCamera();
requestMatricesUpdate = true;
}
}
void TransformState::updateMatricesIfNeeded() const {
if (!needsMatricesUpdate() || size.isEmpty()) return;

View File

@@ -219,6 +219,9 @@ public:
const mat4& getProjectionMatrix() const;
const mat4& getInvProjectionMatrix() const;
FreeCameraOptions getFreeCameraOptions() const;
void setFreeCameraOptions(const FreeCameraOptions& options);
private:
bool rotatedNorth() const;
@@ -249,6 +252,8 @@ private:
void updateMatricesIfNeeded() const;
bool needsMatricesUpdate() const { return requestMatricesUpdate; }
bool setCameraPosition(const vec3& position);
bool setCameraOrientation(const Quaternion& orientation);
void updateCameraState() const;
void updateStateFromCamera();

View File

@@ -212,4 +212,51 @@ optional<Quaternion> Camera::orientationFromFrame(const vec3& forward, const vec
return util::orientationFromPitchBearing(pitch, bearing);
}
} // namespace util
void FreeCameraOptions::setLocation(const LatLngAltitude& location) {
position = util::toMercator(location.location, location.altitude);
}
optional<LatLngAltitude> FreeCameraOptions::getLocation() const {
if (!position) {
return nullopt;
}
const vec3 positionValue = position.value();
if (positionValue[1] < 0.0 || positionValue[1] > 1.0) {
return nullopt;
}
const LatLng location = {util::latFromMercatorY(positionValue[1]), util::lngFromMercatorX(positionValue[0])};
const double metersPerPixel = Projection::getMetersPerPixelAtLatitude(location.latitude(), 0.0);
const double worldSize = Projection::worldSize(std::pow(2.0, 0.0));
const double altitude = positionValue[2] * worldSize * metersPerPixel;
return LatLngAltitude{location, altitude};
}
void FreeCameraOptions::lookAtPoint(const LatLng& location, const optional<vec3>& upVector) {
orientation = nullopt;
if (!position) {
return;
}
const vec3 target = util::toMercator(location, 0.0);
const vec3 forward = vec3Sub(target, position.value());
vec3 up = upVector ? upVector.value() : vec3{{0.0, 0.0, 1.0}};
// Flip z-component of the up vector if it's pointing downwards
up[2] = std::abs(up[2]);
const auto newOrientation = util::Camera::orientationFromFrame(forward, up);
if (newOrientation) {
orientation = newOrientation.value().m;
}
}
void FreeCameraOptions::setPitchBearing(double pitch, double bearing) {
orientation = util::orientationFromPitchBearing(pitch * util::DEG2RAD, bearing * util::DEG2RAD).m;
}
} // namespace mbgl