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(); } }