Skip to content

Instantly share code, notes, and snippets.

@camnewnham
Last active June 20, 2024 23:01
Show Gist options
  • Select an option

  • Save camnewnham/4f504c3de78926cd03166f5f7113ef97 to your computer and use it in GitHub Desktop.

Select an option

Save camnewnham/4f504c3de78926cd03166f5f7113ef97 to your computer and use it in GitHub Desktop.
Anchor storage compatible with HoloLens (Microsoft OpenXR Plugin) and ARFoundation (Meta)
#if UNITY_WSA
using Microsoft.MixedReality.OpenXR;
using System.Threading.Tasks;
#endif
using System;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using Cysharp.Threading.Tasks;
using System.Threading;
/// <summary>
/// Negotiates between the anchor store and the ARAnchorManager to save, load, and erase anchors.
/// </summary>
public class CrossPlatformAnchorStorage : MonoBehaviour
{
[SerializeField] private ARAnchorManager AnchorManager;
public bool IsSupported =>
#if UNITY_EDITOR
false;
#elif UNITY_WSA
true;
#else
AnchorManager.subsystem.subsystemDescriptor.supportsSaveAnchor;
#endif
#if UNITY_WSA
private Task<XRAnchorStore> anchorStoreTask;
private async Task<XRAnchorStore> GetAnchorStore()
{
if (anchorStoreTask == null)
{
anchorStoreTask = XRAnchorStore.LoadAnchorStoreAsync(AnchorManager.subsystem);
}
return await anchorStoreTask;
}
#endif
public async UniTask<string> SaveAnchor(ARAnchor anchor)
{
try
{
#if UNITY_WSA
XRAnchorStore store = await GetAnchorStore();
string name = Guid.NewGuid().ToString();
if (store.TryPersistAnchor(anchor.trackableId, name))
{
Debug.Log($"Saved anchor \"{anchor.trackableId}\" to store.");
return name;
}
else
{
Debug.LogError("Failed to persist anchor.");
}
return null;
#else
if (!AnchorManager.subsystem.subsystemDescriptor.supportsSaveAnchor)
{
Debug.Log("Saving anchors is not supported by this subsystem.");
return null;
}
Result<SerializableGuid> saveResult = await AnchorManager.TrySaveAnchorAsync(anchor);
if (saveResult.status.IsSuccess())
{
Debug.Log("Serialized anchor.");
Guid anchorGuid = saveResult.value.guid;
return anchorGuid.ToString();
}
else
{
Debug.LogWarning("Unable to serialize anchor. Status: " + saveResult.status.statusCode);
}
return null;
#endif
}
catch (Exception ex)
{
Debug.LogError($"An exception occurred while loading running {nameof(SaveAnchor)}: " + ex.ToString());
return null;
}
}
public async UniTask<ARAnchor> LoadAnchor(string id)
{
try
{
#if UNITY_WSA
XRAnchorStore store = await GetAnchorStore();
TrackableId trackableId = store.LoadAnchor(id);
if (trackableId == TrackableId.invalidId)
{
Debug.LogError($"Failed to load persisted anchor {id} from store. Trackable ID was invalid.");
return null;
}
SemaphoreSlim semaphore = new SemaphoreSlim(0, 1);
ARAnchor result = null;
await UniTask.WaitWhile(() => !AnchorManager.trackables.TryGetTrackable(trackableId, out result));
Debug.Log($"Retrieved anchor \"{id}\" from store");
return result;
#else
Guid guid = Guid.Parse(id);
if (!AnchorManager.subsystem.subsystemDescriptor.supportsLoadAnchor)
{
Debug.Log("Loading anchors is not supported by this subsystem.");
return null;
}
Result<ARAnchor> loadResult = await AnchorManager.TryLoadAnchorAsync(new UnityEngine.XR.ARSubsystems.SerializableGuid(guid));
if (loadResult.status.IsSuccess())
{
Debug.Log("Loaded anchor.");
return loadResult.value;
}
else
{
Debug.LogWarning("Error loading anchor from store. Status: " + loadResult.status.statusCode);
}
return null;
#endif
}
catch (Exception ex)
{
Debug.LogError($"An exception occurred while loading running {nameof(LoadAnchor)}: " + ex.ToString());
return null;
}
}
public async UniTask<bool> EraseAnchor(string id)
{
try
{
#if UNITY_WSA
XRAnchorStore store = await GetAnchorStore();
store.UnpersistAnchor(id);
Debug.Log($"Unpersisted anchor \"{id}\".");
return true;
#else
if (!AnchorManager.subsystem.subsystemDescriptor.supportsEraseAnchor)
{
Debug.Log("Erasing anchors is not supported by this subsystem.");
return false;
}
XRResultStatus eraseResult = await AnchorManager.TryEraseAnchorAsync(new SerializableGuid(Guid.Parse(id)));
if (eraseResult.IsSuccess())
{
Debug.Log("Erased anchor.");
return true;
}
else
{
Debug.LogWarning("Unable to erase anchor. Status: " + eraseResult.statusCode);
}
return false;
#endif
}
catch (Exception ex)
{
Debug.LogError($"An exception occurred while loading running {nameof(EraseAnchor)}: " + ex.ToString());
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment