Last active
June 20, 2024 23:01
-
-
Save camnewnham/4f504c3de78926cd03166f5f7113ef97 to your computer and use it in GitHub Desktop.
Anchor storage compatible with HoloLens (Microsoft OpenXR Plugin) and ARFoundation (Meta)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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