// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using UnityEngine.XR.ARSubsystems; namespace Microsoft.MixedReality.OpenXR { /// /// Provides the ability to build up a batch of anchors and export them to a binary stream for transfer. /// Typically on a second device, it then supports importing the transfer stream and loading in the original batch of anchors. /// /// Use of this class requires an ARAnchorManager in the scene or some other manual management of an XRAnchorSubsystem. public class XRAnchorTransferBatch { /// /// Constructor. /// public XRAnchorTransferBatch() : this(new AnchorTransferBatch()) { } private XRAnchorTransferBatch(AnchorTransferBatch anchorTransferBatch) { m_anchorTransferBatch = anchorTransferBatch; } private readonly AnchorTransferBatch m_anchorTransferBatch; /// /// Provides a list of all identifiers currently mapped in this AnchorTransferBatch. /// public IReadOnlyList AnchorNames => m_anchorTransferBatch.AnchorNames; /// /// Tries to convert and add an anchor with the corresponding to an export list. /// /// Call to get the transferable anchor data. /// The of an anchor to be exported. /// A string to identify this anchor upon import to another device. /// Whether the anchor was successfully converted into a Perception SpatialAnchor and added to the export list. public bool AddAnchor(TrackableId trackableId, string name) => m_anchorTransferBatch.AddAnchor(trackableId, name); /// /// Removes an anchor from the transfer batch. Doesn't remove the existing Unity anchor, if one is present. /// /// After an anchor is removed from the transfer batch, it will still be valid and locatable in the current session. /// The name of the anchor to be removed from the transfer batch. public void RemoveAnchor(string name) => m_anchorTransferBatch.RemoveAnchor(name); /// /// Removes all anchors from the transfer batch. Doesn't remove any existing Unity anchors, if present. /// /// After the anchors are cleared from the transfer batch, they will still be valid and locatable in the current session. public void Clear() => m_anchorTransferBatch.Clear(); /// /// Attempts to load a specified anchor from the transfer batch and reports it to Unity as an XRAnchor/ARAnchor. /// /// It's then typically recommended to use an ARAnchorManager to access the resulting Unity anchor. /// The anchor's identifier from the transfer batch. /// The of the resulting Unity anchor if successfully loaded, or TrackableId.invalidId if the given name is not found. public TrackableId LoadAnchor(string name) => m_anchorTransferBatch.LoadAnchor(name); /// /// Attempts to load a specified anchor from the transfer batch and replace the specified Unity anchor's tracking data with the new anchor. /// /// The anchor's identifier from the transfer batch. /// The existing Unity anchor to update to track this new spatial anchor. /// The of the resulting Unity anchor (usually the same as the passed-in parameter) if successfully loaded, /// or TrackableId.invalidId if the given name is not found. public TrackableId LoadAndReplaceAnchor(string name, TrackableId trackableId) => m_anchorTransferBatch.LoadAndReplaceAnchor(name, trackableId); /// /// Exports any anchors added via into a Stream for transfer. Use for reading this Stream. /// /// The anchor transfer batch instance to export from. This instance should have had anchors added before attempting export. /// A task which, when completed, will contain the exported array, or null if the export was unsuccessful. public static async Task ExportAsync(XRAnchorTransferBatch anchorTransferBatch) { MemoryStream output = new MemoryStream(); SerializationCompletionReason reason = await anchorTransferBatch.m_anchorTransferBatch.ExportAsync(output); if (reason == SerializationCompletionReason.Succeeded) { return output; } return null; } /// /// Imports the provided Stream into an . /// /// The streamed data representing the result of a call to . This stream must be readable. /// A task which, when completed, will contain the resulting XRAnchorTransferBatch, or null if the import was unsuccessful. public static async Task ImportAsync(Stream inputStream) { AnchorTransferBatch anchorTransfer = new AnchorTransferBatch(); SerializationCompletionReason reason = await anchorTransfer.ImportAsync(inputStream); if (reason == SerializationCompletionReason.Succeeded) { return new XRAnchorTransferBatch(anchorTransfer); } return null; } } }