| /* |
| * 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 |