blob: 30f04c08bcc7939836697ea30d7015efc6c917f8 [file] [log] [blame]
// Copyright (c) 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/numerics/math_constants.h"
#include "base/time/time.h"
#include "device/vr/orientation/orientation_device.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading_shared_buffer_reader.h"
#include "services/device/public/mojom/sensor_provider.mojom.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/quaternion.h"
#include "ui/gfx/geometry/vector3d_f.h"
namespace device {
using gfx::Quaternion;
using gfx::Vector3dF;
namespace {
static constexpr int kDefaultPumpFrequencyHz = 60;
mojom::VRDisplayInfoPtr CreateVRDisplayInfo(mojom::XRDeviceId id) {
static const char DEVICE_NAME[] = "VR Orientation Device";
mojom::VRDisplayInfoPtr display_info = mojom::VRDisplayInfo::New();
display_info->id = id;
display_info->displayName = DEVICE_NAME;
display_info->capabilities = mojom::VRDisplayCapabilities::New();
display_info->capabilities->hasPosition = false;
display_info->capabilities->hasExternalDisplay = false;
display_info->capabilities->canPresent = false;
return display_info;
}
display::Display::Rotation GetRotation() {
display::Screen* screen = display::Screen::GetScreen();
if (!screen) {
// If we can't get rotation we'll assume it's 0.
return display::Display::ROTATE_0;
}
return screen->GetPrimaryDisplay().rotation();
}
} // namespace
VROrientationDevice::VROrientationDevice(
mojom::SensorProviderPtr* sensor_provider,
base::OnceClosure ready_callback)
: VRDeviceBase(mojom::XRDeviceId::ORIENTATION_DEVICE_ID),
ready_callback_(std::move(ready_callback)),
binding_(this) {
(*sensor_provider)
->GetSensor(kOrientationSensorType,
base::BindOnce(&VROrientationDevice::SensorReady,
base::Unretained(this)));
SetVRDisplayInfo(CreateVRDisplayInfo(GetId()));
}
VROrientationDevice::~VROrientationDevice() = default;
void VROrientationDevice::SensorReady(
device::mojom::SensorCreationResult,
device::mojom::SensorInitParamsPtr params) {
if (!params) {
// This means that there are no orientation sensors on this device.
HandleSensorError();
std::move(ready_callback_).Run();
return;
}
constexpr size_t kReadBufferSize = sizeof(device::SensorReadingSharedBuffer);
DCHECK_EQ(0u, params->buffer_offset % kReadBufferSize);
device::PlatformSensorConfiguration default_config =
params->default_configuration;
sensor_.Bind(std::move(params->sensor));
binding_.Bind(std::move(params->client_request));
shared_buffer_handle_ = std::move(params->memory);
DCHECK(!shared_buffer_);
shared_buffer_ = shared_buffer_handle_->MapAtOffset(kReadBufferSize,
params->buffer_offset);
if (!shared_buffer_) {
// If we cannot read data, we cannot supply a device.
HandleSensorError();
std::move(ready_callback_).Run();
return;
}
const device::SensorReadingSharedBuffer* buffer =
static_cast<const device::SensorReadingSharedBuffer*>(
shared_buffer_.get());
shared_buffer_reader_.reset(
new device::SensorReadingSharedBufferReader(buffer));
default_config.set_frequency(kDefaultPumpFrequencyHz);
sensor_.set_connection_error_handler(base::BindOnce(
&VROrientationDevice::HandleSensorError, base::Unretained(this)));
sensor_->ConfigureReadingChangeNotifications(false /* disabled */);
sensor_->AddConfiguration(
default_config,
base::BindOnce(&VROrientationDevice::OnSensorAddConfiguration,
base::Unretained(this)));
}
// Mojo callback for Sensor::AddConfiguration().
void VROrientationDevice::OnSensorAddConfiguration(bool success) {
if (!success) {
// Sensor config is not supported so we can't provide sensor events.
HandleSensorError();
} else {
// We're good to go.
available_ = true;
}
std::move(ready_callback_).Run();
}
void VROrientationDevice::RaiseError() {
HandleSensorError();
}
void VROrientationDevice::HandleSensorError() {
sensor_.reset();
shared_buffer_handle_.reset();
shared_buffer_.reset();
binding_.Close();
}
void VROrientationDevice::RequestSession(
mojom::XRRuntimeSessionOptionsPtr options,
mojom::XRRuntime::RequestSessionCallback callback) {
DCHECK(!options->immersive);
// TODO(http://crbug.com/695937): Perform a check to see if sensors are
// available when RequestSession is called for non-immersive sessions.
ReturnNonImmersiveSession(std::move(callback));
}
void VROrientationDevice::OnGetInlineFrameData(
mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
mojom::VRPosePtr pose = mojom::VRPose::New();
pose->orientation.emplace(4);
SensorReading latest_reading;
// If the reading fails just return the last pose that we got.
if (shared_buffer_reader_->GetReading(&latest_reading)) {
latest_pose_.set_x(latest_reading.orientation_quat.x);
latest_pose_.set_y(latest_reading.orientation_quat.y);
latest_pose_.set_z(latest_reading.orientation_quat.z);
latest_pose_.set_w(latest_reading.orientation_quat.w);
latest_pose_ =
WorldSpaceToUserOrientedSpace(SensorSpaceToWorldSpace(latest_pose_));
}
pose->orientation.value()[0] = latest_pose_.x();
pose->orientation.value()[1] = latest_pose_.y();
pose->orientation.value()[2] = latest_pose_.z();
pose->orientation.value()[3] = latest_pose_.w();
mojom::XRFrameDataPtr frame_data = mojom::XRFrameData::New();
frame_data->pose = std::move(pose);
std::move(callback).Run(std::move(frame_data));
}
Quaternion VROrientationDevice::SensorSpaceToWorldSpace(Quaternion q) {
display::Display::Rotation rotation = GetRotation();
if (rotation == display::Display::ROTATE_90) {
// Rotate the sensor reading to account for the screen rotation.
q = q * Quaternion(Vector3dF(0, 0, 1), -base::kPiDouble / 2);
} else if (rotation == display::Display::ROTATE_270) {
// Rotate the sensor reading to account for the screen rotation the other
// way.
q = q * Quaternion(Vector3dF(0, 0, 1), base::kPiDouble / 2);
}
// Tilt the view up to have the y axis as the vertical axis instead of z
q = Quaternion(Vector3dF(1, 0, 0), -base::kPiDouble / 2) * q;
return q;
}
Quaternion VROrientationDevice::WorldSpaceToUserOrientedSpace(Quaternion q) {
if (!base_pose_) {
// Check that q is valid by checking if the length is not 0 (it should
// technically always be 1, but this accounts for rounding errors).
if (!(q.Length() > .1)) {
// q is invalid. Do not use for base pose.
return q;
}
// A base pose to read the initial forward direction off of.
base_pose_ = q;
// Extract the yaw from base pose to use as the base forward direction.
base_pose_->set_x(0);
base_pose_->set_z(0);
base_pose_ = base_pose_->Normalized();
}
// Adjust the base forward on the orientation to where the original forward
// was.
q = base_pose_->inverse() * q;
return q;
}
} // namespace device