Skip to main content
Version: 20 Mar 2024

Querying Planes

Note: This example uses the OpenXR version of the Magic Leap 2 Unity SDK. For a previous version using the Unity MLSDK, see here.

Permissions

In order to get access to plane finding on the Magic Leap 2, your project must have SPATIAL_MAPPING enabled in project settings in addition to manually requesting permissions at runtime as demonstrated in the script below.

Scene Structure

The core necessary components to get access to planes in your scene are:

  • Scene configured for OpenXR Magic Leap
  • Enabled Magic Leap 2 Plane Detection under Project Settings > XR Plugin Management > OpenXR > Android > Magic Leap
  • SPATIAL_MAPPING permission enabled
  • MLRig with ARPlaneManager script attached
  • Plane Prefab for ARPlaneManager
  • Script making a MLXrPlaneSubsystem query

Example Script

The following script can be found in the Planes example scene in the Unity Examples package available on Magic Leap Hub.

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

public class PlaneExample : MonoBehaviour
{
private ARPlaneManager _planeManager;

[SerializeField, Tooltip("Maximum number of planes to return each query")]
private uint maxResults = 100;

[SerializeField, Tooltip("Minimum plane area to treat as a valid plane")]
private float minPlaneArea = 0.25f;

private readonly MLPermissions.Callbacks _permissionCallbacks = new MLPermissions.Callbacks();
private Camera _camera;
private bool permissionGranted = false;

private void Awake()
{
_permissionCallbacks.OnPermissionGranted += OnPermissionGranted;
_permissionCallbacks.OnPermissionDenied += OnPermissionDenied;
_permissionCallbacks.OnPermissionDeniedAndDontAskAgain += OnPermissionDenied;
}

private void OnDestroy()
{
_permissionCallbacks.OnPermissionGranted -= OnPermissionGranted;
_permissionCallbacks.OnPermissionDenied -= OnPermissionDenied;
_permissionCallbacks.OnPermissionDeniedAndDontAskAgain -= OnPermissionDenied;
}

private IEnumerator Start()
{
_camera = Camera.main;
yield return new WaitUntil(AreSubsystemsLoaded);
_planeManager = FindObjectOfType<ARPlaneManager>();
if (_planeManager == null)
{
Debug.LogError("Failed to find ARPlaneManager in scene. Disabling Script");
enabled = false;
}
else
{
// disable planeManager until we have successfully requested required permissions
_planeManager.enabled = false;
}

permissionGranted = false;
MLPermissions.RequestPermission(MLPermission.SpatialMapping, _permissionCallbacks);
}

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;
return activeLoader.GetLoadedSubsystem<XRPlaneSubsystem>() != null;
}

private void Update()
{
UpdateQuery();
}

private void UpdateQuery()
{
if (_planeManager != null && _planeManager.enabled && permissionGranted)
{
MLXrPlaneSubsystem.Query = new MLXrPlaneSubsystem.PlanesQuery
{
Flags = _planeManager.requestedDetectionMode.ToMLXrQueryFlags() | MLXrPlaneSubsystem.MLPlanesQueryFlags.SemanticAll,
BoundsCenter = _camera.transform.position,
BoundsRotation = _camera.transform.rotation,
BoundsExtents = Vector3.one * 20f,
MaxResults = maxResults,
MinPlaneArea = minPlaneArea
};
}
}

private void OnPermissionGranted(string permission)
{
_planeManager.enabled = true;
permissionGranted = true;
}

private void OnPermissionDenied(string permission)
{
Debug.LogError($"Failed to create Planes Subsystem due to missing or denied {MLPermission.SpatialMapping} permission. Please add to manifest. Disabling script.");
enabled = false;
}

}

Components

ARPlaneManagerPart of the Unity ARFoundation package, this is a manager for ARPlanes which creates, updates, and removes GameObjects in response to detected surfaces in the physical environment. The AR Plane Manager script is attached to the ML Rig component in the example scene.
MLXrPlaneSubsystemThe Magic Leap implementation of the XRPlaneSubsystem. Do not create this directly. Use PlanesSubsystemDescriptor.Create() instead.
MLPermissions.CallbacksAllows developers to create specific callback functions which execute when the user is requested, accepts or denies permissions.

MLXrPlaneSubsystem Query Parameters

FlagsThe flags to apply to this query. In the script above, this is set to SemanticAll, which includes SemanticCeiling, SemanticFloor, SemanticWall, SemanticPlatform
BoundsCenterThe center of the bounding box which defines where planes extraction should occur.
BoundsRotationThe rotation of the bounding box where planes extraction will occur.
BoundsExtentsThe size of the bounding box where planes extraction will occur.
MaxResultsThe maximum number of results that should be returned.
MinPlaneAreaThe minimum area (in squared meters) of planes to be returned. This value cannot be lower than 0.04 (lower values will be capped to this minimum).

You can use the PlaneQuery example in conjunction with the Plane Classification example script added to the plane prefab in order to visually display differently classified planes in your space.

Resources