Commit 0c8f47ee authored by Piotr Bialecki's avatar Piotr Bialecki Committed by Commit Bot

WebXR - anchor and plane pose can be null coming from the device

Other changes:
- rename anchor and plane pose in mojo to mojo_from_xxxxxx to match convention
- adjust samples to account for the possibility that anchors and planes
  can have their poses unknown
- send null poses from device if tracking is paused
- ARCore device now does not treat planes and anchors whose tracking is
  paused as removed
- add DVLOGs all around to help with issue investigation
- add test page for experiments with local space stability & drift

Note: the test mentioned above is not exposed via proposals/index.html.
Change-Id: I786e86d69b69badbace57ce62585e3d5fd1ae756
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2130778Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarKlaus Weidner <klausw@chromium.org>
Commit-Queue: Piotr Bialecki <bialpio@chromium.org>
Cr-Commit-Position: refs/heads/master@{#757560}
parent c2bd3712
...@@ -28,9 +28,13 @@ ArCoreAnchorManager::ArCoreAnchorManager(util::PassKey<ArCoreImpl> pass_key, ...@@ -28,9 +28,13 @@ ArCoreAnchorManager::ArCoreAnchorManager(util::PassKey<ArCoreImpl> pass_key,
ArCoreAnchorManager::~ArCoreAnchorManager() = default; ArCoreAnchorManager::~ArCoreAnchorManager() = default;
mojom::XRAnchorsDataPtr ArCoreAnchorManager::GetAnchorsData() const { mojom::XRAnchorsDataPtr ArCoreAnchorManager::GetAnchorsData() const {
DVLOG(3) << __func__ << ": anchor_id_to_anchor_info_.size()="
<< anchor_id_to_anchor_info_.size()
<< ", updated_anchor_ids_.size()=" << updated_anchor_ids_.size();
std::vector<uint64_t> all_anchors_ids; std::vector<uint64_t> all_anchors_ids;
all_anchors_ids.reserve(anchor_id_to_anchor_object_.size()); all_anchors_ids.reserve(anchor_id_to_anchor_info_.size());
for (const auto& anchor_id_and_object : anchor_id_to_anchor_object_) { for (const auto& anchor_id_and_object : anchor_id_to_anchor_info_) {
all_anchors_ids.push_back(anchor_id_and_object.first.GetUnsafeValue()); all_anchors_ids.push_back(anchor_id_and_object.first.GetUnsafeValue());
} }
...@@ -38,18 +42,28 @@ mojom::XRAnchorsDataPtr ArCoreAnchorManager::GetAnchorsData() const { ...@@ -38,18 +42,28 @@ mojom::XRAnchorsDataPtr ArCoreAnchorManager::GetAnchorsData() const {
updated_anchors.reserve(updated_anchor_ids_.size()); updated_anchors.reserve(updated_anchor_ids_.size());
for (const auto& anchor_id : updated_anchor_ids_) { for (const auto& anchor_id : updated_anchor_ids_) {
const device::internal::ScopedArCoreObject<ArAnchor*>& anchor = const device::internal::ScopedArCoreObject<ArAnchor*>& anchor =
anchor_id_to_anchor_object_.at(anchor_id); anchor_id_to_anchor_info_.at(anchor_id).anchor;
// pose if (anchor_id_to_anchor_info_.at(anchor_id).tracking_state ==
ArAnchor_getPose(arcore_session_, anchor.get(), ar_pose_.get()); AR_TRACKING_STATE_TRACKING) {
mojom::Pose pose = GetMojomPoseFromArPose(arcore_session_, ar_pose_.get()); // pose
ArAnchor_getPose(arcore_session_, anchor.get(), ar_pose_.get());
DVLOG(3) << __func__ << ": anchor id: " << anchor_id.GetUnsafeValue() mojom::Pose pose =
<< ", position=" << pose.position.ToString() GetMojomPoseFromArPose(arcore_session_, ar_pose_.get());
<< ", orientation=" << pose.orientation.ToString();
DVLOG(3) << __func__ << ": anchor_id: " << anchor_id.GetUnsafeValue()
updated_anchors.push_back(mojom::XRAnchorData::New( << ", position=" << pose.position.ToString()
anchor_id.GetUnsafeValue(), device::mojom::Pose::New(pose))); << ", orientation=" << pose.orientation.ToString();
updated_anchors.push_back(mojom::XRAnchorData::New(
anchor_id.GetUnsafeValue(), device::mojom::Pose::New(pose)));
} else {
DVLOG(3) << __func__ << ": anchor_id: " << anchor_id.GetUnsafeValue()
<< ", position=untracked, orientation=untracked";
updated_anchors.push_back(
mojom::XRAnchorData::New(anchor_id.GetUnsafeValue(), nullptr));
}
} }
return mojom::XRAnchorsData::New(std::move(all_anchors_ids), return mojom::XRAnchorsData::New(std::move(all_anchors_ids),
...@@ -73,7 +87,7 @@ void ArCoreAnchorManager::ForEachArCoreAnchor(ArAnchorList* arcore_anchors, ...@@ -73,7 +87,7 @@ void ArCoreAnchorManager::ForEachArCoreAnchor(ArAnchorList* arcore_anchors,
ArTrackingState tracking_state; ArTrackingState tracking_state;
ArAnchor_getTrackingState(arcore_session_, anchor.get(), &tracking_state); ArAnchor_getTrackingState(arcore_session_, anchor.get(), &tracking_state);
if (tracking_state != ArTrackingState::AR_TRACKING_STATE_TRACKING) { if (tracking_state == ArTrackingState::AR_TRACKING_STATE_STOPPED) {
// Skip all anchors that are not currently tracked. // Skip all anchors that are not currently tracked.
continue; continue;
} }
...@@ -100,7 +114,7 @@ void ArCoreAnchorManager::Update(ArFrame* ar_frame) { ...@@ -100,7 +114,7 @@ void ArCoreAnchorManager::Update(ArFrame* ar_frame) {
std::tie(anchor_id, created) = CreateOrGetAnchorId(ar_anchor.get()); std::tie(anchor_id, created) = CreateOrGetAnchorId(ar_anchor.get());
DVLOG(3) << __func__ DVLOG(3) << __func__
<< ": anchor updated, anchor id=" << anchor_id.GetUnsafeValue() << ": anchor updated, anchor_id=" << anchor_id.GetUnsafeValue()
<< ", tracking_state=" << tracking_state; << ", tracking_state=" << tracking_state;
DCHECK(!created) DCHECK(!created)
...@@ -118,44 +132,54 @@ void ArCoreAnchorManager::Update(ArFrame* ar_frame) { ...@@ -118,44 +132,54 @@ void ArCoreAnchorManager::Update(ArFrame* ar_frame) {
// Collect the objects of all currently tracked anchors. // Collect the objects of all currently tracked anchors.
// |ar_plane_address_to_id_| should *not* grow. // |ar_plane_address_to_id_| should *not* grow.
std::map<AnchorId, device::internal::ScopedArCoreObject<ArAnchor*>> std::map<AnchorId, AnchorInfo> new_anchor_id_to_anchor_info;
anchor_id_to_anchor_object;
ForEachArCoreAnchor(arcore_anchors_.get(), [this, ForEachArCoreAnchor(arcore_anchors_.get(), [this,
&anchor_id_to_anchor_object]( &new_anchor_id_to_anchor_info,
&updated_anchor_ids](
device::internal:: device::internal::
ScopedArCoreObject< ScopedArCoreObject<
ArAnchor*> ar_anchor, ArAnchor*> anchor,
ArTrackingState ArTrackingState
tracking_state) { tracking_state) {
// ID // ID
AnchorId anchor_id; AnchorId anchor_id;
bool created; bool created;
std::tie(anchor_id, created) = CreateOrGetAnchorId(ar_anchor.get()); std::tie(anchor_id, created) = CreateOrGetAnchorId(anchor.get());
DVLOG(3) << __func__ DVLOG(3) << __func__
<< ": anchor present, anchor id=" << anchor_id.GetUnsafeValue() << ": anchor present, anchor_id=" << anchor_id.GetUnsafeValue()
<< ", tracking state=" << tracking_state; << ", tracking state=" << tracking_state;
DCHECK(!created) DCHECK(!created)
<< "Anchor creation is app-initiated - we should never encounter an " << "Anchor creation is app-initiated - we should never encounter an "
"anchor that was created outside of `ArCoreImpl::CreateAnchor()`."; "anchor that was created outside of `ArCoreImpl::CreateAnchor()`.";
anchor_id_to_anchor_object[anchor_id] = std::move(ar_anchor); // Inspect the tracking state of this anchor in the previous frame. If it
// changed, mark the anchor as updated.
if (base::Contains(anchor_id_to_anchor_info_, anchor_id) &&
anchor_id_to_anchor_info_.at(anchor_id).tracking_state !=
tracking_state) {
updated_anchor_ids.insert(anchor_id);
}
AnchorInfo new_anchor_info = AnchorInfo(std::move(anchor), tracking_state);
new_anchor_id_to_anchor_info.emplace(anchor_id, std::move(new_anchor_info));
}); });
DVLOG(3) << __func__ << ": anchor_id_to_anchor_object.size()=" DVLOG(3) << __func__ << ": new_anchor_id_to_anchor_info.size()="
<< anchor_id_to_anchor_object.size(); << new_anchor_id_to_anchor_info.size();
// Shrink |ar_plane_address_to_id_|, removing all planes that are no longer // Shrink |ar_plane_address_to_id_|, removing all planes that are no longer
// tracked or were subsumed - if they do not show up in // tracked or were subsumed - if they do not show up in
// |plane_id_to_plane_object| map, they are no longer tracked. // |plane_id_to_plane_object| map, they are no longer tracked.
base::EraseIf( base::EraseIf(
ar_anchor_address_to_id_, ar_anchor_address_to_id_,
[&anchor_id_to_anchor_object](const auto& anchor_address_and_id) { [&new_anchor_id_to_anchor_info](const auto& anchor_address_and_id) {
return !base::Contains(anchor_id_to_anchor_object, return !base::Contains(new_anchor_id_to_anchor_info,
anchor_address_and_id.second); anchor_address_and_id.second);
}); });
anchor_id_to_anchor_object_.swap(anchor_id_to_anchor_object); anchor_id_to_anchor_info_.swap(new_anchor_id_to_anchor_info);
updated_anchor_ids_.swap(updated_anchor_ids); updated_anchor_ids_.swap(updated_anchor_ids);
} }
...@@ -196,7 +220,8 @@ base::Optional<AnchorId> ArCoreAnchorManager::CreateAnchor( ...@@ -196,7 +220,8 @@ base::Optional<AnchorId> ArCoreAnchorManager::CreateAnchor(
DCHECK(created) << "This should always be a new anchor, not something we've " DCHECK(created) << "This should always be a new anchor, not something we've "
"seen previously."; "seen previously.";
anchor_id_to_anchor_object_[anchor_id] = std::move(ar_anchor); anchor_id_to_anchor_info_.emplace(
anchor_id, AnchorInfo(std::move(ar_anchor), AR_TRACKING_STATE_TRACKING));
return anchor_id; return anchor_id;
} }
...@@ -222,38 +247,47 @@ base::Optional<AnchorId> ArCoreAnchorManager::CreateAnchor( ...@@ -222,38 +247,47 @@ base::Optional<AnchorId> ArCoreAnchorManager::CreateAnchor(
DCHECK(created) << "This should always be a new anchor, not something we've " DCHECK(created) << "This should always be a new anchor, not something we've "
"seen previously."; "seen previously.";
anchor_id_to_anchor_object_[anchor_id] = std::move(ar_anchor); anchor_id_to_anchor_info_.emplace(
anchor_id, AnchorInfo(std::move(ar_anchor), AR_TRACKING_STATE_TRACKING));
return anchor_id; return anchor_id;
} }
void ArCoreAnchorManager::DetachAnchor(AnchorId anchor_id) { void ArCoreAnchorManager::DetachAnchor(AnchorId anchor_id) {
auto it = anchor_id_to_anchor_object_.find(anchor_id); auto it = anchor_id_to_anchor_info_.find(anchor_id);
if (it == anchor_id_to_anchor_object_.end()) { if (it == anchor_id_to_anchor_info_.end()) {
return; return;
} }
ArAnchor_detach(arcore_session_, it->second.get()); ArAnchor_detach(arcore_session_, it->second.anchor.get());
anchor_id_to_anchor_object_.erase(it); anchor_id_to_anchor_info_.erase(it);
} }
bool ArCoreAnchorManager::AnchorExists(AnchorId id) const { bool ArCoreAnchorManager::AnchorExists(AnchorId id) const {
return base::Contains(anchor_id_to_anchor_object_, id); return base::Contains(anchor_id_to_anchor_info_, id);
} }
base::Optional<gfx::Transform> ArCoreAnchorManager::GetMojoFromAnchor( base::Optional<gfx::Transform> ArCoreAnchorManager::GetMojoFromAnchor(
AnchorId id) const { AnchorId id) const {
auto it = anchor_id_to_anchor_object_.find(id); auto it = anchor_id_to_anchor_info_.find(id);
if (it == anchor_id_to_anchor_object_.end()) { if (it == anchor_id_to_anchor_info_.end()) {
return base::nullopt; return base::nullopt;
} }
ArAnchor_getPose(arcore_session_, it->second.get(), ar_pose_.get()); ArAnchor_getPose(arcore_session_, it->second.anchor.get(), ar_pose_.get());
mojom::Pose mojo_pose = mojom::Pose mojo_pose =
GetMojomPoseFromArPose(arcore_session_, ar_pose_.get()); GetMojomPoseFromArPose(arcore_session_, ar_pose_.get());
return mojo::ConvertTo<gfx::Transform>(mojo_pose); return mojo::ConvertTo<gfx::Transform>(mojo_pose);
} }
ArCoreAnchorManager::AnchorInfo::AnchorInfo(
device::internal::ScopedArCoreObject<ArAnchor*> anchor,
ArTrackingState tracking_state)
: anchor(std::move(anchor)), tracking_state(tracking_state) {}
ArCoreAnchorManager::AnchorInfo::AnchorInfo(AnchorInfo&& other) = default;
ArCoreAnchorManager::AnchorInfo::~AnchorInfo() = default;
} // namespace device } // namespace device
...@@ -49,9 +49,20 @@ class ArCoreAnchorManager { ...@@ -49,9 +49,20 @@ class ArCoreAnchorManager {
void DetachAnchor(AnchorId anchor_id); void DetachAnchor(AnchorId anchor_id);
private: private:
struct AnchorInfo {
device::internal::ScopedArCoreObject<ArAnchor*> anchor;
ArTrackingState tracking_state;
AnchorInfo(device::internal::ScopedArCoreObject<ArAnchor*> anchor,
ArTrackingState tracking_state);
AnchorInfo(AnchorInfo&& other);
~AnchorInfo();
};
// Executes |fn| for each still tracked, anchor present in |arcore_anchors|. // Executes |fn| for each still tracked, anchor present in |arcore_anchors|.
// |fn| will receive a `device::internal::ScopedArCoreObject<ArAnchor*>` that // |fn| will receive a `device::internal::ScopedArCoreObject<ArAnchor*>` that
// can be stored, as well as ArTrackingState of the passed in anchor. // can be stored, as well as ArTrackingState of the passed in anchor. An
// anchor is tracked if its state is not AR_TRACKING_STATE_STOPPED.
template <typename FunctionType> template <typename FunctionType>
void ForEachArCoreAnchor(ArAnchorList* arcore_anchors, FunctionType fn); void ForEachArCoreAnchor(ArAnchorList* arcore_anchors, FunctionType fn);
...@@ -72,10 +83,9 @@ class ArCoreAnchorManager { ...@@ -72,10 +83,9 @@ class ArCoreAnchorManager {
// Mapping from anchor address to anchor ID. It should be modified only during // Mapping from anchor address to anchor ID. It should be modified only during
// calls to |Update()| and anchor creation. // calls to |Update()| and anchor creation.
std::map<void*, AnchorId> ar_anchor_address_to_id_; std::map<void*, AnchorId> ar_anchor_address_to_id_;
// Mapping from anchor ID to ARCore anchor object. It should be modified only // Mapping from anchor ID to ARCore anchor information. It should be modified
// during calls to |Update()|. // only during calls to |Update()|.
std::map<AnchorId, device::internal::ScopedArCoreObject<ArAnchor*>> std::map<AnchorId, AnchorInfo> anchor_id_to_anchor_info_;
anchor_id_to_anchor_object_;
// Set containing IDs of anchors updated in the last frame. It should be // Set containing IDs of anchors updated in the last frame. It should be
// modified only during calls to |Update()|. // modified only during calls to |Update()|.
std::set<AnchorId> updated_anchor_ids_; std::set<AnchorId> updated_anchor_ids_;
......
...@@ -63,10 +63,21 @@ class ArCorePlaneManager { ...@@ -63,10 +63,21 @@ class ArCorePlaneManager {
const device::mojom::Pose& pose) const; const device::mojom::Pose& pose) const;
private: private:
struct PlaneInfo {
device::internal::ScopedArCoreObject<ArTrackable*> plane;
ArTrackingState tracking_state;
PlaneInfo(device::internal::ScopedArCoreObject<ArTrackable*> plane,
ArTrackingState tracking_state);
PlaneInfo(PlaneInfo&& other);
~PlaneInfo();
};
// Executes |fn| for each still tracked, non-subsumed plane present in // Executes |fn| for each still tracked, non-subsumed plane present in
// |arcore_planes|. |fn| will receive 3 parameters - a // |arcore_planes|. |fn| will receive 3 parameters - a
// `ScopedArCoreObject<ArAnchor*>` that can be stored, the non-owning ArPlane* // `ScopedArCoreObject<ArAnchor*>` that can be stored, the non-owning ArPlane*
// typecast from the first parameter, and ArTrackingState. // typecast from the first parameter, and ArTrackingState. A plane is tracked
// if its state is not AR_TRACKING_STATE_STOPPED.
template <typename FunctionType> template <typename FunctionType>
void ForEachArCorePlane(ArTrackableList* arcore_planes, FunctionType fn); void ForEachArCorePlane(ArTrackableList* arcore_planes, FunctionType fn);
...@@ -89,10 +100,9 @@ class ArCorePlaneManager { ...@@ -89,10 +100,9 @@ class ArCorePlaneManager {
// Mapping from plane address to plane ID. It should be modified only during // Mapping from plane address to plane ID. It should be modified only during
// calls to |Update()|. // calls to |Update()|.
std::map<void*, PlaneId> ar_plane_address_to_id_; std::map<void*, PlaneId> ar_plane_address_to_id_;
// Mapping from plane ID to ARCore plane object. It should be modified only // Mapping from plane ID to ARCore plane information. It should be modified
// during calls to |Update()|. // only during calls to |Update()|.
std::map<PlaneId, device::internal::ScopedArCoreObject<ArTrackable*>> std::map<PlaneId, PlaneInfo> plane_id_to_plane_info_;
plane_id_to_plane_object_;
// Set containing IDs of planes updated in the last frame. It should be // Set containing IDs of planes updated in the last frame. It should be
// modified only during calls to |Update()|. // modified only during calls to |Update()|.
std::set<PlaneId> updated_plane_ids_; std::set<PlaneId> updated_plane_ids_;
......
...@@ -18,7 +18,7 @@ import "ui/gfx/mojom/transform.mojom"; ...@@ -18,7 +18,7 @@ import "ui/gfx/mojom/transform.mojom";
// //
// Note on terminology: unless otherwise noted, all poses passed across mojo are // Note on terminology: unless otherwise noted, all poses passed across mojo are
// expressed in device space, aka mojo space. // expressed in device space, aka mojo space, aka world space.
// TODO(https://crbug.com/966099): Use EnableIf to only define values on // TODO(https://crbug.com/966099): Use EnableIf to only define values on
// platforms that have implementations. // platforms that have implementations.
...@@ -376,7 +376,9 @@ struct XRPlaneData { ...@@ -376,7 +376,9 @@ struct XRPlaneData {
// Pose of the plane's center. Defines new coordinate space. // Pose of the plane's center. Defines new coordinate space.
// Y axis of the coordinate space describes plane's normal, the rotation of // Y axis of the coordinate space describes plane's normal, the rotation of
// X and Z around the Y axis is arbitrary. // X and Z around the Y axis is arbitrary.
Pose pose; // Null if the device does not know where the plane is located in the world
// space (tracking loss), but the tracking can still be recovered.
Pose? mojo_from_plane;
// Vertices of 2D convex polygon approximating the plane. // Vertices of 2D convex polygon approximating the plane.
array<XRPlanePointData> polygon; array<XRPlanePointData> polygon;
...@@ -405,8 +407,10 @@ struct XRAnchorData { ...@@ -405,8 +407,10 @@ struct XRAnchorData {
// Unique (within a session) identifier of the anchor. // Unique (within a session) identifier of the anchor.
uint64 id; uint64 id;
// Pose of the anchor. // Pose of the anchor. Null if the device does not know where the anchor is
Pose pose; // located in the world space (tracking loss), but the tracking can still be
// recovered.
Pose? mojo_from_anchor;
}; };
// Struct containing information about all tracked & updated anchors in a given // Struct containing information about all tracked & updated anchors in a given
......
...@@ -14,18 +14,20 @@ XRAnchor::XRAnchor(uint64_t id, ...@@ -14,18 +14,20 @@ XRAnchor::XRAnchor(uint64_t id,
XRSession* session, XRSession* session,
const device::mojom::blink::XRAnchorData& anchor_data) const device::mojom::blink::XRAnchorData& anchor_data)
: id_(id), session_(session) { : id_(id), session_(session) {
// No need for else - if pose is not present, the default-constructed unique // No need for else - if mojo_from_anchor is not present, the
// ptr is fine. // default-constructed unique ptr is fine. It would signify that the anchor
if (anchor_data.pose) { // exists and is tracked by the underlying system, but its current location is
SetMojoFromAnchor( // unknown.
mojo::ConvertTo<blink::TransformationMatrix>(anchor_data.pose)); if (anchor_data.mojo_from_anchor) {
SetMojoFromAnchor(mojo::ConvertTo<blink::TransformationMatrix>(
anchor_data.mojo_from_anchor));
} }
} }
void XRAnchor::Update(const device::mojom::blink::XRAnchorData& anchor_data) { void XRAnchor::Update(const device::mojom::blink::XRAnchorData& anchor_data) {
if (anchor_data.pose) { if (anchor_data.mojo_from_anchor) {
SetMojoFromAnchor( SetMojoFromAnchor(mojo::ConvertTo<blink::TransformationMatrix>(
mojo::ConvertTo<blink::TransformationMatrix>(anchor_data.pose)); anchor_data.mojo_from_anchor));
} else { } else {
mojo_from_anchor_ = nullptr; mojo_from_anchor_ = nullptr;
} }
...@@ -36,8 +38,6 @@ uint64_t XRAnchor::id() const { ...@@ -36,8 +38,6 @@ uint64_t XRAnchor::id() const {
} }
XRSpace* XRAnchor::anchorSpace() const { XRSpace* XRAnchor::anchorSpace() const {
DCHECK(mojo_from_anchor_);
if (!anchor_space_) { if (!anchor_space_) {
anchor_space_ = anchor_space_ =
MakeGarbageCollected<XRObjectSpace<XRAnchor>>(session_, this); MakeGarbageCollected<XRObjectSpace<XRAnchor>>(session_, this);
......
...@@ -44,12 +44,8 @@ class XRAnchor : public ScriptWrappable { ...@@ -44,12 +44,8 @@ class XRAnchor : public ScriptWrappable {
Member<XRSession> session_; Member<XRSession> session_;
// |mojo_from_anchor_| will be non-null in an XRAnchor after the anchor was // Anchor's pose in device (mojo) space. Nullptr if the pose of the anchor is
// updated for the first time - this *must* happen in the same frame in which // unknown in the current frame.
// the anchor was created for the anchor to be fully usable. It is currently
// ensured by XRSession - anchors that got created prior to receiving the
// result from mojo call to GetFrameData are not returned to the application
// until their poses are known.
std::unique_ptr<TransformationMatrix> mojo_from_anchor_; std::unique_ptr<TransformationMatrix> mojo_from_anchor_;
// Cached anchor space - it will be created by `anchorSpace()` if it's not // Cached anchor space - it will be created by `anchorSpace()` if it's not
......
...@@ -13,8 +13,14 @@ namespace blink { ...@@ -13,8 +13,14 @@ namespace blink {
class XRSession; class XRSession;
// Helper class that returns an XRSpace that tracks the position of object of // Helper class that returns an XRSpace that tracks the position of object of
// type T (for example XRPlane, XRAnchor). The type T has to have a poseMatrix() // type T (for example XRPlane, XRAnchor). The type T has to have a
// method. // MojoFromObject() method, returning a base::Optional<TransformationMatrix>.
//
// If the object's MojoFromObject() method returns a base::nullopt, it means
// that the object is not localizable in the current frame (i.e. its pose is
// unknown) - the `frame.getPose(objectSpace, otherSpace)` will return null.
// That does not necessarily mean that object tracking is lost - it may be that
// the object's location will become known in subsequent frames.
template <typename T> template <typename T>
class XRObjectSpace : public XRSpace { class XRObjectSpace : public XRSpace {
public: public:
......
...@@ -24,11 +24,13 @@ XRPlane::XRPlane(uint64_t id, ...@@ -24,11 +24,13 @@ XRPlane::XRPlane(uint64_t id,
mojo::ConvertTo<HeapVector<Member<DOMPointReadOnly>>>( mojo::ConvertTo<HeapVector<Member<DOMPointReadOnly>>>(
plane_data.polygon), plane_data.polygon),
timestamp) { timestamp) {
// No need for else - if pose is not present, the default-constructed unique // No need for else - if mojo_from_plane is not present, the
// ptr is fine. // default-constructed unique ptr is fine. It would signify that the plane
if (plane_data.pose) { // exists and is tracked by the underlying system, but its current location is
SetMojoFromPlane( // unknown.
mojo::ConvertTo<blink::TransformationMatrix>(plane_data.pose)); if (plane_data.mojo_from_plane) {
SetMojoFromPlane(mojo::ConvertTo<blink::TransformationMatrix>(
plane_data.mojo_from_plane));
} }
} }
...@@ -125,9 +127,9 @@ void XRPlane::Update(const device::mojom::blink::XRPlaneData& plane_data, ...@@ -125,9 +127,9 @@ void XRPlane::Update(const device::mojom::blink::XRPlaneData& plane_data,
orientation_ = mojo::ConvertTo<base::Optional<blink::XRPlane::Orientation>>( orientation_ = mojo::ConvertTo<base::Optional<blink::XRPlane::Orientation>>(
plane_data.orientation); plane_data.orientation);
if (plane_data.pose) { if (plane_data.mojo_from_plane) {
SetMojoFromPlane( SetMojoFromPlane(mojo::ConvertTo<blink::TransformationMatrix>(
mojo::ConvertTo<blink::TransformationMatrix>(plane_data.pose)); plane_data.mojo_from_plane));
} else { } else {
mojo_from_plane_ = nullptr; mojo_from_plane_ = nullptr;
} }
......
...@@ -68,7 +68,8 @@ class XRPlane : public ScriptWrappable { ...@@ -68,7 +68,8 @@ class XRPlane : public ScriptWrappable {
HeapVector<Member<DOMPointReadOnly>> polygon_; HeapVector<Member<DOMPointReadOnly>> polygon_;
base::Optional<Orientation> orientation_; base::Optional<Orientation> orientation_;
// Plane center's pose in device (mojo) space. // Plane center's pose in device (mojo) space. Nullptr if the pose of the
// anchor is unknown in the current frame.
std::unique_ptr<TransformationMatrix> mojo_from_plane_; std::unique_ptr<TransformationMatrix> mojo_from_plane_;
Member<XRSession> session_; Member<XRSession> session_;
......
...@@ -188,6 +188,10 @@ export class PlaneNode extends Node { ...@@ -188,6 +188,10 @@ export class PlaneNode extends Node {
if(this.polygon) if(this.polygon)
throw new Error(`Polygon is set on a plane where it shouldn't be!`); throw new Error(`Polygon is set on a plane where it shouldn't be!`);
if(polygon.length === 0) {
return Promise.resolve();
}
this.createPlanePrimitive(polygon); this.createPlanePrimitive(polygon);
// eagerly clean up render primitive's VAO // eagerly clean up render primitive's VAO
......
...@@ -119,6 +119,10 @@ let calculateHitMatrix = function(ray_vector, plane_normal, point) { ...@@ -119,6 +119,10 @@ let calculateHitMatrix = function(ray_vector, plane_normal, point) {
// single plane hit test - doesn't take into account the plane's polygon // single plane hit test - doesn't take into account the plane's polygon
function hitTestPlane(frame, ray, plane, frameOfReference) { function hitTestPlane(frame, ray, plane, frameOfReference) {
const plane_pose = frame.getPose(plane.planeSpace, frameOfReference); const plane_pose = frame.getPose(plane.planeSpace, frameOfReference);
if(!plane_pose) {
return null;
}
const plane_normal = transform_point_by_matrix( const plane_normal = transform_point_by_matrix(
plane_pose.transform.matrix, {x : 0, y : 1.0, z : 0, w : 0}); plane_pose.transform.matrix, {x : 0, y : 1.0, z : 0, w : 0});
const plane_center = normalize_perspective( const plane_center = normalize_perspective(
......
...@@ -431,8 +431,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -431,8 +431,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}); });
tracked_anchors.forEach(anchor => { tracked_anchors.forEach(anchor => {
anchor.context.sceneObject.matrix = frame.getPose(anchor.anchorSpace, xrRefSpace).transform.matrix; const anchorPose = frame.getPose(anchor.anchorSpace, xrRefSpace);
anchor.context.sceneObject.visible = true; if(anchorPose) {
anchor.context.sceneObject.matrix = anchorPose.transform.matrix;
anchor.context.sceneObject.visible = true;
} else {
anchor.context.sceneObject.visible = false;
}
}); });
all_previous_anchors = tracked_anchors; all_previous_anchors = tracked_anchors;
......
...@@ -261,8 +261,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -261,8 +261,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}); });
tracked_anchors.forEach(anchor => { tracked_anchors.forEach(anchor => {
anchor.context.sceneObject.matrix = frame.getPose(anchor.anchorSpace, xrRefSpace).transform.matrix; const anchorPose = frame.getPose(anchor.anchorSpace, xrRefSpace);
anchor.context.sceneObject.visible = true; if(anchorPose) {
anchor.context.sceneObject.matrix = anchorPose.transform.matrix;
anchor.context.sceneObject.visible = true;
} else {
anchor.context.sceneObject.visible = false;
}
}); });
all_previous_anchors = tracked_anchors; all_previous_anchors = tracked_anchors;
...@@ -274,25 +279,30 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -274,25 +279,30 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
all_previous_anchors = new Set(); all_previous_anchors = new Set();
} }
if(xrAnchor) { if (xrAnchor) {
const anchorPose = frame.getPose(xrAnchor.anchorSpace, xrRefSpace); const anchorPose = frame.getPose(xrAnchor.anchorSpace, xrRefSpace);
const pos = anchorPose.transform.position; if (anchorPose) {
const rot = anchorPose.transform.orientation; const pos = anchorPose.transform.position;
const rot = anchorPose.transform.orientation;
const degs = getAngle(
quat.fromValues(rot.x, rot.y, rot.z, rot.w), const degs = getAngle(
quat.create() quat.fromValues(rot.x, rot.y, rot.z, rot.w),
) * 180 / Math.PI; quat.create()
) * 180 / Math.PI;
anchorPoseElement.innerText = pos.x.toFixed(3) + "m,"
+ pos.y.toFixed(3) + "m," anchorPoseElement.innerText = pos.x.toFixed(3) + "m,"
+ pos.z.toFixed(3) + "m," + pos.y.toFixed(3) + "m,"
+ degs.toFixed(3) + "deg"; + pos.z.toFixed(3) + "m,"
+ degs.toFixed(3) + "deg";
const drift = Math.sqrt(pos.x * pos.x + pos.y * pos.y + pos.z * pos.z);
anchorDriftElement.innerText = drift.toFixed(3) + "m"; const drift = Math.sqrt(pos.x * pos.x + pos.y * pos.y + pos.z * pos.z);
anchorDriftElement.innerText = drift.toFixed(3) + "m";
console.debug(pos, rot, drift);
console.debug(pos, rot, drift);
} else {
anchorPoseElement.innerText = "Anchor pose null.";
anchorDriftElement.innerText = "Anchor pose null.";
}
} else if(anchorRemoved) { } else if(anchorRemoved) {
anchorPoseElement.innerText = "Anchor already gone."; anchorPoseElement.innerText = "Anchor already gone.";
anchorDriftElement.innerText = "Anchor already gone."; anchorDriftElement.innerText = "Anchor already gone.";
......
...@@ -445,10 +445,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -445,10 +445,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
planeFrameOfReference.addNode(zRay); planeFrameOfReference.addNode(zRay);
plane_node.addNode(planeFrameOfReference); plane_node.addNode(planeFrameOfReference);
plane.scene_node = plane_node; plane.scene_node = plane_node;
plane.scene_node.matrix = frame.getPose(plane.planeSpace, xrRefSpace).transform.matrix;
const planePose = frame.getPose(plane.planeSpace, xrRefSpace);
if (planePose) {
plane.scene_node.matrix = planePose.transform.matrix;
plane.scene_node.visible = true;
} else {
plane.scene_node.visible = false;
}
plane.extended_polygon = extendPolygon(plane.polygon); plane.extended_polygon = extendPolygon(plane.polygon);
plane.extended_polygon_node = new PlaneNode({ plane.extended_polygon_node = new PlaneNode({
polygon : plane.extended_polygon, polygon : plane.extended_polygon,
...@@ -464,7 +469,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -464,7 +469,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{ {
// old plane that was updated in current frame // old plane that was updated in current frame
plane.scene_node.onPlaneChanged(plane.polygon); plane.scene_node.onPlaneChanged(plane.polygon);
plane.scene_node.matrix = frame.getPose(plane.planeSpace, xrRefSpace).transform.matrix; const planePose = frame.getPose(plane.planeSpace, xrRefSpace);
if (planePose) {
plane.scene_node.matrix = planePose.transform.matrix;
plane.scene_node.visible = true;
} else {
plane.scene_node.visible = false;
}
plane.extended_polygon = extendPolygon(plane.polygon); plane.extended_polygon = extendPolygon(plane.polygon);
plane.extended_polygon_node.onPlaneChanged(plane.extended_polygon); plane.extended_polygon_node.onPlaneChanged(plane.extended_polygon);
} }
...@@ -526,8 +537,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -526,8 +537,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}); });
tracked_anchors.forEach(anchor => { tracked_anchors.forEach(anchor => {
anchor.context.sceneObject.matrix = frame.getPose(anchor.anchorSpace, xrRefSpace).transform.matrix; const anchorPose = frame.getPose(anchor.anchorSpace, xrRefSpace);
anchor.context.sceneObject.visible = true; if (anchorPose) {
anchor.context.sceneObject.matrix = anchorPose.transform.matrix;
anchor.context.sceneObject.visible = true;
} else {
anchor.context.sceneObject.visible = false;
}
}); });
all_previous_anchors = tracked_anchors; all_previous_anchors = tracked_anchors;
......
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