Search Unity

Question World anchors on the HL2 with the new XR plugin

Discussion in 'AR' started by Thomas-Mountainborn, Jan 8, 2021.

  1. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    I'm trying to get world anchors to work using the new XR plugin. The implementation using WorldAnchorStore and its related calls have been marked as deprecated. When I try to run the code on the HL2 anyway using the WMR XR plugin, nothing happens. I'm assuming there is a working replacement for them.

    In the manual for the WMR XR plugin, this is what is mentioned for the "reference points" (which have been renamed to Anchors in 3.0+ versions of the AR subsystems, but for the time being I'm on AR Subsystems 2.1.3 since that's the one used in the documentation's code sample below)

    The highlighted sentence makes me worried - has the old system been deprecated without there actually being a working replacement?

    This is the code sample:

    Code (csharp):
    1. TrackableChanges<XRReferencePoint>? currentRps = RPSubsystem?.GetChanges(Allocator.Temp) ?? null;
    2.  
    3. if (currentRps != null)
    4. {
    5.    foreach(var rp in currentRps?.added)
    6.    {
    7. #if ENABLE_WINMD_SUPPORT // Necessary since the Windows Types are only valid through WinMD projection.
    8.        AnchorData data = Marshal.PtrToStructure<AnchorData>(rp.nativePtr);
    9.        SpatialAnchor anchor = data.spatialAnchor as SpatialAnchor;
    10.        if (anchor != null)
    11.        {
    12.            // Do something with the anchor here.
    13.        }
    14. #endif
    15.    }
    16. }
    Unfortunately, AnchorData and SpatialAnchor are unknown classes, even in the C# player project (i.e. where ENABLE_WINMD_SUPPORT is defined). I can't find any reference to them online. The sample also does not tell me how to save and restore anchors - the API also seems a lot more complicated than the deprecated one. Is it possible to get some information on how anchors work with the XR plugin? Thanks!
     
    Last edited: Jan 8, 2021
  2. joejo

    joejo

    Unity Technologies

    Joined:
    May 26, 2016
    Posts:
    958
    Add this:
    Code (CSharp):
    1. #if ENABLE_WINMD_SUPPORT
    2. using Windows.Perception.Spatial;
    3. #endif
    4.  
    5. [StructLayout(LayoutKind.Sequential)]
    6. struct AnchorData
    7. {
    8.     public int version;
    9.  
    10.     [MarshalAs(UnmanagedType.IUnknown)]
    11.     public System.Object spatialAnchor;
    12. }
    13.  
    14.  
     
  3. joejo

    joejo

    Unity Technologies

    Joined:
    May 26, 2016
    Posts:
    958
    If that works, can you file a bug about incorrect sample in documentation and post the case ID here?
     
  4. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    I was pointed in the direction of the XRAnchorStore by Edd Smith on the HoloDevelopers Slack channel, which allowed me to figure out how the anchors are supposed to be used with the new XR plugin. Here's some sample code:

    Code (CSharp):
    1. using System;
    2. using System.ComponentModel;
    3. using Unity.Collections;
    4. using UnityEngine;
    5. using UnityEngine.Events;
    6. using UnityEngine.XR.ARSubsystems;
    7.  
    8. public class AnchorableObject : MonoBehaviour
    9. {
    10.     public bool LoadAnchorOnStart = true;
    11.  
    12.     [SerializeField, Tooltip("World anchors must have a unique name. Enter a descriptive name if you like.")]
    13.     protected string _worldAnchorName;
    14.     [SerializeField]
    15.     protected TMPro.TMP_Text _debugLabel;
    16.  
    17.     [Space(10)]
    18.     public UnityEvent OnAnchorLoaded;
    19.  
    20.     private TrackableId _xrAnchorId;
    21.     private AnchorStoreManager _anchorStoreManager;
    22.  
    23.     private void OnValidate()
    24.     {
    25.         if (string.IsNullOrEmpty(_worldAnchorName))
    26.         {
    27.             _worldAnchorName = Guid.NewGuid().ToString();
    28.         }
    29.     }
    30.  
    31.     private void Start()
    32.     {
    33.         _anchorStoreManager = FindObjectOfType<AnchorStoreManager>();
    34.         if (_anchorStoreManager)
    35.         {
    36.             _anchorStoreManager.PropertyChanged += AnchorStore_PropertyChanged;
    37.  
    38.             if (LoadAnchorOnStart && _anchorStoreManager.AnchorStore != null)
    39.             {
    40.                 LoadAnchor();
    41.             }
    42.         }
    43.         else
    44.         {
    45.             LogDebugMessage($"No {nameof(AnchorStoreManager)} present in scene.", true);
    46.         }
    47.     }
    48.  
    49.     private void LateUpdate()
    50.     {
    51.         UpdateAnchorPose();
    52.     }
    53.  
    54.     public bool SaveAnchor(bool overwrite = true)
    55.     {
    56.         if (_anchorStoreManager?.AnchorPointsSubsystem == null || _anchorStoreManager?.AnchorStore == null)
    57.         {
    58.             LogDebugMessage($"Can't save anchor {_worldAnchorName}: reference point subsystem or anchor store have not been initialized.", true);
    59.             return false;
    60.         }
    61.  
    62.         XRReferencePoint anchor;
    63.  
    64.         if (overwrite)
    65.         {
    66.             // Delete the current anchor in the store, so we can persist the new position.
    67.             _anchorStoreManager.AnchorStore.UnpersistAnchor(_worldAnchorName);
    68.         }
    69.  
    70.         // Attempt to save the anchor.
    71.         if (_anchorStoreManager.AnchorPointsSubsystem.TryAddReferencePoint(new Pose(transform.position, transform.rotation), out anchor))
    72.         {
    73.             if (_anchorStoreManager.AnchorStore.TryPersistAnchor(anchor.trackableId, _worldAnchorName))
    74.             {
    75.                 _xrAnchorId = anchor.trackableId;
    76.                 LogDebugMessage($"Successfully saved anchor {_worldAnchorName}.");
    77.                 PositionFromAnchor(anchor);
    78.                 return true;
    79.             }
    80.             else
    81.             {
    82.                 LogDebugMessage($"Failed to save anchor {_worldAnchorName}.", true);
    83.             }
    84.         }
    85.         else
    86.         {
    87.             LogDebugMessage($"Failed to add reference point for anchor {_worldAnchorName}.", true);
    88.         }
    89.  
    90.         return false;
    91.     }
    92.  
    93.     public bool LoadAnchor()
    94.     {
    95.         if (_anchorStoreManager?.AnchorPointsSubsystem == null || _anchorStoreManager?.AnchorStore == null)
    96.         {
    97.             LogDebugMessage($"Can't load anchor {_worldAnchorName}: reference point subsystem or anchor store have not been initialized.", true);
    98.             return false;
    99.         }
    100.  
    101.         // Retrieve the trackable id from the anchor store.
    102.         TrackableId trackableId = _anchorStoreManager.AnchorStore.LoadAnchor(_worldAnchorName);
    103.  
    104.         // Look for the matching anchor in the anchor point subsystem.
    105.         TrackableChanges<XRReferencePoint> referencePointChanges = _anchorStoreManager.AnchorPointsSubsystem.GetChanges(Allocator.Temp);
    106.         foreach (XRReferencePoint anchor in referencePointChanges.added)
    107.         {
    108.             if (anchor.trackableId == trackableId)
    109.             {
    110.                 _xrAnchorId = anchor.trackableId;
    111.                 PositionFromAnchor(anchor);
    112.                 OnAnchorLoaded.Invoke();
    113.                 LogDebugMessage($"Found anchor {_worldAnchorName} in added reference points.");
    114.                 return true;
    115.             }
    116.         }
    117.  
    118.         LogDebugMessage($"Did not find anchor {_worldAnchorName} in reference points subsystem after XRAnchorStore load.", true);
    119.         return false;
    120.     }
    121.  
    122.     public void ClearAnchor()
    123.     {
    124.         _xrAnchorId = default;
    125.  
    126.         if (_anchorStoreManager.AnchorStore != null)
    127.         {
    128.             _anchorStoreManager.AnchorStore.UnpersistAnchor(_worldAnchorName);
    129.         }
    130.     }
    131.  
    132.     private void UpdateAnchorPose()
    133.     {
    134.         if (_xrAnchorId == default || _anchorStoreManager?.AnchorPointsSubsystem == null)
    135.         {
    136.             return;
    137.         }
    138.  
    139.         TrackableChanges<XRReferencePoint> anchorChanges = _anchorStoreManager.AnchorPointsSubsystem.GetChanges(Allocator.Temp);
    140.  
    141.         foreach (XRReferencePoint anchor in anchorChanges.updated)
    142.         {
    143.             if (anchor.trackableId == _xrAnchorId)
    144.             {
    145.                 PositionFromAnchor(anchor);
    146.                 break;
    147.             }
    148.         }
    149.     }
    150.  
    151.     private void PositionFromAnchor(XRReferencePoint anchor)
    152.     {
    153.         if (_debugLabel)
    154.         {
    155.             _debugLabel.text = $"{_worldAnchorName} tracking: {anchor.trackingState}\r\n{anchor.pose.position}\r\n{anchor.pose.rotation}";
    156.         }
    157.  
    158.         transform.position = anchor.pose.position;
    159.         transform.rotation = anchor.pose.rotation;
    160.     }
    161.  
    162.     private void AnchorStore_PropertyChanged(object sender, PropertyChangedEventArgs e)
    163.     {
    164.         if (LoadAnchorOnStart && e.PropertyName == nameof(AnchorStoreManager.AnchorStore) && _anchorStoreManager.AnchorStore != null)
    165.         {
    166.             LoadAnchor();
    167.         }
    168.     }
    169.  
    170.     private void LogDebugMessage(string message, bool isWarning = false)
    171.     {
    172.         if (_debugLabel)
    173.         {
    174.             _debugLabel.text = message;
    175.         }
    176.  
    177.         if (isWarning)
    178.         {
    179.             Debug.LogWarning($"[{GetType()}] {message}");
    180.         }
    181.         else
    182.         {
    183.             Debug.Log($"[{GetType()}] {message}");
    184.         }
    185.     }
    186. }

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using System.ComponentModel;
    3. using UnityEngine;
    4. using UnityEngine.XR.ARSubsystems;
    5. using UnityEngine.XR.WindowsMR;
    6.  
    7. public class AnchorStoreManager : MonoBehaviour
    8. {
    9.     public event PropertyChangedEventHandler PropertyChanged;
    10.  
    11.     private XRReferencePointSubsystem _anchorPointSubsystem;
    12.     public XRReferencePointSubsystem AnchorPointsSubsystem
    13.     {
    14.         get { return _anchorPointSubsystem; }
    15.         private set
    16.         {
    17.             _anchorPointSubsystem = value;
    18.             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AnchorPointsSubsystem)));
    19.         }
    20.     }
    21.  
    22.     private XRAnchorStore _anchorStore;
    23.     public XRAnchorStore AnchorStore
    24.     {
    25.         get { return _anchorStore; }
    26.         private set
    27.         {
    28.             _anchorStore = value;
    29.  
    30.             if (AnchorStore != null)
    31.             {
    32.                 Debug.Log($"[{GetType()}] Anchor store initialized.");
    33.             }
    34.  
    35.             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AnchorStore)));
    36.         }
    37.     }
    38.  
    39.     private async void Start()
    40.     {
    41.         AnchorPointsSubsystem = CreateReferencePointSubSystem();
    42.         AnchorStore = await _anchorPointSubsystem.TryGetAnchorStoreAsync();
    43.     }
    44.  
    45.     private void OnDestroy()
    46.     {
    47.         if (AnchorPointsSubsystem != null)
    48.         {
    49.             AnchorPointsSubsystem.Stop();
    50.         }
    51.         if (AnchorStore != null)
    52.         {
    53.             AnchorStore.Dispose();
    54.         }
    55.     }
    56.  
    57.     private XRReferencePointSubsystem CreateReferencePointSubSystem()
    58.     {
    59.         List<XRReferencePointSubsystemDescriptor> rpSubSystemsDescriptors = new List<XRReferencePointSubsystemDescriptor>();
    60.         SubsystemManager.GetSubsystemDescriptors(rpSubSystemsDescriptors);
    61.  
    62.         string descriptors = "";
    63.         foreach (var descriptor in rpSubSystemsDescriptors)
    64.         {
    65.             descriptors += $"{descriptor.id} {descriptor.subsystemImplementationType}\r\n";
    66.         }
    67.  
    68.         Debug.Log($"[{GetType()}] {rpSubSystemsDescriptors.Count} reference point subsystem descriptors:\r\n{descriptors}");
    69.  
    70.         XRReferencePointSubsystem rpSubSystem = null;
    71.         if (rpSubSystemsDescriptors.Count > 0)
    72.         {
    73.             rpSubSystem = rpSubSystemsDescriptors[0].Create();
    74.             rpSubSystem.Start();
    75.         }
    76.  
    77.         return rpSubSystem;
    78.     }
    79. }

    Be aware that when you are on AR Subsystems 3.0+, ReferencePoint has been renamed to Anchor.
     
    Last edited: Jan 11, 2021
  5. epnt

    epnt

    Joined:
    Jan 19, 2021
    Posts:
    1
    Thank you @Thomas-Mountainborn for sharing your code. I am trying to add persistent anchors using the new XR SDK on Hololens 1 and your code has been really helpful but I cannot get it to work. I am using Unity 20.2, MRTK 2.5.3, Windows XR Plugin 4.2.1, XR Plugin Management 4.0.0 and AR Foundation 4.0.9.
    Could you provide a minimal working example? I am confused on how to combine the UnityEngine.XR.ARSubsystems with the MRTK.
     
  6. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    What exactly is it that won't work? ARSubsystems and MRTK should be able to exist next to each other without much issue.
     
    hassy84 and keveleigh like this.
  7. Axiang666

    Axiang666

    Joined:
    Jun 23, 2017
    Posts:
    6
  8. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    I have not, sorry.
     
  9. joejo

    joejo

    Unity Technologies

    Joined:
    May 26, 2016
    Posts:
    958
    You'll want to use this to get the anchor store: Class XRAnchorSubsystemExtensions | Windows XR Plugin | 5.3.0 (unity3d.com)

    Then you can use this: Class XRAnchorStore | Windows XR Plugin | 5.3.0 (unity3d.com) to persist anchors to the SpatialAnchorStore if you haven't already.

    Then you can use the SpatialAnchorStore Class (Windows.Perception.Spatial) - Windows UWP applications | Microsoft Docs API to GetAllSavedAnchors and serialized those to a stream that you can then share/send.

    From there you deserialize the stream on the target device, and use XRAnchorStore to add the anchors.

    Alternatively you can use Quickstart: Create a HoloLens app with Unity - Azure Spatial Anchors | Microsoft Docs
     
  10. siva2451971

    siva2451971

    Joined:
    Jan 22, 2022
    Posts:
    1
    What is an alternative for WorldAnchor, WorldAnchorStore and WorldAnchorTransferBatch(local anchor sharing) for WMR. Is there an minimal example implemented for this somewhere?
     
  11. joejo

    joejo

    Unity Technologies

    Joined:
    May 26, 2016
    Posts:
    958
    The pose right above you goes into detail about this. For the best experience and future portability I'd look at the last link.
     
  12. mustafizurcd

    mustafizurcd

    Joined:
    Mar 2, 2022
    Posts:
    1
    Hi,
    I am using this code but it shows error in the line
    AnchorStore = await _anchorPointSubsystem.TryGetAnchorStoreAsync();
    Because I am using OpenXR. Could you suggest me the new API reference?
     
  13. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    There's a bunch of links in joejo's post, check those.