Commit cb06ba9e authored by Klaus Weidner's avatar Klaus Weidner Committed by Commit Bot

Refactor WebXR reference space and pose internals

Avoid using generic names such as "base pose" or "transform", and try to
always specifically say which two spaces are connected by transforms.

Use the B_from_A naming convention where possible, so that transforms
can be chained naturally.

Bug: 1008102
Change-Id: I74988f89340375197017f9f518f69d4fd03e7d76
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1825585Reviewed-by: default avatarAlexander Cooper <alcooper@chromium.org>
Reviewed-by: default avatarPiotr Bialecki <bialpio@chromium.org>
Commit-Queue: Klaus Weidner <klausw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#700403}
parent 9b3b4da6
......@@ -23,7 +23,8 @@ XRBoundedReferenceSpace::XRBoundedReferenceSpace(
XRBoundedReferenceSpace::~XRBoundedReferenceSpace() = default;
// No default pose for bounded reference spaces.
std::unique_ptr<TransformationMatrix> XRBoundedReferenceSpace::DefaultPose() {
std::unique_ptr<TransformationMatrix>
XRBoundedReferenceSpace::DefaultViewerPose() {
return nullptr;
}
......@@ -40,12 +41,14 @@ void XRBoundedReferenceSpace::EnsureUpdated() {
if (display_info && display_info->stage_parameters) {
// Use the transform given by xrDisplayInfo's stage_parameters if available.
floor_level_transform_ = std::make_unique<TransformationMatrix>(
bounded_space_from_mojo_ = std::make_unique<TransformationMatrix>(
display_info->stage_parameters->standing_transform.matrix());
// In order to ensure that the bounds continue to line up with the user's
// physical environment we need to transform by the inverse of the
// originOffset.
// originOffset. TODO(https://crbug.com/1008466): move originOffset to
// separate class? If yes, that class would need to apply a transform
// in the boundsGeometry accessor.
TransformationMatrix bounds_transform = InverseOriginOffsetMatrix();
if (display_info->stage_parameters->bounds) {
......@@ -78,31 +81,28 @@ void XRBoundedReferenceSpace::EnsureUpdated() {
} else {
// If stage parameters aren't available set the transform to null, which
// will subsequently cause this reference space to return null poses.
floor_level_transform_.reset();
bounded_space_from_mojo_.reset();
bounds_geometry_.clear();
}
DispatchEvent(*XRReferenceSpaceEvent::Create(event_type_names::kReset, this));
}
// Transforms a given pose from a "base" reference space used by the XR
// service to the bounded (floor level) reference space. Ideally in the future
// this reference space can be used without additional transforms, with
// the various XR backends returning poses already in the right space.
std::unique_ptr<TransformationMatrix>
XRBoundedReferenceSpace::TransformBasePose(
const TransformationMatrix& base_pose) {
// Gets the pose of the mojo origin in this reference space, corresponding to a
// transform from mojo coordinates to reference space coordinates. Ideally in
// the future this reference space can be used without additional transforms,
// with the various XR backends returning poses already in the right space.
std::unique_ptr<TransformationMatrix> XRBoundedReferenceSpace::SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer) {
EnsureUpdated();
// If the reference space has a transform apply it to the base pose and return
// that, otherwise return null.
if (floor_level_transform_) {
std::unique_ptr<TransformationMatrix> pose(
std::make_unique<TransformationMatrix>(*floor_level_transform_));
pose->Multiply(base_pose);
return pose;
if (bounded_space_from_mojo_) {
std::unique_ptr<TransformationMatrix> space_from_mojo(
std::make_unique<TransformationMatrix>(*bounded_space_from_mojo_));
return space_from_mojo;
}
return nullptr;
}
......
......@@ -19,9 +19,9 @@ class XRBoundedReferenceSpace final : public XRReferenceSpace {
XRBoundedReferenceSpace(XRSession*, XRRigidTransform*);
~XRBoundedReferenceSpace() override;
std::unique_ptr<TransformationMatrix> DefaultPose() override;
std::unique_ptr<TransformationMatrix> TransformBasePose(
const TransformationMatrix& base_pose) override;
std::unique_ptr<TransformationMatrix> DefaultViewerPose() override;
std::unique_ptr<TransformationMatrix> SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer) override;
HeapVector<Member<DOMPointReadOnly>> boundsGeometry();
......@@ -36,7 +36,7 @@ class XRBoundedReferenceSpace final : public XRReferenceSpace {
void EnsureUpdated();
HeapVector<Member<DOMPointReadOnly>> bounds_geometry_;
std::unique_ptr<TransformationMatrix> floor_level_transform_;
std::unique_ptr<TransformationMatrix> bounded_space_from_mojo_;
unsigned int stage_parameters_id_ = 0;
};
......
......@@ -111,11 +111,12 @@ void XRCanvasInputProvider::UpdateInputSource(PointerEvent* event) {
// projection matrix to get a 3D point in space, which is then returned in
// matrix form so we can use it as an XRInputSource's pointerMatrix.
XRViewData& view = session_->views()[0];
TransformationMatrix pointer_transform_matrix = view.UnprojectPointer(
TransformationMatrix viewer_from_pointer = view.UnprojectPointer(
element_x, element_y, canvas_->OffsetWidth(), canvas_->OffsetHeight());
// Update the input source's pointer matrix.
input_source_->SetPointerTransformMatrix(&pointer_transform_matrix);
// Update the pointer pose in input space. For screen tapping, input
// space is equivalent to viewer space.
input_source_->SetInputFromPointer(&viewer_from_pointer);
}
void XRCanvasInputProvider::ClearInputSource() {
......
......@@ -59,7 +59,8 @@ XRViewerPose* XRFrame::getViewerPose(XRReferenceSpace* reference_space,
session_->LogGetPose();
std::unique_ptr<TransformationMatrix> pose =
reference_space->GetViewerPoseMatrix(base_pose_matrix_.get());
reference_space->SpaceFromViewerWithDefaultAndOffset(
mojo_from_viewer_.get());
if (!pose) {
return nullptr;
}
......@@ -102,11 +103,11 @@ XRPose* XRFrame::getPose(XRSpace* space_A,
return nullptr;
}
return space_A->getPose(space_B, base_pose_matrix_.get());
return space_A->getPose(space_B, mojo_from_viewer_.get());
}
void XRFrame::SetBasePoseMatrix(const TransformationMatrix& base_pose_matrix) {
base_pose_matrix_ = std::make_unique<TransformationMatrix>(base_pose_matrix);
void XRFrame::SetMojoFromViewer(const TransformationMatrix& mojo_from_viewer) {
mojo_from_viewer_ = std::make_unique<TransformationMatrix>(mojo_from_viewer);
}
void XRFrame::Deactivate() {
......
......@@ -41,7 +41,7 @@ class XRFrame final : public ScriptWrappable {
XRWorldInformation* worldInformation() const { return world_information_; }
XRAnchorSet* trackedAnchors() const;
void SetBasePoseMatrix(const TransformationMatrix&);
void SetMojoFromViewer(const TransformationMatrix&);
void Trace(blink::Visitor*) override;
......@@ -64,8 +64,9 @@ class XRFrame final : public ScriptWrappable {
const Member<XRSession> session_;
// Maps from mojo space to headset space.
std::unique_ptr<TransformationMatrix> base_pose_matrix_;
// Viewer pose in mojo space, the matrix maps from viewer (headset) space to
// mojo space.
std::unique_ptr<TransformationMatrix> mojo_from_viewer_;
// Frames are only active during callbacks. getPose and getViewerPose should
// only be called from JS on active frames.
......
......@@ -15,7 +15,7 @@ XRGripSpace::XRGripSpace(XRSession* session, XRInputSource* source)
: XRSpace(session), input_source_(source) {}
XRPose* XRGripSpace::getPose(XRSpace* other_space,
const TransformationMatrix* base_pose_matrix) {
const TransformationMatrix* mojo_from_viewer) {
// Grip is only available when using tracked pointer for input.
if (input_source_->TargetRayMode() !=
device::mojom::XRTargetRayMode::POINTING) {
......@@ -23,23 +23,27 @@ XRPose* XRGripSpace::getPose(XRSpace* other_space,
}
// Make sure the required pose matrices are available.
if (!base_pose_matrix || !input_source_->BasePose()) {
if (!mojo_from_viewer || !input_source_->MojoFromInput()) {
return nullptr;
}
std::unique_ptr<TransformationMatrix> grip_pose =
other_space->TransformBaseInputPose(*(input_source_->BasePose()),
*base_pose_matrix);
if (!grip_pose) {
// Calculate grip pose in other_space, aka other_from_grip
std::unique_ptr<TransformationMatrix> other_from_grip =
other_space->SpaceFromInputForViewer(*(input_source_->MojoFromInput()),
*mojo_from_viewer);
if (!other_from_grip) {
return nullptr;
}
// Account for any changes made to the reference space's origin offset so
// that things like teleportation works.
TransformationMatrix adjusted_pose =
other_space->InverseOriginOffsetMatrix().Multiply(*grip_pose);
return MakeGarbageCollected<XRPose>(adjusted_pose,
//
// This is offset_from_grip = offset_from_other * other_from_grip,
// where offset_from_other = inverse(other_from_offset).
// TODO(https://crbug.com/1008466): move originOffset to separate class?
TransformationMatrix offset_from_grip =
other_space->InverseOriginOffsetMatrix().Multiply(*other_from_grip);
return MakeGarbageCollected<XRPose>(offset_from_grip,
input_source_->emulatedPosition());
}
......
......@@ -81,7 +81,7 @@ XRInputSource* XRInputSource::CreateOrUpdateFrom(
updated_source->state_.handedness = desc->handedness;
updated_source->state_.emulated_position = desc->emulated_position;
updated_source->pointer_transform_matrix_ =
updated_source->input_from_pointer_ =
TryGetTransformationMatrix(desc->pointer_offset);
updated_source->state_.profiles.clear();
......@@ -90,7 +90,7 @@ XRInputSource* XRInputSource::CreateOrUpdateFrom(
}
}
updated_source->base_pose_matrix_ = TryGetTransformationMatrix(state->grip);
updated_source->mojo_from_input_ = TryGetTransformationMatrix(state->grip);
return updated_source;
}
......@@ -114,10 +114,10 @@ XRInputSource::XRInputSource(const XRInputSource& other)
MakeGarbageCollected<XRTargetRaySpace>(other.session_, this)),
grip_space_(MakeGarbageCollected<XRGripSpace>(other.session_, this)),
gamepad_(other.gamepad_),
base_pose_matrix_(
TryGetTransformationMatrix(other.base_pose_matrix_.get())),
pointer_transform_matrix_(
TryGetTransformationMatrix(other.pointer_transform_matrix_.get())) {}
mojo_from_input_(
TryGetTransformationMatrix(other.mojo_from_input_.get())),
input_from_pointer_(
TryGetTransformationMatrix(other.input_from_pointer_.get())) {}
const String XRInputSource::handedness() const {
switch (state_.handedness) {
......@@ -186,10 +186,9 @@ bool XRInputSource::InvalidatesSameObject(
return false;
}
void XRInputSource::SetPointerTransformMatrix(
const TransformationMatrix* pointer_transform_matrix) {
pointer_transform_matrix_ =
TryGetTransformationMatrix(pointer_transform_matrix);
void XRInputSource::SetInputFromPointer(
const TransformationMatrix* input_from_pointer) {
input_from_pointer_ = TryGetTransformationMatrix(input_from_pointer);
}
void XRInputSource::SetGamepadConnected(bool state) {
......
......@@ -59,7 +59,7 @@ class XRInputSource : public ScriptWrappable, public Gamepad::Client {
uint32_t source_id() const { return state_.source_id; }
void SetPointerTransformMatrix(const TransformationMatrix*);
void SetInputFromPointer(const TransformationMatrix*);
void SetGamepadConnected(bool state);
// Gamepad::Client
......@@ -74,11 +74,11 @@ class XRInputSource : public ScriptWrappable, public Gamepad::Client {
device::mojom::XRTargetRayMode TargetRayMode() const {
return state_.target_ray_mode;
}
const TransformationMatrix* BasePose() const {
return base_pose_matrix_.get();
const TransformationMatrix* MojoFromInput() const {
return mojo_from_input_.get();
}
const TransformationMatrix* PointerTransform() const {
return pointer_transform_matrix_.get();
const TransformationMatrix* InputFromPointer() const {
return input_from_pointer_.get();
}
void OnSelectStart();
......@@ -132,11 +132,14 @@ class XRInputSource : public ScriptWrappable, public Gamepad::Client {
Member<XRGripSpace> grip_space_;
Member<Gamepad> gamepad_;
std::unique_ptr<TransformationMatrix> base_pose_matrix_;
// Input device pose in mojo space. This is the grip pose for
// tracked controllers, and the viewer pose for screen input.
std::unique_ptr<TransformationMatrix> mojo_from_input_;
// This is the transform to apply to the base_pose_matrix_ to get the pointer
// matrix. In most cases it should be static.
std::unique_ptr<TransformationMatrix> pointer_transform_matrix_;
// Pointer pose in input device space, this is the transform to apply to
// mojo_from_input_ to get the pointer matrix. In most cases it should be
// static.
std::unique_ptr<TransformationMatrix> input_from_pointer_;
};
} // namespace blink
......
......@@ -21,14 +21,14 @@ class XRObjectSpace : public XRSpace {
explicit XRObjectSpace(XRSession* session, const T* object)
: XRSpace(session), object_(object) {}
std::unique_ptr<TransformationMatrix> GetTransformToMojoSpace() override {
auto mojo_to_object = object_->poseMatrix();
std::unique_ptr<TransformationMatrix> MojoFromSpace() override {
auto object_from_mojo = object_->poseMatrix();
if (!mojo_to_object.IsInvertible()) {
if (!object_from_mojo.IsInvertible()) {
return nullptr;
}
return std::make_unique<TransformationMatrix>(mojo_to_object.Inverse());
return std::make_unique<TransformationMatrix>(object_from_mojo.Inverse());
}
void Trace(blink::Visitor* visitor) override {
......
......@@ -48,54 +48,51 @@ XRReferenceSpace::~XRReferenceSpace() = default;
XRPose* XRReferenceSpace::getPose(
XRSpace* other_space,
const TransformationMatrix* base_pose_matrix) {
const TransformationMatrix* mojo_from_viewer) {
if (type_ == Type::kTypeViewer) {
std::unique_ptr<TransformationMatrix> viewer_pose_matrix =
other_space->GetViewerPoseMatrix(base_pose_matrix);
if (!viewer_pose_matrix) {
std::unique_ptr<TransformationMatrix> offsetspace_from_viewer =
other_space->SpaceFromViewerWithDefaultAndOffset(mojo_from_viewer);
if (!offsetspace_from_viewer) {
return nullptr;
}
return MakeGarbageCollected<XRPose>(*viewer_pose_matrix,
return MakeGarbageCollected<XRPose>(*offsetspace_from_viewer,
session()->EmulatedPosition());
} else {
return XRSpace::getPose(other_space, base_pose_matrix);
return XRSpace::getPose(other_space, mojo_from_viewer);
}
}
void XRReferenceSpace::UpdateFloorLevelTransform() {
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_level_transform_ = std::make_unique<TransformationMatrix>(
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_level_transform_ = std::make_unique<TransformationMatrix>();
floor_level_transform_->Translate3d(0, kDefaultEmulationHeightMeters, 0);
floor_from_mojo_ = std::make_unique<TransformationMatrix>();
floor_from_mojo_->Translate3d(0, kDefaultEmulationHeightMeters, 0);
}
display_info_id_ = session()->DisplayInfoPtrId();
}
// Returns a default pose if no base pose is available. Only applicable to
// viewer reference spaces.
std::unique_ptr<TransformationMatrix> XRReferenceSpace::DefaultPose() {
// 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;
}
// Transforms a given pose from a "base" reference space used by the XR
// service to the space represenced by this reference space.
std::unique_ptr<TransformationMatrix> XRReferenceSpace::TransformBasePose(
const TransformationMatrix& base_pose) {
std::unique_ptr<TransformationMatrix> XRReferenceSpace::SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer) {
switch (type_) {
case Type::kTypeLocal:
// Currently all base poses are 'local' poses, so return directly.
return std::make_unique<TransformationMatrix>(base_pose);
break;
// 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
......@@ -105,57 +102,77 @@ std::unique_ptr<TransformationMatrix> XRReferenceSpace::TransformBasePose(
// 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())
UpdateFloorLevelTransform();
// Apply the floor-level transform to the base pose.
if (floor_level_transform_) {
auto pose =
std::make_unique<TransformationMatrix>(*floor_level_transform_);
pose->Multiply(base_pose);
return pose;
}
break;
SetFloorFromMojo();
return std::make_unique<TransformationMatrix>(*floor_from_mojo_);
case Type::kTypeViewer:
// Always return the default pose because we will only get here for an
// "viewer" reference space.
return DefaultPose();
// 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 std::make_unique<TransformationMatrix>(base_pose);
// 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;
}
// Serves the same purpose as TransformBasePose, but for input poses. Needs to
// know the head pose so that cases like the viewer frame of reference can
// properly adjust the input's relative position.
std::unique_ptr<TransformationMatrix> XRReferenceSpace::TransformBaseInputPose(
const TransformationMatrix& base_input_pose,
const TransformationMatrix& base_pose) {
return TransformBasePose(base_input_pose);
// 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::GetTransformToMojoSpace() {
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> transform_matrix =
TransformBasePose(identity);
std::unique_ptr<TransformationMatrix> mojo_from_offsetspace =
SpaceFromViewer(identity);
if (!transform_matrix) {
if (!mojo_from_offsetspace) {
// Transform wasn't possible.
return nullptr;
}
// Must account for position and orientation defined by origin offset.
transform_matrix->Multiply(origin_offset_->TransformMatrix());
return transform_matrix;
// 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() {
......
......@@ -36,14 +36,16 @@ class XRReferenceSpace : public XRSpace {
XRPose* getPose(XRSpace* other_space,
const TransformationMatrix* base_pose_matrix) override;
std::unique_ptr<TransformationMatrix> DefaultPose() override;
std::unique_ptr<TransformationMatrix> TransformBasePose(
const TransformationMatrix& base_pose) override;
std::unique_ptr<TransformationMatrix> TransformBaseInputPose(
const TransformationMatrix& base_input_pose,
const TransformationMatrix& base_pose) override;
std::unique_ptr<TransformationMatrix> DefaultViewerPose() override;
std::unique_ptr<TransformationMatrix> SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer) override;
std::unique_ptr<TransformationMatrix> SpaceFromViewer(
const TransformationMatrix& mojo_from_viewer) override;
std::unique_ptr<TransformationMatrix> SpaceFromInputForViewer(
const TransformationMatrix& mojo_from_input,
const TransformationMatrix& mojo_from_viewer) override;
std::unique_ptr<TransformationMatrix> GetTransformToMojoSpace() override;
std::unique_ptr<TransformationMatrix> MojoFromSpace() override;
TransformationMatrix OriginOffsetMatrix() override;
TransformationMatrix InverseOriginOffsetMatrix() override;
......@@ -60,11 +62,11 @@ class XRReferenceSpace : public XRSpace {
virtual XRReferenceSpace* cloneWithOriginOffset(
XRRigidTransform* origin_offset);
void UpdateFloorLevelTransform();
void SetFloorFromMojo();
unsigned int display_info_id_ = 0;
std::unique_ptr<TransformationMatrix> floor_level_transform_;
std::unique_ptr<TransformationMatrix> floor_from_mojo_;
Member<XRRigidTransform> origin_offset_;
Type type_;
};
......
......@@ -419,38 +419,38 @@ ScriptPromise XRSession::CreateAnchor(ScriptState* script_state,
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// Transformation from mojo space to passed in |space|.
std::unique_ptr<TransformationMatrix> space_from_mojo =
space->GetTransformToMojoSpace();
// Transformation from passed in |space| to mojo space.
std::unique_ptr<TransformationMatrix> mojo_from_space =
space->MojoFromSpace();
DVLOG(3) << __func__
<< ": space_from_mojo = " << space_from_mojo->ToString(true);
<< ": mojo_from_space = " << mojo_from_space->ToString(true);
// Matrix will be null if transformation from mojo space to object space is
// Matrix will be null if transformation from object space to mojo space is
// not invertible, log & bail out in that case.
if (!space_from_mojo || !space_from_mojo->IsInvertible()) {
if (!mojo_from_space || !mojo_from_space->IsInvertible()) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
kNonInvertibleMatrix);
return ScriptPromise();
}
auto mojo_from_space = space_from_mojo->Inverse();
auto space_from_mojo = mojo_from_space->Inverse();
DVLOG(3) << __func__
<< ": mojo_from_space = " << mojo_from_space.ToString(true);
<< ": space_from_mojo = " << space_from_mojo.ToString(true);
// Transformation from passed in pose to |space|.
auto space_from_initial_pose = initial_pose->TransformMatrix();
auto mojo_from_initial_pose = mojo_from_space * space_from_initial_pose;
DVLOG(3) << __func__ << ": space_from_initial_pose = "
<< space_from_initial_pose.ToString(true);
auto mojo_from_initial_pose = initial_pose->TransformMatrix();
auto space_from_initial_pose = space_from_mojo * mojo_from_initial_pose;
DVLOG(3) << __func__ << ": mojo_from_initial_pose = "
<< mojo_from_initial_pose.ToString(true);
DVLOG(3) << __func__ << ": space_from_initial_pose = "
<< space_from_initial_pose.ToString(true);
TransformationMatrix::DecomposedType decomposed;
if (!mojo_from_initial_pose.Decompose(decomposed)) {
if (!space_from_initial_pose.Decompose(decomposed)) {
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
kUnableToDecomposeMatrix);
return ScriptPromise();
......@@ -923,7 +923,7 @@ void XRSession::ApplyPendingRenderState() {
void XRSession::UpdatePresentationFrameState(
double timestamp,
std::unique_ptr<TransformationMatrix> base_pose_matrix,
std::unique_ptr<TransformationMatrix> mojo_from_viewer,
const device::mojom::blink::XRFrameDataPtr& frame_data) {
TRACE_EVENT0("gpu", __FUNCTION__);
DVLOG(2) << __FUNCTION__ << " : frame_data valid? "
......@@ -932,9 +932,9 @@ void XRSession::UpdatePresentationFrameState(
if (ended_)
return;
base_pose_matrix_ = std::move(base_pose_matrix);
DVLOG(2) << __FUNCTION__ << " : base_pose_matrix_ valid? "
<< (base_pose_matrix_ ? true : false);
mojo_from_viewer_ = std::move(mojo_from_viewer);
DVLOG(2) << __FUNCTION__ << " : mojo_from_viewer_ valid? "
<< (mojo_from_viewer_ ? true : false);
// Update objects that might change on per-frame basis.
if (frame_data) {
......@@ -1036,12 +1036,12 @@ XRFrame* XRSession::CreatePresentationFrame() {
XRFrame* presentation_frame =
MakeGarbageCollected<XRFrame>(this, world_information_);
// TODO(1004201): Determine if world_information_ should be treated similarly
// to the base pose matrix.
if (base_pose_matrix_ && visibility_state_ != XRVisibilityState::HIDDEN) {
DVLOG(2) << __func__ << " : base_pose_matrix_ is set and not hidden,"
// TODO(https://crbug.com/1004201): Determine if world_information_ should be
// treated similarly to mojo_from_viewer_.
if (mojo_from_viewer_ && visibility_state_ != XRVisibilityState::HIDDEN) {
DVLOG(2) << __func__ << " : mojo_from_viewer_ is set and not hidden,"
<< " updating presentation frame";
presentation_frame->SetBasePoseMatrix(*base_pose_matrix_);
presentation_frame->SetMojoFromViewer(*mojo_from_viewer_);
}
return presentation_frame;
}
......
......@@ -224,7 +224,7 @@ class XRSession final
bool HasPendingActivity() const override;
// Creates presentation frame based on current state of the session.
// State currently used in XRFrame creation is base_pose_matrix_ and
// State currently used in XRFrame creation is mojo_from_viewer_ and
// world_information_. The created XRFrame also stores a reference to this
// XRSession.
XRFrame* CreatePresentationFrame();
......@@ -233,7 +233,7 @@ class XRSession final
// presentation frames.
void UpdatePresentationFrameState(
double timestamp,
std::unique_ptr<TransformationMatrix> base_pose_matrix,
std::unique_ptr<TransformationMatrix> mojo_from_viewer,
const device::mojom::blink::XRFrameDataPtr& frame_data);
private:
......@@ -315,7 +315,8 @@ class XRSession final
input_binding_;
Member<XRFrameRequestCallbackCollection> callback_collection_;
std::unique_ptr<TransformationMatrix> base_pose_matrix_;
// Viewer pose in mojo space.
std::unique_ptr<TransformationMatrix> mojo_from_viewer_;
bool ended_ = false;
bool pending_frame_ = false;
......
......@@ -16,24 +16,29 @@ XRSpace::XRSpace(XRSession* session) : session_(session) {}
XRSpace::~XRSpace() = default;
std::unique_ptr<TransformationMatrix> XRSpace::GetTransformToMojoSpace() {
std::unique_ptr<TransformationMatrix> XRSpace::MojoFromSpace() {
// The base XRSpace does not have any relevant information, so can't determine
// a transform here.
return nullptr;
}
std::unique_ptr<TransformationMatrix> XRSpace::DefaultPose() {
std::unique_ptr<TransformationMatrix> XRSpace::DefaultViewerPose() {
return nullptr;
}
std::unique_ptr<TransformationMatrix> XRSpace::TransformBasePose(
const TransformationMatrix& base_pose) {
std::unique_ptr<TransformationMatrix> XRSpace::SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer) {
return nullptr;
}
std::unique_ptr<TransformationMatrix> XRSpace::TransformBaseInputPose(
const TransformationMatrix& base_input_pose,
const TransformationMatrix& base_pose) {
std::unique_ptr<TransformationMatrix> XRSpace::SpaceFromViewer(
const TransformationMatrix& mojo_from_viewer) {
return nullptr;
}
std::unique_ptr<TransformationMatrix> XRSpace::SpaceFromInputForViewer(
const TransformationMatrix& mojo_from_input,
const TransformationMatrix& mojo_from_viewer) {
return nullptr;
}
......@@ -49,18 +54,17 @@ TransformationMatrix XRSpace::InverseOriginOffsetMatrix() {
XRPose* XRSpace::getPose(XRSpace* other_space,
const TransformationMatrix* base_pose_matrix) {
std::unique_ptr<TransformationMatrix> mojo_from_this =
GetTransformToMojoSpace();
if (!mojo_from_this) {
std::unique_ptr<TransformationMatrix> mojo_from_space = MojoFromSpace();
if (!mojo_from_space) {
return nullptr;
}
// Rigid transforms should always be invertible.
DCHECK(mojo_from_this->IsInvertible());
TransformationMatrix this_from_mojo = mojo_from_this->Inverse();
DCHECK(mojo_from_space->IsInvertible());
TransformationMatrix space_from_mojo = mojo_from_space->Inverse();
std::unique_ptr<TransformationMatrix> mojo_from_other =
other_space->GetTransformToMojoSpace();
other_space->MojoFromSpace();
if (!mojo_from_other) {
return nullptr;
}
......@@ -68,33 +72,38 @@ XRPose* XRSpace::getPose(XRSpace* other_space,
// TODO(crbug.com/969133): Update how EmulatedPosition is determined here once
// spec issue https://github.com/immersive-web/webxr/issues/534 has been
// resolved.
TransformationMatrix this_from_other =
this_from_mojo.Multiply(*mojo_from_other);
return MakeGarbageCollected<XRPose>(this_from_other,
TransformationMatrix space_from_other =
space_from_mojo.Multiply(*mojo_from_other);
return MakeGarbageCollected<XRPose>(space_from_other,
session()->EmulatedPosition());
}
std::unique_ptr<TransformationMatrix> XRSpace::GetViewerPoseMatrix(
const TransformationMatrix* base_pose_matrix) {
std::unique_ptr<TransformationMatrix> pose;
std::unique_ptr<TransformationMatrix>
XRSpace::SpaceFromViewerWithDefaultAndOffset(
const TransformationMatrix* mojo_from_viewer) {
std::unique_ptr<TransformationMatrix> space_from_viewer;
// If we don't have a valid base pose, request the reference space's default
// pose. Most common when tracking is lost.
if (base_pose_matrix) {
pose = TransformBasePose(*base_pose_matrix);
// viewer pose. Most common when tracking is lost.
if (mojo_from_viewer) {
space_from_viewer = SpaceFromViewer(*mojo_from_viewer);
} else {
pose = DefaultPose();
space_from_viewer = DefaultViewerPose();
}
// Can only update an XRViewerPose's views with an invertible matrix.
if (!pose || !pose->IsInvertible()) {
if (!space_from_viewer || !space_from_viewer->IsInvertible()) {
return nullptr;
}
// Account for any changes made to the reference space's origin offset so that
// things like teleportation works.
//
// This is offset_from_viewer = offset_from_space * space_from_viewer,
// where offset_from_viewer = inverse(viewer_from_offset).
// TODO(https://crbug.com/1008466): move originOffset to separate class?
return std::make_unique<TransformationMatrix>(
InverseOriginOffsetMatrix().Multiply(*pose));
InverseOriginOffsetMatrix().Multiply(*space_from_viewer));
}
ExecutionContext* XRSpace::GetExecutionContext() const {
......
......@@ -28,24 +28,45 @@ class XRSpace : public EventTargetWithInlineData {
explicit XRSpace(XRSession*);
~XRSpace() override;
// Get a transform that maps from this space to mojo space (aka device space).
// Unless noted otherwise, all data returned over vr_service.mojom interfaces
// is relative to mojo space.
// Returns nullptr if computing a transform is not possible.
virtual std::unique_ptr<TransformationMatrix> GetTransformToMojoSpace();
virtual std::unique_ptr<TransformationMatrix> DefaultPose();
virtual std::unique_ptr<TransformationMatrix> TransformBasePose(
const TransformationMatrix& base_pose);
virtual std::unique_ptr<TransformationMatrix> TransformBaseInputPose(
const TransformationMatrix& base_input_pose,
const TransformationMatrix& base_pose);
// Gets a default viewer pose appropriate for this space. This is an identity
// for viewer space, null for everything else.
virtual std::unique_ptr<TransformationMatrix> DefaultViewerPose();
// Gets the pose of this space's origin in mojo space. This is a transform
// that maps from this space to mojo space (aka device space). Unless noted
// otherwise, all data returned over vr_service.mojom interfaces is expressed
// in mojo space coordinates. Returns nullptr if computing a transform is not
// possible.
virtual std::unique_ptr<TransformationMatrix> MojoFromSpace();
// Gets the pose of the mojo origin in this reference space, corresponding
// to a transform from mojo coordinates to reference space coordinates.
virtual std::unique_ptr<TransformationMatrix> SpaceFromMojo(
const TransformationMatrix& mojo_from_viewer);
// Gets the viewer pose in this space, corresponding to a transform from
// viewer coordinates to this space's coordinates. (The position elements of
// the transformation matrix are the viewer's location in this space's
// coordinates.)
virtual std::unique_ptr<TransformationMatrix> SpaceFromViewer(
const TransformationMatrix& mojo_from_viewer);
// Gets an input pose in this space. This requires the viewer pose as
// an additional input since a "viewer" space needs to transform the
// input pose to headset-relative coordinates.
virtual std::unique_ptr<TransformationMatrix> SpaceFromInputForViewer(
const TransformationMatrix& mojo_from_input,
const TransformationMatrix& mojo_from_viewer);
virtual XRPose* getPose(XRSpace* other_space,
const TransformationMatrix* base_pose_matrix);
std::unique_ptr<TransformationMatrix> GetViewerPoseMatrix(
const TransformationMatrix* base_pose_matrix);
const TransformationMatrix* mojo_from_viewer);
// Gets the viewer pose in this space, including using an appropriate
// default pose (i.e. if tracking is lost), and applying originOffset
// as applicable. TODO(https://crbug.com/1008466): consider moving
// the originOffset handling to a separate class?
std::unique_ptr<TransformationMatrix> SpaceFromViewerWithDefaultAndOffset(
const TransformationMatrix* mojo_from_viewer);
XRSession* session() const { return session_; }
......
......@@ -14,90 +14,96 @@ namespace blink {
XRTargetRaySpace::XRTargetRaySpace(XRSession* session, XRInputSource* source)
: XRSpace(session), input_source_(source) {}
std::unique_ptr<TransformationMatrix> XRTargetRaySpace::GetPointerPoseForScreen(
std::unique_ptr<TransformationMatrix> XRTargetRaySpace::OtherSpaceFromScreenTap(
XRSpace* other_space,
const TransformationMatrix& base_pose_matrix) {
// If the pointer origin is the screen we need the head's base pose and
// the pointer transform matrix to continue. The pointer transform will
// represent the point the canvas was clicked as an offset from the view.
if (!input_source_->PointerTransform()) {
const TransformationMatrix& mojo_from_viewer) {
// If the pointer origin is the screen, the input space is viewer space, and
// we need the head's viewer pose and the pointer pose in continue. The
// pointer transform will represent the point the canvas was clicked as an
// offset from the view.
if (!input_source_->InputFromPointer()) {
return nullptr;
}
// Multiply the head pose and pointer transform to get the final pointer.
std::unique_ptr<TransformationMatrix> pointer_pose =
other_space->TransformBasePose(base_pose_matrix);
if (!pointer_pose) {
// other_from_pointer = other_from_input * input_from_pointer,
// where input space is equivalent to viewer space for screen taps.
std::unique_ptr<TransformationMatrix> other_from_pointer =
other_space->SpaceFromViewer(mojo_from_viewer);
if (!other_from_pointer) {
return nullptr;
}
pointer_pose->Multiply(*(input_source_->PointerTransform()));
return pointer_pose;
other_from_pointer->Multiply(*(input_source_->InputFromPointer()));
return other_from_pointer;
}
std::unique_ptr<TransformationMatrix> XRTargetRaySpace::GetTrackedPointerPose(
std::unique_ptr<TransformationMatrix>
XRTargetRaySpace::OtherSpaceFromTrackedPointer(
XRSpace* other_space,
const TransformationMatrix& base_pose_matrix) {
if (!input_source_->BasePose()) {
const TransformationMatrix& mojo_from_viewer) {
if (!input_source_->MojoFromInput()) {
return nullptr;
}
std::unique_ptr<TransformationMatrix> grip_pose =
other_space->TransformBaseInputPose(*(input_source_->BasePose()),
base_pose_matrix);
// Calculate other_from_pointer = other_from_input * input_from_pointer
std::unique_ptr<TransformationMatrix> other_from_pointer =
other_space->SpaceFromInputForViewer(*(input_source_->MojoFromInput()),
mojo_from_viewer);
if (!grip_pose) {
if (!other_from_pointer) {
return nullptr;
}
if (input_source_->PointerTransform()) {
grip_pose->Multiply(*(input_source_->PointerTransform()));
if (input_source_->InputFromPointer()) {
other_from_pointer->Multiply(*(input_source_->InputFromPointer()));
}
return grip_pose;
return other_from_pointer;
}
XRPose* XRTargetRaySpace::getPose(
XRSpace* other_space,
const TransformationMatrix* base_pose_matrix) {
const TransformationMatrix* mojo_from_viewer) {
// If we don't have a valid base pose (most common when tracking is lost),
// we can't get a target ray pose regardless of the mode.
if (!base_pose_matrix) {
DVLOG(2) << __func__ << " : base_pose_matrix is null, returning nullptr";
if (!mojo_from_viewer) {
DVLOG(2) << __func__ << " : mojo_from_viewer is null, returning nullptr";
return nullptr;
}
std::unique_ptr<TransformationMatrix> pointer_pose = nullptr;
std::unique_ptr<TransformationMatrix> other_from_ray = nullptr;
switch (input_source_->TargetRayMode()) {
case device::mojom::XRTargetRayMode::TAPPING: {
pointer_pose = GetPointerPoseForScreen(other_space, *base_pose_matrix);
other_from_ray = OtherSpaceFromScreenTap(other_space, *mojo_from_viewer);
break;
}
case device::mojom::XRTargetRayMode::GAZING: {
// If the pointer origin is the users head, this is a gaze cursor and the
// returned pointer is based on the device pose. Just return the head pose
// as the pointer pose.
pointer_pose = other_space->TransformBasePose(*base_pose_matrix);
other_from_ray = other_space->SpaceFromViewer(*mojo_from_viewer);
break;
}
case device::mojom::XRTargetRayMode::POINTING: {
pointer_pose = GetTrackedPointerPose(other_space, *base_pose_matrix);
other_from_ray =
OtherSpaceFromTrackedPointer(other_space, *mojo_from_viewer);
break;
}
}
if (!pointer_pose) {
if (!other_from_ray) {
DVLOG(2) << __func__ << " : "
<< "pointer_pose is null, input_source_->TargetRayMode() = "
<< "other_from_ray is null, input_source_->TargetRayMode() = "
<< input_source_->TargetRayMode();
return nullptr;
}
// Account for any changes made to the reference space's origin offset so that
// things like teleportation works.
TransformationMatrix adjusted_pose =
other_space->InverseOriginOffsetMatrix().Multiply(*pointer_pose);
return MakeGarbageCollected<XRPose>(adjusted_pose,
//
// otheroffset_from_ray = otheroffset_from_other * other_from_ray
// where otheroffset_from_other = inverse(other_from_otheroffset)
// TODO(https://crbug.com/1008466): move originOffset to separate class?
TransformationMatrix otheroffset_from_ray =
other_space->InverseOriginOffsetMatrix().Multiply(*other_from_ray);
return MakeGarbageCollected<XRPose>(otheroffset_from_ray,
input_source_->emulatedPosition());
}
......
......@@ -20,12 +20,12 @@ class XRTargetRaySpace : public XRSpace {
void Trace(blink::Visitor*) override;
private:
std::unique_ptr<TransformationMatrix> GetPointerPoseForScreen(
std::unique_ptr<TransformationMatrix> OtherSpaceFromScreenTap(
XRSpace* other_space,
const TransformationMatrix& base_pose_matrix);
std::unique_ptr<TransformationMatrix> GetTrackedPointerPose(
const TransformationMatrix& mojo_from_viewer);
std::unique_ptr<TransformationMatrix> OtherSpaceFromTrackedPointer(
XRSpace* other_space,
const TransformationMatrix& base_pose_matrix);
const TransformationMatrix& mojo_from_viewer);
Member<XRInputSource> input_source_;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment