// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) (2018-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 Native; /// /// Spatial Anchor management is closely tied to the selected mapping mode on the device. The modes are /// mutually exclusive and affect the functionality of these APIs. /// The available mapping modes are: /// /// On-Device Mode - A persistent mode in which anchors are persisted locally and will be available /// in multiple sessions when localized to the same space in which they were published. /// /// AR Cloud Mode - A persistent mode in which anchors are persisted in the cloud environment and /// will be available in multiple sessions to devices that are localized to the same space in which /// they were published. /// /// public partial class MLAnchors { /// /// See ml_spatial_anchor.h for additional comments. /// public class NativeBindings : MagicLeapNativeBindings { /// /// A structure representing a user-defined Spatial Anchor. /// [StructLayout(LayoutKind.Sequential)] public readonly struct MLSpatialAnchor { /// /// Version of the structure. /// public readonly uint Version; /// /// The anchor's unique ID. This is a unique identifier for a single Spatial Anchor that is generated and managed by the /// Spatial Anchor system. The ID is created when MLSpatialAnchorCreate is called. /// public readonly MLUUIDBytes Id; /// /// The coordinate frame identifier of the Spatial Anchor. This should be passed to MLSnapshotGetTransform to get the /// anchor's transform. The anchor's transform is set when the anchor is created but may be updated later by the Spatial Anchor /// system based on factors such as quality of the mapped space and subsequent localizations. /// public readonly NativeBindings.MLCoordinateFrameUID Cfuid; /// /// The suggested expiration time for this anchor represented in seconds since the Unix epoch. This is implemented as an /// expiration timestamp in the future after which the associated anchor should be considered no longer valid and may be /// removed by the Spatial Anchor system based on factors such as quality of the mapped space and subsequent localizations. /// public readonly ulong ExpirationTimeStamp; /// /// Indicates whether or not the anchor has been persisted via a call to #MLSpatialAnchorPublish. /// public readonly bool IsPersisted; /// /// The ID of the space that this anchor belongs to. This is only relevant if IsPersisted is true. /// public readonly MLUUIDBytes SpaceId; /// /// The quality of the local space that this anchor occupies. This value may change over time. /// public readonly Quality Quality; public MLSpatialAnchor(MLAnchors.Anchor anchor) { this.Version = 2; this.Id = anchor.id; this.Cfuid = anchor.cfuid; this.ExpirationTimeStamp = anchor.ExpirationTimeStamp; this.IsPersisted = anchor.IsPersisted; this.SpaceId = anchor.spaceId; this.Quality = anchor.quality; } public MLSpatialAnchor(MLAnchors.Anchor anchor, ulong expirationTimeStamp) { this.Version = 2; this.Id = anchor.id; this.Cfuid = anchor.cfuid; this.ExpirationTimeStamp = expirationTimeStamp; this.IsPersisted = anchor.IsPersisted; this.SpaceId = anchor.spaceId; this.Quality = anchor.quality; } }; /// /// A structure used to populate anchor creation info when creating a new Spatial Anchor. /// [StructLayout(LayoutKind.Sequential)] public readonly struct MLSpatialAnchorCreateInfo { /// /// Version of the structure. /// public readonly uint Version; /// /// The desired transform of the new Spatial Anchor. The anchor's transform is set when the anchor is created but may be /// updated later by the Spatial Anchor system based on factors such as quality of the mapped space and subsequent localizations. /// public readonly NativeBindings.MLTransform Transform; /// /// The suggested expiration time for this anchor represented in seconds since the Unix epoch. This is implemented as an /// expiration timestamp in the future after which the associated anchor should be considered no longer valid and may be /// removed by the Spatial Anchor system. /// If the timestamp is set to zero in this struct, it will default to one year from when the anchor is created. /// public readonly ulong ExpirationTimestamp; public MLSpatialAnchorCreateInfo(Pose Pose, ulong ExpirationTimestamp) { this.Version = 1; this.Transform = new NativeBindings.MLTransform(); this.Transform.Position = MLConvert.FromUnity(Pose.position); this.Transform.Rotation = MLConvert.FromUnity(Pose.rotation); this.ExpirationTimestamp = ExpirationTimestamp; } }; /// /// A collection of filters for Spatial Anchor queries. Filters that have been set will be combined via logical /// conjunction. E. g. results must match the ids filter AND fall within the radius constraint when both have been set. This struct /// must be initialized by calling #MLSpatialAnchorQueryFilterInit before use. /// [StructLayout(LayoutKind.Sequential)] public readonly struct MLSpatialAnchorQueryFilter { /// /// Version of the structure. /// public readonly uint Version; /// /// The center point of where a spatial query will originate. /// public readonly MLVec3f Center; /// /// The radius in meters used for a spatial query, relative to the specified center. Only anchors inside this radius will /// be returned. Set to 0 for unbounded results. /// public readonly float RadiusM; /// /// A list of Spatial Anchor IDs to query for. /// public readonly IntPtr Ids; /// /// The number of IDs provided. /// public readonly uint IdsCount; /// /// The upper bound of expected results. Set to 0 for unbounded results. /// public readonly uint MaxResults; /// /// Indicate whether the results will be returned sorted by distance from center. Sorting results by distance will incur a /// performance penalty. /// [MarshalAs(UnmanagedType.I1)] public readonly bool Sorted; // Used by the anchor subsystem. internal MLSpatialAnchorQueryFilter(Vector3 Center, float RadiusM, IntPtr Ids, uint IdsCount, uint MaxResults, bool Sorted) { this.Version = 1; this.Center = MLConvert.FromUnity(Center); this.RadiusM = RadiusM; this.Ids = Ids; this.IdsCount = IdsCount; this.MaxResults = MaxResults; this.Sorted = Sorted; } internal MLSpatialAnchorQueryFilter(Request.Params requestParams) { this.Version = 1; this.Center = MLConvert.FromUnity(requestParams.Center); this.RadiusM = requestParams.Radius; this.Ids = IntPtr.Zero; this.IdsCount = 0; if (requestParams.Anchors != null) MarshalArrayToPtr(requestParams.Anchors, out this.Ids, out this.IdsCount); else if (requestParams.AnchorIds != null) MarshalArrayToPtr(requestParams.AnchorIds, out this.Ids, out this.IdsCount); this.MaxResults = requestParams.MaxResults; this.Sorted = requestParams.Sorted; } internal static void MarshalArrayToPtr(T[] array, out IntPtr ids, out uint idsCount) { IntPtr arrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf() * array.Length); IntPtr walkPtr = arrayPtr; foreach (var element in array) { if (element is Anchor) Marshal.StructureToPtr((element as Anchor?)?.id, walkPtr, false); else if (element is string) Marshal.StructureToPtr(new MLUUIDBytes(element as string), walkPtr, false); walkPtr = new IntPtr(walkPtr.ToInt64()) + Marshal.SizeOf(); } ids = arrayPtr; idsCount = (uint)array.Length; } } /// /// A structure containing information about the device's localization state. /// [StructLayout(LayoutKind.Sequential)] public readonly struct MLSpatialAnchorLocalizationInfo { /// /// Version of the structure. /// public readonly uint Version; /// /// The localization status at the time this structure was returned. /// public readonly LocalizationStatus LocalizationStatus; /// /// The current mapping mode. /// public readonly MappingMode MappingMode; /// /// If localized, this will contain the name of the current space. /// [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)MaxSpaceNameLength)] public readonly string SpaceName; /// /// If localized, this will contain the unique ID of the current space. /// public readonly NativeBindings.MLUUIDBytes SpaceId; /// /// If localized, this will contain the identifier of the transform of /// the target space's origin relative to the world origin. /// public readonly MagicLeapNativeBindings.MLCoordinateFrameUID TargetSpaceOrigin; public static MLSpatialAnchorLocalizationInfo Create() => new MLSpatialAnchorLocalizationInfo(2); public MLSpatialAnchorLocalizationInfo(uint version) { this.Version = version; this.LocalizationStatus = default; this.MappingMode = default; this.SpaceName = default; this.SpaceId = default; this.TargetSpaceOrigin = default; } }; /// /// Create a new local Spatial Anchor at the desired location. On success, out_anchor will be returned with the desired /// transform and a newly generated ID. /// Any unpublished anchor will be lost if the Headpose session is lost. See #MLHeadTrackingGetMapEvents for more details. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorCreate(ulong handle, in MLSpatialAnchorCreateInfo createInfo, out MLSpatialAnchor anchor); /// /// Create a Spatial Anchor tracker. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorTrackerCreate(out ulong handle); /// /// Destroy a previously created Spatial Anchor tracker. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorTrackerDestroy(ulong handle); /// /// Publish an existing local Spatial Anchor to the persistent backend. Depending on the currently selected mapping mode, /// this can store the anchor locally or in the cloud. This call will fail if the device is not localized to a space. /// This call will fail if the device is not localized to a space. /// Any unpublished anchor will be lost if the Headpose session is lost.See #MLHeadTrackingGetMapEvents for more details /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorPublish(ulong handle, MLUUIDBytes anchorId); /// /// Delete an existing Spatial Anchor. If successful, this will delete the anchor from persistent storage based on the /// currently selected mapping mode. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorDelete(ulong handle, MLUUIDBytes anchorId); /// /// Update a Spatial Anchor's properties. The only property that can currently be updated is the expirationTimeStamp. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorUpdate(ulong handle, in MLSpatialAnchor anchor); /// /// Create a new query for Spatial Anchors in the current space. It is the responsibility of the caller to call /// #MLSpatialAnchorQueryDestroy with the query handle returned from this function after the results are no longer needed. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorQueryCreate(ulong handle, in MLSpatialAnchorQueryFilter queryFilter, out ulong queryHandle, out uint resultsCount); /// /// Destroy a previously created query handle and release its associated resources. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorQueryDestroy(ulong handle, ulong queryHandle); /// /// Get the result of a previous Spatial Anchor query. Putting index bounds on the results allows the caller to only /// receive a subset of the total number of results generated by the query. This is useful as a form of pagination in the case of /// a large number of anchors in the current space. Indexing is zero-based so if there are N results in the query, then it /// is required that 0 <= first_index <= last_index < N. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorQueryGetResult(ulong handle, ulong queryHandle, uint firstIndex, uint lastIndex, [In, Out] MLSpatialAnchor[] results); /// /// Get the current localization status of the device. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSpatialAnchorGetLocalizationInfo(ulong handle, ref MLSpatialAnchorLocalizationInfo localizationInfo); } } }