blob: 9741f74aff6e02ef321b805703e5bc96b2e949ca [file] [log] [blame]
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/dom/camera_3d.h"
#include "third_party/glm/glm/gtx/euler_angles.hpp"
#include "base/time.h"
#include "cobalt/dom/device_orientation_event.h"
namespace cobalt {
namespace dom {
Camera3D::Camera3D(const scoped_refptr<input::Camera3D>& impl)
: impl_(impl),
last_event_alpha_(720.0),
last_event_beta_(720.0),
last_event_gamma_(720.0) {}
void Camera3D::CreateKeyMapping(int keycode, uint32 camera_axis,
float degrees_per_second) {
impl_->CreateKeyMapping(keycode, camera_axis, degrees_per_second);
}
void Camera3D::ClearKeyMapping(int keycode) { impl_->ClearKeyMapping(keycode); }
void Camera3D::ClearAllKeyMappings() { impl_->ClearAllKeyMappings(); }
void Camera3D::Reset() { impl_->Reset(); }
void Camera3D::StartOrientationEvents(
const base::WeakPtr<EventTarget>& target) {
if (!impl()) {
return;
}
orientation_event_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSecondsD(1.0 / ORIENTATION_POLL_FREQUENCY_HZ),
base::Bind(&Camera3D::FireOrientationEvent, base::Unretained(this),
target));
}
void Camera3D::StopOrientationEvents() { orientation_event_timer_.Stop(); }
namespace {
// These produces angles within the ranges: alpha in [-180,180], beta in
// [-90,90], gamma in [-180,180].
static void QuaternionToIntrinsicZXY(const glm::dquat& q, double* intrinsic_z,
double* intrinsic_x, double* intrinsic_y) {
*intrinsic_z = atan2(2 * (q.x * q.y + q.w * q.z),
q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z);
*intrinsic_x = asin(-2 * (q.y * q.z - q.w * q.x));
*intrinsic_y = atan2(2 * (q.x * q.z + q.w * q.y),
q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z);
}
} // namespace
void Camera3D::FireOrientationEvent(base::WeakPtr<EventTarget> target) {
glm::dquat quaternion = glm::normalize(
glm::dquat(impl()->GetOrientation()) *
// The API assumes a different initial orientation (straight down instead
// of ahead), requiring a previous rotation of 90 degrees.
glm::angleAxis(M_PI / 2, glm::dvec3(1.0f, 0.0f, 0.0f)));
double intrinsic_z = 0.0f, intrinsic_x = 0.0f, intrinsic_y = 0.0f;
QuaternionToIntrinsicZXY(quaternion, &intrinsic_z, &intrinsic_x,
&intrinsic_y);
// Adjust to the angle ranges specified by the Device Orientation API.
// They should be: alpha in [0,360], beta in [-180,180], gamma in [-90,90].
double alpha = input::Camera3D::RadiansToDegrees(intrinsic_z);
double beta = input::Camera3D::RadiansToDegrees(intrinsic_x);
double gamma = -input::Camera3D::RadiansToDegrees(intrinsic_y);
if (gamma > 90 || gamma < -90) {
gamma = copysign(180, gamma) - gamma;
// Represent excess gamma as rotations along other axes.
beta = 180 - beta;
alpha = 180 - alpha;
}
alpha = fmod(360 + alpha, 360);
beta = beta >= 180 ? beta - 360 : beta;
// Filter event based the time elapsed and the angle deltas since the last
// event.
base::TimeTicks now = base::TimeTicks::Now();
if (!last_event_time_ ||
(now - *last_event_time_).InSecondsF() >
1.0 / ORIENTATION_EVENT_MIN_FREQUENCY_HZ ||
fabs(alpha - last_event_alpha_) >
ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES ||
fabs(beta - last_event_beta_) >
ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES ||
fabs(gamma - last_event_gamma_) >
ORIENTATION_EVENT_DELTA_THRESHOLD_DEGREES) {
DeviceOrientationEventInit init;
init.set_absolute(false);
init.set_alpha(alpha);
init.set_beta(beta);
init.set_gamma(gamma);
target->DispatchEvent(
new DeviceOrientationEvent("deviceorientation", init));
last_event_time_ = now;
last_event_alpha_ = alpha;
last_event_beta_ = beta;
last_event_gamma_ = gamma;
}
}
} // namespace dom
} // namespace cobalt