blob: c1f1fa388fd272512923de6082b482e49725b65d [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/modules/xr/xr_reference_space.h"
#include "device/vr/public/mojom/vr_service.mojom-blink.h"
#include "third_party/blink/renderer/modules/xr/xr_pose.h"
#include "third_party/blink/renderer/modules/xr/xr_reference_space_event.h"
#include "third_party/blink/renderer/modules/xr/xr_rigid_transform.h"
#include "third_party/blink/renderer/modules/xr/xr_session.h"
#include "third_party/blink/renderer/modules/xr/xr_utils.h"
namespace blink {
// Rough estimate of avg human eye height in meters.
const double kDefaultEmulationHeightMeters = 1.6;
XRReferenceSpace::Type XRReferenceSpace::StringToReferenceSpaceType(
const String& reference_space_type) {
if (reference_space_type == "viewer") {
return XRReferenceSpace::Type::kTypeViewer;
} else if (reference_space_type == "local") {
return XRReferenceSpace::Type::kTypeLocal;
} else if (reference_space_type == "local-floor") {
return XRReferenceSpace::Type::kTypeLocalFloor;
} else if (reference_space_type == "bounded-floor") {
return XRReferenceSpace::Type::kTypeBoundedFloor;
} else if (reference_space_type == "unbounded") {
return XRReferenceSpace::Type::kTypeUnbounded;
}
NOTREACHED();
return Type::kTypeViewer;
}
// origin offset starts as identity transform
XRReferenceSpace::XRReferenceSpace(XRSession* session, Type type)
: XRReferenceSpace(session,
MakeGarbageCollected<XRRigidTransform>(nullptr, nullptr),
type) {}
XRReferenceSpace::XRReferenceSpace(XRSession* session,
XRRigidTransform* origin_offset,
Type type)
: XRSpace(session), origin_offset_(origin_offset), type_(type) {}
XRReferenceSpace::~XRReferenceSpace() = default;
XRPose* XRReferenceSpace::getPose(
XRSpace* other_space,
const TransformationMatrix* mojo_from_viewer) {
if (type_ == Type::kTypeViewer) {
std::unique_ptr<TransformationMatrix> other_offsetspace_from_viewer =
other_space->SpaceFromViewerWithDefaultAndOffset(mojo_from_viewer);
if (!other_offsetspace_from_viewer) {
return nullptr;
}
auto viewer_from_offset = OriginOffsetMatrix();
auto other_offsetspace_from_offset =
*other_offsetspace_from_viewer * viewer_from_offset;
return MakeGarbageCollected<XRPose>(other_offsetspace_from_offset,
session()->EmulatedPosition());
} else {
return XRSpace::getPose(other_space, mojo_from_viewer);
}
}
void XRReferenceSpace::SetFloorFromMojo() {
const device::mojom::blink::VRDisplayInfoPtr& display_info =
session()->GetVRDisplayInfo();
if (display_info && display_info->stage_parameters) {
// Use the transform given by xrDisplayInfo's stage_parameters if available.
floor_from_mojo_ = std::make_unique<TransformationMatrix>(
display_info->stage_parameters->standing_transform.matrix());
} else {
// Otherwise, create a transform based on the default emulated height.
floor_from_mojo_ = std::make_unique<TransformationMatrix>();
floor_from_mojo_->Translate3d(0, kDefaultEmulationHeightMeters, 0);
}
display_info_id_ = session()->DisplayInfoPtrId();
}
// Returns a default viewer pose if no actual viewer pose is available. Only
// applicable to viewer reference spaces.
std::unique_ptr<TransformationMatrix> XRReferenceSpace::DefaultViewerPose() {
// A viewer reference space always returns an identity matrix.
return type_ == Type::kTypeViewer ? std::make_unique<TransformationMatrix>()
: nullptr;
}
std::unique_ptr<TransformationMatrix> XRReferenceSpace::SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer) {
switch (type_) {
case Type::kTypeLocal:
// Currently 'local' space is equivalent to mojo space.
return std::make_unique<TransformationMatrix>();
case Type::kTypeLocalFloor:
// Currently all base poses are 'local' space, so use of 'local-floor'
// reference spaces requires adjustment. Ideally the service will
// eventually provide poses in the requested space directly, avoiding the
// need to do additional transformation here.
// Check first to see if the xrDisplayInfo has updated since the last
// call. If so, update the floor-level transform.
if (display_info_id_ != session()->DisplayInfoPtrId())
SetFloorFromMojo();
return std::make_unique<TransformationMatrix>(*floor_from_mojo_);
case Type::kTypeViewer:
// Return viewer_from_mojo which is the inverse of mojo_from_viewer.
return std::make_unique<TransformationMatrix>(mojo_from_viewer.Inverse());
case Type::kTypeUnbounded:
// For now we assume that poses returned by systems that support unbounded
// reference spaces are already in the correct space. Return an identity.
return std::make_unique<TransformationMatrix>();
case Type::kTypeBoundedFloor:
NOTREACHED() << "kTypeBoundedFloor should be handled by subclass";
break;
}
return nullptr;
}
// Returns the refspace-from-viewerspace transform, corresponding to the pose of
// the viewer in this space. This takes the mojo_from_viewer transform (viewer
// pose in mojo space) as input, and left-multiplies space_from_mojo onto that.
std::unique_ptr<TransformationMatrix> XRReferenceSpace::SpaceFromViewer(
const TransformationMatrix& mojo_from_viewer) {
if (type_ == Type::kTypeViewer) {
// Special case for viewer space, always return an identity matrix
// explicitly. In theory the default behavior of multiplying SpaceFromMojo *
// MojoFromViewer would be equivalent, but that would likely return an
// almost-identity due to rounding errors.
return std::make_unique<TransformationMatrix>();
}
// Return space_from_viewer = space_from_mojo * mojo_from_viewer
auto space_from_viewer = SpaceFromMojo(mojo_from_viewer);
if (!space_from_viewer)
return nullptr;
space_from_viewer->Multiply(mojo_from_viewer);
return space_from_viewer;
}
std::unique_ptr<TransformationMatrix> XRReferenceSpace::SpaceFromInputForViewer(
const TransformationMatrix& mojo_from_input,
const TransformationMatrix& mojo_from_viewer) {
// Return space_from_input = space_from_mojo * mojo_from_input
auto space_from_input = SpaceFromMojo(mojo_from_viewer);
if (!space_from_input)
return nullptr;
space_from_input->Multiply(mojo_from_input);
return space_from_input;
}
std::unique_ptr<TransformationMatrix> XRReferenceSpace::MojoFromSpace() {
// XRReferenceSpace doesn't do anything special with the base pose, but
// derived reference spaces (bounded, unbounded, stationary, etc.) have their
// own custom behavior.
// Calculate the offset space's pose (including originOffset) in mojo
// space.
TransformationMatrix identity;
std::unique_ptr<TransformationMatrix> mojo_from_offsetspace =
SpaceFromViewer(identity);
if (!mojo_from_offsetspace) {
// Transform wasn't possible.
return nullptr;
}
// Must account for position and orientation defined by origin offset.
// Result is mojo_from_offset = mojo_from_ref * ref_from_offset,
// where ref_from_offset is originOffset's transform matrix.
// TODO(https://crbug.com/1008466): move originOffset to separate class?
mojo_from_offsetspace->Multiply(origin_offset_->TransformMatrix());
return mojo_from_offsetspace;
}
TransformationMatrix XRReferenceSpace::OriginOffsetMatrix() {
return origin_offset_->TransformMatrix();
}
TransformationMatrix XRReferenceSpace::InverseOriginOffsetMatrix() {
return origin_offset_->InverseTransformMatrix();
}
XRReferenceSpace* XRReferenceSpace::getOffsetReferenceSpace(
XRRigidTransform* additional_offset) {
auto matrix =
OriginOffsetMatrix().Multiply(additional_offset->TransformMatrix());
auto* result_transform = MakeGarbageCollected<XRRigidTransform>(matrix);
return cloneWithOriginOffset(result_transform);
}
XRReferenceSpace* XRReferenceSpace::cloneWithOriginOffset(
XRRigidTransform* origin_offset) {
return MakeGarbageCollected<XRReferenceSpace>(this->session(), origin_offset,
type_);
}
void XRReferenceSpace::Trace(blink::Visitor* visitor) {
visitor->Trace(origin_offset_);
XRSpace::Trace(visitor);
}
void XRReferenceSpace::OnReset() {
if (type_ != Type::kTypeViewer) {
DispatchEvent(
*XRReferenceSpaceEvent::Create(event_type_names::kReset, this));
}
}
} // namespace blink