using System;
using System.Collections.Generic;
using Unity.Jobs;
using UnityEngine.Serialization;
using UnityEngine.XR.ARSubsystems;
using Unity.XR.CoreUtils;
namespace UnityEngine.XR.ARFoundation
{
///
/// A manager for s. Uses the XRImageTrackingSubsystem
/// to recognize and track 2D images in the physical environment.
///
///
/// Related information: AR Tracked Image Manager component
///
[DefaultExecutionOrder(ARUpdateOrder.k_TrackedImageManager)]
[RequireComponent(typeof(XROrigin))]
[AddComponentMenu("XR/AR Foundation/AR Tracked Image Manager")]
[HelpURL("features/image-tracking")]
public sealed class ARTrackedImageManager : ARTrackableManager<
XRImageTrackingSubsystem,
XRImageTrackingSubsystemDescriptor,
XRImageTrackingSubsystem.Provider,
XRTrackedImage,
ARTrackedImage>
{
[SerializeField]
[FormerlySerializedAs("m_ReferenceLibrary")]
[Tooltip("The library of images which will be detected and/or tracked in the physical environment.")]
XRReferenceImageLibrary m_SerializedLibrary;
///
/// Get or set the reference image library (that is, the set of images to search for in the physical environment).
///
///
/// An IReferenceImageLibrary can be either an XRReferenceImageLibrary
/// or a RuntimeReferenceImageLibrary. XRReferenceImageLibrarys can only be
/// constructed in the Editor and are immutable at runtime. A RuntimeReferenceImageLibrary
/// is the runtime representation of a XRReferenceImageLibrary and can be mutable
/// at runtime (see MutableRuntimeReferenceImageLibrary).
///
/// Thrown if the is set to null while image tracking is enabled.
public IReferenceImageLibrary referenceLibrary
{
get
{
if (subsystem != null)
{
return subsystem.imageLibrary;
}
else
{
return m_SerializedLibrary;
}
}
set
{
if (value == null && subsystem != null && subsystem.running)
throw new InvalidOperationException("Cannot set a null reference library while image tracking is enabled.");
if (value is XRReferenceImageLibrary serializedLibrary)
{
m_SerializedLibrary = serializedLibrary;
if (subsystem != null)
subsystem.imageLibrary = subsystem.CreateRuntimeLibrary(serializedLibrary);
}
else if (value is RuntimeReferenceImageLibrary runtimeLibrary)
{
m_SerializedLibrary = null;
EnsureSubsystemInstanceSet();
if (subsystem != null)
subsystem.imageLibrary = runtimeLibrary;
}
if (subsystem != null)
UpdateReferenceImages(subsystem.imageLibrary);
}
}
///
/// Creates a UnityEngine.XR.ARSubsystems.RuntimeReferenceImageLibrary from an existing
/// UnityEngine.XR.ARSubsystems.XRReferenceImageLibrary
/// or an empty library if is null.
/// Use this to construct reference image libraries at runtime. If the library is of type
/// MutableRuntimeReferenceImageLibrary, it is modifiable at runtime.
///
/// An existing XRReferenceImageLibrary, or null to create an empty mutable image library.
/// A new RuntimeReferenceImageLibrary representing the deserialized version of or an empty library if is null.
/// Thrown if there is no subsystem. This usually means image tracking is not supported.
public RuntimeReferenceImageLibrary CreateRuntimeLibrary(XRReferenceImageLibrary serializedLibrary = null)
{
EnsureSubsystemInstanceSet();
if (subsystem == null)
throw new NotSupportedException("No image tracking subsystem found. This usually means image tracking is not supported.");
return subsystem.CreateRuntimeLibrary(serializedLibrary);
}
[SerializeField]
[Tooltip("The maximum number of moving images to track in realtime. Not all implementations support this feature.")]
int m_MaxNumberOfMovingImages;
bool supportsMovingImages => descriptor?.supportsMovingImages == true;
///
/// The requested maximum number of moving images to track in real time. Support can vary between devices and providers. Check
/// for support at runtime with 's
/// `supportsMovingImages` property.
///
public int requestedMaxNumberOfMovingImages
{
get => supportsMovingImages ? subsystem.requestedMaxNumberOfMovingImages : m_MaxNumberOfMovingImages;
set
{
m_MaxNumberOfMovingImages = value;
if (enabled && (descriptor?.supportsMovingImages == true))
{
subsystem.requestedMaxNumberOfMovingImages = value;
}
}
}
///
/// Get the maximum number of moving images to track in real time that is currently in use by the subsystem.
///
public int currentMaxNumberOfMovingImages => supportsMovingImages ? subsystem.currentMaxNumberOfMovingImages : 0;
[SerializeField]
[Tooltip("If not null, instantiates this prefab for each detected image.")]
GameObject m_TrackedImagePrefab;
///
/// If not null, instantiates this Prefab for each detected image.
///
///
/// The purpose of this property is to extend the functionality of s.
/// It is not the recommended way to instantiate content associated with an .
///
public GameObject trackedImagePrefab
{
get => m_TrackedImagePrefab;
set => m_TrackedImagePrefab = value;
}
///
/// Get the Prefab that will be instantiated for each .
///
/// The Prefab that will be instantiated for each .
protected override GameObject GetPrefab() => m_TrackedImagePrefab;
///
/// Invoked once per frame with information about the s that have changed (that is, been added, updated, or removed).
/// This happens just before s are destroyed, so you can set ARTrackedImage.destroyOnRemoval to false
/// from this event to suppress this behavior.
///
[Obsolete("trackedImagesChanged has been deprecated in AR Foundation version 6.0. Use trackablesChanged instead.", false)]
public event Action trackedImagesChanged;
///
/// The name to be used for the GameObject whenever a new image is detected.
///
protected override string gameObjectName => nameof(ARTrackedImage);
///
/// Sets the image library on the subsystem before Start() is called on the XRImageTrackingSubsystem.
///
protected override void OnBeforeStart()
{
if (subsystem.imageLibrary == null && m_SerializedLibrary != null)
{
subsystem.imageLibrary = subsystem.CreateRuntimeLibrary(m_SerializedLibrary);
m_SerializedLibrary = null;
}
UpdateReferenceImages(subsystem.imageLibrary);
if (supportsMovingImages)
{
subsystem.requestedMaxNumberOfMovingImages = m_MaxNumberOfMovingImages;
}
enabled = (subsystem.imageLibrary != null);
#if DEVELOPMENT_BUILD
if (subsystem.imageLibrary == null)
{
Debug.LogWarning($"{nameof(ARTrackedImageManager)} '{name}' was enabled but no reference image library is specified. To enable, set a valid reference image library and then re-enable this component.");
}
#endif
}
bool FindReferenceImage(Guid guid, out XRReferenceImage referenceImage)
{
if (m_ReferenceImages.TryGetValue(guid, out referenceImage))
return true;
// If we are using a mutable library, then it's possible an image
// has been added that we don't yet know about, so search the library.
if (referenceLibrary is MutableRuntimeReferenceImageLibrary mutableLibrary)
{
foreach (var candidateImage in mutableLibrary)
{
if (candidateImage.guid.Equals(guid))
{
referenceImage = candidateImage;
m_ReferenceImages.Add(referenceImage.guid, referenceImage);
return true;
}
}
}
return false;
}
///
/// Invoked just after updating each . Used to update the .
///
/// The tracked image being updated.
/// New data associated with the tracked image. Spatial data is
/// relative to the .
protected override void OnAfterSetSessionRelativeData(
ARTrackedImage image,
XRTrackedImage sessionRelativeData)
{
if (FindReferenceImage(sessionRelativeData.sourceImageId, out XRReferenceImage referenceImage))
{
image.referenceImage = referenceImage;
}
#if DEVELOPMENT_BUILD
else
{
Debug.LogError($"Could not find reference image with guid {sessionRelativeData.sourceImageId}");
}
#endif
}
///
/// Invokes the event.
///
/// A list of images added this frame.
/// A list of images updated this frame.
/// A list of images removed this frame.
[Obsolete("OnTrackablesChanged() has been deprecated in AR Foundation version 6.0.", false)]
protected override void OnTrackablesChanged(
List added,
List updated,
List removed)
{
if (trackedImagesChanged != null)
{
using (new ScopedProfiler("OnTrackedImagesChanged"))
trackedImagesChanged?.Invoke(
new ARTrackedImagesChangedEventArgs(
added,
updated,
removed));
}
}
void UpdateReferenceImages(RuntimeReferenceImageLibrary library)
{
if (library == null)
return;
int count = library.count;
for (int i = 0; i < count; ++i)
{
var referenceImage = library[i];
m_ReferenceImages[referenceImage.guid] = referenceImage;
}
}
Dictionary m_ReferenceImages = new Dictionary();
}
}