// %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.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using UnityEngine; using UnityEngine.XR.MagicLeap; /// /// Utility class used for converting vectors and matrices between native and unity format. /// public static class MLConvert { public static readonly float[] IdentityMatrixColMajor = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; /// /// Converts a Vector3 to Unity coordinate space and scale. /// /// Vector to convert /// If coordinate space should change. /// If world scale should be applied. /// Converted Vector public static Vector3 ToUnity(Vector3 vec, bool transformToRUF = true) => new Vector3(vec.x, vec.y, (transformToRUF) ? -vec.z : vec.z); /// /// Creates a Unity 3D vector from a native vector. /// /// A native vector. /// (Optional) If false, prevents conversion to Unity's coordinate system. /// (Optional) If false, prevents scaling to Unity's unit per meter scale. /// A Unity vector. public static Vector3 ToUnity(MagicLeapNativeBindings.MLVec3f vec, bool transformToRUF = true) => ToUnity(vec.X, vec.Y, vec.Z, transformToRUF); /// /// Creates a Unity 2D vector from a native vector. /// /// A native vector. /// (Optional) If false, prevents conversion to Unity's coordinate system. /// (Optional) If false, prevents scaling to Unity's unit per meter scale. /// A Unity vector. public static Vector2 ToUnity(MagicLeapNativeBindings.MLVec2f vec, bool transformToRUF = true) => ToUnity(vec.X, vec.Y, 0, transformToRUF); /// /// Creates a Unity 3D vector from a x, y and z parameters. /// /// X component /// Y component /// Z component /// (Optional) If false, prevents conversion to Unity's coordinate system. /// (Optional) If false, prevents scaling to Unity's unit per meter scale. /// A Unity vector. public static Vector3 ToUnity(float x, float y, float z, bool transformToRUF = true) => new Vector3(x, y, transformToRUF ? -z : z); /// /// Creates a Unity quaternion from a native quaternion. /// /// A native quaternion. /// (Optional) If false, prevents conversion to Unity's coordinate system. /// A Unity quaternion. public static Quaternion ToUnity(MagicLeapNativeBindings.MLQuaternionf quat, bool transformToRUF = true) => new Quaternion(quat.X, quat.Y, transformToRUF ? -quat.Z : quat.Z, transformToRUF ? -quat.W : quat.W); /// /// Converts a Quaternion to unity space. /// /// Quaternion to convert. /// (Optional) If false, prevents conversion to Unity's coordinate system. /// A Unity quaternion. public static Quaternion ToUnity(Quaternion quat, bool transformToRUF = true) { if (transformToRUF) { quat.z = -quat.z; quat.w = -quat.w; } return quat; } /// /// Creates Unity 4x4 matrix from native matrix. /// /// A native matrix. /// A Unity matrix. public static Matrix4x4 ToUnity(MagicLeapNativeBindings.MLMat4f mat) { return FloatsToMat(mat.MatrixColmajor); } /// /// Creates Unity 4x4 matrix from native transform. /// /// A native transform. /// (Optional) If false, prevents conversion to Unity's coordinate system. /// (Optional) If false, prevents scaling to Unity's unit per meter scale. /// A Unity matrix. public static Matrix4x4 ToUnity(MagicLeapNativeBindings.MLTransform transform, bool transformToRUF = true) { Vector3 position = ToUnity(transform.Position, transformToRUF); Quaternion rotation = ToUnity(transform.Rotation, transformToRUF); return Matrix4x4.TRS(position, rotation, Vector3.one); } /// /// Creates a System.Guid from an MLUUID /// /// A native UUID /// A System.Guid public static Guid ToUnity(MagicLeapNativeBindings.MLUUID uuid) { return new Guid(uuid.TimeLow, uuid.TimeMid, uuid.TimeHiAndVersion, uuid.ClockSeqHiAndReserved, uuid.ClockSeqLow, uuid.Node0, uuid.Node1, uuid.Node2, uuid.Node3, uuid.Node4, uuid.Node5); } /// /// Creates native transform from a Unity matrix. /// /// A Unity matrix. /// (Optional) If false, prevents conversion to the native SDK coordinate system. /// (Optional) If false, prevents scaling to the native SDK's unit per meter scale. /// A native transform. public static MagicLeapNativeBindings.MLTransform FromUnity(Matrix4x4 mat, bool transformFromRUF = true) { MagicLeapNativeBindings.MLTransform transform = new MagicLeapNativeBindings.MLTransform(); transform.Position = FromUnity(GetPositionFromTransformMatrix(mat), transformFromRUF); transform.Rotation = FromUnity(GetRotationFromTransformMatrix(mat), transformFromRUF); return transform; } /// /// Fills out array with values from 4x4 Unity matrix. /// /// An input native matrix. /// An array to populate in Unity format. public static void FromUnity(Matrix4x4 mat, ref float[] matrixColMajor) { for (int i = 0; i < 16; ++i) { matrixColMajor[i] = mat[i]; } } /// /// Creates native 3d vector from a Unity vector. /// /// A Unity vector. /// (Optional) If false, prevents conversion to the native SDK coordinate system. /// (Optional) If false, prevents scaling to the native SDK's unit per meter scale. /// A native vector. public static MagicLeapNativeBindings.MLVec3f FromUnity(Vector3 vec, bool transformFromRUF = true) { if (transformFromRUF) { vec.z = -vec.z; } MagicLeapNativeBindings.MLVec3f outVec = new MagicLeapNativeBindings.MLVec3f(); outVec.X = vec.x; outVec.Y = vec.y; outVec.Z = vec.z; return outVec; } /// /// Creates native quaternion from a Unity quaternion. /// /// A Unity quaternion. /// (Optional) If false, prevents conversion to the native SDK coordinate system. /// A native quaternion. public static MagicLeapNativeBindings.MLQuaternionf FromUnity(Quaternion quat, bool transformFromRUF = true) { MagicLeapNativeBindings.MLQuaternionf outQuat = new MagicLeapNativeBindings.MLQuaternionf(); outQuat.X = quat.x; outQuat.Y = quat.y; if (transformFromRUF) { outQuat.Z = -quat.z; outQuat.W = -quat.w; } else { outQuat.Z = quat.z; outQuat.W = quat.w; } return outQuat; } /// /// Creates an MLUUID from a System.Guid /// /// A System.Guid /// A native MLUUID public static MagicLeapNativeBindings.MLUUID FromUnity(Guid guid) { MagicLeapNativeBindings.MLUUID result = new MagicLeapNativeBindings.MLUUID(); string guidString = guid.ToString("N"); result.TimeLow = uint.Parse(guidString.Substring(0, 8), NumberStyles.HexNumber); result.TimeMid = ushort.Parse(guidString.Substring(8, 4), NumberStyles.HexNumber); result.TimeHiAndVersion = ushort.Parse(guidString.Substring(12, 4), NumberStyles.HexNumber); result.ClockSeqHiAndReserved = byte.Parse(guidString.Substring(16, 2), NumberStyles.HexNumber); result.ClockSeqLow = byte.Parse(guidString.Substring(18, 2), NumberStyles.HexNumber); result.Node0 = byte.Parse(guidString.Substring(20, 2), NumberStyles.HexNumber); result.Node1 = byte.Parse(guidString.Substring(22, 2), NumberStyles.HexNumber); result.Node2 = byte.Parse(guidString.Substring(24, 2), NumberStyles.HexNumber); result.Node3 = byte.Parse(guidString.Substring(26, 2), NumberStyles.HexNumber); result.Node4 = byte.Parse(guidString.Substring(28, 2), NumberStyles.HexNumber); result.Node5 = byte.Parse(guidString.Substring(30, 2), NumberStyles.HexNumber); return result; } /// /// Gets the position vector stored in a transform matrix. /// /// A Unity matrix treated as a transform matrix. /// A Unity vector representing a position. public static Vector3 GetPositionFromTransformMatrix(Matrix4x4 transformMatrix) { return transformMatrix.GetColumn(3); } /// /// Gets the rotation quaternion stored in a transform matrix. /// /// A Unity matrix treated as a transform matrix. /// A Unity quaternion. public static Quaternion GetRotationFromTransformMatrix(Matrix4x4 transformMatrix) { return Quaternion.LookRotation(transformMatrix.GetColumn(2), transformMatrix.GetColumn(1)); } /// /// Take a string, snips it to a desired length and converts it to UTF8. /// /// String to snip and convert /// length to snip to /// UTF8 string byte array public static byte[] ToUTF8Snipped(string inString, int snipLength) { int snipSize = Math.Min(inString.Length, snipLength); int size = Encoding.UTF8.GetByteCount(inString.Substring(0, snipSize)); while (snipSize >= 0 && size > snipLength) { size -= Encoding.UTF8.GetByteCount(inString.Substring(snipSize - 1, 1)); --snipSize; } return Encoding.UTF8.GetBytes(inString.Substring(0, snipSize)); } /// /// Decodes a buffer of bytes into an ASCII string. /// /// bytes to convert to a string /// A managed string public static string DecodeAscii(byte[] buffer) { int count = Array.IndexOf(buffer, 0, 0); if (count < 0) { count = buffer.Length; } return Encoding.ASCII.GetString(buffer, 0, count); } /// /// Decodes a buffer of bytes into a UTF8 string. /// /// bytes to convert to a UTF8 string /// A managed string public static string DecodeUTF8(byte[] buffer) { int count = Array.IndexOf(buffer, 0, 0); if (count < 0) { count = buffer.Length; } return Encoding.UTF8.GetString(buffer, 0, count); } /// /// Converts a managed string into an unmanaged null terminated UTF-8 string. /// /// The managed string to convert /// A pointer to the unmanaged string public static IntPtr EncodeToUnmanagedUTF8(string s) { int length = Encoding.UTF8.GetByteCount(s); byte[] buffer = new byte[length + 1]; Encoding.UTF8.GetBytes(s, 0, s.Length, buffer, 0); IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length); Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length); return nativeUtf8; } /// /// This encodes the string into a UTF-8 byte array. /// /// string to encode /// UTF8 string byte array public static byte[] EncodeUTF8(string decodedString) { return Encoding.UTF8.GetBytes(decodedString); } /// /// Converts an unmanaged null terminated UTF-8 string into a managed string. /// /// The unmanaged string to convert /// maximum number of characters to convert /// A managed string public static string DecodeUTF8(IntPtr nativeString, int maximumSize = -1) { if (nativeString == IntPtr.Zero) { return string.Empty; } int byteLength = 0; if (maximumSize > 0) { while (Marshal.ReadByte(nativeString, byteLength) != 0) { ++byteLength; if (byteLength == maximumSize) { break; } } } else { while (Marshal.ReadByte(nativeString, byteLength) != 0) { ++byteLength; } } if (byteLength == 0) { return string.Empty; } byte[] buffer = new byte[byteLength]; Marshal.Copy(nativeString, buffer, 0, buffer.Length); return Encoding.UTF8.GetString(buffer); } /// /// Converts an unmanaged UTF-16 string into a managed string. /// /// Native byte array to convert /// A managed string public static string DecodeUTF16BE(byte[] nativeArray) { return Encoding.BigEndianUnicode.GetString(nativeArray); } /// /// Converts an unmanaged UTF-16 string into a managed string. /// /// Native byte array to convert /// A managed string public static string DecodeUTF16LE(byte[] nativeArray) { return Encoding.Unicode.GetString(nativeArray); } /// /// Convert an object to a byte array. Uses C# Binary formatter to serialize /// /// Data type of object /// Object to convert /// Returns a binary array representation of the object public static byte[] ObjectToByteArray(T obj) { BinaryFormatter bf = new BinaryFormatter(); using (var ms = new MemoryStream()) { bf.Serialize(ms, obj); return ms.ToArray(); } } /// /// Convert a byte array to an Object /// /// Byte array to convert /// Returns the newly converted object public static object ByteArrayToObject(byte[] byteArray) { using (var memStream = new MemoryStream()) { var binForm = new BinaryFormatter(); memStream.Write(byteArray, 0, byteArray.Length); memStream.Seek(0, SeekOrigin.Begin); var obj = binForm.Deserialize(memStream); return obj; } } public static void FlipTransformMatrixVertically(float[] frameTransformMatColMajor) { /* Using the matrix provided by MLNativeSurfaceGetFrameTransformationMatrix() directly renders the video upside down. Multiply it with the following matrix to flip it and render with the right orientation in Vulkan - | 1 0 0 0 | | 0 -1 0 1 | | 0 0 1 0 | | 0 0 0 1 | The multiplication result in this - | m00 -m01 m02 m03 + m01 | | m10 -m11 m12 m13 + m11 | | m20 -m21 m22 m23 + m21 | | m30 -m31 m32 m33 + m32 | */ for (int i = 12; i <= 15; ++i) { frameTransformMatColMajor[i] += frameTransformMatColMajor[i - 8]; } for (int i = 4; i <= 7; ++i) { frameTransformMatColMajor[i] = -1 * frameTransformMatColMajor[i]; } } public static void FlipTransformMatrixHorizontally(float[] frameTransformMatColMajor) { /* Multiply with the following matrix to flip horizontally - | -1 0 0 1 | | 0 1 0 0 | | 0 0 1 0 | | 0 0 0 1 | The multiplication result in this - | -m00 m01 m02 m03 + m00 | | -m10 m11 m12 m13 + m10 | | -m20 m21 m22 m23 + m20 | | -m30 m31 m32 m33 + m30 | */ for (int i = 12; i <= 15; ++i) { frameTransformMatColMajor[i] += frameTransformMatColMajor[i - 12]; } for (int i = 0; i <= 4; ++i) { frameTransformMatColMajor[i] = -1 * frameTransformMatColMajor[i]; } } /// /// Creates Unity 4x4 matrix from an array of 16 floats. /// /// An array of 16 floats. /// A Unity matrix. private static Matrix4x4 FloatsToMat(float[] vals) { Matrix4x4 mat = new Matrix4x4(); for (int i = 0; i < 16; ++i) { mat[i] = vals[i]; } return mat; } /// /// Converts an unmanged array to a managed array of type T. /// public static T[] MarshalUnmanagedArray(IntPtr arrayPtr, int count) { T[] convertedArray = new T[count]; int tSize = Marshal.SizeOf(); for (int i = 0; i < count; ++i) { convertedArray[i] = Marshal.PtrToStructure((arrayPtr + (tSize * i))); } return convertedArray; } } }