Skip to main content
Version: 20 Mar 2024

Simple Meshing Setup

In this tutorial, you'll learn how to set up the Spatial Mapping component with Magic Leap 2 in Unity. By the end of this guide, you'll have a scene capable of meshing the world around you, providing a foundation for immersive mixed reality experiences. The Spatial Mapping in Magic Leap 2 is similar to AR Foundation's Spatial Mapping component.

Prerequisites

Before you begin, ensure you have completed the following steps:

  1. Create a new Unity project
  2. Configure Unity project settings
  3. Create a new Magic Leap 2 scene

Part 1: Requesting Spatial Mapping Permissions

To access the Spatial Mapping feature, developers will need to enable and request the necessary SPATIAL_MAPPING permission.

  1. Add Spatial Mapping Permission to Manifest:

    1. Go to Edit > Project settings > Magic Leap > Permissions and ensure SPATIAL_MAPPING is enabled.
  2. Create a Permission Requester:

    1. Create a new empty GameObject named Mapping Permissions.
    2. Create a new script (Assets > Create > C# Script) named SpatialMappingPermissionRequester.cs and attach it to the Mapping Permissions.
    3. Copy the script below into the SpatialMappingPermissionRequester.cs. The script will request the required permissions at runtime, which is required because SPATIAL_MAPPING is a dangerous permission.
note

This script will request spatial mapping permission and enable or disable the meshing subsystem based on whether the user has allowed or denied the permission. You can learn more about requesting permissions in the Unity Requesting Permissions Guide.

using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.MagicLeap;

// Requests the Spatial_Mapping permission and enables/disables the MeshingSubsystemComponent depending on the user's preference.
public class SpatialMappingPermissionRequester: MonoBehaviour
{
// Serialized field to hold a reference to the Meshing Subsystem Component
private MeshingSubsystemComponent meshingSubsystemComponent;
// Instantiate a callback object to listen for permission events from Magic Leap
private readonly MLPermissions.Callbacks mlPermissionsCallbacks = new MLPermissions.Callbacks();

private void Awake()
{
// Subscribe to permission granted and denied events from Magic Leap
// These subscriptions will invoke methods when permissions are granted or denied
mlPermissionsCallbacks.OnPermissionGranted += MlPermissionsCallbacks_OnPermissionGranted;
mlPermissionsCallbacks.OnPermissionDenied += MlPermissionsCallbacks_OnPermissionDenied;
mlPermissionsCallbacks.OnPermissionDeniedAndDontAskAgain += MlPermissionsCallbacks_OnPermissionDenied;

meshingSubsystemComponent = FindObjectOfType<MeshingSubsystemComponent>(true);
if(meshingSubsystemComponent)
{
// Disable the Meshing Subsystem until permissions are granted
meshingSubsystemComponent.enabled = false;
}
}

void Start()
{
// Request Spatial Mapping permission from the user at the start of the scene
// The mlPermissionsCallbacks object will handle the user's response
MLPermissions.RequestPermission(MLPermission.SpatialMapping, mlPermissionsCallbacks);
}

// Method to handle the event when permission is denied
// It disables the meshing subsystem to prevent access to spatial mapping features
private void MlPermissionsCallbacks_OnPermissionDenied(string permission)
{
if(meshingSubsystemComponent)
{
meshingSubsystemComponent.enabled = false;
}
}

// Method to handle the event when permission is granted
// It enables the meshing subsystem to allow access to spatial mapping features
private void MlPermissionsCallbacks_OnPermissionGranted(string permission)
{
if(meshingSubsystemComponent)
{
meshingSubsystemComponent.enabled = true;
}
}
}

Part 2: Setting Up the Meshing Subsystem Component

In this section, we'll establish the core meshing functionality by setting up the Meshing Subsystem. This component controls how the meshes are generated, updated, and displayed.

  1. Create Meshing Subsystem GameObject:

    1. Create a new empty GameObject and name it Meshing Subsystem.
    2. Set its scale to (10,10,10), this will define the meshing area to 10x10x10 meters.
  2. Add Meshing Subsystem Component:

    1. With Meshing Subsystem selected in the hierarchy, click Add Component in the Inspector and add MeshingSubsystemComponent.
  3. Create the Mesh Parent.

    1. Create an empty GameObject named Mesh Parent in the hierarchy and set its position to (0,0,0). This object will hold all the meshes that get generated at runtime.
    2. Drag and drop the Mesh Parent object into the MeshParent property on the Meshing Subsystem Component. This object will hold all the meshes that get generated at runtime.

Part 3: Creating a Rendered Mesh Prefab

Now, we will create a prefab named Rendered Mesh that will serve as a template for the mesh data generated by the Meshing Subsystem.

  1. Create Rendered Mesh GameObject:

    1. Create a new empty GameObject named Rendered Mesh and set the position to (0,0,0).
  2. Configure Rendered Mesh Components:

    1. Add a Mesh Filter (Add Component > Mesh Filter) and a Mesh Renderer (Add Component > Mesh Renderer) to the Rendered Mesh.
    2. Create a new transparent unlit URP material named MeshMat and assign it to the object's Mesh Renderer Material slot.
    3. Add a Mesh Collider (Add Component > Mesh Collider) to Rendered Mesh prefab and leave the Mesh set to None (The mesh will be generated at runtime by the meshing subsystem component).
  3. Create Rendered Mesh Prefab:

    1. Drag the Rendered Mesh GameObject into your Assets folder to create a prefab.
    2. Delete the Rendered Mesh GameObject from the scene Hierarchy.
  4. Target Rendered Mesh inside Meshing Subsystem Component:

    1. Select the Meshing Subsystem in the Hierarchy, and inside the Meshing Subsystem Component, drag the Rendered Mesh prefab from the Assets folder into the MeshPrefab property.
  5. Testing in App Simulator

    1. Test the scene using Application Simulator in simulator mode. You should be able to see generated meshes from the room inside Application Simulator appear inside the Unity Game View.
    2. For the application to work on device you must request and grant the Spatial Mapping Permissions which we will do in the next section.
Mesh overlay displayed in Application Simulator.
Mesh from Application Simulator displayed in Unity at runtime.

Part 4: Handling Head Pose Loss and Movement

In this section, we will create a script to manage head pose loss and update the position of the Meshing Subsystem based on the user's head position.

  1. Create Meshing Controller Script:
    1. Create a new script named MeshingController.cs and attach it to a suitable GameObject in your scene (e.g., Meshing Subsystem).
    2. Copy the provided code into your MeshingController.cs script. This script will handle head pose loss and set the position of the Meshing Subsystem Component to the position of the headset to ensure the environment closest to the user generates a mesh.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.MagicLeap;
using UnityEngine.XR.Management;

public class MeshingController : MonoBehaviour
{
private XRInputSubsystem inputSubsystem;
private MeshingSubsystemComponent meshingSubsystemComponent;

// Start is called before the first frame update
private void Start()
{
meshingSubsystemComponent = FindObjectOfType<MeshingSubsystemComponent>(true);
var xrMgrSettings = XRGeneralSettings.Instance.Manager;
if (xrMgrSettings != null)
{
var loader = xrMgrSettings.activeLoader;
if (loader != null)
{
//Calls event if tracking origin is changed
inputSubsystem = loader.GetLoadedSubsystem<XRInputSubsystem>();
inputSubsystem.trackingOriginUpdated += OnTrackingOriginChanged;
}
}
}

private void Update()
{
_meshingSubsystemComponent.gameObject.transform.position = Camera.main.transform.position;
}

private void OnTrackingOriginChanged(XRInputSubsystem inputSubsystem)
{
//Destroy existing meshes under the mesh parent prefab
_meshingSubsystemComponent.DestroyAllMeshes();
//Force update all mesh prefabs
_meshingSubsystemComponent.RefreshAllMeshes();
}
}

Part 5: Testing Your Setup

Having configured the Meshing Subsystem, it's time to test your setup and see the meshing in action.

  1. Save your scene and make sure it is present in the Build Settings
  2. Test the project using the Magic Leap App Simulator or Build and Run on the headset to observe the meshing functionality in action.

Other Configurations

The following table explains the function of each of the public attributes of the Meshing Subsystem Component script:

Mesh PrefabGet or set the prefab which should be instantiated to create individual mesh instances. May have a mesh renderer and an optional mesh collider for physics
Compute normalsWhen enabled, the system will compute the normals for the triangle vertices
DensityDetermines the level of detail that the batched mesh blocks should be. This property is not used if custom mesh block requests are created via the SetCustomMeshBlockRequests() method.
Batch sizeDetermines how many mesh blocks should be requested by the meshing subsystem at once. This property is not used if custom mesh block requests are created via the SetCustomMeshBlockRequests() method. How many meshes to update per batch. Larger values are more efficient, but have higher latency.
Mesh ParentThe parent transform for generated meshes.
Requested mesh typeThe current mesh type being surfaced by the subsystem provider.
Fill Hole lengthBoundary distance (in meters) of holes you wish to have filled
PlanarizeWhen enabled, the system will planarize the returned mesh (planar regions will be smoothed out).
Disconnected component areaAny component that is disconnected from the main mesh and which has an area less than this size will be removed.
Mesh queue sizeControls the number of meshes to queue for generation at once. Larger numbers will lead to higher CPU usage.
Polling rateHow often to check for updates, in seconds. More frequent updates will increase CPU usage.
Request vertex confidenceWhen enabled, the system will generate confidence values for each vertex, ranging from 0-1.
Remove mesh skirtWhen enabled, the mesh skirt (overlapping area between two mesh blocks) will be removed. This field is only valid when the Mesh Type is Blocks.
Object Pool sizeAmount of objects that can be viewed at onc.
Object Pool growth rateHow quickly the object pool gets generated.

Further Resources