// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) (2021-2022) 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% namespace UnityEngine.XR.MagicLeap { using System; using System.Runtime.InteropServices; using UnityEngine.XR.ARSubsystems; using UnityEngine.XR.MagicLeap.Native; public partial class MeshingSubsystem { public static partial class Extensions { public static partial class MLMeshing { /// /// State of a block mesh. /// public enum MeshState { /// /// Mesh has been created. /// New, /// /// Mesh has been updated. /// Updated, /// /// Mesh has been deleted. /// Deleted, /// /// Mesh is unchanged. /// Unchanged, } /// /// Level of detail of the block mesh. /// public enum LevelOfDetail { /// /// Minimum Level of Detail (LOD) for the mesh. /// Minimum, /// /// Medium Level of Detail (LOD) for the mesh. /// Medium, /// /// Maximum Level of Detail (LOD) for the mesh. /// Maximum } /// /// Step the Level of detail to Minimum, Medium and Maximum /// /// float input value /// public static LevelOfDetail DensityToLevelOfDetail( float density ) { if (density < 0.33f) return LevelOfDetail.Minimum; else if (density < 0.66f) return LevelOfDetail.Medium; else return LevelOfDetail.Maximum; } /// /// Convert a LevelOfDetail to a float, ranged between 0 and 1 /// /// Level of detail /// Float value between 0 and 1 public static float LevelOfDetailToDensity( LevelOfDetail lod ) { if (lod == LevelOfDetail.Minimum) return 0.0f; else if (lod == LevelOfDetail.Medium) return 0.5f; else return 1.0f; } public delegate MeshingSubsystem.Extensions.MLMeshing.MeshBlockRequest[] OnMeshBlockRequests(MeshingSubsystem.Extensions.MLMeshing.MeshBlockInfo[] blockInfos); /// /// Representation of a mesh block. /// public readonly struct MeshBlockInfo { internal MeshBlockInfo(MLMeshing.NativeBindings.MLMeshingBlockInfo nativeBlockInfo) { id = nativeBlockInfo.cfuid.ToString(); pose = new Pose(MLConvert.ToUnity(nativeBlockInfo.extents.center), MLConvert.ToUnity(nativeBlockInfo.extents.rotation)); extents = MLConvert.ToUnity(nativeBlockInfo.extents.extents); MLTime.ConvertSystemTimeToMLTime(nativeBlockInfo.timestamp, out timestamp); state = nativeBlockInfo.state; } /// /// The coordinate frame UID to represent the block. /// public readonly string id; /// /// The pose of the mesh block. /// public readonly Pose pose; /// /// The extents of the bounding box. /// public readonly Vector3 extents; /// /// The timestamp when block was updated. /// public readonly MLTime timestamp; /// /// The state of the mesh block. /// public readonly MeshState state; } public readonly struct MeshBlockRequest { internal MeshBlockRequest(MLMeshing.NativeBindings.MLMeshingBlockRequest nativeBlockRequest) { this.id = nativeBlockRequest.cfuid.ToString(); this.levelOfDetail = nativeBlockRequest.levelOfDetail; } public MeshBlockRequest(string id, LevelOfDetail levelOfDetail) { this.id = id; this.levelOfDetail = levelOfDetail; } /// /// The id to represent the block to be requested. /// public readonly string id; /// /// The LOD level to request. /// public readonly LevelOfDetail levelOfDetail; } internal static class NativeBindings { /// /// Axis aligned bounding box for querying updated mesh info. /// [StructLayout(LayoutKind.Sequential)] public struct MLMeshingExtents { /// /// The center of the bounding box. /// public MagicLeapNativeBindings.MLVec3f center; /// /// The rotation of the bounding box. /// public MagicLeapNativeBindings.MLQuaternionf rotation; /// /// The size of the bounding box. When a mesh request is complete all mesh blocks within 10 meters /// of the device that intersect with the extents provided here will be returned in MLMeshingMeshInfo. /// public MagicLeapNativeBindings.MLVec3f extents; } /// /// Representation of a mesh block. /// [StructLayout(LayoutKind.Sequential)] public struct MLMeshingBlockInfo { /// /// The coordinate frame UID to represent the block. /// public MagicLeapNativeBindings.MLCoordinateFrameUID cfuid; /// /// The extents of the bounding box. /// public MLMeshingExtents extents; /// /// The timestamp when block was updated. /// public long timestamp; /// /// The state of the Mesh Block. /// public MeshState state; } /// /// Response structure for the mesh block info. /// [StructLayout(LayoutKind.Sequential)] public struct MLMeshingMeshInfo { /// /// The response timestamp to a earlier request. /// public long timestamp; /// /// The number of responses in data pointer. /// public uint dataCount; /// /// The meshinfo returned by the system. /// public IntPtr data; } [StructLayout(LayoutKind.Sequential)] public readonly struct MLMeshingBlockRequest { internal MLMeshingBlockRequest(MLMeshing.MeshBlockRequest blockRequest) { this.cfuid = MagicLeapNativeBindings.MLCoordinateFrameUID.EmptyFrame; this.cfuid.FromString(blockRequest.id); this.levelOfDetail = blockRequest.levelOfDetail; } internal MLMeshingBlockRequest(MagicLeapNativeBindings.MLCoordinateFrameUID cfuid, MLMeshing.LevelOfDetail levelOfDetail) { this.cfuid = cfuid; this.levelOfDetail = levelOfDetail; } /// /// The UID to represent the block. /// public readonly MagicLeapNativeBindings.MLCoordinateFrameUID cfuid; /// /// The LOD level to request. /// public readonly MLMeshing.LevelOfDetail levelOfDetail; } [StructLayout(LayoutKind.Sequential)] public struct MLMeshingMeshRequest { /// /// Type selector for the structure. /// public int requestCount; /// /// Binary data size. /// public IntPtr blockRequests; } } public static partial class Config { public static void SetCustomMeshBlockRequests(OnMeshBlockRequests onBlockRequests) { customBlockRequests = onBlockRequests; if (customBlockRequests != null) { MagicLeapXrProviderNativeBindings.MeshingSetMeshRequestCallback(OnMeshRequest); MagicLeapXrProviderNativeBindings.MeshingSetFreeBlockRequestPointerCallback(OnFreeMeshRequestPointer); } else { MagicLeapXrProviderNativeBindings.MeshingSetMeshRequestCallback(null); MagicLeapXrProviderNativeBindings.MeshingSetFreeBlockRequestPointerCallback(null); } } public static IntPtr AcquireConfidence(MeshId meshId, out int count) => NativeBindings.MeshingAcquireConfidence(meshId, out count); public static void ReleaseConfidence(MeshId meshId) => NativeBindings.MeshingReleaseConfidence(meshId); public static void SetBounds(Transform transform, Vector3 extents) => SetBounds(transform.localPosition, transform.localRotation, extents); public static void SetBounds(Vector3 position, Quaternion rotation, Vector3 extents) => NativeBindings.MeshingSetBounds(position, rotation, extents); public static int batchSize { set { NativeBindings.MeshingSetBatchSize(value); } } public static float density { set { NativeBindings.MeshingSetDensity(value); } } public static Settings meshingSettings { set { SubsystemFeatures.SetCurrentFeatureEnabled(Feature.Meshing | Feature.PointCloud, false); if (value.flags.HasFlag(Flags.PointCloud)) { SubsystemFeatures.SetCurrentFeatureEnabled(Feature.PointCloud, true); } else { SubsystemFeatures.SetCurrentFeatureEnabled(Feature.Meshing, true); } NativeBindings.MeshingUpdateSettings(ref value); } } [Flags] public enum Flags { None = 0, PointCloud = 1 << 0, ComputeNormals = 1 << 1, ComputeConfidence = 1 << 2, Planarize = 1 << 3, RemoveMeshSkirt = 1 << 4, IndexOrderCW = 1 << 5 } private static IntPtr arrayPtr; private static OnMeshBlockRequests customBlockRequests; [AOT.MonoPInvokeCallback(typeof(MagicLeapXrProviderNativeBindings.CreateBlockRequestsDelegate))] private static void OnMeshRequest(ref MeshingSubsystem.Extensions.MLMeshing.NativeBindings.MLMeshingMeshInfo meshInfo, ref MeshingSubsystem.Extensions.MLMeshing.NativeBindings.MLMeshingMeshRequest meshRequest) { if (meshInfo.data == IntPtr.Zero) return; var blockInfos = new MeshingSubsystem.Extensions.MLMeshing.MeshBlockInfo[meshInfo.dataCount]; IntPtr walkPtr = meshInfo.data; for (int i = 0; i < meshInfo.dataCount; ++i) { var nativeBlockInfo = Marshal.PtrToStructure(walkPtr); blockInfos[i] = new MeshingSubsystem.Extensions.MLMeshing.MeshBlockInfo(nativeBlockInfo); walkPtr = new IntPtr(walkPtr.ToInt64() + Marshal.SizeOf()); } var blockRequests = customBlockRequests(blockInfos); var nativeBlockRequests = new MLMeshing.NativeBindings.MLMeshingBlockRequest[blockRequests.Length]; for (int i = 0; i < nativeBlockRequests.Length; ++i) { nativeBlockRequests[i] = new MLMeshing.NativeBindings.MLMeshingBlockRequest(blockRequests[i]); } meshRequest.requestCount = nativeBlockRequests.Length; meshRequest.blockRequests = MarshalArrayToPtr(nativeBlockRequests); } [AOT.MonoPInvokeCallback(typeof(MagicLeapXrProviderNativeBindings.CallFreeBlockRequestPointerDelegate))] private static void OnFreeMeshRequestPointer() { Marshal.FreeHGlobal(arrayPtr); } private static IntPtr MarshalArrayToPtr(T[] array) { arrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf() * array.Length); IntPtr walkPtr = arrayPtr; foreach (var element in array) { Marshal.StructureToPtr(element, walkPtr, true); walkPtr = new IntPtr(walkPtr.ToInt64()) + Marshal.SizeOf(); } return arrayPtr; } [StructLayout(LayoutKind.Sequential)] public struct Settings { public Flags flags; public float fillHoleLength; public float disconnectedComponentArea; } } } } } }