// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
//
//
// Copyright (c) 2018-present, Magic Leap, Inc. All Rights Reserved.
//
//
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
// Disabling deprecated warning for the internal project
#pragma warning disable 618
namespace UnityEngine.XR.MagicLeap
{
using System;
using System.Runtime.InteropServices;
///
/// Magic Leap 2 has three world cameras which it uses for environment tracking.
/// The three cameras area located on the left, center, and right side of the
/// headset. This API will provide a way to query for the frames from these world
/// cameras, at this point the app will not be able to configure the world camera parameters.
///
/// This is an experimental API which may be modified or removed without
/// any prior notice.
///
public partial class MLWorldCamera : MLAutoAPISingleton
{
///
/// Enumeration of all the available world camera sensors.
///
[Flags]
public enum CameraId
{
///
/// Left World camera.
///
Left = 1 << 0,
///
/// Right World camera.
///
Right = 1 << 1,
///
/// Center World camera.
///
Center = 1 << 2,
///
/// All World cameras.
///
All = Left | Right | Center,
};
///
/// Enumeration of world camera modes.
///
[Flags]
public enum Mode
{
///
/// None.
///
Unknown = 0,
///
/// Low exposure mode.
/// This mode is currently only available when the controller is being tracked.
///
LowExposure = 1 << 0,
///
/// Normal exposure mode.
///
NormalExposure = 1 << 1
};
public Settings CurrentSettings { get; private set; }
public bool IsConnected { get; private set; }
private bool connectionPaused;
protected override MLResult.Code StartAPI() => MLResult.Code.Ok;
protected override MLResult.Code StopAPI() => Disconnect().Result;
///
/// Connect to world cameras.
///
public MLResult Connect(in Settings settings) => MLResult.Create(InternalConnect(in settings));
///
/// Update the world camera settings.
///
public MLResult UpdateSettings(in Settings settings) => MLResult.Create(InternalUpdateSettings(in settings));
///
/// Poll for Frames. Returns #MLWorldCameraData with this latest data when available. The memory is owned by the system.
/// Application should copy the data it needs to cache it and then release the memory by calling
/// #MLWorldCameraReleaseCameraData. This is a blocking call. API is not thread safe. If there are no new world camera data frames for a given
/// duration (duration determined by the system) then the API will return MLResult_Timeout. To Do : Are there any other
/// meaningful return codes that we need to consider. Say something like MLResult_ResourceNotAvailble for cases where the world
/// camera is not ready yet or is not generating any data because its been turned off.
///
public MLResult GetLatestWorldCameraData(out Frame[] data, uint timeOutMs = 1) => MLResult.Create(InternalGetLatestWorldCameraData(timeOutMs, out data));
///
/// Disconnect from world camera. This will disconnect from all the world camera currently connected.
///
public MLResult Disconnect() => MLResult.Create(InternalDisconnect());
///
/// Connect to world cameras.
///
private MLResult.Code InternalConnect(in Settings settings)
{
if (IsConnected)
return MLResult.Code.Ok;
var nativeSettings = new NativeBindings.MLWorldCameraSettings(in settings);
var resultCode = NativeBindings.MLWorldCameraConnect(in nativeSettings, out Handle);
if (MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWorldCameraConnect)))
{
IsConnected = true;
CurrentSettings = settings;
}
return resultCode;
}
///
/// Update the world camera settings.
///
private MLResult.Code InternalUpdateSettings(in Settings settings)
{
var nativeSettings = new NativeBindings.MLWorldCameraSettings(in settings);
var resultCode = NativeBindings.MLWorldCameraUpdateSettings(Handle, in nativeSettings);
if (MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWorldCameraUpdateSettings)))
CurrentSettings = settings;
return resultCode;
}
///
/// Poll for Frames. Returns #MLWorldCameraData with this latest data when available. The memory is owned by the system.
/// Application should copy the data it needs to cache it and then release the memory by calling
/// #MLWorldCameraReleaseCameraData. This is a blocking call. API is not thread safe. If there are no new world camera data frames for a given
/// duration (duration determined by the system) then the API will return MLResult_Timeout. To Do : Are there any other
/// meaningful return codes that we need to consider. Say something like MLResult_ResourceNotAvailble for cases where the world
/// camera is not ready yet or is not generating any data because its been turned off.
///
private MLResult.Code InternalGetLatestWorldCameraData(uint timeOutMs, out Frame[] data)
{
var nativeData = new NativeBindings.MLWorldCameraData(1);
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(nativeData));
Marshal.StructureToPtr(nativeData, ptr, false);
var resultCode = NativeBindings.MLWorldCameraGetLatestWorldCameraData(Handle, timeOutMs, ref ptr);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWorldCameraGetLatestWorldCameraData), TimeOutPredicate);
if (!MLResult.IsOK(resultCode))
{
data = new Frame[0];
return resultCode;
}
nativeData = Marshal.PtrToStructure(ptr);
data = MarshalFrames(in nativeData);
resultCode = NativeBindings.MLWorldCameraReleaseCameraData(Handle, ptr);
MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWorldCameraReleaseCameraData));
Marshal.FreeHGlobal(ptr);
return resultCode;
}
///
/// Disconnect from world camera. This will disconnect from all the world camera currently connected.
///
private MLResult.Code InternalDisconnect()
{
if (!IsConnected)
return MLResult.Code.Ok;
var resultCode = NativeBindings.MLWorldCameraDisconnect(Handle);
IsConnected = !MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLWorldCameraDisconnect));
return resultCode;
}
private Frame[] MarshalFrames(in NativeBindings.MLWorldCameraData data)
{
var frames = new Frame[data.FrameCount];
IntPtr walkPtr = data.Frames;
for (int i = 0; i < data.FrameCount; ++i)
{
var nativeFrame = Marshal.PtrToStructure(walkPtr);
frames[i] = new Frame(nativeFrame);
walkPtr = new IntPtr(walkPtr.ToInt64() + Marshal.SizeOf());
}
return frames;
}
private bool TimeOutPredicate(MLResult.Code code) => code == MLResult.Code.Ok || code == MLResult.Code.Timeout;
protected override void OnApplicationPause(bool pauseStatus)
{
base.OnApplicationPause(pauseStatus);
if (pauseStatus)
{
if (IsConnected)
{
connectionPaused = true;
Disconnect();
}
}
else
{
if (connectionPaused)
{
connectionPaused = false;
if (MLPermissions.CheckPermission(MLPermission.Camera).IsOk)
{
var settings = CurrentSettings;
Connect(in settings);
}
}
}
}
}
}