// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
// Copyright (c) (2024) Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2
// Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein.
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.XR.ARSubsystems;
using UnityEngine.XR.MagicLeap;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.XR.OpenXR.Features;
#endif
namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport
{
using NativeInterop;
[System.Obsolete("Type has been relocated to new namespace. Update reference to MagicLeap.OpenXR.Features.Meshing")]
public partial class MagicLeapMeshingFeature : MagicLeapOpenXRFeatureBase
{
public const string FeatureId = "com.magicleap.openxr.feature.ml2_mesh_detection";
private const string MeshingExtensionName = "XR_ML_world_mesh_detection XR_EXT_future";
private MeshingMode currentMode = MeshingMode.Triangles;
private Vector3 meshBoundsOrigin;
private Vector3 meshBoundsScale;
private Quaternion meshBoundsRotation;
private FeatureLifecycleNativeListener meshingLifecycleListener;
///
/// The origin of the meshing volume bounds
///
public Vector3 MeshBoundsOrigin
{
get => meshBoundsOrigin;
set
{
meshBoundsOrigin = value;
MagicLeapXrMeshingNativeBindings.MLOpenXRSetMeshBoundsOrigin(value);
}
}
///
/// The scale of the meshing bounds volume
///
public Vector3 MeshBoundsScale
{
get => meshBoundsScale;
set
{
meshBoundsScale = value;
MagicLeapXrMeshingNativeBindings.MLOpenXRSetMeshBoundsScale(value);
}
}
///
/// The rotation of the meshing bounds volume
///
public Quaternion MeshBoundsRotation
{
get => meshBoundsRotation;
set
{
meshBoundsRotation = value;
MagicLeapXrMeshingNativeBindings.MLOpenXRSetMeshBoundsOrientation(value);
}
}
///
/// The render mode of the generated mesh.
///
public MeshingMode MeshRenderMode
{
get => currentMode;
set => SetRenderMode(value);
}
///
/// The density of the meshes generated
///
public float MeshDensity
{
set => MagicLeapXrMeshingNativeBindings.MLOpenXRSetMeshDensity(value);
}
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
MagicLeapXrMeshingNativeBindings.MLOpenXRGetMeshingFeatureListener(ref meshingLifecycleListener);
return base.HookGetInstanceProcAddr(func);
}
protected override bool OnInstanceCreate(ulong xrInstance)
{
var extensions = MeshingExtensionName.Split(' ');
foreach (var extension in extensions)
{
if (OpenXRRuntime.IsExtensionEnabled(extension))
{
continue;
}
Debug.LogError($"{extension} is not enabled. Disabling {nameof(MagicLeapMeshingFeature)}");
return false;
}
var instanceCreationResult = base.OnInstanceCreate(xrInstance);
if (!instanceCreationResult)
{
return false;
}
meshingLifecycleListener.InstanceCreated(xrInstance, xrGetInstanceProcAddr);
return true;
}
protected override void OnSessionCreate(ulong xrSession)
{
base.OnSessionCreate(xrSession);
meshingLifecycleListener.SessionCreated(xrSession);
}
protected override void OnAppSpaceChange(ulong xrSpace)
{
base.OnAppSpaceChange(xrSpace);
meshingLifecycleListener.AppSpaceChanged(xrSpace);
}
protected override void OnInstanceDestroy(ulong xrInstance)
{
base.OnInstanceDestroy(xrInstance);
meshingLifecycleListener.InstanceDestroyed(xrInstance);
}
protected override void OnSubsystemCreate()
{
base.OnSubsystemCreate();
CreateSubsystem(new List(), MagicLeapXrProvider.MeshingSubsystemId);
CreateSubsystem(new List(), MagicLeapXrProvider.PointCloudSubsystemId);
}
protected override void OnSubsystemStart()
{
base.OnSubsystemStart();
StartSubsystem();
}
protected override void OnSubsystemStop()
{
base.OnSubsystemStop();
StopSubsystem();
StopSubsystem();
}
protected override void OnSubsystemDestroy()
{
base.OnSubsystemDestroy();
DestroySubsystem();
DestroySubsystem();
}
internal void StartSubsystemForMLMeshing()
{
StartSubsystem();
}
internal void StopSubsystemForMLMeshing()
{
StopSubsystem();
}
///
/// Update the query settings for the mesh generation
///
///
public void UpdateMeshQuerySettings(in MeshingQuerySettings settings)
{
MagicLeapXrMeshingNativeBindings.MLOpenXRMeshingUpdateSettings(in settings);
}
private void SetRenderMode(MeshingMode mode)
{
if (mode == currentMode)
{
return;
}
currentMode = mode;
MagicLeapXrMeshingNativeBindings.MLOpenXRSetMeshRenderMode(currentMode);
StopSubsystem();
StopSubsystem();
if (currentMode == MeshingMode.Triangles)
{
StartSubsystem();
}
if (currentMode == MeshingMode.PointCloud)
{
StartSubsystem();
}
}
public void SetMeshQueryBounds(Vector3 position, Vector3 scale, Quaternion rotation)
{
meshBoundsOrigin = position;
meshBoundsScale = scale;
meshBoundsRotation = rotation;
MagicLeapXrMeshingNativeBindings.MLOpenXRSetMeshQueryBounds(in position,in rotation,in scale);
}
public void GetMeshIds(out TrackableId[] trackableIds)
{
trackableIds = Array.Empty();
unsafe
{
var buffer = (TrackableId*)IntPtr.Zero;
MagicLeapXrMeshingNativeBindings.MLOpenXRAcquireMeshIds(ref buffer, out var trackableCount);
if (trackableCount == 0)
{
return;
}
trackableIds = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(buffer, trackableCount, Allocator.None).ToArray();
}
}
public bool GetMeshData(in TrackableId meshId, out Vector3[] positions, out Vector3[] normals, out float[] confidence)
{
positions = Array.Empty();
normals = Array.Empty();
confidence = Array.Empty();
unsafe
{
var positionsBuffer = (Vector3*)IntPtr.Zero;
var normalBuffer = (Vector3*)IntPtr.Zero;
var confidenceBuffer = (float*)IntPtr.Zero;
var result = MagicLeapXrMeshingNativeBindings.MLOpenXRAcquireMeshData(in meshId, ref positionsBuffer, out var positionCount, ref normalBuffer, out var normalCount, ref confidenceBuffer, out var confidenceCount);
if (!result)
{
return false;
}
positions = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(positionsBuffer, positionCount, Allocator.None).ToArray();
normals = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(normalBuffer, normalCount, Allocator.None).ToArray();
confidence = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(confidenceBuffer, confidenceCount, Allocator.None).ToArray();
return true;
}
}
public void InvalidateMeshes()
{
MagicLeapXrMeshingNativeBindings.MLOpenXRInvalidateMeshes();
}
}
}