blob: 7651bad27c8f9e3f95333c6a8e01baedcc314eb7 [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 <sstream>
#include <string>
#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 {
using ReferenceSpaceType = device::mojom::blink::XRReferenceSpaceType;
// Rough estimate of avg human eye height in meters.
const double kDefaultEmulationHeightMeters = -1.6;
ReferenceSpaceType XRReferenceSpace::StringToReferenceSpaceType(
const String& reference_space_type) {
if (reference_space_type == "viewer") {
return ReferenceSpaceType::kViewer;
} else if (reference_space_type == "local") {
return ReferenceSpaceType::kLocal;
} else if (reference_space_type == "local-floor") {
return ReferenceSpaceType::kLocalFloor;
} else if (reference_space_type == "bounded-floor") {
return ReferenceSpaceType::kBoundedFloor;
} else if (reference_space_type == "unbounded") {
return ReferenceSpaceType::kUnbounded;
}
NOTREACHED();
return ReferenceSpaceType::kViewer;
}
// origin offset starts as identity transform
XRReferenceSpace::XRReferenceSpace(XRSession* session, ReferenceSpaceType type)
: XRReferenceSpace(session,
MakeGarbageCollected<XRRigidTransform>(nullptr, nullptr),
type) {}
XRReferenceSpace::XRReferenceSpace(XRSession* session,
XRRigidTransform* origin_offset,
ReferenceSpaceType type)
: XRSpace(session), origin_offset_(origin_offset), type_(type) {}
XRReferenceSpace::~XRReferenceSpace() = default;
XRPose* XRReferenceSpace::getPose(const XRSpace* other_space) const {
if (type_ == ReferenceSpaceType::kViewer) {
absl::optional<TransformationMatrix> other_offset_from_viewer =
other_space->OffsetFromViewer();
if (!other_offset_from_viewer) {
return nullptr;
}
auto viewer_from_offset = NativeFromOffsetMatrix();
auto other_offset_from_offset =
*other_offset_from_viewer * viewer_from_offset;
return MakeGarbageCollected<XRPose>(other_offset_from_offset,
session()->EmulatedPosition());
} else {
return XRSpace::getPose(other_space);
}
}
void XRReferenceSpace::SetMojoFromFloor() const {
const device::mojom::blink::VRStageParametersPtr& stage_parameters =
session()->GetStageParameters();
if (stage_parameters) {
// Use the transform given by stage_parameters if available.
mojo_from_floor_ = std::make_unique<TransformationMatrix>(
stage_parameters->mojo_from_floor);
} else {
mojo_from_floor_.reset();
}
stage_parameters_id_ = session()->StageParametersId();
}
absl::optional<TransformationMatrix> XRReferenceSpace::MojoFromNative() const {
DVLOG(3) << __func__ << ": type_=" << type_;
switch (type_) {
case ReferenceSpaceType::kViewer:
case ReferenceSpaceType::kLocal:
case ReferenceSpaceType::kUnbounded: {
// The session is the source of truth for latest state of the transform
// between local & unbounded spaces and mojo space.
auto mojo_from_native = session()->GetMojoFrom(type_);
if (!mojo_from_native) {
// The viewer reference space always has a default pose of identity if
// it's not tracked; but for any other type if it's not locatable, we
// return nullopt.
return type_ == ReferenceSpaceType::kViewer
? absl::optional<TransformationMatrix>(
TransformationMatrix{})
: absl::nullopt;
}
return *mojo_from_native;
}
case ReferenceSpaceType::kLocalFloor: {
// Check first to see if the stage_parameters has updated since the last
// call. If so, update the floor-level transform.
if (stage_parameters_id_ != session()->StageParametersId())
SetMojoFromFloor();
if (mojo_from_floor_) {
return *mojo_from_floor_;
}
// If the floor-level transform is unavailable, try to use the default
// transform based off of local space:
auto mojo_from_local = session()->GetMojoFrom(ReferenceSpaceType::kLocal);
if (!mojo_from_local) {
return absl::nullopt;
}
// local_from_floor-local transform corresponding to the default height.
auto local_from_floor = TransformationMatrix().Translate3d(
0, kDefaultEmulationHeightMeters, 0);
return *mojo_from_local * local_from_floor;
}
case ReferenceSpaceType::kBoundedFloor: {
NOTREACHED() << "kBoundedFloor should be handled by subclass";
return absl::nullopt;
}
}
}
absl::optional<TransformationMatrix> XRReferenceSpace::NativeFromViewer(
const absl::optional<TransformationMatrix>& mojo_from_viewer) const {
if (type_ == ReferenceSpaceType::kViewer) {
// Special case for viewer space, always return an identity matrix
// explicitly. In theory the default behavior of multiplying NativeFromMojo
// onto MojoFromViewer would be equivalent, but that would likely return an
// almost-identity due to rounding errors.
return TransformationMatrix();
}
if (!mojo_from_viewer)
return absl::nullopt;
// Return native_from_viewer = native_from_mojo * mojo_from_viewer
auto native_from_viewer = NativeFromMojo();
if (!native_from_viewer)
return absl::nullopt;
native_from_viewer->Multiply(*mojo_from_viewer);
return native_from_viewer;
}
TransformationMatrix XRReferenceSpace::NativeFromOffsetMatrix() const {
return origin_offset_->TransformMatrix();
}
TransformationMatrix XRReferenceSpace::OffsetFromNativeMatrix() const {
return origin_offset_->InverseTransformMatrix();
}
bool XRReferenceSpace::IsStationary() const {
switch (type_) {
case ReferenceSpaceType::kLocal:
case ReferenceSpaceType::kLocalFloor:
case ReferenceSpaceType::kBoundedFloor:
case ReferenceSpaceType::kUnbounded:
return true;
case ReferenceSpaceType::kViewer:
return false;
}
}
ReferenceSpaceType XRReferenceSpace::GetType() const {
return type_;
}
XRReferenceSpace* XRReferenceSpace::getOffsetReferenceSpace(
XRRigidTransform* additional_offset) const {
auto matrix =
NativeFromOffsetMatrix().Multiply(additional_offset->TransformMatrix());
auto* result_transform = MakeGarbageCollected<XRRigidTransform>(matrix);
return cloneWithOriginOffset(result_transform);
}
XRReferenceSpace* XRReferenceSpace::cloneWithOriginOffset(
XRRigidTransform* origin_offset) const {
return MakeGarbageCollected<XRReferenceSpace>(this->session(), origin_offset,
type_);
}
device::mojom::blink::XRNativeOriginInformationPtr
XRReferenceSpace::NativeOrigin() const {
return device::mojom::blink::XRNativeOriginInformation::NewReferenceSpaceType(
this->GetType());
}
std::string XRReferenceSpace::ToString() const {
std::stringstream ss;
ss << "XRReferenceSpace(type=" << type_ << ")";
return ss.str();
}
void XRReferenceSpace::Trace(Visitor* visitor) const {
visitor->Trace(origin_offset_);
XRSpace::Trace(visitor);
}
void XRReferenceSpace::OnReset() {
if (type_ != ReferenceSpaceType::kViewer) {
// DispatchEvent inherited from core/dom/events/event_target.h isn't const.
XRReferenceSpace* mutable_this = const_cast<XRReferenceSpace*>(this);
mutable_this->DispatchEvent(
*XRReferenceSpaceEvent::Create(event_type_names::kReset, mutable_this));
}
}
} // namespace blink