// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.OpenXR.ARSubsystems; using System; using System.Collections.Generic; using System.Linq; using Unity.Collections; using Unity.XR.CoreUtils; using UnityEngine; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; namespace Microsoft.MixedReality.OpenXR { /// /// A manager for s. Creates, updates, and removes /// GameObjects in response to detected surfaces in the physical /// environment. /// [DefaultExecutionOrder(ARUpdateOrder.k_PlaneManager)] [DisallowMultipleComponent] #if USE_ARFOUNDATION_5_OR_NEWER [RequireComponent(typeof(XROrigin))] #else [RequireComponent(typeof(ARSessionOrigin))] #endif public sealed class ARMarkerManager : ARTrackableManager< XRMarkerSubsystem, XRMarkerSubsystemDescriptor, XRMarkerSubsystem.Provider, XRMarker, ARMarker> { private static ARMarkerManager m_instance = null; /// /// Singleton instance for ARMarkerManager /// public static ARMarkerManager Instance => m_instance; /// /// Getter or setter for the Marker Prefab. /// [Tooltip("If not null, instantiates this prefab for each created marker. Else, a default empty GameObject is created with the new ARMarker attached.")] public GameObject markerPrefab; /// /// The list of s that will be detected. /// [Tooltip("The list of ARMarker types that will be detected.")] public ARMarkerType[] enabledMarkerTypes = { ARMarkerType.QRCode }; /// /// Default for newly detected markers. /// [Tooltip("Default transform mode for newly detected markers.")] public TransformMode defaultTransformMode = TransformMode.MostStable; /// /// Invoked when markers have changed (been added, updated, or removed). /// public event Action markersChanged; /// /// Attempt to retrieve an existing by . /// /// The of the marker to retrieve. /// The with , or null if it does not exist. public ARMarker GetMarker(TrackableId trackableId) => m_Trackables.TryGetValue(trackableId, out ARMarker marker) ? marker : null; /// /// Set transform mode of an existing . /// /// The of the marker to be transformed. /// The to be applied. public void SetTransformMode(TrackableId trackableId, TransformMode transformMode) { if (enabled && subsystem != null) { subsystem.SetTransformMode(trackableId, transformMode); } } /// /// Get raw data for an existing . /// /// The of the marker. public NativeArray GetRawData(TrackableId trackableId, Allocator allocator) { if (enabled && subsystem != null) { return subsystem.GetRawData(trackableId, allocator); } return new NativeArray(); } /// /// Get decoded string for an existing . /// /// The of the marker. public string GetDecodedString(TrackableId trackableId) { if (enabled && subsystem != null) { return subsystem.GetDecodedString(trackableId); } return null; } /// /// Get QR code properties for an existing of type . /// /// The of the QRCode marker. public QRCodeProperties GetQRCodeProperties(TrackableId trackableId) { if (enabled && subsystem != null) { return subsystem.GetQRCodeProperties(trackableId); } return new QRCodeProperties(); } /// /// Get the Prefab which will be instantiated for each . Can be `null`. /// /// The Prefab which will be instantiated for each . protected override GameObject GetPrefab() => markerPrefab; /// /// Invoked when the base class detects trackable changes. /// /// The list of added s. /// The list of updated s. /// The list of removed s. protected override void OnTrackablesChanged( List added, List updated, List removed) { if (markersChanged != null) { using (new ScopedProfiler("OnMarkersChanged")) markersChanged( new ARMarkersChangedEventArgs( added, updated, removed)); } } protected override void Update() { base.Update(); if (enabled && subsystem != null) { Array.Sort(enabledMarkerTypes); if (!enabledMarkerTypes.SequenceEqual(subsystem.EnabledMarkerTypes)) { subsystem.EnabledMarkerTypes = enabledMarkerTypes; } if (defaultTransformMode != subsystem.DefaultTransformMode) { subsystem.DefaultTransformMode = defaultTransformMode; } } } protected override void OnEnable() { base.OnEnable(); // Replicating behavior in ARFoundation to initialize singleton instance m_instance = this; } /// /// The name to be used for the GameObject whenever a new marker object is created from . /// protected override string gameObjectName => "ARMarker"; } }