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