Skip to main content
Version: 14 Oct 2024

Spatial Anchor API Overview

This section provides an overview and API references for the the Magic Leap 2 Spatial Anchor Subsystem OpenXR Feature.

Magic Leap 2 allows developers to place Spatial Anchors that can be used as reference points inside Applications. These anchors are can be created using AR Foundation's ARAnchor and AnchorManager. This section elaborates on creating and destroying these anchors, providing a foundation for managing anchored content effectively. If you are operating the headset inside a recognized space, you can also choose to publish the Anchors so they persist across multiple sessions. For more information on how to save ARAnchors to a specific location and have their data persist across multiple sessions, see Magic Leap 2's Spatial Anchor Storage API.

caution

This feature requires the SPATIAL_ANCHORS permission to be enabled in your project's Manifest Settings. (Edit > Project Settings > Magic Leap > Manifest Settings)

Prerequisites

Before using spatial anchors, ensure your project has the necessary permissions and subsystems activated:

  • Spatial Anchors Permission: Activate SPATIAL_ANCHORS in Edit > Project Settings > Magic Leap > Manifest Settings.
  • OpenXR Feature: Enable the Magic Leap 2 Spatial Anchor Subsystem in Window > XR Plugin Manager > OpenXR Settings.
  • AR Anchor Manager: For Spatial Anchor to be tracked properly, add the ARAnchorManager component to the XROrigin in your scene.

NameSpaces

The following namespaces are

// To access AR Anchor Functions through ARFoundation
using UnityEngine.XR.ARFoundation;
// Provides access to the data that is passed into AR Foundation such as Tracking State
using UnityEngine.XR.ARSubsystems;
// Contains classes that allow developers references the active subsystem
using UnityEngine.XR.Management;

// Contains Magic Leap Specific functionality
using MagicLeap.OpenXR.Subsystems;

Creating Anchors

To add a new spatial anchor to your scene, simply add the ARAnchor component to any GameObject using AddComponent<ARAnchor>(). This can be done programmatically to either existing game objects or to new instances of prefabs once they are positioned inside your scene.

void AddAnchor(Vector3 position, GameObject content)
{
// Position the object at the anchor location
content.transform.position = position;
// Add an anchor to your content
content.AddComponent<ARAnchor>();
}

void CreateAnchor(Vector3 position, GameObject prefab)
{
// Create an instance of the prefab
var instance = Instantiate(prefab, position, Quaternion.identity);

// Add an ARAnchor component if it doesn't have one already.
if (instance.GetComponent<ARAnchor>() == null)
{
instance.AddComponent<ARAnchor>();
}
}

Newly created anchors may initially be in a "pending" state and not immediately tracked. Use the pending property to check their status.

public ARAnchorManager anchorManager;

public void PublishActiveAnchors()
{
foreach (var localAnchor in anchorManager.trackables)
{
if (localAnchor.pending == false && localAnchor.trackingState == TrackingState.Tracking)
{
// The anchor is now actively tracked
}
}
}

Deleting Anchors

To remove an anchor, simply remove the ARAnchor component from the GameObject it's attached to. This action unanchors the content from the physical world, allowing its position and orientation to be changed freely.

void RemoveAnchorFromGameObject(GameObject anchoredGameObject)
{
ARAnchor anchor = anchoredGameObject.GetComponent<ARAnchor>();
if (anchor != null)
{
Destroy(anchor);
}
}

Monitoring Anchor Updates

To monitor updates to anchors, poll the ARAnchor's trackingState to determine its current status.

Example method for anchor state monitoring:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class AnchorUpdateManager : MonoBehaviour
{
private List<ARAnchor> activeAnchors = new List<ARAnchor>();

void Update()
{
foreach (var anchor in activeAnchors)
{
switch (anchor.trackingState)
{
case TrackingState.Tracking:
// Handle tracked anchor
break;
case TrackingState.Limited:
// Handle limited tracking
break;
case TrackingState.None:
// Handle lost tracking
break;
}
}
}
}

Magic Leap Anchor Properties

This section demonstrates how to obtain Magic Leap specific properties from the anchors created by AR Foundations such as their Magic Leap ID, the Anchor Pose and the Anchor Confidence.

ML XR Anchor Subsystem

The Magic Leap 2 uses the MLXrAnchorSubsystem to manage anchors inside Unity. This Subsystem extends the standard XRAnchorSubsystem and adds Magic Leap Specific APIs such as anchor confidence. Additionally, anchors created with the MLXrAnchorSubsystem contain a platform specific Anchor ID. So they can be identified for platform specific functions.

The example code below returns true if the Magic Leap XR Anchor Subsystem is loaded.

Namespace
using MagicLeap.OpenXR.Subsystems;
 private bool AreSubsystemsLoaded(out MLXrAnchorSubsystem activeSubsystem)
{
activeSubsystem = null;
if (XRGeneralSettings.Instance == null) return false;
if (XRGeneralSettings.Instance.Manager == null) return false;
var activeLoader = XRGeneralSettings.Instance.Manager.activeLoader;
if (activeLoader == null) return false;
activeSubsystem = activeLoader.GetLoadedSubsystem<XRAnchorSubsystem>() as MLXrAnchorSubsystem;
return activeSubsystem != null;
}

This subsystem can then be used to query Magic Leap specific properties such as anchor confidence

 void Update()
{
if (AreSubsystemsLoaded(out var activeSubsystem))
{
foreach (ARAnchor anchor in anchorManager.trackables)
{
if (anchor.trackingState == TrackingState.Tracking && activeSubsystem != null)
{
ulong mlAnchorID = activeSubsystem.GetAnchorId(anchor);
//Get the anchor pose using the Anchor ID, for demonstrative purposes only
Pose anchorPose = activeSubsystem.GetAnchorPoseFromID(mlAnchorID);
//Get The anchor confidence
MLXrAnchorSubsystem.AnchorConfidence anchorConfidence = activeSubsystem.GetAnchorConfidence(anchor);
Debug.Log($"ML Anchor : {mlAnchorID} , Pose: {anchorPose} , Confidence: {anchorConfidence}");
}
}
}
}

Example

This example iterates through all of the anchors tracked by the anchor manager and logs their ML Anchor ID, Pose and Confidence. To use the script add it to a GameObject and assign the Anchor Manager property.

using System.Collections;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using UnityEngine.XR.Management;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;

public class SpatialAnchorsTest : MonoBehaviour
{
[SerializeField]
private ARAnchorManager anchorManager;
private MLXrAnchorSubsystem activeSubsystem;
private bool isSubsystemLoaded;

private IEnumerator Start()
{
yield return new WaitUntil(AreSubsystemsLoaded);
isSubsystemLoaded = true;
}

void Update()
{
if (isSubsystemLoaded)
{
foreach (ARAnchor anchor in anchorManager.trackables)
{
if (anchor.trackingState == TrackingState.Tracking && activeSubsystem != null)
{
ulong mlAnchorID = activeSubsystem.GetAnchorId(anchor);
//Get the anchor pose using the Anchor ID, for demonstrative purposes only
Pose anchorPose = activeSubsystem.GetAnchorPoseFromID(mlAnchorID);
//Get The anchor confidence
MLXrAnchorSubsystem.AnchorConfidence anchorConfidence = activeSubsystem.GetAnchorConfidence(anchor);
Debug.Log($"ML Anchor : {mlAnchorID} , Pose: {anchorPose} , Confidence: {anchorConfidence}");
}
}
}
}

private bool AreSubsystemsLoaded()
{
if (XRGeneralSettings.Instance == null) return false;
if (XRGeneralSettings.Instance.Manager == null) return false;
var activeLoader = XRGeneralSettings.Instance.Manager.activeLoader;
if (activeLoader == null) return false;
activeSubsystem = activeLoader.GetLoadedSubsystem<XRAnchorSubsystem>() as MLXrAnchorSubsystem;
return activeSubsystem != null;
}
}