// %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.Native { using System; using System.Runtime.InteropServices; using System.Text; /// /// Defines C# API interface to C-API layer. /// public partial class MagicLeapNativeBindings { /// /// The 64 bit id for an invalid native handle. /// public const ulong InvalidHandle = 0xFFFFFFFFFFFFFFFF; /// /// Initializes a new instance of the class. /// protected MagicLeapNativeBindings() { } /// /// The current state of a given tracker. /// public enum MLSensoryState { /// /// The tracker is not ready, don't use the data. /// Initializing, /// /// The tracker's data can be used. /// Ready } /// /// Checks if 64 bit handle is valid. /// /// true, if handle is valid, false if invalid. /// The handle to check. public static bool MLHandleIsValid(ulong handle) { return handle != InvalidHandle; } /// /// Returns an ASCII string for MLResultGlobal codes. /// /// The input MLResult enum from ML API methods. /// An ASCII string containing readable version of result code. public static string MLGetResultString(MLResult.Code result) { switch (result) { case MLResult.Code.Ok: { return "MLResult_Ok"; } case MLResult.Code.Pending: { return "MLResult_Pending"; } case MLResult.Code.Timeout: { return "MLResult_Timeout"; } case MLResult.Code.Locked: { return "MLResult_Locked"; } case MLResult.Code.UnspecifiedFailure: { return "MLResult_UnspecifiedFailure"; } case MLResult.Code.InvalidParam: { return "MLResult_InvalidParam"; } case MLResult.Code.AllocFailed: { return "MLResult_AllocFailed"; } case MLResult.Code.PermissionDenied: { return "MLResult_PermissionDenied"; } case MLResult.Code.NotImplemented: { return "MLResult_NotImplemented"; } case MLResult.Code.ClientLimitExceeded: { return "MLResult_ClientLimitExceeded"; } case MLResult.Code.PoseNotFound: { return "MLResult_PoseNotFound"; } case MLResult.Code.APIDLLNotFound: { return "MLResult_APIDLLNotFound"; } case MLResult.Code.APISymbolsNotFound: { return "MLResult_APISymbolsNotFound"; } case MLResult.Code.IncompatibleSKU: { return "MLResult_IncompatibleSKU"; } case MLResult.Code.PerceptionSystemNotStarted: { return "MLResult_PerceptionSystemNotStarted"; } case MLResult.Code.LicenseError: { return "MLResult_LicenseError"; } default: { return "MLResult_Unknown"; } } } /// /// Returns an ASCII string for MLSnapshotResult codes. /// /// The input MLResult enum from ML API methods. /// An ASCII string containing readable version of result code. internal static string MLGetSnapshotResultString(MLResult.Code resultCode) { try { return Marshal.PtrToStringAnsi(MagicLeapNativeBindings.MLSnapshotGetResultString(resultCode)); } catch (System.DllNotFoundException) { MLPluginLog.Error("MagicLeapNativeBindings.MLGetSnapshotResultString failed. Reason: MagicLeapNativeBindings is currently available only on device."); } catch (System.EntryPointNotFoundException) { MLPluginLog.Error("MagicLeapNativeBindings.MLGetSnapshotResultString failed. Reason: API symbols not found"); } return string.Empty; } /// /// Returns an ASCII string for MLSnapshotResult codes. /// /// The input MLResult enum from ML API methods. /// An ASCII string containing readable version of result code. internal static string MLGetInputResultString(MLResult.Code resultCode) { try { return Marshal.PtrToStringAnsi(MagicLeapNativeBindings.MLInputGetResultString(resultCode)); } catch (System.DllNotFoundException) { MLPluginLog.Error("MagicLeapNativeBindings.MLGetInputResultString failed. Reason: MagicLeapNativeBindings is currently available only on device."); } catch (System.EntryPointNotFoundException) { MLPluginLog.Error("MagicLeapNativeBindings.MLGetInputResultString failed. Reason: API symbols not found"); } return string.Empty; } /// /// Pull in the latest state of all persistent transforms and all /// enabled trackers extrapolated to the next frame time. /// Returns an MLSnapshot with this latest state. This snap should be /// used for the duration of the frame being constructed and then /// released with a call to MLPerceptionReleaseSnapshot(). /// /// Pointer to a pointer containing an MLSnapshot on success. /// /// MLResult.Result will be MLResult.Code.Ok if operation was successful. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to internal error. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLPerceptionGetSnapshot(ref IntPtr snapshot); /// /// Pulls in the state of all persistent transforms and all /// enabled trackers extrapolated to the provided timestamp. /// This timestamp typically comes from out_frame_info.predicted_display_time out parameter from /// the MLGraphicsBeginFrameEx function. /// Returns a MLSnapshot with this latest state. This snap should be /// used for the duration of the frame being constructed and then /// released with a call to MLPerceptionReleaseSnapshot(). /// /// Timestamp representing the time for which to predict poses. /// Pointer to a pointer containing an MLSnapshot on success. /// /// MLResult.Result will be MLResult.Code.Ok if operation was successful. /// MLResult.Result will be MLResult.Code.InvalidTimestamp if Timestamp is either more than 100ms in the future or too old for cached state. /// MLResult.Result will be MLResult.Code.InvalidParam if Output parameter was not valid (null). /// MLResult.Result will be MLResult.Code.PerceptionSystemNotStartede if Perception System has not been started. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLPerceptionGetPredictedSnapshot(ulong timestamp, ref IntPtr out_snapshot); /// /// Pull in the latest state of all persistent transforms and all /// enabled trackers extrapolated to the next frame time. /// Return an MLSnapshot with this latest state. This snap should be /// used for the duration of the frame being constructed and then /// released with a call to MLPerceptionReleaseSnapshot(). /// /// Pointer to a pointer containing an MLSnapshot on success. /// /// MLResult.Result will be MLResult.Code.Ok if a Snapshot was created successfully successful. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if a Snapshot was not created successfully. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLPerceptionReleaseSnapshot(IntPtr snap); /// /// Gets the transform between world origin and the coordinate frame `id'. /// /// A snapshot of the tracker state. Can be obtained with MLPerceptionGetSnapshot(). /// Look up the transform between the current origin and this coordinate frame id. /// Valid pointer to an MLTransform. To be filled out with requested transform data. /// /// MLResult.Result will be MLResult.Code.Ok if the transform was obtained successfully. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to internal error. /// MLResult.Result will be MLResult.Code.InvalidParam if failed due to an invalid parameter. /// MLResult.Result will be MLResult.Code.PoseNotFound if the coordinate frame is valid, but not found in the current pose snapshot. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSnapshotGetTransform(IntPtr snap, ref MLCoordinateFrameUID id, ref MLTransform outTransform); /// /// Get the static data pertaining to the snapshot system. Requires API level 30. /// /// Valid pointer to an MLSnapshotStaticData. To be filled out with snapshot static data. /// /// MLResult.Result will be MLResult.Code.InvalidParam if failed to obtain static data due to invalid parameter. /// MLResult.Result will be MLResult.Code.Ok if obtained static data successfully. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed to obtain static data due to internal error. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSnapshotGetStaticData(ref MLSnapshotStaticData outStaticData); /// /// Get transform between coordinate frame 'base_id' and the coordinate frame `id' as well as any derivatives /// that have been calculated. /// /// A snapshot of tracker state. Can be obtained with MLPerceptionGetSnapshot(). /// The coordinate frame in which to locate 'id'. /// The coordinate frame which needs to be located in the base_id coordinate frame. /// Valid pointer to an MLPose. To be filled out with requested pose data. /// /// MLResult.Result will be MLResult.Code.InvalidParam if failed to obtain transform due to invalid parameter. /// MLResult.Result will be MLResult.Code.Ok if obtained transform successfully. /// MLResult.Result will be MLResult.Code.PoseNotFoundk if coordinate Frame is valid, but not found in the current pose snapshot. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed to obtain transform due to internal error. /// [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLSnapshotGetPoseInBase(IntPtr snap, ref MLCoordinateFrameUID base_id, ref MLCoordinateFrameUID id, ref MLPose outPose); /// /// Returns a pointer to an ASCII string representation for each result code. /// This call can return a pointer to the string for any of the MLSnapshot related MLResult codes. /// Developers should use MLResult.CodeToString(MLResult.Code). /// /// MLResult type to be converted to string. /// Returns a pointer to an ASCII string containing readable version of the result code. [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr MLSnapshotGetResultString(MLResult.Code result); /// /// Returns a pointer to an ASCII string representation for each result code. /// This call can return a pointer to the string for any of the MLInput related MLResult codes. /// Developers should use MLResult.CodeToString(MLResult.Code). /// /// MLResult type to be converted to string. /// Returns a pointer to an ASCII string containing readable version of the result code. [DllImport(MLInputDll, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr MLInputGetResultString(MLResult.Code result); /// /// Query the OS for which Platform API Level is supported. /// /// Pointer to an integer that will store the API level. /// /// MLResult.Result will be MLResult.Code.Ok if operation was successful. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to internal error. /// MLResult.Result will be MLResult.Code.InvalidParam if level was not valid (null). /// [DllImport(MLPlatformDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLPlatformGetAPILevel(ref uint level); /// /// Returns the minimum API level of the MLSDK used by Unity /// /// Value containing the minimum API level. /// /// MLResult.Code.Ok: Minimum API level was retrieved successfully.
/// MLResult.Code.NotImplemented: The ml_sdk_loader plugin was not compiled with knowledge of the minimum API level. ///
[DllImport(MLSdkLoaderDll, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLUnitySdkGetMinApiLevel(out uint minApiLevel); /// /// Tries to get the pose for the given coordinate frame id. /// /// The coordinate frame id to get the pose of. /// The object to initialize the found pose with. /// True if a pose was successfully found. [DllImport(UnityMagicLeapDll, EntryPoint = "UnityMagicLeap_TryGetPose")] [return: MarshalAs(UnmanagedType.I1)] public static extern bool UnityMagicLeap_TryGetPose(MLCoordinateFrameUID id, out UnityEngine.Pose pose); /// /// 2D vector represented with X and Y floats. /// [StructLayout(LayoutKind.Sequential)] public struct MLVec2f { /// /// X coordinate. /// public float X; /// /// Y coordinate. /// public float Y; public Vector2 ToVector2() => new Vector3(X, Y); } /// /// 3D vector in native format. /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct MLVec3f { /// /// X coordinate. /// public float X; /// /// Y coordinate. /// public float Y; /// /// Z coordinate. /// public float Z; public Vector3 ToVector3() => new Vector3(X, Y, Z); } /// /// Quaternion in native format. /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct MLQuaternionf { /// /// X coordinate. /// public float X; /// /// Y coordinate. /// public float Y; /// /// Z coordinate. /// public float Z; /// /// W coordinate. /// public float W; /// /// Returns an initialized MLQuaternionf with default values. /// /// An initialized MLQuaternionf. public static MLQuaternionf Identity() { MLQuaternionf quat = new MLQuaternionf() { X = 0, Y = 0, Z = 0, W = 1 }; return quat; } } /// /// Information used to transform from one coordinate frame to another. /// [StructLayout(LayoutKind.Sequential)] public struct MLTransform { /// /// The rotation of the coordinate frame to apply after the translation. /// public MLQuaternionf Rotation; /// /// The translation to apply to get the coordinate frame in the proper location. /// public MLVec3f Position; /// /// Returns an initialized MLTransform with default values. /// /// An initialized MLTransform. public static MLTransform Identity() { MLTransform t = new MLTransform(); t.Rotation = MLQuaternionf.Identity(); return t; } public override string ToString() { return $"Position: ({Position.X}, {Position.Y}, {Position.Z}) Rotation: ({Rotation.X}, {Rotation.Y}, {Rotation.Z}, {Rotation.W})"; } } /// /// 4x4 matrix in native format. /// [StructLayout(LayoutKind.Sequential)] public struct MLMat4f { /// /// The 16 matrix values. /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public float[] MatrixColmajor; } /// /// 2D rectangle in native format. /// [StructLayout(LayoutKind.Sequential)] public struct MLRectf { /// /// The x coordinate. /// public float X; /// /// The y coordinate. /// public float Y; /// /// The width. /// public float W; /// /// The height. /// public float H; } /// /// 2D rectangle with integer values in native format. /// [StructLayout(LayoutKind.Sequential)] public struct MLRecti { /// The x coordinate. /// public int X; /// /// The y coordinate. /// public int Y; /// /// The width. /// public int W; /// /// The height. /// public int H; } /// /// Universally unique identifier /// [StructLayout(LayoutKind.Sequential)] public struct MLUUID { /// /// The TimeLow field. /// public uint TimeLow; /// /// The TimeMid field. /// public ushort TimeMid; /// /// The TimeHiAndVersion field. /// public ushort TimeHiAndVersion; /// /// The ClockSeqHiAndReserved field. /// public byte ClockSeqHiAndReserved; /// /// The ClockSeqLow field. /// public byte ClockSeqLow; /// /// The Node0 field. /// public byte Node0; /// /// The Node1 field. /// public byte Node1; /// /// The Node2 field. /// public byte Node2; /// /// The Node3 field. /// public byte Node3; /// /// The Node4 field. /// public byte Node4; /// /// The Node5 field. /// public byte Node5; } /// /// Universally unique identifier, byte array. /// [StructLayout(LayoutKind.Sequential)] public struct MLUUIDBytes { private static readonly int[] hyphenIndices = new int[] { 8, 13, 18, 23 }; /// /// The 16 byte data array. /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] Data; public static bool operator ==(MLUUIDBytes one, MLUUIDBytes two) { if (one.Data.Length != two.Data.Length) return false; for (int i = 0; i < one.Data.Length; ++i) { if (one.Data[i] != two.Data[i]) return false; } return true; } public static bool operator !=(MLUUIDBytes one, MLUUIDBytes two) { return !(one == two); } public override bool Equals(object obj) { if (obj is MLUUIDBytes) { var rhs = (MLUUIDBytes)obj; return this == rhs; } return false; } public override int GetHashCode() => this.Data[0].GetHashCode(); public override string ToString() { string idString = string.Empty; foreach (byte b in this.Data) idString += string.Format("{0:x2}", b); foreach (int i in hyphenIndices) idString = idString.Insert(i, "-"); return idString; } internal MLUUIDBytes(string id) { id = id.Replace("-", string.Empty); this.Data = StringToByteArray(id); } private static byte[] StringToByteArray(string hex) { int numChars = hex.Length; byte[] bytes = new byte[numChars / 2]; for (int i = 0; i < numChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); return bytes; } } /// /// A unique identifier which represents a coordinate frame. /// The unique identifier is comprised of two values. /// [Serializable] [StructLayout(LayoutKind.Sequential)] public struct MLCoordinateFrameUID { /// /// The first data value. /// public ulong First; /// /// The second data value. /// public ulong Second; /// /// Gets an initialized MLCoordinateFrameUID. /// /// An initialized MLCoordinateFrameUID. public static MLCoordinateFrameUID EmptyFrame { get { return new MLCoordinateFrameUID(); } } /// /// Constructor getting two ulongs as argument. /// /// First data value. /// Second data value. public MLCoordinateFrameUID(ulong first, ulong second) { First = first; Second = second; } /// /// Constructor getting GUID as argument. /// /// GUID from which both values will be calculated. public MLCoordinateFrameUID(Guid guid) { First = 0; Second = 0; FromGuid(guid); } /// /// Constructor getting GUID as argument in string form. /// /// GUID from which both values will be calculated. public MLCoordinateFrameUID(string guidString) { First = 0; Second = 0; Guid guid; if (Guid.TryParse(guidString, out guid)) { FromGuid(guid); } } /// /// The equality check to be used for comparing two MLCoordinateFrameUID structs. /// /// The first struct to compare with the second struct. /// The second struct to compare with the first struct. /// True if the two provided structs have the same two data values. public static bool operator ==(MLCoordinateFrameUID one, MLCoordinateFrameUID two) { return one.First == two.First && one.Second == two.Second; } /// /// The inequality check to be used for comparing two MLCoordinateFrameUID structs. /// /// The first struct to compare with the second struct. /// The second struct to compare with the first struct. /// True if the two provided structs do not have the same two data values. public static bool operator !=(MLCoordinateFrameUID one, MLCoordinateFrameUID two) { return !(one == two); } /// /// The equality check to be used for when being compared to an object. /// /// The object to compare to this one with. /// True if the the provided object is of the MLCoordinateFrameUID type and has the same two data values. public override bool Equals(object obj) { if (obj is MLCoordinateFrameUID) { var rhs = (MLCoordinateFrameUID)obj; return this == rhs; } return false; } /// /// Gets the hash code to use from the first data value. /// /// The hash code returned by the first data value of this object public override int GetHashCode() { return this.First.GetHashCode(); } /// /// Returns the string value of the GUID of this MLCoordinateFrameUID. /// /// The string value of the GUID. public override string ToString() { return this.ToGuid().ToString(); } /// /// Returns the GUID based on the values of this MLCoordinateFrameUID. /// /// The calculated GUID. public Guid ToGuid() { byte[] toConvert = BitConverter.GetBytes(this.First); byte[] newSecond = BitConverter.GetBytes(this.Second); FlipGuidComponents(toConvert); ulong newFirst = BitConverter.ToUInt64(toConvert, 0); return new Guid((int)(newFirst >> 32 & 0x00000000FFFFFFFF), (short)(newFirst >> 16 & 0x000000000000FFFF), (short)(newFirst & 0x000000000000FFFF), newSecond); } /// /// Sets First and Second data value based on given GUID. /// /// GUID needed to calculate both data values. public void FromGuid(Guid guid) { byte[] guidBytes = guid.ToByteArray(); byte[] arrayInt = new byte[4]; Array.Copy(guidBytes, arrayInt, 4); int firstPart = (int)BitConverter.ToUInt32(arrayInt, 0); byte[] arrayShort = new byte[2]; Array.Copy(guidBytes, 4, arrayShort, 0, 2); short secondPart = (short)BitConverter.ToUInt16(arrayShort, 0); Array.Copy(guidBytes, 6, arrayShort, 0, 2); short thirdPart = (short)BitConverter.ToUInt16(arrayShort, 0); ulong first = ((ulong)firstPart << 32) + ((ulong)secondPart << 16) + (ulong)thirdPart; byte[] firstBytes = BitConverter.GetBytes(first); FlipGuidComponents(firstBytes); Second = BitConverter.ToUInt64(guidBytes, 8); First = BitConverter.ToUInt64(firstBytes, 0); } /// /// Sets First and Second data value based on given GUID in stirng form. /// /// GUID needed to calculate both data values public void FromString(string guidString) { Guid guid; if (Guid.TryParse(guidString, out guid)) { FromGuid(guid); } } /// /// Sets First and Second value. /// /// First data value. /// Second data value. public void FromULongPair(ulong first, ulong second) { First = first; Second = second; } /// /// Flips a component of the GUID based on endianness. /// /// The array of bytes to reverse. private static void FlipGuidComponents(byte[] bytes) { if (BitConverter.IsLittleEndian) { Array.Reverse(bytes); } } } /// /// Geometric relationship between two coordinate frames. /// [StructLayout(LayoutKind.Sequential)] public struct MLPose { /// /// 6-DoF transformation between the two coordinate frames that can be /// directly used to express source frame coordinates in destination frame /// coordinates. /// public MLTransform Transform; /// /// Indicate if this pose has derivative values. /// public bool HasDerivatives; /// /// The linear velocity in meters per second. /// public MLVec3f LinearVelocity; /// /// The linear acceleration in meters per second squared. /// public MLVec3f LinearAcceleration; /// /// Angular velocity in radians per second. /// public MLVec3f AngularVelocity; /// /// Angular accleration in radians per second squared. /// public MLVec3f AngularAcceleration; /// /// Time when this relationship was measured. /// public long OriginTimeNs; /// /// Time to which this relationship has been predicted. /// May be equal to origin_time_ns. /// public long PredictTimeNs; } /// /// Static information about the snapshot system. /// Initalize this structure with MLSnapshotStaticDataInit() and populate with MLSnapshotGetStaticData() /// [StructLayout(LayoutKind.Sequential)] public struct MLSnapshotStaticData { /// /// Version of this structure. /// UInt32 version; /// /// Coordinate frame ID. /// MLCoordinateFrameUID CoordWorldOrigin; public static MLSnapshotStaticData Init() { return new MLSnapshotStaticData() { version = 1u, }; } } } }