blob: 5c29f5d3d177ea0c39683ffeb6a639e2740fbe2c [file] [log] [blame]
// Copyright 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 "third_party/blink/renderer/modules/xr/xr_frame_of_reference.h"
#include "device/vr/public/mojom/vr_service.mojom-blink.h"
#include "third_party/blink/renderer/modules/xr/xr_device.h"
#include "third_party/blink/renderer/modules/xr/xr_session.h"
#include "third_party/blink/renderer/modules/xr/xr_stage_bounds.h"
namespace blink {
// Rough estimate of avg human eye height in meters.
const double kDefaultEmulationHeight = 1.6;
XRFrameOfReference::XRFrameOfReference(XRSession* session, Type type)
: XRCoordinateSystem(session), type_(type) {}
XRFrameOfReference::~XRFrameOfReference() = default;
void XRFrameOfReference::UpdatePoseTransform(
std::unique_ptr<TransformationMatrix> transform) {
pose_transform_ = std::move(transform);
}
void XRFrameOfReference::UpdateStageBounds(XRStageBounds* bounds) {
bounds_ = bounds;
// TODO(bajones): Fire a boundschange event
}
void XRFrameOfReference::UpdateStageTransform() {
const device::mojom::blink::VRDisplayInfoPtr& display_info =
session()->GetVRDisplayInfo();
if (display_info && display_info->stageParameters) {
// Use the transform given by xrDisplayInfo's stageParamters if available.
const WTF::Vector<float>& m =
display_info->stageParameters->standingTransform;
pose_transform_ = TransformationMatrix::Create(
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10],
m[11], m[12], m[13], m[14], m[15]);
} else if (emulated_height_ != 0.0) {
// Otherwise, if this frame of reference has specified that an emulated
// height may be used, create a transform based on that.
pose_transform_ = TransformationMatrix::Create();
pose_transform_->Translate3d(0, emulated_height_, 0);
} else {
// If stage parameters aren't available and emulation is disabled, set the
// transform to null, which will subsequently cause this frame of reference
// to return null poses.
pose_transform_.reset();
}
display_info_id_ = session()->DisplayInfoPtrId();
}
// Enables emulated height when using a stage frame of reference, which should
// only be used if the sytem does not have a native concept of how far above the
// floor the XRDevice is at any given moment. This applies a static vertical
// offset to the coordinate system so that the user feels approximately like
// they are standing on a floor plane located at Y = 0. An explicit offset in
// meters can be given if the page has specific needs.
void XRFrameOfReference::UseEmulatedHeight(double value) {
if (value == 0.0)
value = kDefaultEmulationHeight;
emulated_height_ = value;
UpdateStageTransform();
}
// Transforms a given pose from a "base" coordinate system used by the XR
// service to the frame of reference's coordinate system. This model is a bit
// over-simplified and will need to be made more robust when we start dealing
// with world-scale 6DoF tracking.
std::unique_ptr<TransformationMatrix> XRFrameOfReference::TransformBasePose(
const TransformationMatrix& base_pose) {
switch (type_) {
case kTypeHeadModel: {
// TODO(bajones): Detect if base pose is already neck modeled and return
// it unchanged if so for better performance.
// Strip out translation component.
std::unique_ptr<TransformationMatrix> pose(
TransformationMatrix::Create(base_pose));
pose->SetM41(0.0);
pose->SetM42(0.0);
pose->SetM43(0.0);
// TODO(bajones): Apply our own neck model
return pose;
} break;
case kTypeEyeLevel:
// For now we assume that all base poses are delivered as eye-level poses.
// Thus in this case we just return the pose without transformation.
return TransformationMatrix::Create(base_pose);
break;
case kTypeStage:
// Check first to see if the xrDisplayInfo has updated since the last
// call. If so, update the pose transform.
if (display_info_id_ != session()->DisplayInfoPtrId())
UpdateStageTransform();
// If the stage has a transform apply it to the base pose and return that,
// otherwise return null.
if (pose_transform_) {
std::unique_ptr<TransformationMatrix> pose(
TransformationMatrix::Create(*pose_transform_));
pose->Multiply(base_pose);
return pose;
}
break;
}
return nullptr;
}
// Serves the same purpose as TransformBasePose, but for input poses. Needs to
// know the head pose so that cases like the head-model frame of reference can
// properly adjust the input's relative position.
std::unique_ptr<TransformationMatrix>
XRFrameOfReference::TransformBaseInputPose(
const TransformationMatrix& base_input_pose,
const TransformationMatrix& base_pose) {
switch (type_) {
case kTypeHeadModel: {
std::unique_ptr<TransformationMatrix> head_model_pose(
TransformBasePose(base_pose));
// Get the positional delta between the base pose and the head model pose.
float dx = head_model_pose->M41() - base_pose.M41();
float dy = head_model_pose->M42() - base_pose.M42();
float dz = head_model_pose->M43() - base_pose.M43();
// Translate the controller by the same delta so that it shows up in the
// right relative position.
std::unique_ptr<TransformationMatrix> pose(
TransformationMatrix::Create(base_input_pose));
pose->SetM41(pose->M41() + dx);
pose->SetM42(pose->M42() + dy);
pose->SetM43(pose->M43() + dz);
return pose;
} break;
case kTypeEyeLevel:
case kTypeStage:
return TransformBasePose(base_input_pose);
break;
}
return nullptr;
}
void XRFrameOfReference::Trace(blink::Visitor* visitor) {
visitor->Trace(bounds_);
XRCoordinateSystem::Trace(visitor);
}
} // namespace blink