// 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";
}
}