| // 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. |
| |
| #ifndef CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_IMPL_H_ |
| #define CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_IMPL_H_ |
| |
| #include "base/macros.h" |
| #include "base/optional.h" |
| #include "base/scoped_generic.h" |
| #include "base/util/type_safety/id_type.h" |
| #include "chrome/browser/android/vr/arcore_device/arcore.h" |
| #include "chrome/browser/android/vr/arcore_device/arcore_sdk.h" |
| #include "device/vr/public/mojom/vr_service.mojom.h" |
| |
| namespace device { |
| |
| namespace internal { |
| |
| template <class T> |
| struct ScopedGenericArObject { |
| static T InvalidValue() { return nullptr; } |
| static void Free(T object) {} |
| }; |
| |
| template <> |
| void inline ScopedGenericArObject<ArSession*>::Free(ArSession* ar_session) { |
| ArSession_destroy(ar_session); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArFrame*>::Free(ArFrame* ar_frame) { |
| ArFrame_destroy(ar_frame); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArConfig*>::Free(ArConfig* ar_config) { |
| ArConfig_destroy(ar_config); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArPose*>::Free(ArPose* ar_pose) { |
| ArPose_destroy(ar_pose); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArTrackable*>::Free( |
| ArTrackable* ar_trackable) { |
| ArTrackable_release(ar_trackable); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArPlane*>::Free(ArPlane* ar_plane) { |
| // ArPlane itself doesn't have a method to decrease refcount, but it is an |
| // instance of ArTrackable & we have to use ArTrackable_release. |
| ArTrackable_release(ArAsTrackable(ar_plane)); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArAnchor*>::Free(ArAnchor* ar_anchor) { |
| ArAnchor_release(ar_anchor); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArTrackableList*>::Free( |
| ArTrackableList* ar_trackable_list) { |
| ArTrackableList_destroy(ar_trackable_list); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArCamera*>::Free(ArCamera* ar_camera) { |
| // Do nothing - ArCamera has no destroy method and is managed by ArCore. |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArHitResultList*>::Free( |
| ArHitResultList* ar_hit_result_list) { |
| ArHitResultList_destroy(ar_hit_result_list); |
| } |
| |
| template <> |
| void inline ScopedGenericArObject<ArHitResult*>::Free( |
| ArHitResult* ar_hit_result) { |
| ArHitResult_destroy(ar_hit_result); |
| } |
| |
| template <class T> |
| using ScopedArCoreObject = base::ScopedGeneric<T, ScopedGenericArObject<T>>; |
| |
| } // namespace internal |
| |
| using PlaneId = util::IdTypeU32<class PlaneTag>; |
| using AnchorId = util::IdTypeU32<class AnchorTag>; |
| using HitTestSubscriptionId = util::IdTypeU32<class HitTestSubscriptionTag>; |
| |
| struct HitTestSubscriptionData { |
| mojom::XRNativeOriginInformationPtr native_origin_information; |
| mojom::XRRayPtr ray; |
| |
| HitTestSubscriptionData( |
| mojom::XRNativeOriginInformationPtr native_origin_information, |
| mojom::XRRayPtr ray); |
| HitTestSubscriptionData(HitTestSubscriptionData&& other); |
| ~HitTestSubscriptionData(); |
| }; |
| |
| // This class should be created and accessed entirely on a Gl thread. |
| class ArCoreImpl : public ArCore { |
| public: |
| ArCoreImpl(); |
| ~ArCoreImpl() override; |
| |
| bool Initialize( |
| base::android::ScopedJavaLocalRef<jobject> application_context) override; |
| void SetDisplayGeometry(const gfx::Size& frame_size, |
| display::Display::Rotation display_rotation) override; |
| void SetCameraTexture(GLuint camera_texture_id) override; |
| std::vector<float> TransformDisplayUvCoords( |
| const base::span<const float> uvs) override; |
| gfx::Transform GetProjectionMatrix(float near, float far) override; |
| mojom::VRPosePtr Update(bool* camera_updated) override; |
| |
| mojom::XRPlaneDetectionDataPtr GetDetectedPlanesData() override; |
| |
| mojom::XRAnchorsDataPtr GetAnchorsData() override; |
| |
| void Pause() override; |
| void Resume() override; |
| |
| float GetEstimatedFloorHeight() override; |
| |
| bool RequestHitTest(const mojom::XRRayPtr& ray, |
| std::vector<mojom::XRHitResultPtr>* hit_results) override; |
| |
| // Helper. |
| bool RequestHitTest(const gfx::Point3F& origin, |
| const gfx::Vector3dF& direction, |
| std::vector<mojom::XRHitResultPtr>* hit_results); |
| |
| base::Optional<uint32_t> SubscribeToHitTest( |
| mojom::XRNativeOriginInformationPtr nativeOriginInformation, |
| mojom::XRRayPtr ray) override; |
| |
| mojom::XRHitTestSubscriptionResultsDataPtr GetHitTestSubscriptionResults( |
| const device::mojom::VRPosePtr& pose) override; |
| |
| void UnsubscribeFromHitTest(uint32_t subscription_id) override; |
| |
| base::Optional<uint32_t> CreateAnchor( |
| const device::mojom::VRPosePtr& pose) override; |
| base::Optional<uint32_t> CreateAnchor(const device::mojom::VRPosePtr& pose, |
| uint32_t plane_id) override; |
| |
| void DetachAnchor(uint32_t anchor_id) override; |
| |
| private: |
| bool IsOnGlThread(); |
| base::WeakPtr<ArCoreImpl> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| scoped_refptr<base::SingleThreadTaskRunner> gl_thread_task_runner_; |
| |
| // An ArCore session, which is distinct and independent of XRSessions. |
| // There will only ever be one in Chrome even when supporting |
| // multiple XRSessions. |
| internal::ScopedArCoreObject<ArSession*> arcore_session_; |
| internal::ScopedArCoreObject<ArFrame*> arcore_frame_; |
| |
| // List of trackables - used for retrieving planes detected by ARCore. The |
| // list will initially be null - call EnsureArCorePlanesList() before using |
| // it. |
| // Allows reuse of the list across updates; ARCore clears the list on each |
| // call to the ARCore SDK. |
| internal::ScopedArCoreObject<ArTrackableList*> arcore_planes_; |
| |
| // List of anchors - used for retrieving anchors tracked by ARCore. The list |
| // will initially be null - call EnsureArCoreAnchorsList() before using it. |
| // Allows reuse of the list across updates; ARCore clears the list on each |
| // call to the ARCore SDK. |
| internal::ScopedArCoreObject<ArAnchorList*> arcore_anchors_; |
| |
| // Initializes |arcore_planes_| list. |
| void EnsureArCorePlanesList(); |
| |
| // Initializes |arcore_anchors_| list. |
| void EnsureArCoreAnchorsList(); |
| |
| // Returns vector containing information about all planes updated in current |
| // frame, assigning IDs for newly detected planes as needed. |
| std::vector<mojom::XRPlaneDataPtr> GetUpdatedPlanesData(); |
| |
| // This must be called after GetUpdatedPlanesData as it assumes that all |
| // planes already have an ID assigned. The result includes freshly assigned |
| // IDs for newly detected planes along with previously known IDs for updated |
| // and unchanged planes. It excludes planes that are no longer being tracked. |
| std::vector<uint32_t> GetAllPlaneIds(); |
| |
| // Returns vector containing information about all anchors updated in the |
| // current frame. |
| std::vector<mojom::XRAnchorDataPtr> GetUpdatedAnchorsData(); |
| |
| // The result will contain IDs of all anchors still tracked in the current |
| // frame. |
| std::vector<uint32_t> GetAllAnchorIds(); |
| |
| uint32_t next_id_ = 1; |
| std::map<void*, PlaneId> ar_plane_address_to_id_; |
| std::map<PlaneId, device::internal::ScopedArCoreObject<ArTrackable*>> |
| plane_id_to_plane_object_; |
| std::map<void*, AnchorId> ar_anchor_address_to_id_; |
| std::map<AnchorId, device::internal::ScopedArCoreObject<ArAnchor*>> |
| anchor_id_to_anchor_object_; |
| |
| std::map<HitTestSubscriptionId, HitTestSubscriptionData> |
| hit_test_subscription_id_to_data_; |
| |
| // Returns tuple containing plane id and a boolean signifying that the plane |
| // was created. The result will be a nullopt in case the ID should be assigned |
| // but next ID would result in an integer overflow. |
| base::Optional<std::pair<PlaneId, bool>> CreateOrGetPlaneId( |
| void* plane_address); |
| base::Optional<std::pair<AnchorId, bool>> CreateOrGetAnchorId( |
| void* anchor_address); |
| |
| base::Optional<HitTestSubscriptionId> CreateHitTestSubscriptionId(); |
| |
| // Returns hit test subscription results for a single subscription given |
| // current XRSpace transformation. |
| device::mojom::XRHitTestSubscriptionResultDataPtr |
| GetHitTestSubscriptionResult(HitTestSubscriptionId id, |
| const mojom::XRRay& ray, |
| const gfx::Transform& ray_transformation); |
| |
| base::Optional<gfx::Transform> GetMojoFromNativeOrigin( |
| const mojom::XRNativeOriginInformationPtr& native_origin_information, |
| const device::mojom::VRPosePtr& pose); |
| |
| base::Optional<gfx::Transform> GetMojoFromReferenceSpace( |
| device::mojom::XRReferenceSpaceCategory category, |
| const device::mojom::VRPosePtr& pose); |
| |
| // Executes |fn| for each still tracked, non-subsumed plane present in |
| // |arcore_planes_|. |
| template <typename FunctionType> |
| void ForEachArCorePlane(FunctionType fn); |
| |
| // Executes |fn| for each still tracked anchor present in |arcore_anchors_|. |
| template <typename FunctionType> |
| void ForEachArCoreAnchor(FunctionType fn); |
| |
| // Must be last. |
| base::WeakPtrFactory<ArCoreImpl> weak_ptr_factory_{this}; |
| DISALLOW_COPY_AND_ASSIGN(ArCoreImpl); |
| }; |
| |
| class ArCoreImplFactory : public ArCoreFactory { |
| public: |
| std::unique_ptr<ArCore> Create() override; |
| }; |
| |
| } // namespace device |
| |
| #endif // CHROME_BROWSER_ANDROID_VR_ARCORE_DEVICE_ARCORE_IMPL_H_ |