// %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,
};
}
}
}
}