blob: 4fa3de29ae0df70c3d183c4e293101d41280d965 [file]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/openxr/openxr_anchor_manager.h"
#include <tuple>
#include "device/vr/openxr/openxr_api_wrapper.h"
#include "device/vr/openxr/openxr_util.h"
namespace device {
namespace {
device::Pose XrPoseToDevicePose(const XrPosef& pose) {
gfx::Quaternion orientation{pose.orientation.x, pose.orientation.y,
pose.orientation.z, pose.orientation.w};
gfx::Point3F position{pose.position.x, pose.position.y, pose.position.z};
return device::Pose{position, orientation};
}
} // namespace
OpenXrAnchorManager::~OpenXrAnchorManager() {
DisposeActiveAnchorCallbacks();
for (const auto& it : openxr_anchors_) {
DestroyAnchorData(it.second);
}
}
OpenXrAnchorManager::OpenXrAnchorManager(
const OpenXrExtensionHelper& extension_helper,
XrSession session,
XrSpace mojo_space)
: extension_helper_(extension_helper),
session_(session),
mojo_space_(mojo_space) {}
void OpenXrAnchorManager::AddCreateAnchorRequest(
const mojom::XRNativeOriginInformation& native_origin_information,
const device::Pose& native_origin_from_anchor,
CreateAnchorCallback callback) {
create_anchor_requests_.emplace_back(native_origin_information,
native_origin_from_anchor.ToTransform(),
std::move(callback));
}
device::mojom::XRAnchorsDataPtr OpenXrAnchorManager::ProcessAnchorsForFrame(
OpenXrApiWrapper* openxr,
const mojom::VRStageParametersPtr& current_stage_parameters,
const std::vector<mojom::XRInputSourceStatePtr>& input_state,
XrTime predicted_display_time) {
ProcessCreateAnchorRequests(openxr, current_stage_parameters, input_state);
return GetCurrentAnchorsData(predicted_display_time);
}
void OpenXrAnchorManager::ProcessCreateAnchorRequests(
OpenXrApiWrapper* openxr,
const mojom::VRStageParametersPtr& current_stage_parameters,
const std::vector<mojom::XRInputSourceStatePtr>& input_state) {
for (auto& request : create_anchor_requests_) {
absl::optional<XrLocation> anchor_location =
GetXrLocationFromNativeOriginInformation(
openxr, current_stage_parameters,
request.GetNativeOriginInformation(),
request.GetNativeOriginFromAnchor(), input_state);
if (!anchor_location.has_value()) {
request.TakeCallback().Run(device::mojom::CreateAnchorResult::FAILURE, 0);
continue;
}
AnchorId anchor_id = kInvalidAnchorId;
XrTime display_time = openxr->GetPredictedDisplayTime();
anchor_id = CreateAnchor(anchor_location->pose, anchor_location->space,
display_time);
if (anchor_id.is_null()) {
request.TakeCallback().Run(device::mojom::CreateAnchorResult::FAILURE, 0);
} else {
request.TakeCallback().Run(device::mojom::CreateAnchorResult::SUCCESS,
anchor_id.GetUnsafeValue());
}
}
create_anchor_requests_.clear();
}
void OpenXrAnchorManager::DisposeActiveAnchorCallbacks() {
for (auto& create_anchor : create_anchor_requests_) {
create_anchor.TakeCallback().Run(mojom::CreateAnchorResult::FAILURE, 0);
}
create_anchor_requests_.clear();
}
AnchorId OpenXrAnchorManager::CreateAnchor(XrPosef pose,
XrSpace space,
XrTime predicted_display_time) {
XrSpatialAnchorMSFT xr_anchor;
XrSpatialAnchorCreateInfoMSFT anchor_create_info{
XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT};
anchor_create_info.space = space;
anchor_create_info.pose = pose;
anchor_create_info.time = predicted_display_time;
DCHECK(extension_helper_->ExtensionMethods().xrCreateSpatialAnchorMSFT);
DCHECK(extension_helper_->ExtensionMethods().xrCreateSpatialAnchorSpaceMSFT);
DCHECK(extension_helper_->ExtensionMethods().xrDestroySpatialAnchorMSFT);
if (XR_FAILED(extension_helper_->ExtensionMethods().xrCreateSpatialAnchorMSFT(
session_, &anchor_create_info, &xr_anchor))) {
return kInvalidAnchorId;
}
XrSpace anchor_space;
XrSpatialAnchorSpaceCreateInfoMSFT space_create_info{
XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT};
space_create_info.anchor = xr_anchor;
space_create_info.poseInAnchorSpace = PoseIdentity();
if (FAILED(
extension_helper_->ExtensionMethods().xrCreateSpatialAnchorSpaceMSFT(
session_, &space_create_info, &anchor_space))) {
std::ignore =
extension_helper_->ExtensionMethods().xrDestroySpatialAnchorMSFT(
xr_anchor);
return kInvalidAnchorId;
}
AnchorId anchor_id = anchor_id_generator_.GenerateNextId();
DCHECK(anchor_id);
openxr_anchors_.insert({anchor_id, AnchorData{xr_anchor, anchor_space}});
return anchor_id;
}
XrSpace OpenXrAnchorManager::GetAnchorSpace(AnchorId anchor_id) const {
DCHECK(anchor_id);
auto it = openxr_anchors_.find(anchor_id);
if (it == openxr_anchors_.end()) {
return XR_NULL_HANDLE;
}
return it->second.space;
}
void OpenXrAnchorManager::DestroyAnchorData(
const AnchorData& anchor_data) const {
std::ignore = xrDestroySpace(anchor_data.space);
std::ignore =
extension_helper_->ExtensionMethods().xrDestroySpatialAnchorMSFT(
anchor_data.anchor);
}
void OpenXrAnchorManager::DetachAnchor(AnchorId anchor_id) {
DCHECK(anchor_id);
auto it = openxr_anchors_.find(anchor_id);
if (it == openxr_anchors_.end()) {
return;
}
DestroyAnchorData(it->second);
openxr_anchors_.erase(it);
}
mojom::XRAnchorsDataPtr OpenXrAnchorManager::GetCurrentAnchorsData(
XrTime predicted_display_time) const {
std::vector<uint64_t> all_anchors_ids(openxr_anchors_.size());
std::vector<mojom::XRAnchorDataPtr> updated_anchors(openxr_anchors_.size());
uint32_t index = 0;
for (const auto& map_entry : openxr_anchors_) {
const AnchorId anchor_id = map_entry.first;
const XrSpace anchor_space = map_entry.second.space;
all_anchors_ids[index] = anchor_id.GetUnsafeValue();
XrSpaceLocation anchor_from_mojo = {XR_TYPE_SPACE_LOCATION};
if (FAILED(xrLocateSpace(anchor_space, mojo_space_, predicted_display_time,
&anchor_from_mojo)) ||
!IsPoseValid(anchor_from_mojo.locationFlags)) {
updated_anchors[index] =
mojom::XRAnchorData::New(anchor_id.GetUnsafeValue(), absl::nullopt);
} else {
updated_anchors[index] =
mojom::XRAnchorData::New(anchor_id.GetUnsafeValue(),
XrPoseToDevicePose(anchor_from_mojo.pose));
}
index++;
}
return mojom::XRAnchorsData::New(std::move(all_anchors_ids),
std::move(updated_anchors));
}
absl::optional<OpenXrAnchorManager::XrLocation>
OpenXrAnchorManager::GetXrLocationFromNativeOriginInformation(
OpenXrApiWrapper* openxr,
const mojom::VRStageParametersPtr& current_stage_parameters,
const mojom::XRNativeOriginInformation& native_origin_information,
const gfx::Transform& native_origin_from_anchor,
const std::vector<mojom::XRInputSourceStatePtr>& input_state) const {
switch (native_origin_information.which()) {
case mojom::XRNativeOriginInformation::Tag::kInputSourceSpaceInfo:
// Currently unimplemented as only anchors are supported and are never
// created relative to input sources
return absl::nullopt;
case mojom::XRNativeOriginInformation::Tag::kReferenceSpaceType:
return GetXrLocationFromReferenceSpace(openxr, current_stage_parameters,
native_origin_information,
native_origin_from_anchor);
case mojom::XRNativeOriginInformation::Tag::kPlaneId:
case mojom::XRNativeOriginInformation::Tag::kHandJointSpaceInfo:
case mojom::XRNativeOriginInformation::Tag::kImageIndex:
// Unsupported for now
return absl::nullopt;
case mojom::XRNativeOriginInformation::Tag::kAnchorId:
return XrLocation{
GfxTransformToXrPose(native_origin_from_anchor),
GetAnchorSpace(AnchorId(native_origin_information.get_anchor_id()))};
}
}
absl::optional<OpenXrAnchorManager::XrLocation>
OpenXrAnchorManager::GetXrLocationFromReferenceSpace(
OpenXrApiWrapper* openxr,
const mojom::VRStageParametersPtr& current_stage_parameters,
const mojom::XRNativeOriginInformation& native_origin_information,
const gfx::Transform& native_origin_from_anchor) const {
// Floor corresponds to offset_from_local * local, so we must apply the
// offset to get the correct pose in the local space.
auto type = native_origin_information.get_reference_space_type();
if (type == device::mojom::XRReferenceSpaceType::kLocalFloor) {
if (!current_stage_parameters) {
return absl::nullopt;
}
// The offset from the floor to mojo (aka local) is encoded in
// current_stage_parameters->mojo_from_floor. mojo_from_floor *
// native_origin_from_anchor gives us the correct location of the anchor
// relative to the local floor reference space.
return XrLocation{
GfxTransformToXrPose(current_stage_parameters->mojo_from_floor *
native_origin_from_anchor),
openxr->GetReferenceSpace(device::mojom::XRReferenceSpaceType::kLocal)};
}
return XrLocation{GfxTransformToXrPose(native_origin_from_anchor),
openxr->GetReferenceSpace(type)};
}
} // namespace device