diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fa4f09..6e615c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,38 @@ # Changelog -## [1.9.0] +## [1.10.0] ### Features -- `MLDepthCamera`: Added support for switching to Short Range streaming mode and to change FPS and exposure values. +- Added `MLOcclusion` API. +- Added public properties `MagicLeapCamera.EnforceFarClip` and `MagicLeapCamera.RecenterXROriginAtStart` ### Bugfixes -- `MLWebRTC`: Fixed camera disconnection when using `MLNativeSurface` based buffers for rendering. -- Fixed `MLWorldCamera` error on application quit. - `MLSegmentedDimmer`: Fixed crash when playing scene twice in editor. - +- `MLMediaPlayerBehavior`: Fixed unresponsive UI after pressing stop button in `MediaPlayer` example. +- Added missing dropdown for short-range depth camera in `DepthCamera` example. +- `MLCamera`: Fixed error on sleep mode and doze mode. +- `MLCameraBase`: Fixed failure to render preview capture more than once. +- `MLNativeSurface`: Fixed AccessRenderBufferTexture() rendering failure when reusing same player. +- `MLMediaPlayer`: Fixed erroneuous error logging on pending result from Streaming Assets path prepare. +- Fixed `MLMeshing` on map reset. +- `MLMeshing`: Fixed Null Reference Exception on `Meshing` example scene start up. +- Fixed second disconnect attempt in `WebRTC` example. +- `HandTrackingExample`: Fixed `PoseNotFound` errors that might occur after `HandTracking` scene changes. +- `MLGestureClassification`: Fixed errors thrown when hands not detected. +- `MLUnityNativeLogging`: Conditionally reduced log level of snapshot errors based on build configuration. +- `GraphicsHook`: Added cleanup logic that resets the snapshot prediction state of the input subsystem ### Deprecations & Removals ### Known Issues +## [1.9.0] + +### Features +- `MLDepthCamera`: Added support for switching to Short Range streaming mode and to change FPS and exposure values. + +### Bugfixes +- `MLWebRTC`: Fixed camera disconnection when using `MLNativeSurface` based buffers for rendering. +- Fixed `MLWorldCamera` error on application quit. ## [1.8.0] ### Features diff --git a/Editor/MLAppSim/AppSimShimLibSupport.cs b/Editor/MLAppSim/AppSimShimLibSupport.cs index 19e89f1..6446e44 100644 --- a/Editor/MLAppSim/AppSimShimLibSupport.cs +++ b/Editor/MLAppSim/AppSimShimLibSupport.cs @@ -26,7 +26,7 @@ public static class AppSimShimLibSupport { private static string LaunchProcess => Path.Combine(MagicLeapSDKUtil.AppSimRuntimePath, "bin/ZIDiscovery"); - private static readonly string SessionStateKey = "ZI_SEARCH_PATHS"; + public static readonly string SessionStateKey_ZISearchPaths = "ZI_SEARCH_PATHS"; private static List libSearchPaths = new List(); /// @@ -56,13 +56,10 @@ private static void CheckForLibrarySearchPaths() return; } - string cachedSearchPaths = SessionState.GetString(SessionStateKey, string.Empty); + string cachedSearchPaths = SessionState.GetString(SessionStateKey_ZISearchPaths, string.Empty); if (string.IsNullOrEmpty(cachedSearchPaths)) { var ziRuntime = MagicLeapSDKUtil.AppSimRuntimePath; -#if UNITY_EDITOR_WIN - ziRuntime = ziRuntime.Replace("/", "\\"); -#endif if (string.IsNullOrEmpty(ziRuntime)) { Debug.LogError("Zero Iteration Runtime path is not set."); @@ -70,6 +67,9 @@ private static void CheckForLibrarySearchPaths() return; } +#if UNITY_EDITOR_WIN + ziRuntime = ziRuntime.Replace("/", "\\"); +#endif var startInfo = new System.Diagnostics.ProcessStartInfo { UseShellExecute = false, @@ -101,7 +101,7 @@ private static void CheckForLibrarySearchPaths() } libSearchPaths = new List(output.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries)); - SessionState.SetString(SessionStateKey, string.Join(Path.PathSeparator, libSearchPaths)); + SessionState.SetString(SessionStateKey_ZISearchPaths, string.Join(Path.PathSeparator, libSearchPaths)); } else { diff --git a/Editor/MagicLeapSDKUtil.cs b/Editor/MagicLeapSDKUtil.cs index cf861ae..e84b6d9 100644 --- a/Editor/MagicLeapSDKUtil.cs +++ b/Editor/MagicLeapSDKUtil.cs @@ -24,21 +24,7 @@ public sealed class MagicLeapSDKUtil #else private const UnityEditor.BuildTarget kBuildTarget = BuildTarget.Relish; #endif - private static readonly uint minApiLevel = 0; - - static MagicLeapSDKUtil() - { - try - { - var result = UnityEngine.XR.MagicLeap.Native.MagicLeapNativeBindings.MLUnitySdkGetMinApiLevel(out minApiLevel); - UnityEngine.XR.MagicLeap.MLResult.DidNativeCallSucceed(result, nameof(UnityEngine.XR.MagicLeap.Native.MagicLeapNativeBindings.MLUnitySdkGetMinApiLevel)); - } - catch(DllNotFoundException) - { - Debug.LogWarning($"Unable to look up minimum Magic Leap API level for editor scripting as the ml_sdk_loader has not been built for host.\n" + - $"\tDeveloper: Run \"build.py -h\" to rebuild NativeLibs including for host OS to access this value."); - } - } + private static uint minApiLevel = 0; [Serializable] private class SDKManifest @@ -62,7 +48,25 @@ public static bool SdkAvailable } } - public static uint MinimumApiLevel => minApiLevel; + public static uint MinimumApiLevel + { + get + { + if(minApiLevel == 0) + { + try + { + var result = UnityEngine.XR.MagicLeap.Native.MagicLeapNativeBindings.MLUnitySdkGetMinApiLevel(out minApiLevel); + UnityEngine.XR.MagicLeap.MLResult.DidNativeCallSucceed(result, nameof(UnityEngine.XR.MagicLeap.Native.MagicLeapNativeBindings.MLUnitySdkGetMinApiLevel)); + } + catch(DllNotFoundException) + { + Debug.LogWarning($"Native plugins have not been built for host ({Application.platform}). Minimum API level can't be queried."); + } + } + return minApiLevel; + } + } /// /// MLSDK path for the relish target. diff --git a/Editor/OpenXR/MagicLeapFeatureGroup.cs b/Editor/OpenXR/MagicLeapFeatureGroup.cs new file mode 100644 index 0000000..8c8785f --- /dev/null +++ b/Editor/OpenXR/MagicLeapFeatureGroup.cs @@ -0,0 +1,34 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2019-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% + +#if UNITY_OPENXR_1_7_0_OR_NEWER +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + [OpenXRFeatureSet( + UiName = "Magic Leap", + Description = "All Magic Leap OpenXR Features", + FeatureSetId = "com.magicleap.openxr.featuregroup", + SupportedBuildTargets = new [] { BuildTargetGroup.Android, BuildTargetGroup.Standalone }, + FeatureIds = new [] { + MagicLeapFeature.featureId , + MagicLeapHandTrackingFeature.featureId, + MagicLeapRenderingExtensionsFeature.featureId, + MagicLeapReferenceSpacesFeature.featureId, + MagicLeapClippingPlaneEnforcementFeature.featureId, + MagicLeapPlanesFeature.FeatureId, + } + )] + public class MagicLeapFeatureGroup + { } +} +#endif diff --git a/Editor/OpenXR/MagicLeapFeatureGroup.cs.meta b/Editor/OpenXR/MagicLeapFeatureGroup.cs.meta new file mode 100644 index 0000000..2e75178 --- /dev/null +++ b/Editor/OpenXR/MagicLeapFeatureGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 010fd52188909de4cbb773f607150606 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/OpenXR/SegmentedDimmerMenuItem.cs b/Editor/OpenXR/SegmentedDimmerMenuItem.cs new file mode 100644 index 0000000..638cef5 --- /dev/null +++ b/Editor/OpenXR/SegmentedDimmerMenuItem.cs @@ -0,0 +1,36 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2019-2023) 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% + +#if UNITY_OPENXR_1_7_0_OR_NEWER +using UnityEngine; +using UnityEngine.XR.OpenXR.Features.MagicLeapSupport; + +namespace UnityEditor.XR.OpenXR.Features.MagicLeapSupport +{ + public static class SegmentedDimmerMenuItem + { + [MenuItem("GameObject/XR/Magic Leap Segmented Dimmer")] + public static void AddSegmentedDimmerToScene() + { + var mainCamera = Camera.main; + if (mainCamera == null) + { + Debug.LogError($"Could not find Main Camera! Before adding a Segmented Dimmer to your scene, you must add a Camera and tag it \"MainCamera\""); + return; + } + + var prefab = AssetDatabase.LoadAssetAtPath("Packages/com.magicleap.unitysdk/Runtime/Tools/Prefabs/Segmented Dimmer.prefab"); + var instance = GameObject.Instantiate(prefab.gameObject, mainCamera.transform); + instance.name = "Segmented Dimmer"; + Selection.objects = new Object[] { instance }; + } + } +} +#endif diff --git a/Editor/OpenXR/SegmentedDimmerMenuItem.cs.meta b/Editor/OpenXR/SegmentedDimmerMenuItem.cs.meta new file mode 100644 index 0000000..44dd5e2 --- /dev/null +++ b/Editor/OpenXR/SegmentedDimmerMenuItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdcf1d44533cd4007808ff7291548a89 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/SettingsProviders/Preferences/MagicLeapEditorPreferences.cs b/Editor/SettingsProviders/Preferences/MagicLeapEditorPreferences.cs index 456816b..88bb3d1 100644 --- a/Editor/SettingsProviders/Preferences/MagicLeapEditorPreferences.cs +++ b/Editor/SettingsProviders/Preferences/MagicLeapEditorPreferences.cs @@ -70,7 +70,7 @@ static PreferencesLoader() { if (!string.IsNullOrEmpty(labdriverResultPath)) { - SessionState.SetString(LabdriverFoundBackendPath, labdriverResultPath); + EditorPrefs.SetString(LabdriverFoundBackendPath, labdriverResultPath); if (ziRuntimePath != labdriverResultPath) { ziRuntimePath = labdriverResultPath; @@ -330,7 +330,10 @@ private static string GetSavedSDKPath() private static void SaveNewSDKPath(string path) { - SessionState.EraseString(LabdriverFoundBackendPath); + // SDK path is changed. Clear these two. + EditorPrefs.DeleteKey(LabdriverFoundBackendPath); + SessionState.EraseString(AppSimShimLibSupport.SessionStateKey_ZISearchPaths); + EditorPrefs.SetString(SdkPathEditorPrefsKey, path); mlsdkPath = path; if (!ziPathOverrideToggle.value) @@ -348,17 +351,18 @@ private static void SaveNewSDKPath(string path) private static void LocateZIRuntimeFromMLSDK() { - var path = SessionState.GetString(LabdriverFoundBackendPath, ""); - if(Directory.Exists(path)) + var path = EditorPrefs.GetString(LabdriverFoundBackendPath, ""); + if (Directory.Exists(path)) // Use the stored path if valid so we don't need to invoke Labdriver any more. { if (path != ziRuntimePath) { ziRuntimePath = path; ZIRuntimePathChangeEvt?.Invoke(ziRuntimePath); } + return; } - if(string.IsNullOrEmpty(ziRuntimePath) || !Directory.Exists(ziRuntimePath)) + if (string.IsNullOrEmpty(ziRuntimePath) || !Directory.Exists(ziRuntimePath)) { if(mlsdkPath.EndsWith("LAYOUT")) { diff --git a/Plugins/Android/libMLAudioOutput.so b/Plugins/Android/libMLAudioOutput.so new file mode 100644 index 0000000..8f96521 Binary files /dev/null and b/Plugins/Android/libMLAudioOutput.so differ diff --git a/Plugins/Android/libMLAudioOutput.so.meta b/Plugins/Android/libMLAudioOutput.so.meta new file mode 100644 index 0000000..6168e98 --- /dev/null +++ b/Plugins/Android/libMLAudioOutput.so.meta @@ -0,0 +1,76 @@ +fileFormatVersion: 2 +guid: bbc1a2113cd550c4b9a6d97eb0e5a468 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libMagicLeapXrProvider.so b/Plugins/Android/libMagicLeapXrProvider.so new file mode 100644 index 0000000..840418e Binary files /dev/null and b/Plugins/Android/libMagicLeapXrProvider.so differ diff --git a/Plugins/Android/libMagicLeapXrProvider.so.meta b/Plugins/Android/libMagicLeapXrProvider.so.meta new file mode 100644 index 0000000..20e3caf --- /dev/null +++ b/Plugins/Android/libMagicLeapXrProvider.so.meta @@ -0,0 +1,83 @@ +fileFormatVersion: 2 +guid: dd68d1d0a4bceba49ba282037f2e8d62 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Relish: Relish + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libml_c_utils.so b/Plugins/Android/libml_c_utils.so new file mode 100644 index 0000000..de055e8 Binary files /dev/null and b/Plugins/Android/libml_c_utils.so differ diff --git a/Plugins/Android/libml_c_utils.so.meta b/Plugins/Android/libml_c_utils.so.meta new file mode 100644 index 0000000..f4091af --- /dev/null +++ b/Plugins/Android/libml_c_utils.so.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: bc1ae38f4e272c746a1bb008fc089396 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude Lumin: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Relish: Relish + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libml_sdk_loader.so b/Plugins/Android/libml_sdk_loader.so new file mode 100644 index 0000000..dd64101 Binary files /dev/null and b/Plugins/Android/libml_sdk_loader.so differ diff --git a/Plugins/Android/libml_sdk_loader.so.meta b/Plugins/Android/libml_sdk_loader.so.meta new file mode 100644 index 0000000..a46f112 --- /dev/null +++ b/Plugins/Android/libml_sdk_loader.so.meta @@ -0,0 +1,83 @@ +fileFormatVersion: 2 +guid: b76386cc9a3a9494d9d3208600add257 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Relish: Relish + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libml_sdk_tests_provider.so b/Plugins/Android/libml_sdk_tests_provider.so new file mode 100644 index 0000000..294f848 Binary files /dev/null and b/Plugins/Android/libml_sdk_tests_provider.so differ diff --git a/Plugins/Android/libml_sdk_tests_provider.so.meta b/Plugins/Android/libml_sdk_tests_provider.so.meta new file mode 100644 index 0000000..a8b092e --- /dev/null +++ b/Plugins/Android/libml_sdk_tests_provider.so.meta @@ -0,0 +1,71 @@ +fileFormatVersion: 2 +guid: c60157d133e412e4d8832876d6f93577 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + AndroidSharedLibraryType: Executable + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libml_systrace_plugin.so b/Plugins/Android/libml_systrace_plugin.so new file mode 100644 index 0000000..9d33264 Binary files /dev/null and b/Plugins/Android/libml_systrace_plugin.so differ diff --git a/Plugins/Android/libml_systrace_plugin.so.meta b/Plugins/Android/libml_systrace_plugin.so.meta new file mode 100644 index 0000000..b0e10a1 --- /dev/null +++ b/Plugins/Android/libml_systrace_plugin.so.meta @@ -0,0 +1,83 @@ +fileFormatVersion: 2 +guid: b13c388a294844ab38971c720d72ce0a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Relish: Relish + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libml_unity_native_logging.so b/Plugins/Android/libml_unity_native_logging.so new file mode 100644 index 0000000..28c4096 Binary files /dev/null and b/Plugins/Android/libml_unity_native_logging.so differ diff --git a/Plugins/Android/libml_unity_native_logging.so.meta b/Plugins/Android/libml_unity_native_logging.so.meta new file mode 100644 index 0000000..d47a13f --- /dev/null +++ b/Plugins/Android/libml_unity_native_logging.so.meta @@ -0,0 +1,83 @@ +fileFormatVersion: 2 +guid: 5b45ee61eaefe46f5af58386326289ca +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Relish: Relish + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/Android/libml_ycbcr_renderer.so b/Plugins/Android/libml_ycbcr_renderer.so new file mode 100644 index 0000000..a73b195 Binary files /dev/null and b/Plugins/Android/libml_ycbcr_renderer.so differ diff --git a/Plugins/Android/libml_ycbcr_renderer.so.meta b/Plugins/Android/libml_ycbcr_renderer.so.meta new file mode 100644 index 0000000..f05d98f --- /dev/null +++ b/Plugins/Android/libml_ycbcr_renderer.so.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 00c95f80085304646a8e292cf1fc46ec +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 1 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude Lumin: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Relish: Relish + second: + enabled: 1 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/MacEditor/libMLAudioOutput.dylib b/Plugins/MacEditor/libMLAudioOutput.dylib new file mode 100644 index 0000000..1910592 Binary files /dev/null and b/Plugins/MacEditor/libMLAudioOutput.dylib differ diff --git a/Plugins/MacEditor/libMLAudioOutput.dylib.meta b/Plugins/MacEditor/libMLAudioOutput.dylib.meta new file mode 100644 index 0000000..1f2dee7 --- /dev/null +++ b/Plugins/MacEditor/libMLAudioOutput.dylib.meta @@ -0,0 +1,76 @@ +fileFormatVersion: 2 +guid: ccc95627ca3d3e248840a1875338915b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/MacEditor/libMagicLeapXrProvider.dylib b/Plugins/MacEditor/libMagicLeapXrProvider.dylib new file mode 100644 index 0000000..0db3457 Binary files /dev/null and b/Plugins/MacEditor/libMagicLeapXrProvider.dylib differ diff --git a/Plugins/MacEditor/libMagicLeapXrProvider.dylib.meta b/Plugins/MacEditor/libMagicLeapXrProvider.dylib.meta new file mode 100644 index 0000000..70fe4b9 --- /dev/null +++ b/Plugins/MacEditor/libMagicLeapXrProvider.dylib.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 8ee09b602e598430c9783733fed6bd31 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Relish: Relish + second: + enabled: 0 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/MacEditor/libml_sdk_loader.dylib b/Plugins/MacEditor/libml_sdk_loader.dylib new file mode 100644 index 0000000..da4d6e6 Binary files /dev/null and b/Plugins/MacEditor/libml_sdk_loader.dylib differ diff --git a/Plugins/MacEditor/libml_sdk_loader.dylib.meta b/Plugins/MacEditor/libml_sdk_loader.dylib.meta new file mode 100644 index 0000000..878ea79 --- /dev/null +++ b/Plugins/MacEditor/libml_sdk_loader.dylib.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 4711f20ffb67947c68d312378e98a27c +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Relish: Relish + second: + enabled: 0 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/MacEditor/libml_sdk_tests_provider.dylib b/Plugins/MacEditor/libml_sdk_tests_provider.dylib new file mode 100644 index 0000000..508ecd6 Binary files /dev/null and b/Plugins/MacEditor/libml_sdk_tests_provider.dylib differ diff --git a/Plugins/MacEditor/libml_sdk_tests_provider.dylib.meta b/Plugins/MacEditor/libml_sdk_tests_provider.dylib.meta new file mode 100644 index 0000000..158a470 --- /dev/null +++ b/Plugins/MacEditor/libml_sdk_tests_provider.dylib.meta @@ -0,0 +1,71 @@ +fileFormatVersion: 2 +guid: 1db09bf3dde894fa1bd69909738c9d38 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/MacEditor/libml_unity_native_logging.dylib b/Plugins/MacEditor/libml_unity_native_logging.dylib new file mode 100644 index 0000000..414d8fe Binary files /dev/null and b/Plugins/MacEditor/libml_unity_native_logging.dylib differ diff --git a/Plugins/MacEditor/libml_unity_native_logging.dylib.meta b/Plugins/MacEditor/libml_unity_native_logging.dylib.meta new file mode 100644 index 0000000..0575780 --- /dev/null +++ b/Plugins/MacEditor/libml_unity_native_logging.dylib.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 634ce7a12a92f464e861130941b0cdaa +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Relish: Relish + second: + enabled: 0 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/WindowsEditor/MLAudioOutput.dll b/Plugins/WindowsEditor/MLAudioOutput.dll new file mode 100644 index 0000000..95d76f1 Binary files /dev/null and b/Plugins/WindowsEditor/MLAudioOutput.dll differ diff --git a/Plugins/WindowsEditor/MLAudioOutput.dll.meta b/Plugins/WindowsEditor/MLAudioOutput.dll.meta new file mode 100644 index 0000000..3d89096 --- /dev/null +++ b/Plugins/WindowsEditor/MLAudioOutput.dll.meta @@ -0,0 +1,76 @@ +fileFormatVersion: 2 +guid: a23aaadff2001d547a0aa0b909a346eb +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/WindowsEditor/MagicLeapXrProvider.dll b/Plugins/WindowsEditor/MagicLeapXrProvider.dll new file mode 100644 index 0000000..ecc864b Binary files /dev/null and b/Plugins/WindowsEditor/MagicLeapXrProvider.dll differ diff --git a/Plugins/WindowsEditor/MagicLeapXrProvider.dll.meta b/Plugins/WindowsEditor/MagicLeapXrProvider.dll.meta new file mode 100644 index 0000000..886f4df --- /dev/null +++ b/Plugins/WindowsEditor/MagicLeapXrProvider.dll.meta @@ -0,0 +1,77 @@ +fileFormatVersion: 2 +guid: 32b6aa898f39a5f4196ad05f62f0dd11 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude Lumin: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/WindowsEditor/ml_sdk_loader.dll b/Plugins/WindowsEditor/ml_sdk_loader.dll new file mode 100644 index 0000000..c1b42f8 Binary files /dev/null and b/Plugins/WindowsEditor/ml_sdk_loader.dll differ diff --git a/Plugins/WindowsEditor/ml_sdk_loader.dll.meta b/Plugins/WindowsEditor/ml_sdk_loader.dll.meta new file mode 100644 index 0000000..347b8c8 --- /dev/null +++ b/Plugins/WindowsEditor/ml_sdk_loader.dll.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 9808d0a7e7e2e44779d521d085fd7f17 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Relish: Relish + second: + enabled: 0 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/WindowsEditor/ml_sdk_tests_provider.dll b/Plugins/WindowsEditor/ml_sdk_tests_provider.dll new file mode 100644 index 0000000..03351c4 Binary files /dev/null and b/Plugins/WindowsEditor/ml_sdk_tests_provider.dll differ diff --git a/Plugins/WindowsEditor/ml_sdk_tests_provider.dll.meta b/Plugins/WindowsEditor/ml_sdk_tests_provider.dll.meta new file mode 100644 index 0000000..b17f806 --- /dev/null +++ b/Plugins/WindowsEditor/ml_sdk_tests_provider.dll.meta @@ -0,0 +1,71 @@ +fileFormatVersion: 2 +guid: f529e180d6dce4e4595dff2a3874d403 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/WindowsEditor/ml_unity_native_logging.dll b/Plugins/WindowsEditor/ml_unity_native_logging.dll new file mode 100644 index 0000000..b7d41a3 Binary files /dev/null and b/Plugins/WindowsEditor/ml_unity_native_logging.dll differ diff --git a/Plugins/WindowsEditor/ml_unity_native_logging.dll.meta b/Plugins/WindowsEditor/ml_unity_native_logging.dll.meta new file mode 100644 index 0000000..0f637a7 --- /dev/null +++ b/Plugins/WindowsEditor/ml_unity_native_logging.dll.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: cfcf71190e60348a6a3962ef7e601848 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Relish: 1 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + AndroidSharedLibraryType: Executable + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: x86_64 + DefaultValueInitialized: true + OS: Windows + - first: + Relish: Relish + second: + enabled: 0 + settings: + CPU: X86_64 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/Audio/MLAudioOutputPlugin/MLAudioOutputPluginBehavior.cs b/Runtime/APIs/Audio/MLAudioOutputPlugin/MLAudioOutputPluginBehavior.cs index 0963420..16e306e 100644 --- a/Runtime/APIs/Audio/MLAudioOutputPlugin/MLAudioOutputPluginBehavior.cs +++ b/Runtime/APIs/Audio/MLAudioOutputPlugin/MLAudioOutputPluginBehavior.cs @@ -10,9 +10,6 @@ namespace UnityEngine.XR.MagicLeap { - using System; - using System.Runtime.InteropServices; - [RequireComponent(typeof(AudioListener))] [DisallowMultipleComponent] public class MLAudioOutputPluginBehavior : MonoBehaviour diff --git a/Runtime/APIs/Camera/API/MLCamera.cs b/Runtime/APIs/Camera/API/MLCamera.cs index 10a68e8..7529536 100644 --- a/Runtime/APIs/Camera/API/MLCamera.cs +++ b/Runtime/APIs/Camera/API/MLCamera.cs @@ -11,6 +11,7 @@ namespace UnityEngine.XR.MagicLeap { + using System; using System.Threading; using System.Threading.Tasks; using Native; @@ -21,6 +22,8 @@ namespace UnityEngine.XR.MagicLeap /// public sealed partial class MLCamera : MLCameraBase { + private const int WakeTime = 2000; + private static int instanceCounter = 0; private MLCamera() : base() => Interlocked.Increment(ref instanceCounter); @@ -254,7 +257,15 @@ public Task CaptureImageAsync(uint numImages = 1) protected override void OnApplicationPause(bool pauseStatus) { applicationPausePerfMarker.Begin(); - MLResult.Code result = pauseStatus ? Pause() : Resume(); + MLResult.Code result = MLResult.Code.Ok; + if (pauseStatus) + { + result = Pause(); + } + else + { + AsyncResume(); + } applicationPausePerfMarker.End(); if (result != MLResult.Code.Ok) { @@ -291,6 +302,12 @@ private MLResult.Code Pause(bool flagsOnly = false) return result; } + private async void AsyncResume() + { + await Task.Delay(WakeTime); + Resume(); + } + /// /// Resume the camera capture. /// diff --git a/Runtime/APIs/Camera/API/MLCameraBaseInternal.cs b/Runtime/APIs/Camera/API/MLCameraBaseInternal.cs index bed2ee1..5c82bac 100644 --- a/Runtime/APIs/Camera/API/MLCameraBaseInternal.cs +++ b/Runtime/APIs/Camera/API/MLCameraBaseInternal.cs @@ -144,14 +144,8 @@ protected void CreatePreviewTexture() int width = cameraCaptureConfig.StreamConfigs[0].Width; int height = cameraCaptureConfig.StreamConfigs[0].Height; - if (previewTexture != null && (previewTexture.width != width || previewTexture.height != height)) - { - ClearPreviewTexture(); - } - if (previewTexture == null) - { - previewTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32); - } + ClearPreviewTexture(); + previewTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32); // preview rendering not supported under Magic Leap App Simulator #if !UNITY_EDITOR diff --git a/Runtime/APIs/Dimmers/DimmerCameraControl.cs b/Runtime/APIs/Dimmers/DimmerCameraControl.cs new file mode 100644 index 0000000..17bb0e7 --- /dev/null +++ b/Runtime/APIs/Dimmers/DimmerCameraControl.cs @@ -0,0 +1,66 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2019-2023) 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% + +#if UNITY_OPENXR_1_7_0_OR_NEWER +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + [RequireComponent(typeof(Camera))] + [ExecuteInEditMode] + public class DimmerCameraControl : MonoBehaviour + { + private Camera _camera; + private Camera mainCamera; + + [SerializeField] + private Shader shader; + + // Start is called before the first frame update + private void OnEnable() + { + _camera = GetComponent(); + mainCamera = Camera.main; + if (mainCamera == null) + { + Debug.LogError($"Could not find Main Camera!"); + return; + } + + _camera.clearFlags = CameraClearFlags.Depth; + _camera.depth = mainCamera.depth - 1; + + if (shader != null) + { + if (Application.isPlaying && !Application.isEditor) + { + _camera.SetReplacementShader(shader, "RenderType"); + } + } + } + + private void OnValidate() + { + if (mainCamera != null) + { + if (_camera.depth >= mainCamera.depth) + { + Debug.LogWarning($"Camera for Dimmer must have a depth lower than the Main Camera."); + _camera.depth = mainCamera.depth - 1; + } + + if (_camera.clearFlags != CameraClearFlags.Depth) + { + Debug.LogWarning($"Camera for Dimmer must have clearFlags set to Depth only."); + _camera.clearFlags = CameraClearFlags.Depth; + } + } + } + } +} +#endif diff --git a/Runtime/APIs/Dimmers/DimmerCameraControl.cs.meta b/Runtime/APIs/Dimmers/DimmerCameraControl.cs.meta new file mode 100644 index 0000000..1634f5d --- /dev/null +++ b/Runtime/APIs/Dimmers/DimmerCameraControl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ef25e1befc734d1c90ae3a499ae9677 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/Dimmers/MLGlobalDimmer.cs b/Runtime/APIs/Dimmers/MLGlobalDimmer.cs index 9324e85..bb12a16 100644 --- a/Runtime/APIs/Dimmers/MLGlobalDimmer.cs +++ b/Runtime/APIs/Dimmers/MLGlobalDimmer.cs @@ -34,8 +34,8 @@ public static MLResult.Code SetValue(float dimmerValue, bool enabled) float clampedValue = Mathf.Clamp(dimmerValue, 0.0f, 1.0f); var dimmerInfoStruct = new NativeBindings.XrGlobalDimmerFrameEndInfoML(clampedValue, enabled); - var resultCode = NativeBindings.MLOpenXRSetGlobalDimmerFrameEndInfo(dimmerInfoStruct); - MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLOpenXRSetGlobalDimmerFrameEndInfo)); + var resultCode = NativeBindings.MLOpenXRSetGlobalDimmerFrameEndInfoParams(dimmerInfoStruct); + MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLOpenXRSetGlobalDimmerFrameEndInfoParams)); return resultCode; } diff --git a/Runtime/APIs/Dimmers/MLGlobalDimmerNativeBindings.cs b/Runtime/APIs/Dimmers/MLGlobalDimmerNativeBindings.cs index b4ea023..5642dab 100644 --- a/Runtime/APIs/Dimmers/MLGlobalDimmerNativeBindings.cs +++ b/Runtime/APIs/Dimmers/MLGlobalDimmerNativeBindings.cs @@ -35,7 +35,7 @@ public XrGlobalDimmerFrameEndInfoML(float dimmerValue, bool enabled) public static extern void UnityMagicLeap_RenderingSetGlobalDimmerValue(float dimmerValue); [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] - public static extern MLResult.Code MLOpenXRSetGlobalDimmerFrameEndInfo(XrGlobalDimmerFrameEndInfoML globalDimmerFrameEndInfoML); + public static extern MLResult.Code MLOpenXRSetGlobalDimmerFrameEndInfoParams(XrGlobalDimmerFrameEndInfoML globalDimmerFrameEndInfoML); } } } diff --git a/Runtime/APIs/Dimmers/ZWriteAlphaBlack.shader b/Runtime/APIs/Dimmers/ZWriteAlphaBlack.shader new file mode 100644 index 0000000..a3af72e --- /dev/null +++ b/Runtime/APIs/Dimmers/ZWriteAlphaBlack.shader @@ -0,0 +1,61 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) 2023 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% + +Shader "Magic Leap/Z-Write Alpha Black" +{ + Properties + { + [HideInInspector] _MainTex("Texture", 2D) = "white" {} + } + + SubShader + { + Tags { "RenderType" = "Opaque" } + LOD 100 + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + float4 vertex : SV_POSITION; + }; + + sampler2D _MainTex; + + v2f vert(appdata v) + { + v2f o; + o.vertex = UnityObjectToClipPos(v.vertex); + o.uv = v.uv; + return o; + } + + fixed4 frag(v2f i) : SV_Target + { + fixed4 col = tex2D(_MainTex, i.uv); + return fixed4(0, 0, 0, 1.0); + } + ENDCG + } + } +} diff --git a/Runtime/APIs/Dimmers/ZWriteAlphaBlack.shader.meta b/Runtime/APIs/Dimmers/ZWriteAlphaBlack.shader.meta new file mode 100644 index 0000000..9629597 --- /dev/null +++ b/Runtime/APIs/Dimmers/ZWriteAlphaBlack.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: b25330b849e774ec4b78d1a5c6fa6714 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/FacialExpression.meta b/Runtime/APIs/FacialExpression.meta new file mode 100644 index 0000000..7615cad --- /dev/null +++ b/Runtime/APIs/FacialExpression.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fa87af8ca1704d8fb8e6f61a5274671 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/FacialExpression/MLFacialExpression.cs b/Runtime/APIs/FacialExpression/MLFacialExpression.cs new file mode 100644 index 0000000..b58df4d --- /dev/null +++ b/Runtime/APIs/FacialExpression/MLFacialExpression.cs @@ -0,0 +1,199 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) 2023 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% + +using System; +using System.Runtime.InteropServices; +using UnityEngine.XR.MagicLeap.Native; +namespace UnityEngine.XR.MagicLeap +{ + public partial class MLFacialExpression : MLAutoAPISingleton + { + #region StructsAndEnums + /// + /// Available facial expressions. + /// + public enum EyeExpressionType + { + /// + /// Blinking the left eye. + /// + BlinkLeft = 0, + + /// + /// Blinking of the right eye. + /// + BlinkRight, + + /// + /// Lower Lid upward movement of the left eye. + /// + LidTightenerLeft, + + /// + /// Lower Lid upward movement of the right eye. + /// + LidTightenerRight, + + /// + /// Upper lid upward movement of the left eye. + /// + EyeOpennessLeft, + + /// + /// Upper lid upward movement of the right eye. + /// + EyeOpennessRight, + + /// + /// Upward cheek movement, left. + /// + CheekRaiserLeft, + + /// + /// Upward cheek movement, right. + /// + CheekRaiserRight, + + /// + /// Downward brow movement, left. + /// + BrowLowererLeft, + + /// + /// Downward brow movement, right. + /// + BrowLowererRight, + + /// + /// Upward brow movement, left side. + /// + BrowRaiserLeft, + + /// + /// Upward brow movement, right side. + /// + BrowRaiserRight + } + + /// + /// A structure containing settings for the facial expressions. + /// + public struct Settings + { + /// + /// Enable MLFacialExpressionEyeData. If true, facial expressions will + /// detect EyeData and the same can queried GetEyeData. This should be + /// disabled when app does not need facial expression data + /// + public bool EnableEyeExpression; + } + + /// + /// A structure containing information about eye expressions. + /// + public struct EyeData + { + /// + /// The MLTime timestamp when expression data was updated. + /// + public MLTime Timestamp; + + /// + /// The values are between 0 and 1 and ordered such that the array should + /// be indexed using EyeExpressionType. + /// + public float[] EyeExpressionWeights; + } + #endregion + + /// + /// Start the API. + /// + protected override MLResult.Code StartAPI() => Instance.InternalMLFacialExpressionCreate(); + + /// + /// Stop the API. + /// + protected override MLResult.Code StopAPI() => Instance.InternalMLFacialExpressionStop(); + + /// + /// Updates Facial Expression system with new settings. + /// + /// + public static MLResult.Code UpdateSettings(in Settings settings) => Instance.InternalUpdateSettings(settings); + + /// + /// Get the latest eye data from the Facial Expression system. + /// + public static MLResult.Code GetEyeData(out EyeData data) => Instance.InternalGetEyeData(out data); + + #region InternalMethods + /// + /// Creates Facial Expression system client. + /// + private MLResult.Code InternalMLFacialExpressionCreate() + { + if (!MLResult.DidNativeCallSucceed(MLPermissions.CheckPermission(MLPermission.FacialExpression).Result)) + { + MLPluginLog.Error($"{nameof(MLFacialExpression)} requires missing permission {MLPermission.FacialExpression}"); + return MLResult.Code.PermissionDenied; + } + + NativeBindings.MLFacialExpressionSettings settings = NativeBindings.MLFacialExpressionSettings.Init(); + MLResult.Code resultCode = NativeBindings.MLFacialExpressionCreateClient(ref settings, out Handle); + MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLFacialExpressionCreateClient)); + + return resultCode; + } + + /// + /// Destroy Facial Expression system client. + /// + private MLResult.Code InternalMLFacialExpressionStop() + { + var resultCode = NativeBindings.MLFacialExpressionDestroyClient(Handle); + MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLFacialExpressionDestroyClient)); + return resultCode; + } + + /// + /// Updates API settings. + /// + private MLResult.Code InternalUpdateSettings(Settings settings) + { + NativeBindings.MLFacialExpressionSettings newSettings = NativeBindings.MLFacialExpressionSettings.Init(); + newSettings.EnableEyeExpression = settings.EnableEyeExpression; + var resultCode = NativeBindings.MLFacialExpressionUpdateSettings(Handle, newSettings); + MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLFacialExpressionDestroyClient)); + + return resultCode; + } + + /// + /// Grabs eye data. + /// + /// + private MLResult.Code InternalGetEyeData(out EyeData data) + { + + data = new(); + NativeBindings.MLFacialExpressionEyeData outData = NativeBindings.MLFacialExpressionEyeData.Init(); + var resultCode = NativeBindings.MLFacialExpressionGetEyeData(Handle, out outData); + if (MLResult.DidNativeCallSucceed(resultCode, nameof(NativeBindings.MLFacialExpressionGetEyeData))) + { + data.EyeExpressionWeights = outData.EyeExpressionWeights; + data.Timestamp = outData.Timestamp; + } + + return resultCode; + } + #endregion + } +} diff --git a/Runtime/APIs/FacialExpression/MLFacialExpression.cs.meta b/Runtime/APIs/FacialExpression/MLFacialExpression.cs.meta new file mode 100644 index 0000000..4c9b33d --- /dev/null +++ b/Runtime/APIs/FacialExpression/MLFacialExpression.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fde32b4f2770c42a28c5cc1a7050e2cf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/FacialExpression/MLFacialExpressionNativeBindings.cs b/Runtime/APIs/FacialExpression/MLFacialExpressionNativeBindings.cs new file mode 100644 index 0000000..af9ef64 --- /dev/null +++ b/Runtime/APIs/FacialExpression/MLFacialExpressionNativeBindings.cs @@ -0,0 +1,174 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2018-2023) 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% + +using UnityEngine.XR.MagicLeap.Native; +namespace UnityEngine.XR.MagicLeap +{ + using System; + using System.Runtime.InteropServices; + public partial class MLFacialExpression + { + /// + /// See ml_facial_expression.h for additional comments. + /// + private class NativeBindings : Native.MagicLeapNativeBindings + { + /// + /// Number of eye expression types. + /// + const int MLFacialExpressionEyeExpressionTypeCount = 12; + + /// + /// A structure containing settings for the facial expressions. + /// This structure must be initialized by calling #MLFacialExpressionSettingsInit before use. + /// (And calling ZeroStruct on our side.) + /// + [StructLayout(LayoutKind.Sequential)] + public struct MLFacialExpressionSettings + { + /// + /// Version of this settings + /// + public uint Version; + + /// + /// Enable MLFacialExpressionEyeData. + /// If true, facial expressions will detect #MLFacialExpressionEyeData and the same can queried using + /// MLFacialExpressionGetEyeData. This should be disabled when app does not need facial expression data. + /// + public bool EnableEyeExpression; + + public static MLFacialExpressionSettings Init(uint version = 1) + { + return new MLFacialExpressionSettings + { + Version = version, + EnableEyeExpression = true + }; + } + } + + /// + /// A structure containing information about eye expressions. + /// This structure must be initialized by calling MLFacialExpressionEyeDataInit before use. + /// + [StructLayout(LayoutKind.Sequential)] + public struct MLFacialExpressionEyeData + { + /// + /// Version of the structure. + /// + public uint Version; + + /// + /// The MLTime timestamp when expression data was updated. + /// + public long Timestamp; + + /// + /// An array of floats of size eye_expression_count. The values are between 0 + /// and 1 and ordered such that the array can be indexed using + /// MLFacialExpressionEyeExpressionType. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MLFacialExpressionEyeExpressionTypeCount)] + public float[] EyeExpressionWeights; + + public static MLFacialExpressionEyeData Init(uint version = 1) + { + return new MLFacialExpressionEyeData + { + Version = version, + }; + } + } + + /// + /// Creates a Facial Expression Client handle. Although multiple client handles + /// can be created they all represent the same facial expressions backend system. + /// + /// API Level 29 + /// permissions com.magicleap.permission.FACIAL_EXPRESSION (protection level: dangerous) + /// + /// Settings that configures the facial expressions system. + /// + /// + /// The handle to be created. + /// + /// + /// MLResult.Code.InvalidParam: One or more input parameters are not valid.
+ /// MLResult.Code.Ok: Facial expressions client was successfully created.
+ /// MLResult.Code.PerceptionSystemNotStarted: Perception System has not been started.
+ /// MLResult.Code.PermissionDenied: Necessary permission is missing.
+ /// MLResult.Code.UnspecifiedFailure: Operation failed for unknown reason. + ///
+ [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLFacialExpressionCreateClient(ref MLFacialExpressionSettings settings, out ulong handle); + + /// + /// Update the Facial Expression system with new settings + /// + /// API Level 29 + /// permissions None + /// + /// Handle Facial expressions client handle created by MLFacialExpressionCreateClient. + /// + /// + /// Settings New Facial Expression settings.. + /// + /// + /// MLResult.Code.InvalidParam: One or more input parameters are not valid.
+ /// MLResult.Code.Ok: Facial expression settings was updated successfully.
+ /// MLResult.Code.PerceptionSystemNotStarted: Perception System has not been started.
+ /// MLResult.Code.UnspecifiedFailure: Operation failed for unknown reason.
+ ///
+ [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLFacialExpressionUpdateSettings(ulong handle, in MLFacialExpressionSettings settings); + + /// + /// Get facial expressions data. + /// handle Facial expression client handle created by MLFacialExpressionCreateClient. + /// API Level 29 + /// permissions None + /// + /// + /// Facial expressions client handle created by MLFacialExpressionCreateClient. + /// + /// + /// Eye expressions data. + /// + /// + /// MLResult.Code.InvalidParam: One or more input parameters are not valid.
+ /// MLResult.Code.Ok: Facial expressions data was retrieved successfully.
+ /// MLResult.Code.PerceptionSystemNotStarted: Perception System has not been started.
+ /// MLResult.Code.UnspecifiedFailure: Operation failed for unknown reason.
+ /// MLResult.Code.HeadsetFitIssue: Operation failed because unable to detect the eyes, check MLHeadsetFitStatus. + ///
+ [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLFacialExpressionGetEyeData(ulong handle, out MLFacialExpressionEyeData eye_data); + + /// + /// Destroy client handle and free client resources. + /// API Level 29 + /// permissions None + /// + /// + /// Facial expression client handle created by MLFacialExpressionCreateClient. + /// + /// + /// MLResult.Code.InvalidParam: One or more input parameters are not valid.
+ /// MLResult.Code.Ok: Client handle was successfully destroyed.
+ /// MLResult.Code.PerceptionSystemNotStarted: Perception System has not been started.
+ /// MLResult.Code.UnspecifiedFailure: Operation failed for unknown reason. + ///
+ [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLFacialExpressionDestroyClient(ulong handle); + } + } +} \ No newline at end of file diff --git a/Runtime/APIs/FacialExpression/MLFacialExpressionNativeBindings.cs.meta b/Runtime/APIs/FacialExpression/MLFacialExpressionNativeBindings.cs.meta new file mode 100644 index 0000000..d49d9a7 --- /dev/null +++ b/Runtime/APIs/FacialExpression/MLFacialExpressionNativeBindings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c5a6d431b9fc460f9c9d7cd680a1939 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/Graphics/GraphicsHooks.cs b/Runtime/APIs/Graphics/GraphicsHooks.cs index f215691..fdaf3cd 100644 --- a/Runtime/APIs/Graphics/GraphicsHooks.cs +++ b/Runtime/APIs/Graphics/GraphicsHooks.cs @@ -38,7 +38,7 @@ public static void RequestAlphaBlendFrameRendering(bool useAlphaBlend) } else { - ClearCallbacks(); + CleanUp(); } } @@ -51,7 +51,8 @@ public static void RequestPredictedSnapshots(bool useSnapshots) // clean up. public static void Shutdown() { - ClearCallbacks(); + usePredictedSnapshots = false; + CleanUp(); } private static void RegisterCallbacks() @@ -64,13 +65,13 @@ private static void RegisterCallbacks() } } - private static void ClearCallbacks() + private static void CleanUp() { if (registeredForRenderCallbacks) { - registeredForRenderCallbacks = false; - NativeBindings.MLUnityGraphicsClearCallbacks(); + NativeBindings.ClearCallbacks(); } + registeredForRenderCallbacks = false; } private static event OnPreBeginRenderFrameDelegate internalOnPreBeginRenderFrame = delegate { }; diff --git a/Runtime/APIs/Graphics/GraphicsHooksNativeBindings.cs b/Runtime/APIs/Graphics/GraphicsHooksNativeBindings.cs index 8f8949c..f552961 100644 --- a/Runtime/APIs/Graphics/GraphicsHooksNativeBindings.cs +++ b/Runtime/APIs/Graphics/GraphicsHooksNativeBindings.cs @@ -8,7 +8,7 @@ // --------------------------------------------------------------------- // %BANNER_END% -using System; +using System; using System.Runtime.InteropServices; namespace UnityEngine.XR.MagicLeap @@ -77,7 +77,13 @@ private static void OnPreBeginRenderFrameCallback(IntPtr context, ref MLGraphics [AOT.MonoPInvokeCallback(typeof(OnBeginRenderFrameNativeDelegate))] private static void OnBeginRenderFrameCallback(IntPtr context, long predictedDisplayTime) => InputSubsystem.Utils.PredictSnapshot(predictedDisplayTime, usePredictedSnapshots); - + + public static void ClearCallbacks() + { + MLUnityGraphicsClearCallbacks(); + InputSubsystem.Utils.ResetSnapshotPrediction(); + } + [StructLayout(LayoutKind.Sequential)] public struct MLUnityGraphicsCallbacks { diff --git a/Runtime/APIs/MediaPlayer/API/MLMediaPlayer.cs b/Runtime/APIs/MediaPlayer/API/MLMediaPlayer.cs index 80bc1ad..10a628b 100644 --- a/Runtime/APIs/MediaPlayer/API/MLMediaPlayer.cs +++ b/Runtime/APIs/MediaPlayer/API/MLMediaPlayer.cs @@ -402,7 +402,6 @@ public MLResult SetStreamingSourcePath(string path) return MLResult.Create(MLResult.Code.AllocFailed); } - //streamingAssetWebRequest.downloadHandler = new DownloadHandlerBuffer(); streamingAssetWebRequestAsyncOp = streamingAssetWebRequest.SendWebRequest(); streamingAssetWebRequestAsyncOp.completed += (AsyncOperation asyncOp) => { @@ -424,7 +423,6 @@ public MLResult SetStreamingSourcePath(string path) Debug.LogError($"Failed to create dataSource for {path}"); return; } - this.Source = path; PreparePlayerAsync(); }; diff --git a/Runtime/APIs/MediaPlayer/Scripts/MLMediaPlayerBehavior.cs b/Runtime/APIs/MediaPlayer/Scripts/MLMediaPlayerBehavior.cs index f5cde12..3d8e73f 100644 --- a/Runtime/APIs/MediaPlayer/Scripts/MLMediaPlayerBehavior.cs +++ b/Runtime/APIs/MediaPlayer/Scripts/MLMediaPlayerBehavior.cs @@ -113,15 +113,13 @@ public MLMedia.Player MediaPlayer private MLMedia.Player _mediaPlayer; - private bool renderVideo = false; - void Update() { if (!Application.isEditor && MediaPlayer.IsPlaying && MediaPlayer.VideoRenderer != null) { MediaPlayer.VideoRenderer.Render(); } - if (DurationInMiliseconds > 0 && IsPlaying && !IsSeeking && !IsBuffering && renderVideo) + if (DurationInMiliseconds > 0 && IsPlaying && !IsSeeking && !IsBuffering) { UpdateTimeline(); } @@ -147,6 +145,12 @@ private void OnDestroy() _mediaPlayer.OnResetComplete -= HandleOnResetComplete; _mediaPlayer.OnTrackFound -= HandleOnTrackFound; + if (mediaPlayerTexture != null) + { + mediaPlayerTexture.Release(); + Destroy(mediaPlayerTexture); + } + StopMLMediaPlayer(); _mediaPlayer.Reset(); _mediaPlayer.Destroy(); @@ -203,8 +207,13 @@ public void PrepareMLMediaPlayer() if (!result.IsOk) { - string message = "PrepareMLMediaPlayer failed, source could not be set."; - MLPluginLog.Warning(message); + // SetStreamingSourcePath() will call PreparePlayerAsync() and always return + // MLResult.Code.Pending, so we skip logging that result and return. + if(result != MLResult.Code.Pending) + { + string message = "PrepareMLMediaPlayer failed, source could not be set for " + source + ": " + result.ToString(); + MLPluginLog.Error(message); + } return; } @@ -232,6 +241,7 @@ private void SetRendererTexture(RenderTexture texture) { if (mediaPlayerTexture != texture) { + mediaPlayerTexture.Release(); Destroy(mediaPlayerTexture); mediaPlayerTexture = null; } @@ -322,6 +332,7 @@ private void CreateTexture(int width, int height) if (mediaPlayerTexture != null && (mediaPlayerTexture.width != width || mediaPlayerTexture.height != height)) { + mediaPlayerTexture.Release(); Destroy(mediaPlayerTexture); mediaPlayerTexture = null; } @@ -536,12 +547,6 @@ private void HandleOnInfo(MLMedia.Player mediaPlayer, MLMedia.Player.Info info) IsBuffering = false; OnIsBufferingChanged?.Invoke(false); break; - case MLMedia.Player.Info.RenderingStart: - renderVideo = true; - break; - case MLMedia.Player.Info.Stopped: - renderVideo = false; - break; } } diff --git a/Runtime/APIs/NativeSurface/YcbcrRenderer.cs b/Runtime/APIs/NativeSurface/YcbcrRenderer.cs index 18df208..ad472c8 100644 --- a/Runtime/APIs/NativeSurface/YcbcrRenderer.cs +++ b/Runtime/APIs/NativeSurface/YcbcrRenderer.cs @@ -131,6 +131,11 @@ public void Render() // Only execute SetTexture cmd buffer if render target hw resources have been created. if (renderTarget != null && renderTarget.IsCreated() && !didExecuteSetTextureCmdBuffer) { + if(renderTarget.colorBuffer.GetNativeRenderBufferPtr() == IntPtr.Zero) + { + Debug.LogWarning("Skipping null render buffer"); + return; + } eventData = new NativeBindings.PluginEventData(handle, renderTarget); Marshal.StructureToPtr(eventData, eventDataPtr, false); CreateAndStoreCommandBufferForEvent(NativeBindings.PluginEvent.SetTexture); diff --git a/Runtime/APIs/Occlusion.meta b/Runtime/APIs/Occlusion.meta new file mode 100644 index 0000000..797bae2 --- /dev/null +++ b/Runtime/APIs/Occlusion.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7cd2eb847380e46eaad6a1ae600698a6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/Occlusion/MLOcclusion.cs b/Runtime/APIs/Occlusion/MLOcclusion.cs new file mode 100644 index 0000000..90314f1 --- /dev/null +++ b/Runtime/APIs/Occlusion/MLOcclusion.cs @@ -0,0 +1,193 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) 2023 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% + +using System; +using System.IO; +using System.Runtime.InteropServices; +using static UnityEngine.XR.MagicLeap.Native.MagicLeapNativeBindings; + +namespace UnityEngine.XR.MagicLeap +{ + public partial class MLOcclusion : MLAutoAPISingleton + { + + NativeBindings.MLOcclusionMesh outMesh = NativeBindings.MLOcclusionMesh.Init(); + NativeBindings.MLOcclusionMeshQuery meshQuery = NativeBindings.MLOcclusionMeshQuery.Init(); + + public struct Settings + { + /// + /// Minimum distance in meters to occlude objects. + /// Possible values ranges from 0.3m up to 7.5m. + /// + public float MinDistance; + + /// + /// Maximum distance in meters to occlude objects. + /// Possible values ranges from 0.3m up to 7.5m. + /// + public float MaxDistance; + } + + public struct OcclusionMesh + { + /// + /// The timestamp when data was generated. + /// + public MLTime Timestamp; + + /// + /// The number of indices in index buffer. + /// + public uint IndexCount; + + /// + /// The number of vertices in vertex buffer. + /// + public uint VertexCount; + + /// + ///// Pointer to the vertex buffer. + ///// All vertices are placed w.r.t.world origin. + ///// + public Vector3[] Vertex; + + ///// + ///// Pointer to index buffer. + ///// In the index buffer each value is the index of a vertex in the vertex buffer. + ///// Three indices define one triangle. For example, the first triangle will have + ///// the vertices: vertex[index[0]], vertex[index[1]], vertex[index[2]]. + ///// Index order is CW. + ///// + public int[] Index; + } + + protected override MLResult.Code StartAPI() => Instance.InternalMLOcclusionStart(); + + protected override MLResult.Code StopAPI() => Instance.InternalMLOcclusionStop(); + + public static MLResult.Code UpdateSettings(in Settings settings) => Instance.InternalMLOcclusionUpdateSettings(in settings); + + public static MLResult.Code GetLatestMesh(out OcclusionMesh mesh) => Instance.InternalGetLatestMesh(out mesh); + + MLResult.Code InternalMLOcclusionStart() + { + if (!MLResult.DidNativeCallSucceed(MLPermissions.CheckPermission(MLPermission.SpatialMapping).Result)) + { + MLPluginLog.Error($"{nameof(MLOcclusion)} requires missing permission {MLPermission.SpatialMapping}"); + return MLResult.Code.PermissionDenied; + } + + NativeBindings.MLOcclusionSettings settings = NativeBindings.MLOcclusionSettings.Init(); + MLResult.Code result = NativeBindings.MLOcclusionCreateClient(settings, out Handle); + MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLOcclusionCreateClient)); + return result; + } + + MLResult.Code InternalMLOcclusionStop() + { + MLResult.Code result = NativeBindings.MLOcclusionDestroyClient(Handle); + MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLOcclusionDestroyClient)); + return result; + } + + MLResult.Code InternalMLOcclusionUpdateSettings(in Settings settings) + { + NativeBindings.MLOcclusionSettings internalSettings = NativeBindings.MLOcclusionSettings.Init(); + internalSettings.MaxDistance = settings.MaxDistance; + internalSettings.MinDistance = settings.MinDistance; + MLResult.Code result = NativeBindings.MLOcclusionUpdateSettings(Handle, internalSettings); + MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLOcclusionUpdateSettings)); + return result; + } + + MLResult.Code InternalGetLatestMesh(out OcclusionMesh mesh) + { + mesh = new(); + MLResult.Code result = NativeBindings.MLOcclusionGetLatestMesh(Handle, in meshQuery, out outMesh); + + if (MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLOcclusionGetLatestMesh))) + { + + MLTime.ConvertSystemTimeToMLTime((long)outMesh.Timestamp, out mesh.Timestamp); + mesh.IndexCount = outMesh.IndexCount; + mesh.VertexCount = outMesh.VertexCount; + + MarshalIndexArray(outMesh.Index, (int)outMesh.IndexCount, out mesh.Index); + ReverseIndices(mesh.Index); + + MarshalVectorArray(outMesh.Vertex, (int)outMesh.VertexCount, out mesh.Vertex); + + result = NativeBindings.MLOcclusionReleaseMesh(Handle, out outMesh); + MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLOcclusionReleaseMesh)); + } + + return result; + } + + /// + /// Special vector marshalling used in place of array marshal for performance and to apply + /// extra z-negation operation (inverted rendering otherwise, works alongside ReverseIndices) + /// + /// Pointer to index buffer + /// number of elements in index buffer + /// location for marshalled index array + void MarshalVectorArray(IntPtr verticesPtr, int count, out Vector3[] vertex) + { + vertex = new Vector3[count]; + int totalSize = Marshal.SizeOf() * count; + byte[] tempBuffer = new byte[totalSize]; + Marshal.Copy(verticesPtr, tempBuffer, 0, totalSize); + + using (MemoryStream memoryStream = new MemoryStream(tempBuffer)) + using (BinaryReader reader = new BinaryReader(memoryStream)) + { + float x, y, z; + for (int i = 0; i < count; ++i) + { + x = reader.ReadSingle(); + y = reader.ReadSingle(); + z = reader.ReadSingle(); + vertex[i] = new Vector3(x, y, -z); + } + } + } + /// + /// Special index marshalling used in place of array marshal for performance + /// + /// Pointer to index buffer + /// number of elements in index buffer + /// location for marshalled index array + void MarshalIndexArray(IntPtr indicesPtr, int count, out int[] index) + { + index = new int[count]; + int tSize = Marshal.SizeOf(); + + byte[] tempBuffer = new byte[tSize * count]; + Marshal.Copy(indicesPtr, tempBuffer, 0, tSize * count); + Buffer.BlockCopy(tempBuffer, 0, index, 0, tSize * count); + } + + /// + /// Reverses the indices passed from `MLOcclusionGetLatestMesh` from CW to CCW. + /// It does so by swaping the outer indices (index[0] & index[2]) around. + /// + /// Indices to reverse. + void ReverseIndices(int[] idx) + { + for (int i = 0; i < idx.Length; i += 3) + { + int temp = idx[i]; + idx[i] = idx[i + 2]; + idx[i + 2] = temp; + } + } + } +} diff --git a/Runtime/APIs/Occlusion/MLOcclusion.cs.meta b/Runtime/APIs/Occlusion/MLOcclusion.cs.meta new file mode 100644 index 0000000..43882e0 --- /dev/null +++ b/Runtime/APIs/Occlusion/MLOcclusion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38e297607d8a647f0892321740dc96ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/Occlusion/MLOcclusionNativeBindings.cs b/Runtime/APIs/Occlusion/MLOcclusionNativeBindings.cs new file mode 100644 index 0000000..e0d1b82 --- /dev/null +++ b/Runtime/APIs/Occlusion/MLOcclusionNativeBindings.cs @@ -0,0 +1,130 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2018-2023) 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; + public partial class MLOcclusion + { + /// + /// See ml_occlusion.h for additional comments. + /// + private class NativeBindings : Native.MagicLeapNativeBindings + { + [StructLayout(LayoutKind.Sequential)] + public struct MLOcclusionSettings + { + /// + /// Struct version. + /// + public uint Version; + + /// + /// Minimum distance in meters to occlude objects. + /// Possible values ranges from 0.3m up to 7.5m. + /// + public float MinDistance; + + /// + /// Maximum distance in meters to occlude objects. + // Possible values ranges from 0.3m up to 7.5m. + /// + public float MaxDistance; + public static MLOcclusionSettings Init(uint version = 1) + { + return new MLOcclusionSettings + { + Version = version, + MaxDistance = 5f, + MinDistance = 0.3f, + }; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct MLOcclusionMesh + { + /// + /// Struct version. + /// + public uint Version; + + /// + /// The timestamp when data was generated. + /// + public ulong Timestamp; + + /// + /// The number of indices in index buffer. + /// + public uint IndexCount; + + /// + /// The number of vertices in vertex buffer. + /// + public uint VertexCount; + + /// + /// Pointer to the vertex buffer. + /// All vertices are placed w.r.t.world origin. + /// + public IntPtr Vertex; + + /// + /// Pointer to index buffer. + /// In the index buffer each value is the index of a vertex in the vertex buffer. + /// Three indices define one triangle.For example, the first triangle + /// will have the vertices: vertex[index[0]], vertex[index[1]], vertex[index[2]]. + /// Index order is CW. + /// + public IntPtr Index; + + public static MLOcclusionMesh Init(uint version = 1) + { + return new MLOcclusionMesh + { + Version = version, + Vertex = IntPtr.Zero, + Index = IntPtr.Zero, + }; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct MLOcclusionMeshQuery + { + public uint Version; + public static MLOcclusionMeshQuery Init(uint version = 1) + { + return new MLOcclusionMeshQuery + { + Version = version, + }; + } + } + + [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOcclusionCreateClient(in MLOcclusionSettings settings, out ulong handle); + + [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOcclusionDestroyClient(ulong handle); + + [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOcclusionUpdateSettings(ulong handle, in MLOcclusionSettings settings); + + [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOcclusionGetLatestMesh(ulong handle, in MLOcclusionMeshQuery mesh_query, out MLOcclusionMesh out_mes_data); + + [DllImport(MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOcclusionReleaseMesh(ulong handle, out MLOcclusionMesh out_mes_data); + } + } +} \ No newline at end of file diff --git a/Runtime/APIs/Occlusion/MLOcclusionNativeBindings.cs.meta b/Runtime/APIs/Occlusion/MLOcclusionNativeBindings.cs.meta new file mode 100644 index 0000000..fabc2ec --- /dev/null +++ b/Runtime/APIs/Occlusion/MLOcclusionNativeBindings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0fca903ce86e9478fa5dc03836f55d44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/APIs/PowerManager/MLPowerManager.cs b/Runtime/APIs/PowerManager/MLPowerManager.cs index 3d277bd..1f36e12 100644 --- a/Runtime/APIs/PowerManager/MLPowerManager.cs +++ b/Runtime/APIs/PowerManager/MLPowerManager.cs @@ -24,27 +24,40 @@ public partial class MLPowerManager : MLAutoAPISingleton protected override MLResult.Code StopAPI() => Instance.InternalDestroyManager(); /// - /// Set the power state for the controller. + /// Sets the power state of a component. + /// + /// Settings used by the Power Manager updating a component's power state. + /// /// public static MLResult.Code SetPowerState(Settings settings) => Instance.InternalSetPowerState(settings); /// /// Get controller component properties. + /// Information about the properties of a component. /// public static MLResult.Code GetComponentProperties(out PropertyData out_properties) => Instance.InternalGetComponentProperties(out out_properties); /// - /// Get available power states for the controller. + /// Get available power states for a component. + /// + /// #PowerStateData holding list of available power states. + /// /// public static MLResult.Code GetAvailablePowerStates(out PowerStateData data) => Instance.InternalGetAvailablePowerStates(out data); /// - /// Get the current power state for the controller. + /// Get the current power state for a component. + /// + /// #PowerStateData with the current power state of the component. + /// /// public static MLResult.Code GetPowerState(out PowerStateData data) => Instance.InternalGetPowerState(out data); /// - /// Get available properties for the controller. + /// Request a list of the available #PropertyType. + /// + /// Information about the properties of a component. + /// /// public static MLResult.Code GetAvailableProperties(out PropertyTypeData data) => Instance.InternalGetAvailableProperties(out data); diff --git a/Runtime/APIs/PowerManager/MLPowerManagerStructs.cs b/Runtime/APIs/PowerManager/MLPowerManagerStructs.cs index a2354ef..9f24265 100644 --- a/Runtime/APIs/PowerManager/MLPowerManagerStructs.cs +++ b/Runtime/APIs/PowerManager/MLPowerManagerStructs.cs @@ -76,7 +76,7 @@ public struct PropertyData public struct PropertyTypeData { /// - /// Array of #MLPowerManagerPropertyType elements. + /// Array of #PropertyType elements. /// public PropertyType[] PropertyTypes; } diff --git a/Runtime/APIs/Spaces/MLSpace.cs b/Runtime/APIs/Spaces/MLSpace.cs index 0347eb2..64c9043 100644 --- a/Runtime/APIs/Spaces/MLSpace.cs +++ b/Runtime/APIs/Spaces/MLSpace.cs @@ -28,17 +28,20 @@ public partial class MLSpace : MLAutoAPISingleton protected override MLResult.Code StopAPI() => Instance.InternalMLSpacesStop(); /// - /// Export spaces wrapper. + /// Export an on device Magic Leap Space. /// public static MLResult.Code ExportSpace(in SpaceInfo info, out SpaceData data) => Instance.InternalExportSpace(in info, out data); /// - /// Import spaces wrapper. + /// Import a Magic Leap Space. /// public static MLResult.Code ImportSpace(in SpaceData data, out SpaceInfo id) => Instance.InternalImportSpace(in data, out id); /// - /// Get list of available spaces. + /// Get the list of available spaces. + /// The list of spaces returned will depend on the current device mapping mode. + /// Only the Spaces associated with the current mapping mode will be returned by + /// this call.Device mapping mode can be changed via the system application(s). /// public static MLResult.Code GetSpaceList(out Space[] spaceList) => Instance.InternalGetSpaceList(out spaceList); diff --git a/Runtime/APIs/Spaces/MLSpaceNativeBindings.cs b/Runtime/APIs/Spaces/MLSpaceNativeBindings.cs index 87e6814..9e40ca0 100644 --- a/Runtime/APIs/Spaces/MLSpaceNativeBindings.cs +++ b/Runtime/APIs/Spaces/MLSpaceNativeBindings.cs @@ -33,11 +33,6 @@ public class NativeBindings : Native.MagicLeapNativeBindings public static extern MLResult.Code MLSpaceSetCallbacks(ulong handle, ref SpaceCallbacks callbacks, IntPtr userData ); /// - /// Get the list of available spaces. - /// The list of spaces returned will depend on the current device mapping mode. - /// Only the Spaces associated with the current mapping mode will be returned by - /// this call.Device mapping mode can be changed via the system application(s). - /// /// The list memory is owned by the library, call #MLSpaceReleaseSpaceList to /// release the memory. Each get #MLSpaceGetSpaceList should have a corresponding /// #MLSpaceReleaseSpaceList. @@ -70,7 +65,6 @@ public class NativeBindings : Native.MagicLeapNativeBindings public static extern MLResult.Code MLSpaceManagerDestroy(ulong handle); /// - /// Import a Magic Leap Space. /// The #MLSpaceImportInfo memory is owned by the app and the app should make sure /// to release the memory once the API call has returned /// diff --git a/Runtime/AssemblyInfo.cs b/Runtime/AssemblyInfo.cs index fcbd8f1..8d0acab 100644 --- a/Runtime/AssemblyInfo.cs +++ b/Runtime/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("MagicLeap.SDK.Private")] +[assembly: InternalsVisibleTo("MagicLeap.SDK.Tests")] diff --git a/Runtime/Common/MagicLeapNativeBindings.cs b/Runtime/Common/MagicLeapNativeBindings.cs index 9320d59..ecfaef4 100644 --- a/Runtime/Common/MagicLeapNativeBindings.cs +++ b/Runtime/Common/MagicLeapNativeBindings.cs @@ -265,6 +265,35 @@ internal static string MLGetInputResultString(MLResult.Code resultCode) [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. @@ -847,5 +876,82 @@ private static void FlipGuidComponents(byte[] 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, + }; + } + } + } } diff --git a/Runtime/Common/NativeSyncBuffer.cs b/Runtime/Common/NativeSyncBuffer.cs new file mode 100644 index 0000000..d3804fb --- /dev/null +++ b/Runtime/Common/NativeSyncBuffer.cs @@ -0,0 +1,206 @@ +using System; +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using UnityEngine.PlayerLoop; +using UnityEngine.XR.MagicLeap.Unsafe; + +namespace UnityEngine.XR.MagicLeap +{ + [NativeContainer] + internal unsafe struct NativeSyncBuffer : IDisposable where T : unmanaged + { + private struct BufferData + { + public T input; + public T output; + } + + private struct DisposeData + { + [NativeDisableUnsafePtrRestriction] + public BufferData* m_Data; + + public Allocator m_Allocator; + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + public AtomicSafetyHandle m_Safety; +#endif + } + + private struct DisposeJob : IJob + { + public DisposeData Data; + + public void Execute() + { + if (Data.m_Data == null || Data.m_Allocator <= Allocator.None) + return; + + UnsafeUtility.FreeTracked(Data.m_Data, Data.m_Allocator); + } + } + + private struct SyncJob : IJob + { + public NativeSyncBuffer Buffer; + + public void Execute() => Buffer.Sync(); + } + + private struct UpdateJob : IJob + { + public NativeSyncBuffer Buffer; + public T m_Data; + + public void Execute() => Buffer.UpdateInput(m_Data); + } + + [NativeDisableUnsafePtrRestriction] + private BufferData* m_BufferData; + private Allocator m_Allocator; + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + internal AtomicSafetyHandle m_Safety; + + internal static readonly int s_staticSafetyId = AtomicSafetyHandle.NewStaticSafetyId>(); +#endif + + public bool IsValid => m_BufferData != null && m_Allocator != Allocator.Invalid; + + public T Input + { + set => UpdateInput(value); + } + + public T Output => *GetOutputPointerReadOnly(); + + public NativeSyncBuffer(Allocator allocator, T initialState = default) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + if (!UnsafeUtility.IsUnmanaged()) + throw new Exception( + $"{typeof(T).Name} is a managed type and is not a valid type for use in a NativeSyncBuffer container!"); + + m_Safety = AtomicSafetyHandle.Create(); + AtomicSafetyHandle.SetStaticSafetyId(ref m_Safety, s_staticSafetyId); + AtomicSafetyHandle.SetNestedContainer(m_Safety, UnsafeUtility.IsNativeContainerType()); +#endif + m_BufferData = UnsafeUtilityEx.MallocTracked(allocator, 1); + *m_BufferData = new BufferData() + { + input = initialState, + output = initialState, + }; + m_Allocator = allocator; + } + + public void Dispose() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckDeallocateAndThrow(m_Safety); + AtomicSafetyHandle.Release(m_Safety); +#endif + + if (m_BufferData == null || m_Allocator <= Allocator.None) + return; + + UnsafeUtility.FreeTracked(m_BufferData, m_Allocator); + m_BufferData = null; + m_Allocator = Allocator.Invalid; + } + + // Having a job-ified Dispose() allows containers to be trivially chained + // to the end of a job dependency chain, and thus no longer require explicit + // cleanup by users. + public JobHandle Dispose(JobHandle deps) + { + // Unlike the synchronous Dispose(), we don't need + // to call CheckDeallocateAndThrow() here, because + // we're already waiting on any existing jobs to finish + // via the deps JobHandle. + if (m_BufferData == null || m_Allocator <= Allocator.None) + return deps; + + var job = new DisposeJob() + { + Data = new DisposeData() + { + m_Data = m_BufferData, + m_Allocator = m_Allocator, + // We include the AtomicSafetyHandle here + // so the Job System will catch any + // jobs that reference this + // container that weren't + // properly included in the + // deps JobHandle. +#if ENABLE_UNITY_COLLECTIONS_CHECKS + m_Safety = m_Safety, +#endif + } + }; + + var handle = job.Schedule(deps); + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + // It's safe to call Release here, because + // the preceding Schedule() call causes + // the Job System to hold a reference + // to the Safety Handle until the job + // is complete. + AtomicSafetyHandle.Release(m_Safety); +#endif + + m_BufferData = null; + m_Allocator = Allocator.Invalid; + + return handle; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* GetInputPointer() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); +#endif + return GetInputPointerUnchecked(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T* GetInputPointerUnchecked() + => (T*)UnsafeUtility.AddressOf(ref m_BufferData->input); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* GetOutputPointerReadOnly() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckReadAndThrow(m_Safety); +#endif + return GetOutputPointerUnchecked(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T* GetOutputPointerUnchecked() + => (T*)UnsafeUtility.AddressOf(ref m_BufferData->output); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Sync() + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); +#endif + m_BufferData->output = m_BufferData->input; + } + + public JobHandle Sync(JobHandle deps) + => new SyncJob { Buffer = this }.Schedule(deps); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UpdateInput(in T data) + => *GetInputPointer() = data; + + public JobHandle UpdateInputAsync(in T data, JobHandle deps) + => new UpdateJob { Buffer = this, m_Data = data }.Schedule(deps); + } +} diff --git a/Runtime/Common/NativeSyncBuffer.cs.meta b/Runtime/Common/NativeSyncBuffer.cs.meta new file mode 100644 index 0000000..c5692c4 --- /dev/null +++ b/Runtime/Common/NativeSyncBuffer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6749a12dff3046788136852dca9e727b +timeCreated: 1690396897 \ No newline at end of file diff --git a/Runtime/Common/Utils/MLDevice.cs b/Runtime/Common/Utils/MLDevice.cs index dc031d0..a7c617b 100644 --- a/Runtime/Common/Utils/MLDevice.cs +++ b/Runtime/Common/Utils/MLDevice.cs @@ -15,8 +15,8 @@ using UnityEngine.XR.Management; #if UNITY_OPENXR_1_7_0_OR_NEWER using UnityEngine.XR.OpenXR; +using UnityEngine.XR.OpenXR.Features.MagicLeapSupport; #endif - namespace UnityEngine.XR.MagicLeap { /// @@ -166,6 +166,12 @@ private static void InstantiateSingleton() private int mainThreadId = -1; + private Camera unityCamera; + +#if UNITY_OPENXR_1_7_0_OR_NEWER + private MagicLeapFeature mlOpenXrFeature; +#endif + /// /// Gets the platform API level that the OS supports. /// @@ -220,12 +226,11 @@ public static bool IsMagicLeapLoaderActive() public static bool IsOpenXRLoaderActive() { #if UNITY_OPENXR_1_7_0_OR_NEWER - if (XRGeneralSettings.Instance != null && XRGeneralSettings.Instance.Manager != null) - { - return XRGeneralSettings.Instance.Manager.ActiveLoaderAs() != null; - } + return Utils.TryGetOpenXRLoader(out _); #endif +#pragma warning disable CS0162 return false; +#pragma warning restore CS0162 } public static bool IsMagicLeapOrOpenXRLoaderActive() @@ -443,6 +448,14 @@ public static void UnregisterEndOfFrameUpdate(Action endOfFrameFunction) protected void Awake() { this.mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + +#if UNITY_OPENXR_1_7_0_OR_NEWER + if (IsOpenXRLoaderActive()) + { + mlOpenXrFeature = OpenXRSettings.Instance.GetFeature(); + } +#endif + } /// diff --git a/Runtime/Common/Utils/NativeFixedList.cs b/Runtime/Common/Utils/NativeFixedList.cs new file mode 100644 index 0000000..6629184 --- /dev/null +++ b/Runtime/Common/Utils/NativeFixedList.cs @@ -0,0 +1,170 @@ +#if UNITY_XR_MAGICLEAP_PROVIDER +// This class also exists inside Unity's Magic Leap XR Plugin so we want to avoid naming conflicts. +#else +using System; +using Unity.Collections; + +namespace UnityEngine.XR.MagicLeap +{ + /// + /// Has List-like semantics (Capacity and Length) using a NativeArray as the backing store. + /// The NativeArray is never resized. This is useful for times you don't know how big the + /// array will be, but there is a definite upper bound. + /// This list supports duck-typed foreach Enumerator semantics. + /// + internal struct NativeFixedList : IEquatable>, IDisposable where T : struct + { + /// + /// Allocates a new NativeFixedList with . + /// Caller must Dispose the array when no longer needed. + /// + /// + /// + public NativeFixedList(int Capacity, Allocator allocator) + { + m_NativeArray = new NativeArray(Capacity, allocator); + Length = 0; + } + + /// + /// Create a NativeFixedList from an existing NativeArray and a length. + /// This NativeFixedList now owns the memory and should be Disposed when finished. + /// + /// The array to take ownership of + /// The number of elements actually used by the NativeArray + public NativeFixedList(NativeArray other, int length) + { + m_NativeArray = other; + Length = length; + } + + public bool IsCreated + { + get { return m_NativeArray.IsCreated; } + } + + public int Capacity + { + get { return m_NativeArray.Length; } + } + + public int Length { get; private set; } + + public T this[int index] + { + get + { + return m_NativeArray[index]; + } + set + { + m_NativeArray[index] = value; + } + } + + public void Clear() + { + Length = 0; + } + + public void Add(T item) + { + if (Length == Capacity) + throw new InvalidOperationException($"Cannot Add when Length ({Length}) is already at ({Capacity})"); + + m_NativeArray[Length++] = item; + } + + /// + /// Copies the contents of this list to another NativeArray. + /// must have the same Length as this list. + /// + /// The destination array + public void CopyTo(NativeArray destination) + { + NativeArray.Copy(m_NativeArray, destination, Length); + } + + public void Dispose() + { + m_NativeArray.Dispose(); + } + + public override int GetHashCode() + { + unchecked + { + var hash = m_NativeArray.GetHashCode(); + hash = hash * 486187739 + Length.GetHashCode(); + return hash; + } + } + + public override bool Equals(object obj) + { + return ((obj is NativeFixedList) && Equals((NativeFixedList)obj)); + } + + public bool Equals(NativeFixedList other) + { + return + m_NativeArray.Equals(other.m_NativeArray) && + (Length == other.Length); + } + + public static bool operator ==(NativeFixedList lhs, NativeFixedList rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(NativeFixedList lhs, NativeFixedList rhs) + { + return !lhs.Equals(rhs); + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + public struct Enumerator + { + internal Enumerator(NativeFixedList list) + { + m_Index = -1; + m_NativeFixedList = list; + } + + public bool MoveNext() + { + return ++m_Index < m_NativeFixedList.Length; + } + + public void Reset() + { + m_Index = -1; + } + + public T Current + { + get + { + return m_NativeFixedList[m_Index]; + } + } + + public void Dispose() + { + m_NativeFixedList = default(NativeFixedList); + m_Index = 0; + } + + int m_Index; + + NativeFixedList m_NativeFixedList; + } + + NativeArray m_NativeArray; + } +} +#endif diff --git a/Runtime/Common/Utils/NativeFixedList.cs.meta b/Runtime/Common/Utils/NativeFixedList.cs.meta new file mode 100644 index 0000000..c844c3f --- /dev/null +++ b/Runtime/Common/Utils/NativeFixedList.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0946160862fb645a99bd0075a0402503 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Common/Utils/UnsafeUtilityEx.cs b/Runtime/Common/Utils/UnsafeUtilityEx.cs new file mode 100644 index 0000000..4a917ee --- /dev/null +++ b/Runtime/Common/Utils/UnsafeUtilityEx.cs @@ -0,0 +1,14 @@ +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; + +namespace UnityEngine.XR.MagicLeap.Unsafe +{ + internal static unsafe class UnsafeUtilityEx + { + public static T* Malloc(Allocator allocator) where T : unmanaged + => (T*)UnsafeUtility.Malloc(sizeof(T), UnsafeUtility.AlignOf(), allocator); + + public static T* MallocTracked(Allocator allocator, int callstacksToSkip) where T : unmanaged + => (T*)UnsafeUtility.MallocTracked(sizeof(T), UnsafeUtility.AlignOf(), allocator, callstacksToSkip); + } +} diff --git a/Runtime/Common/Utils/UnsafeUtilityEx.cs.meta b/Runtime/Common/Utils/UnsafeUtilityEx.cs.meta new file mode 100644 index 0000000..87d2f5a --- /dev/null +++ b/Runtime/Common/Utils/UnsafeUtilityEx.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 591e23a7c2e64c2e9389dc4422cf8159 +timeCreated: 1690405599 \ No newline at end of file diff --git a/Runtime/Subsystems/Input/MagicLeapInputs.cs b/Runtime/Deprecated/MagicLeapInputs.cs similarity index 66% rename from Runtime/Subsystems/Input/MagicLeapInputs.cs rename to Runtime/Deprecated/MagicLeapInputs.cs index 6a5d9e8..b02c10b 100644 --- a/Runtime/Subsystems/Input/MagicLeapInputs.cs +++ b/Runtime/Deprecated/MagicLeapInputs.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator -// version 1.5.1 +// version 1.4.4 // from Packages/com.magicleap.unitysdk/Runtime/Subsystems/Input/MagicLeapInputs.inputactions // // Changes to this file may cause incorrect behavior and will be lost if @@ -15,7 +15,7 @@ using UnityEngine.InputSystem; using UnityEngine.InputSystem.Utilities; -public partial class @MagicLeapInputs: IInputActionCollection2, IDisposable +public partial class @MagicLeapInputs : IInputActionCollection2, IDisposable { public InputActionAsset asset { get; } public @MagicLeapInputs() @@ -179,6 +179,33 @@ public @MagicLeapInputs() ""processors"": """", ""interactions"": """", ""initialStateCheck"": false + }, + { + ""name"": ""PointerPosition"", + ""type"": ""Value"", + ""id"": ""49923f1a-79fe-49cc-9aa5-a48de7762f2c"", + ""expectedControlType"": ""Vector3"", + ""processors"": """", + ""interactions"": """", + ""initialStateCheck"": true + }, + { + ""name"": ""PointerRotation"", + ""type"": ""Value"", + ""id"": ""837e19dd-ccc0-4adf-a347-a9d8e1fa69a8"", + ""expectedControlType"": ""Quaternion"", + ""processors"": """", + ""interactions"": """", + ""initialStateCheck"": true + }, + { + ""name"": ""TrackingState"", + ""type"": ""Value"", + ""id"": ""4c3699cf-2b98-456d-95c2-03c99e8b8740"", + ""expectedControlType"": ""Integer"", + ""processors"": """", + ""interactions"": """", + ""initialStateCheck"": true } ], ""bindings"": [ @@ -283,17 +310,6 @@ public @MagicLeapInputs() }, { ""name"": ""second"", - ""id"": ""144f6be1-aae1-49bc-9ceb-94c242bc35ef"", - ""path"": ""{RightHand}/pointerRotation"", - ""interactions"": """", - ""processors"": """", - ""groups"": """", - ""action"": ""Rotation"", - ""isComposite"": false, - ""isPartOfComposite"": true - }, - { - ""name"": ""third"", ""id"": ""47a4ccbf-ec6b-4323-8078-2aa08c005c0c"", ""path"": ""{RightHand}/deviceRotation"", ""interactions"": """", @@ -316,17 +332,6 @@ public @MagicLeapInputs() }, { ""name"": ""first"", - ""id"": ""a1240932-fca5-4cc1-a010-e7cedee497ba"", - ""path"": ""{RightHand}/devicePosition"", - ""interactions"": """", - ""processors"": """", - ""groups"": """", - ""action"": ""Position"", - ""isComposite"": false, - ""isPartOfComposite"": true - }, - { - ""name"": ""second"", ""id"": ""fe13a61d-ab1e-4f06-b282-d3c1d2d98d9a"", ""path"": ""/devicePosition"", ""interactions"": """", @@ -337,9 +342,9 @@ public @MagicLeapInputs() ""isPartOfComposite"": true }, { - ""name"": ""third"", - ""id"": ""f97f494e-8fa8-4be4-826c-aa3c45127f54"", - ""path"": ""{RightHand}/pointerPosition"", + ""name"": ""second"", + ""id"": ""a1240932-fca5-4cc1-a010-e7cedee497ba"", + ""path"": ""{RightHand}/devicePosition"", ""interactions"": """", ""processors"": """", ""groups"": """", @@ -533,6 +538,83 @@ public @MagicLeapInputs() ""action"": ""AngularAcceleration"", ""isComposite"": false, ""isPartOfComposite"": true + }, + { + ""name"": ""Vector 3 Fallback"", + ""id"": ""33a8ce26-45cc-4f74-b803-7428369c6fbe"", + ""path"": ""Vector3Fallback"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""PointerPosition"", + ""isComposite"": true, + ""isPartOfComposite"": false + }, + { + ""name"": ""first"", + ""id"": ""5635980f-ce3d-410d-a1c2-7bdff3040a39"", + ""path"": ""/pointerPosition"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""PointerPosition"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""second"", + ""id"": ""14eb8cdc-9846-4246-861b-6baab01c1fba"", + ""path"": ""{RightHand}/devicePosition"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""PointerPosition"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""Quaternion Fallback"", + ""id"": ""0d619573-be39-44bd-9c2f-8448e8dcce13"", + ""path"": ""QuaternionFallback"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""PointerRotation"", + ""isComposite"": true, + ""isPartOfComposite"": false + }, + { + ""name"": ""first"", + ""id"": ""286ff085-2f65-4aec-9acc-85eeaca37f79"", + ""path"": ""/pointerRotation"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""PointerRotation"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": ""second"", + ""id"": ""bedaf34a-3418-4675-91e2-22fb696b8e39"", + ""path"": ""{RightHand}/deviceRotation"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""PointerRotation"", + ""isComposite"": false, + ""isPartOfComposite"": true + }, + { + ""name"": """", + ""id"": ""6adeadd9-c3c1-4f04-9a93-f1d8c8c98551"", + ""path"": ""/trackingState"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""TrackingState"", + ""isComposite"": false, + ""isPartOfComposite"": false } ] }, @@ -618,6 +700,24 @@ public @MagicLeapInputs() ""processors"": """", ""interactions"": """", ""initialStateCheck"": true + }, + { + ""name"": ""Position"", + ""type"": ""Value"", + ""id"": ""c7e35463-0720-42cc-bd53-6833954f3bdb"", + ""expectedControlType"": ""Vector3"", + ""processors"": """", + ""interactions"": """", + ""initialStateCheck"": true + }, + { + ""name"": ""Rotation"", + ""type"": ""Value"", + ""id"": ""abacd085-1f61-495a-87a4-cb9824ff0ccb"", + ""expectedControlType"": ""Quaternion"", + ""processors"": """", + ""interactions"": """", + ""initialStateCheck"": true } ], ""bindings"": [ @@ -631,6 +731,28 @@ public @MagicLeapInputs() ""action"": ""Data"", ""isComposite"": false, ""isPartOfComposite"": false + }, + { + ""name"": """", + ""id"": ""acc1ab74-2315-4f52-ad02-61d40239c614"", + ""path"": ""/pose/position"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""Position"", + ""isComposite"": false, + ""isPartOfComposite"": false + }, + { + ""name"": """", + ""id"": ""986d5b69-5403-41ae-940d-286ea438f41f"", + ""path"": ""/pose/rotation"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""Rotation"", + ""isComposite"": false, + ""isPartOfComposite"": false } ] }, @@ -824,6 +946,9 @@ public @MagicLeapInputs() m_Controller_TouchpadForce = m_Controller.FindAction("TouchpadForce", throwIfNotFound: true); m_Controller_IsTracked = m_Controller.FindAction("IsTracked", throwIfNotFound: true); m_Controller_Haptic = m_Controller.FindAction("Haptic", throwIfNotFound: true); + m_Controller_PointerPosition = m_Controller.FindAction("PointerPosition", throwIfNotFound: true); + m_Controller_PointerRotation = m_Controller.FindAction("PointerRotation", throwIfNotFound: true); + m_Controller_TrackingState = m_Controller.FindAction("TrackingState", throwIfNotFound: true); // HMD m_HMD = asset.FindActionMap("HMD", throwIfNotFound: true); m_HMD_Position = m_HMD.FindAction("Position", throwIfNotFound: true); @@ -831,6 +956,8 @@ public @MagicLeapInputs() // Eyes m_Eyes = asset.FindActionMap("Eyes", throwIfNotFound: true); m_Eyes_Data = m_Eyes.FindAction("Data", throwIfNotFound: true); + m_Eyes_Position = m_Eyes.FindAction("Position", throwIfNotFound: true); + m_Eyes_Rotation = m_Eyes.FindAction("Rotation", throwIfNotFound: true); // LeftHand m_LeftHand = asset.FindActionMap("LeftHand", throwIfNotFound: true); m_LeftHand_Position = m_LeftHand.FindAction("Position", throwIfNotFound: true); @@ -886,14 +1013,12 @@ public void Disable() { asset.Disable(); } - public IEnumerable bindings => asset.bindings; public InputAction FindAction(string actionNameOrId, bool throwIfNotFound = false) { return asset.FindAction(actionNameOrId, throwIfNotFound); } - public int FindBinding(InputBinding bindingMask, out InputAction action) { return asset.FindBinding(bindingMask, out action); @@ -901,7 +1026,7 @@ public int FindBinding(InputBinding bindingMask, out InputAction action) // Controller private readonly InputActionMap m_Controller; - private List m_ControllerActionsCallbackInterfaces = new List(); + private IControllerActions m_ControllerActionsCallbackInterface; private readonly InputAction m_Controller_Position; private readonly InputAction m_Controller_Rotation; private readonly InputAction m_Controller_Velocity; @@ -919,6 +1044,9 @@ public int FindBinding(InputBinding bindingMask, out InputAction action) private readonly InputAction m_Controller_TouchpadForce; private readonly InputAction m_Controller_IsTracked; private readonly InputAction m_Controller_Haptic; + private readonly InputAction m_Controller_PointerPosition; + private readonly InputAction m_Controller_PointerRotation; + private readonly InputAction m_Controller_TrackingState; public struct ControllerActions { private @MagicLeapInputs m_Wrapper; @@ -940,142 +1068,150 @@ public struct ControllerActions public InputAction @TouchpadForce => m_Wrapper.m_Controller_TouchpadForce; public InputAction @IsTracked => m_Wrapper.m_Controller_IsTracked; public InputAction @Haptic => m_Wrapper.m_Controller_Haptic; + public InputAction @PointerPosition => m_Wrapper.m_Controller_PointerPosition; + public InputAction @PointerRotation => m_Wrapper.m_Controller_PointerRotation; + public InputAction @TrackingState => m_Wrapper.m_Controller_TrackingState; public InputActionMap Get() { return m_Wrapper.m_Controller; } public void Enable() { Get().Enable(); } public void Disable() { Get().Disable(); } public bool enabled => Get().enabled; public static implicit operator InputActionMap(ControllerActions set) { return set.Get(); } - public void AddCallbacks(IControllerActions instance) - { - if (instance == null || m_Wrapper.m_ControllerActionsCallbackInterfaces.Contains(instance)) return; - m_Wrapper.m_ControllerActionsCallbackInterfaces.Add(instance); - @Position.started += instance.OnPosition; - @Position.performed += instance.OnPosition; - @Position.canceled += instance.OnPosition; - @Rotation.started += instance.OnRotation; - @Rotation.performed += instance.OnRotation; - @Rotation.canceled += instance.OnRotation; - @Velocity.started += instance.OnVelocity; - @Velocity.performed += instance.OnVelocity; - @Velocity.canceled += instance.OnVelocity; - @AngularVelocity.started += instance.OnAngularVelocity; - @AngularVelocity.performed += instance.OnAngularVelocity; - @AngularVelocity.canceled += instance.OnAngularVelocity; - @Acceleration.started += instance.OnAcceleration; - @Acceleration.performed += instance.OnAcceleration; - @Acceleration.canceled += instance.OnAcceleration; - @AngularAcceleration.started += instance.OnAngularAcceleration; - @AngularAcceleration.performed += instance.OnAngularAcceleration; - @AngularAcceleration.canceled += instance.OnAngularAcceleration; - @Menu.started += instance.OnMenu; - @Menu.performed += instance.OnMenu; - @Menu.canceled += instance.OnMenu; - @Bumper.started += instance.OnBumper; - @Bumper.performed += instance.OnBumper; - @Bumper.canceled += instance.OnBumper; - @TriggerButton.started += instance.OnTriggerButton; - @TriggerButton.performed += instance.OnTriggerButton; - @TriggerButton.canceled += instance.OnTriggerButton; - @Trigger.started += instance.OnTrigger; - @Trigger.performed += instance.OnTrigger; - @Trigger.canceled += instance.OnTrigger; - @TriggerHold.started += instance.OnTriggerHold; - @TriggerHold.performed += instance.OnTriggerHold; - @TriggerHold.canceled += instance.OnTriggerHold; - @TouchpadPosition.started += instance.OnTouchpadPosition; - @TouchpadPosition.performed += instance.OnTouchpadPosition; - @TouchpadPosition.canceled += instance.OnTouchpadPosition; - @TouchpadClick.started += instance.OnTouchpadClick; - @TouchpadClick.performed += instance.OnTouchpadClick; - @TouchpadClick.canceled += instance.OnTouchpadClick; - @TouchpadTouch.started += instance.OnTouchpadTouch; - @TouchpadTouch.performed += instance.OnTouchpadTouch; - @TouchpadTouch.canceled += instance.OnTouchpadTouch; - @TouchpadForce.started += instance.OnTouchpadForce; - @TouchpadForce.performed += instance.OnTouchpadForce; - @TouchpadForce.canceled += instance.OnTouchpadForce; - @IsTracked.started += instance.OnIsTracked; - @IsTracked.performed += instance.OnIsTracked; - @IsTracked.canceled += instance.OnIsTracked; - @Haptic.started += instance.OnHaptic; - @Haptic.performed += instance.OnHaptic; - @Haptic.canceled += instance.OnHaptic; - } - - private void UnregisterCallbacks(IControllerActions instance) - { - @Position.started -= instance.OnPosition; - @Position.performed -= instance.OnPosition; - @Position.canceled -= instance.OnPosition; - @Rotation.started -= instance.OnRotation; - @Rotation.performed -= instance.OnRotation; - @Rotation.canceled -= instance.OnRotation; - @Velocity.started -= instance.OnVelocity; - @Velocity.performed -= instance.OnVelocity; - @Velocity.canceled -= instance.OnVelocity; - @AngularVelocity.started -= instance.OnAngularVelocity; - @AngularVelocity.performed -= instance.OnAngularVelocity; - @AngularVelocity.canceled -= instance.OnAngularVelocity; - @Acceleration.started -= instance.OnAcceleration; - @Acceleration.performed -= instance.OnAcceleration; - @Acceleration.canceled -= instance.OnAcceleration; - @AngularAcceleration.started -= instance.OnAngularAcceleration; - @AngularAcceleration.performed -= instance.OnAngularAcceleration; - @AngularAcceleration.canceled -= instance.OnAngularAcceleration; - @Menu.started -= instance.OnMenu; - @Menu.performed -= instance.OnMenu; - @Menu.canceled -= instance.OnMenu; - @Bumper.started -= instance.OnBumper; - @Bumper.performed -= instance.OnBumper; - @Bumper.canceled -= instance.OnBumper; - @TriggerButton.started -= instance.OnTriggerButton; - @TriggerButton.performed -= instance.OnTriggerButton; - @TriggerButton.canceled -= instance.OnTriggerButton; - @Trigger.started -= instance.OnTrigger; - @Trigger.performed -= instance.OnTrigger; - @Trigger.canceled -= instance.OnTrigger; - @TriggerHold.started -= instance.OnTriggerHold; - @TriggerHold.performed -= instance.OnTriggerHold; - @TriggerHold.canceled -= instance.OnTriggerHold; - @TouchpadPosition.started -= instance.OnTouchpadPosition; - @TouchpadPosition.performed -= instance.OnTouchpadPosition; - @TouchpadPosition.canceled -= instance.OnTouchpadPosition; - @TouchpadClick.started -= instance.OnTouchpadClick; - @TouchpadClick.performed -= instance.OnTouchpadClick; - @TouchpadClick.canceled -= instance.OnTouchpadClick; - @TouchpadTouch.started -= instance.OnTouchpadTouch; - @TouchpadTouch.performed -= instance.OnTouchpadTouch; - @TouchpadTouch.canceled -= instance.OnTouchpadTouch; - @TouchpadForce.started -= instance.OnTouchpadForce; - @TouchpadForce.performed -= instance.OnTouchpadForce; - @TouchpadForce.canceled -= instance.OnTouchpadForce; - @IsTracked.started -= instance.OnIsTracked; - @IsTracked.performed -= instance.OnIsTracked; - @IsTracked.canceled -= instance.OnIsTracked; - @Haptic.started -= instance.OnHaptic; - @Haptic.performed -= instance.OnHaptic; - @Haptic.canceled -= instance.OnHaptic; - } - - public void RemoveCallbacks(IControllerActions instance) - { - if (m_Wrapper.m_ControllerActionsCallbackInterfaces.Remove(instance)) - UnregisterCallbacks(instance); - } - public void SetCallbacks(IControllerActions instance) { - foreach (var item in m_Wrapper.m_ControllerActionsCallbackInterfaces) - UnregisterCallbacks(item); - m_Wrapper.m_ControllerActionsCallbackInterfaces.Clear(); - AddCallbacks(instance); + if (m_Wrapper.m_ControllerActionsCallbackInterface != null) + { + @Position.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPosition; + @Position.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPosition; + @Position.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPosition; + @Rotation.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnRotation; + @Rotation.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnRotation; + @Rotation.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnRotation; + @Velocity.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnVelocity; + @Velocity.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnVelocity; + @Velocity.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnVelocity; + @AngularVelocity.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAngularVelocity; + @AngularVelocity.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAngularVelocity; + @AngularVelocity.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAngularVelocity; + @Acceleration.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAcceleration; + @Acceleration.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAcceleration; + @Acceleration.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAcceleration; + @AngularAcceleration.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAngularAcceleration; + @AngularAcceleration.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAngularAcceleration; + @AngularAcceleration.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnAngularAcceleration; + @Menu.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnMenu; + @Menu.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnMenu; + @Menu.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnMenu; + @Bumper.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnBumper; + @Bumper.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnBumper; + @Bumper.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnBumper; + @TriggerButton.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTriggerButton; + @TriggerButton.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTriggerButton; + @TriggerButton.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTriggerButton; + @Trigger.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTrigger; + @Trigger.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTrigger; + @Trigger.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTrigger; + @TriggerHold.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTriggerHold; + @TriggerHold.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTriggerHold; + @TriggerHold.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTriggerHold; + @TouchpadPosition.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadPosition; + @TouchpadPosition.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadPosition; + @TouchpadPosition.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadPosition; + @TouchpadClick.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadClick; + @TouchpadClick.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadClick; + @TouchpadClick.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadClick; + @TouchpadTouch.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadTouch; + @TouchpadTouch.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadTouch; + @TouchpadTouch.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadTouch; + @TouchpadForce.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadForce; + @TouchpadForce.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadForce; + @TouchpadForce.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTouchpadForce; + @IsTracked.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnIsTracked; + @IsTracked.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnIsTracked; + @IsTracked.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnIsTracked; + @Haptic.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnHaptic; + @Haptic.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnHaptic; + @Haptic.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnHaptic; + @PointerPosition.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPointerPosition; + @PointerPosition.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPointerPosition; + @PointerPosition.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPointerPosition; + @PointerRotation.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPointerRotation; + @PointerRotation.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPointerRotation; + @PointerRotation.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnPointerRotation; + @TrackingState.started -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTrackingState; + @TrackingState.performed -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTrackingState; + @TrackingState.canceled -= m_Wrapper.m_ControllerActionsCallbackInterface.OnTrackingState; + } + m_Wrapper.m_ControllerActionsCallbackInterface = instance; + if (instance != null) + { + @Position.started += instance.OnPosition; + @Position.performed += instance.OnPosition; + @Position.canceled += instance.OnPosition; + @Rotation.started += instance.OnRotation; + @Rotation.performed += instance.OnRotation; + @Rotation.canceled += instance.OnRotation; + @Velocity.started += instance.OnVelocity; + @Velocity.performed += instance.OnVelocity; + @Velocity.canceled += instance.OnVelocity; + @AngularVelocity.started += instance.OnAngularVelocity; + @AngularVelocity.performed += instance.OnAngularVelocity; + @AngularVelocity.canceled += instance.OnAngularVelocity; + @Acceleration.started += instance.OnAcceleration; + @Acceleration.performed += instance.OnAcceleration; + @Acceleration.canceled += instance.OnAcceleration; + @AngularAcceleration.started += instance.OnAngularAcceleration; + @AngularAcceleration.performed += instance.OnAngularAcceleration; + @AngularAcceleration.canceled += instance.OnAngularAcceleration; + @Menu.started += instance.OnMenu; + @Menu.performed += instance.OnMenu; + @Menu.canceled += instance.OnMenu; + @Bumper.started += instance.OnBumper; + @Bumper.performed += instance.OnBumper; + @Bumper.canceled += instance.OnBumper; + @TriggerButton.started += instance.OnTriggerButton; + @TriggerButton.performed += instance.OnTriggerButton; + @TriggerButton.canceled += instance.OnTriggerButton; + @Trigger.started += instance.OnTrigger; + @Trigger.performed += instance.OnTrigger; + @Trigger.canceled += instance.OnTrigger; + @TriggerHold.started += instance.OnTriggerHold; + @TriggerHold.performed += instance.OnTriggerHold; + @TriggerHold.canceled += instance.OnTriggerHold; + @TouchpadPosition.started += instance.OnTouchpadPosition; + @TouchpadPosition.performed += instance.OnTouchpadPosition; + @TouchpadPosition.canceled += instance.OnTouchpadPosition; + @TouchpadClick.started += instance.OnTouchpadClick; + @TouchpadClick.performed += instance.OnTouchpadClick; + @TouchpadClick.canceled += instance.OnTouchpadClick; + @TouchpadTouch.started += instance.OnTouchpadTouch; + @TouchpadTouch.performed += instance.OnTouchpadTouch; + @TouchpadTouch.canceled += instance.OnTouchpadTouch; + @TouchpadForce.started += instance.OnTouchpadForce; + @TouchpadForce.performed += instance.OnTouchpadForce; + @TouchpadForce.canceled += instance.OnTouchpadForce; + @IsTracked.started += instance.OnIsTracked; + @IsTracked.performed += instance.OnIsTracked; + @IsTracked.canceled += instance.OnIsTracked; + @Haptic.started += instance.OnHaptic; + @Haptic.performed += instance.OnHaptic; + @Haptic.canceled += instance.OnHaptic; + @PointerPosition.started += instance.OnPointerPosition; + @PointerPosition.performed += instance.OnPointerPosition; + @PointerPosition.canceled += instance.OnPointerPosition; + @PointerRotation.started += instance.OnPointerRotation; + @PointerRotation.performed += instance.OnPointerRotation; + @PointerRotation.canceled += instance.OnPointerRotation; + @TrackingState.started += instance.OnTrackingState; + @TrackingState.performed += instance.OnTrackingState; + @TrackingState.canceled += instance.OnTrackingState; + } } } public ControllerActions @Controller => new ControllerActions(this); // HMD private readonly InputActionMap m_HMD; - private List m_HMDActionsCallbackInterfaces = new List(); + private IHMDActions m_HMDActionsCallbackInterface; private readonly InputAction m_HMD_Position; private readonly InputAction m_HMD_Rotation; public struct HMDActions @@ -1089,93 +1225,83 @@ public struct HMDActions public void Disable() { Get().Disable(); } public bool enabled => Get().enabled; public static implicit operator InputActionMap(HMDActions set) { return set.Get(); } - public void AddCallbacks(IHMDActions instance) - { - if (instance == null || m_Wrapper.m_HMDActionsCallbackInterfaces.Contains(instance)) return; - m_Wrapper.m_HMDActionsCallbackInterfaces.Add(instance); - @Position.started += instance.OnPosition; - @Position.performed += instance.OnPosition; - @Position.canceled += instance.OnPosition; - @Rotation.started += instance.OnRotation; - @Rotation.performed += instance.OnRotation; - @Rotation.canceled += instance.OnRotation; - } - - private void UnregisterCallbacks(IHMDActions instance) - { - @Position.started -= instance.OnPosition; - @Position.performed -= instance.OnPosition; - @Position.canceled -= instance.OnPosition; - @Rotation.started -= instance.OnRotation; - @Rotation.performed -= instance.OnRotation; - @Rotation.canceled -= instance.OnRotation; - } - - public void RemoveCallbacks(IHMDActions instance) - { - if (m_Wrapper.m_HMDActionsCallbackInterfaces.Remove(instance)) - UnregisterCallbacks(instance); - } - public void SetCallbacks(IHMDActions instance) { - foreach (var item in m_Wrapper.m_HMDActionsCallbackInterfaces) - UnregisterCallbacks(item); - m_Wrapper.m_HMDActionsCallbackInterfaces.Clear(); - AddCallbacks(instance); + if (m_Wrapper.m_HMDActionsCallbackInterface != null) + { + @Position.started -= m_Wrapper.m_HMDActionsCallbackInterface.OnPosition; + @Position.performed -= m_Wrapper.m_HMDActionsCallbackInterface.OnPosition; + @Position.canceled -= m_Wrapper.m_HMDActionsCallbackInterface.OnPosition; + @Rotation.started -= m_Wrapper.m_HMDActionsCallbackInterface.OnRotation; + @Rotation.performed -= m_Wrapper.m_HMDActionsCallbackInterface.OnRotation; + @Rotation.canceled -= m_Wrapper.m_HMDActionsCallbackInterface.OnRotation; + } + m_Wrapper.m_HMDActionsCallbackInterface = instance; + if (instance != null) + { + @Position.started += instance.OnPosition; + @Position.performed += instance.OnPosition; + @Position.canceled += instance.OnPosition; + @Rotation.started += instance.OnRotation; + @Rotation.performed += instance.OnRotation; + @Rotation.canceled += instance.OnRotation; + } } } public HMDActions @HMD => new HMDActions(this); // Eyes private readonly InputActionMap m_Eyes; - private List m_EyesActionsCallbackInterfaces = new List(); + private IEyesActions m_EyesActionsCallbackInterface; private readonly InputAction m_Eyes_Data; + private readonly InputAction m_Eyes_Position; + private readonly InputAction m_Eyes_Rotation; public struct EyesActions { private @MagicLeapInputs m_Wrapper; public EyesActions(@MagicLeapInputs wrapper) { m_Wrapper = wrapper; } public InputAction @Data => m_Wrapper.m_Eyes_Data; + public InputAction @Position => m_Wrapper.m_Eyes_Position; + public InputAction @Rotation => m_Wrapper.m_Eyes_Rotation; public InputActionMap Get() { return m_Wrapper.m_Eyes; } public void Enable() { Get().Enable(); } public void Disable() { Get().Disable(); } public bool enabled => Get().enabled; public static implicit operator InputActionMap(EyesActions set) { return set.Get(); } - public void AddCallbacks(IEyesActions instance) - { - if (instance == null || m_Wrapper.m_EyesActionsCallbackInterfaces.Contains(instance)) return; - m_Wrapper.m_EyesActionsCallbackInterfaces.Add(instance); - @Data.started += instance.OnData; - @Data.performed += instance.OnData; - @Data.canceled += instance.OnData; - } - - private void UnregisterCallbacks(IEyesActions instance) - { - @Data.started -= instance.OnData; - @Data.performed -= instance.OnData; - @Data.canceled -= instance.OnData; - } - - public void RemoveCallbacks(IEyesActions instance) - { - if (m_Wrapper.m_EyesActionsCallbackInterfaces.Remove(instance)) - UnregisterCallbacks(instance); - } - public void SetCallbacks(IEyesActions instance) { - foreach (var item in m_Wrapper.m_EyesActionsCallbackInterfaces) - UnregisterCallbacks(item); - m_Wrapper.m_EyesActionsCallbackInterfaces.Clear(); - AddCallbacks(instance); + if (m_Wrapper.m_EyesActionsCallbackInterface != null) + { + @Data.started -= m_Wrapper.m_EyesActionsCallbackInterface.OnData; + @Data.performed -= m_Wrapper.m_EyesActionsCallbackInterface.OnData; + @Data.canceled -= m_Wrapper.m_EyesActionsCallbackInterface.OnData; + @Position.started -= m_Wrapper.m_EyesActionsCallbackInterface.OnPosition; + @Position.performed -= m_Wrapper.m_EyesActionsCallbackInterface.OnPosition; + @Position.canceled -= m_Wrapper.m_EyesActionsCallbackInterface.OnPosition; + @Rotation.started -= m_Wrapper.m_EyesActionsCallbackInterface.OnRotation; + @Rotation.performed -= m_Wrapper.m_EyesActionsCallbackInterface.OnRotation; + @Rotation.canceled -= m_Wrapper.m_EyesActionsCallbackInterface.OnRotation; + } + m_Wrapper.m_EyesActionsCallbackInterface = instance; + if (instance != null) + { + @Data.started += instance.OnData; + @Data.performed += instance.OnData; + @Data.canceled += instance.OnData; + @Position.started += instance.OnPosition; + @Position.performed += instance.OnPosition; + @Position.canceled += instance.OnPosition; + @Rotation.started += instance.OnRotation; + @Rotation.performed += instance.OnRotation; + @Rotation.canceled += instance.OnRotation; + } } } public EyesActions @Eyes => new EyesActions(this); // LeftHand private readonly InputActionMap m_LeftHand; - private List m_LeftHandActionsCallbackInterfaces = new List(); + private ILeftHandActions m_LeftHandActionsCallbackInterface; private readonly InputAction m_LeftHand_Position; private readonly InputAction m_LeftHand_Rotation; private readonly InputAction m_LeftHand_Keypose; @@ -1191,53 +1317,40 @@ public struct LeftHandActions public void Disable() { Get().Disable(); } public bool enabled => Get().enabled; public static implicit operator InputActionMap(LeftHandActions set) { return set.Get(); } - public void AddCallbacks(ILeftHandActions instance) - { - if (instance == null || m_Wrapper.m_LeftHandActionsCallbackInterfaces.Contains(instance)) return; - m_Wrapper.m_LeftHandActionsCallbackInterfaces.Add(instance); - @Position.started += instance.OnPosition; - @Position.performed += instance.OnPosition; - @Position.canceled += instance.OnPosition; - @Rotation.started += instance.OnRotation; - @Rotation.performed += instance.OnRotation; - @Rotation.canceled += instance.OnRotation; - @Keypose.started += instance.OnKeypose; - @Keypose.performed += instance.OnKeypose; - @Keypose.canceled += instance.OnKeypose; - } - - private void UnregisterCallbacks(ILeftHandActions instance) - { - @Position.started -= instance.OnPosition; - @Position.performed -= instance.OnPosition; - @Position.canceled -= instance.OnPosition; - @Rotation.started -= instance.OnRotation; - @Rotation.performed -= instance.OnRotation; - @Rotation.canceled -= instance.OnRotation; - @Keypose.started -= instance.OnKeypose; - @Keypose.performed -= instance.OnKeypose; - @Keypose.canceled -= instance.OnKeypose; - } - - public void RemoveCallbacks(ILeftHandActions instance) - { - if (m_Wrapper.m_LeftHandActionsCallbackInterfaces.Remove(instance)) - UnregisterCallbacks(instance); - } - public void SetCallbacks(ILeftHandActions instance) { - foreach (var item in m_Wrapper.m_LeftHandActionsCallbackInterfaces) - UnregisterCallbacks(item); - m_Wrapper.m_LeftHandActionsCallbackInterfaces.Clear(); - AddCallbacks(instance); + if (m_Wrapper.m_LeftHandActionsCallbackInterface != null) + { + @Position.started -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnPosition; + @Position.performed -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnPosition; + @Position.canceled -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnPosition; + @Rotation.started -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnRotation; + @Rotation.performed -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnRotation; + @Rotation.canceled -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnRotation; + @Keypose.started -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnKeypose; + @Keypose.performed -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnKeypose; + @Keypose.canceled -= m_Wrapper.m_LeftHandActionsCallbackInterface.OnKeypose; + } + m_Wrapper.m_LeftHandActionsCallbackInterface = instance; + if (instance != null) + { + @Position.started += instance.OnPosition; + @Position.performed += instance.OnPosition; + @Position.canceled += instance.OnPosition; + @Rotation.started += instance.OnRotation; + @Rotation.performed += instance.OnRotation; + @Rotation.canceled += instance.OnRotation; + @Keypose.started += instance.OnKeypose; + @Keypose.performed += instance.OnKeypose; + @Keypose.canceled += instance.OnKeypose; + } } } public LeftHandActions @LeftHand => new LeftHandActions(this); // RightHand private readonly InputActionMap m_RightHand; - private List m_RightHandActionsCallbackInterfaces = new List(); + private IRightHandActions m_RightHandActionsCallbackInterface; private readonly InputAction m_RightHand_Position; private readonly InputAction m_RightHand_Rotation; private readonly InputAction m_RightHand_Keypose; @@ -1253,46 +1366,33 @@ public struct RightHandActions public void Disable() { Get().Disable(); } public bool enabled => Get().enabled; public static implicit operator InputActionMap(RightHandActions set) { return set.Get(); } - public void AddCallbacks(IRightHandActions instance) - { - if (instance == null || m_Wrapper.m_RightHandActionsCallbackInterfaces.Contains(instance)) return; - m_Wrapper.m_RightHandActionsCallbackInterfaces.Add(instance); - @Position.started += instance.OnPosition; - @Position.performed += instance.OnPosition; - @Position.canceled += instance.OnPosition; - @Rotation.started += instance.OnRotation; - @Rotation.performed += instance.OnRotation; - @Rotation.canceled += instance.OnRotation; - @Keypose.started += instance.OnKeypose; - @Keypose.performed += instance.OnKeypose; - @Keypose.canceled += instance.OnKeypose; - } - - private void UnregisterCallbacks(IRightHandActions instance) - { - @Position.started -= instance.OnPosition; - @Position.performed -= instance.OnPosition; - @Position.canceled -= instance.OnPosition; - @Rotation.started -= instance.OnRotation; - @Rotation.performed -= instance.OnRotation; - @Rotation.canceled -= instance.OnRotation; - @Keypose.started -= instance.OnKeypose; - @Keypose.performed -= instance.OnKeypose; - @Keypose.canceled -= instance.OnKeypose; - } - - public void RemoveCallbacks(IRightHandActions instance) - { - if (m_Wrapper.m_RightHandActionsCallbackInterfaces.Remove(instance)) - UnregisterCallbacks(instance); - } - public void SetCallbacks(IRightHandActions instance) { - foreach (var item in m_Wrapper.m_RightHandActionsCallbackInterfaces) - UnregisterCallbacks(item); - m_Wrapper.m_RightHandActionsCallbackInterfaces.Clear(); - AddCallbacks(instance); + if (m_Wrapper.m_RightHandActionsCallbackInterface != null) + { + @Position.started -= m_Wrapper.m_RightHandActionsCallbackInterface.OnPosition; + @Position.performed -= m_Wrapper.m_RightHandActionsCallbackInterface.OnPosition; + @Position.canceled -= m_Wrapper.m_RightHandActionsCallbackInterface.OnPosition; + @Rotation.started -= m_Wrapper.m_RightHandActionsCallbackInterface.OnRotation; + @Rotation.performed -= m_Wrapper.m_RightHandActionsCallbackInterface.OnRotation; + @Rotation.canceled -= m_Wrapper.m_RightHandActionsCallbackInterface.OnRotation; + @Keypose.started -= m_Wrapper.m_RightHandActionsCallbackInterface.OnKeypose; + @Keypose.performed -= m_Wrapper.m_RightHandActionsCallbackInterface.OnKeypose; + @Keypose.canceled -= m_Wrapper.m_RightHandActionsCallbackInterface.OnKeypose; + } + m_Wrapper.m_RightHandActionsCallbackInterface = instance; + if (instance != null) + { + @Position.started += instance.OnPosition; + @Position.performed += instance.OnPosition; + @Position.canceled += instance.OnPosition; + @Rotation.started += instance.OnRotation; + @Rotation.performed += instance.OnRotation; + @Rotation.canceled += instance.OnRotation; + @Keypose.started += instance.OnKeypose; + @Keypose.performed += instance.OnKeypose; + @Keypose.canceled += instance.OnKeypose; + } } } public RightHandActions @RightHand => new RightHandActions(this); @@ -1324,6 +1424,9 @@ public interface IControllerActions void OnTouchpadForce(InputAction.CallbackContext context); void OnIsTracked(InputAction.CallbackContext context); void OnHaptic(InputAction.CallbackContext context); + void OnPointerPosition(InputAction.CallbackContext context); + void OnPointerRotation(InputAction.CallbackContext context); + void OnTrackingState(InputAction.CallbackContext context); } public interface IHMDActions { @@ -1333,6 +1436,8 @@ public interface IHMDActions public interface IEyesActions { void OnData(InputAction.CallbackContext context); + void OnPosition(InputAction.CallbackContext context); + void OnRotation(InputAction.CallbackContext context); } public interface ILeftHandActions { diff --git a/Runtime/Subsystems/Input/MagicLeapInputs.cs.meta b/Runtime/Deprecated/MagicLeapInputs.cs.meta similarity index 100% rename from Runtime/Subsystems/Input/MagicLeapInputs.cs.meta rename to Runtime/Deprecated/MagicLeapInputs.cs.meta diff --git a/Runtime/Subsystems/Input/MagicLeapInputs.inputactions b/Runtime/Deprecated/MagicLeapInputs.inputactions similarity index 88% rename from Runtime/Subsystems/Input/MagicLeapInputs.inputactions rename to Runtime/Deprecated/MagicLeapInputs.inputactions index e5b679e..7b58d7a 100644 --- a/Runtime/Subsystems/Input/MagicLeapInputs.inputactions +++ b/Runtime/Deprecated/MagicLeapInputs.inputactions @@ -157,6 +157,33 @@ "processors": "", "interactions": "", "initialStateCheck": false + }, + { + "name": "PointerPosition", + "type": "Value", + "id": "49923f1a-79fe-49cc-9aa5-a48de7762f2c", + "expectedControlType": "Vector3", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "PointerRotation", + "type": "Value", + "id": "837e19dd-ccc0-4adf-a347-a9d8e1fa69a8", + "expectedControlType": "Quaternion", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "TrackingState", + "type": "Value", + "id": "4c3699cf-2b98-456d-95c2-03c99e8b8740", + "expectedControlType": "Integer", + "processors": "", + "interactions": "", + "initialStateCheck": true } ], "bindings": [ @@ -261,17 +288,6 @@ }, { "name": "second", - "id": "144f6be1-aae1-49bc-9ceb-94c242bc35ef", - "path": "{RightHand}/pointerRotation", - "interactions": "", - "processors": "", - "groups": "", - "action": "Rotation", - "isComposite": false, - "isPartOfComposite": true - }, - { - "name": "third", "id": "47a4ccbf-ec6b-4323-8078-2aa08c005c0c", "path": "{RightHand}/deviceRotation", "interactions": "", @@ -294,17 +310,6 @@ }, { "name": "first", - "id": "a1240932-fca5-4cc1-a010-e7cedee497ba", - "path": "{RightHand}/devicePosition", - "interactions": "", - "processors": "", - "groups": "", - "action": "Position", - "isComposite": false, - "isPartOfComposite": true - }, - { - "name": "second", "id": "fe13a61d-ab1e-4f06-b282-d3c1d2d98d9a", "path": "/devicePosition", "interactions": "", @@ -315,9 +320,9 @@ "isPartOfComposite": true }, { - "name": "third", - "id": "f97f494e-8fa8-4be4-826c-aa3c45127f54", - "path": "{RightHand}/pointerPosition", + "name": "second", + "id": "a1240932-fca5-4cc1-a010-e7cedee497ba", + "path": "{RightHand}/devicePosition", "interactions": "", "processors": "", "groups": "", @@ -511,6 +516,83 @@ "action": "AngularAcceleration", "isComposite": false, "isPartOfComposite": true + }, + { + "name": "Vector 3 Fallback", + "id": "33a8ce26-45cc-4f74-b803-7428369c6fbe", + "path": "Vector3Fallback", + "interactions": "", + "processors": "", + "groups": "", + "action": "PointerPosition", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "first", + "id": "5635980f-ce3d-410d-a1c2-7bdff3040a39", + "path": "/pointerPosition", + "interactions": "", + "processors": "", + "groups": "", + "action": "PointerPosition", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "second", + "id": "14eb8cdc-9846-4246-861b-6baab01c1fba", + "path": "{RightHand}/devicePosition", + "interactions": "", + "processors": "", + "groups": "", + "action": "PointerPosition", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Quaternion Fallback", + "id": "0d619573-be39-44bd-9c2f-8448e8dcce13", + "path": "QuaternionFallback", + "interactions": "", + "processors": "", + "groups": "", + "action": "PointerRotation", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "first", + "id": "286ff085-2f65-4aec-9acc-85eeaca37f79", + "path": "/pointerRotation", + "interactions": "", + "processors": "", + "groups": "", + "action": "PointerRotation", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "second", + "id": "bedaf34a-3418-4675-91e2-22fb696b8e39", + "path": "{RightHand}/deviceRotation", + "interactions": "", + "processors": "", + "groups": "", + "action": "PointerRotation", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "6adeadd9-c3c1-4f04-9a93-f1d8c8c98551", + "path": "/trackingState", + "interactions": "", + "processors": "", + "groups": "", + "action": "TrackingState", + "isComposite": false, + "isPartOfComposite": false } ] }, diff --git a/Runtime/Subsystems/Input/MagicLeapInputs.inputactions.meta b/Runtime/Deprecated/MagicLeapInputs.inputactions.meta similarity index 93% rename from Runtime/Subsystems/Input/MagicLeapInputs.inputactions.meta rename to Runtime/Deprecated/MagicLeapInputs.inputactions.meta index 74703a1..5ee3413 100644 --- a/Runtime/Subsystems/Input/MagicLeapInputs.inputactions.meta +++ b/Runtime/Deprecated/MagicLeapInputs.inputactions.meta @@ -8,7 +8,7 @@ ScriptedImporter: assetBundleName: assetBundleVariant: script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} - generateWrapperCode: 1 + generateWrapperCode: 0 wrapperCodePath: wrapperClassName: wrapperCodeNamespace: diff --git a/Runtime/MagicLeap.SDK.asmdef b/Runtime/MagicLeap.SDK.asmdef index 19c8c1b..dd949bd 100644 --- a/Runtime/MagicLeap.SDK.asmdef +++ b/Runtime/MagicLeap.SDK.asmdef @@ -10,7 +10,9 @@ "GUID:75469ad4d38634e559750d17036d5f7c", "GUID:15fc0a57446b3144c949da3e2b9737a9", "GUID:dc960734dc080426fa6612f1c5fe95f3", - "GUID:4847341ff46394e83bb78fbd0652937e" + "GUID:4847341ff46394e83bb78fbd0652937e", + "GUID:ce522b6ed64c8be4c989a1d26d0e3275", + "Unity.XR.OpenXR.Editor" ], "includePlatforms": [], "excludePlatforms": [], @@ -42,4 +44,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Runtime/OpenXR/MagicLeapClippingPlaneEnforcementFeature.cs b/Runtime/OpenXR/MagicLeapClippingPlaneEnforcementFeature.cs new file mode 100644 index 0000000..a4d952d --- /dev/null +++ b/Runtime/OpenXR/MagicLeapClippingPlaneEnforcementFeature.cs @@ -0,0 +1,125 @@ +#if UNITY_OPENXR_1_7_0_OR_NEWER +using System.Runtime.InteropServices; +using UnityEngine.XR.MagicLeap; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif + +using MLOpenXRNative = UnityEngine.XR.OpenXR.Features.MagicLeapSupport.MagicLeapFeature.NativeBindings; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ +#if UNITY_EDITOR + [OpenXRFeature(UiName = "Magic Leap 2 Clipping Plane Enforcement Support", + Desc="Support for controlling the min/max depth range on Magic Leap 2", + Company = "Magic Leap", + Version = "1.0.0", + BuildTargetGroups = new []{ BuildTargetGroup.Android, BuildTargetGroup.Standalone }, + FeatureId = featureId, + OpenxrExtensionStrings = "XR_EXT_view_configuration_depth_range" + )] +#endif + public class MagicLeapClippingPlaneEnforcementFeature : MagicLeapOpenXRFeatureBase + { + public enum FarClipMode : byte + { + None, + Recommended, + } + + public enum NearClipMode : byte + { + Recommended, +#if DISABLE_MAGICLEAP_CLIP_ENFORCEMENT + // None is unsupported for near clipping as Legal requires us to enforce the near clip plane. + None, +#endif + } + + public const string featureId = "com.magicleap.openxr.feature.clipping_plane_enforcement"; + + [SerializeField] + [Tooltip("Determines if the far clipping plane should be clamped, and to what maximum value.")] + private FarClipMode m_FarClipPolicy; + + [SerializeField] + [Tooltip("Determines the minimum value the near clipping plane will be clamped to.")] + private NearClipMode m_NearClipPolicy; + + public FarClipMode farClipPolicy => m_FarClipPolicy; + public NearClipMode nearClipPolicy => m_NearClipPolicy; + + public float recommendedNearZ => MLOpenXRGetRecommendedNearClippingPlane(); + public float recommendedFarZ => MLOpenXRGetRecommendedFarClippingPlane(); + + protected override void OnSessionBegin(ulong xrSession) + { + base.OnSessionBegin(xrSession); + + Application.onBeforeRender += EnforceClippingPlanes; + } + + protected override void OnSessionEnd(ulong xrSession) + { + base.OnSessionEnd(xrSession); + + Application.onBeforeRender -= EnforceClippingPlanes; + } + + private void EnforceClippingPlanes() => ApplyToCamera(Camera.main); + + public void ApplyFarClip(ref float zFar) + { + switch (m_FarClipPolicy) + { + case FarClipMode.Recommended: + zFar = Mathf.Min(zFar, recommendedFarZ); + break; + case FarClipMode.None: + default: + break; + } + } + + public void ApplyNearClip(ref float zNear) + { + switch (m_NearClipPolicy) + { + case NearClipMode.Recommended: + zNear = Mathf.Max(zNear, recommendedNearZ); + break; +#if DISABLE_MAGICLEAP_CLIP_ENFORCEMENT + case NearClipMode.None: + default: + break; +#endif + } + } + + public void ApplyToCamera(Camera camera, bool warnIfNearClipChanged = true) + { + if (!camera) + return; + + var zFar = camera.farClipPlane; + var zNear = camera.nearClipPlane; + + ApplyFarClip(ref zFar); + ApplyNearClip(ref zNear); + + if (warnIfNearClipChanged && zNear > camera.nearClipPlane) + Debug.LogWarning($"Main Camera's nearClipPlane value is less than the minimum value for this device. Increasing to {zNear}"); + + camera.farClipPlane = zFar; + camera.nearClipPlane = zNear; + } + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + private static extern float MLOpenXRGetRecommendedNearClippingPlane(); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + private static extern float MLOpenXRGetRecommendedFarClippingPlane(); + } +} +#endif diff --git a/Runtime/OpenXR/MagicLeapClippingPlaneEnforcementFeature.cs.meta b/Runtime/OpenXR/MagicLeapClippingPlaneEnforcementFeature.cs.meta new file mode 100644 index 0000000..68cf3d0 --- /dev/null +++ b/Runtime/OpenXR/MagicLeapClippingPlaneEnforcementFeature.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: abfe078b9e194033b41f1f1220aafe0d +timeCreated: 1689970389 \ No newline at end of file diff --git a/Runtime/OpenXR/MagicLeapControllerProfile.cs b/Runtime/OpenXR/MagicLeapControllerProfile.cs index 2b65b33..bf28586 100644 --- a/Runtime/OpenXR/MagicLeapControllerProfile.cs +++ b/Runtime/OpenXR/MagicLeapControllerProfile.cs @@ -49,8 +49,8 @@ public class MagicLeapControllerProfile : OpenXRInteractionFeature /// [Preserve, InputControlLayout(displayName = "Magic Leap Controller (OpenXR)", commonUsages = new[] { "LeftHand", "RightHand" })] public class MagicLeapController : XRControllerWithRumble - { - /// + { + /// /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) that represents information from the OpenXR binding. /// [Preserve, InputControl(aliases = new[] { "GripButton", "shoulderClicked"}, usage = "GripButton")] @@ -78,25 +78,25 @@ public class MagicLeapController : XRControllerWithRumble /// A [ButtonControl](xref:UnityEngine.InputSystem.Controls.ButtonControl) required for backwards compatibility with the XRSDK layouts. This represents the overall tracking state of the device. This value is equivalent to mapping devicePose/isTracked. /// [Preserve, InputControl(offset = 2)] - new public ButtonControl isTracked { get; private set; } + public new ButtonControl isTracked { get; private set; } /// /// A [IntegerControl](xref:UnityEngine.InputSystem.Controls.IntegerControl) required for backwards compatibility with the XRSDK layouts. This represents the bit flag set indicating what data is valid. This value is equivalent to mapping devicePose/trackingState. /// [Preserve, InputControl(offset = 4)] - new public IntegerControl trackingState { get; private set; } + public new IntegerControl trackingState { get; private set; } /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the device position, or grip position. This value is equivalent to mapping devicePose/position. /// [Preserve, InputControl(offset = 8, alias = "gripPosition")] - new public Vector3Control devicePosition { get; private set; } + public new Vector3Control devicePosition { get; private set; } /// /// A [QuaternionControl](xref:UnityEngine.InputSystem.Controls.QuaternionControl) required for backwards compatibility with the XRSDK layouts. This is the device orientation, or grip orientation. This value is equivalent to mapping devicePose/rotation. /// [Preserve, InputControl(offset = 20, alias = "gripOrientation")] - new public QuaternionControl deviceRotation { get; private set; } + public new QuaternionControl deviceRotation { get; private set; } /// /// A [Vector3Control](xref:UnityEngine.InputSystem.Controls.Vector3Control) required for backwards compatibility with the XRSDK layouts. This is the pointer position. This value is equivalent to mapping pointerPose/position. @@ -239,7 +239,7 @@ protected override void FinishSetup() /// public const string haptic = "/output/haptic"; - private const string kDeviceLocalizedName = "KHR Simple Controller OpenXR"; + private const string kDeviceLocalizedName = "Magic Leap Controller OpenXR"; /// /// Registers the layout with the Input System. @@ -257,7 +257,7 @@ protected override void RegisterDeviceLayout() /// protected override void UnregisterDeviceLayout() { - InputSystem.InputSystem.RemoveLayout(typeof(MagicLeapController).Name); + InputSystem.InputSystem.RemoveLayout(nameof(MagicLeapController)); } /// @@ -265,39 +265,35 @@ protected override void RegisterActionMapsWithRuntime() { ActionMapConfig actionMap = new ActionMapConfig() { - name = "MagicLeapController", + name = nameof(MagicLeapController), localizedName = kDeviceLocalizedName, desiredInteractionProfile = profile, manufacturer = "Magic Leap", - serialNumber = "", deviceInfos = new List() { - new DeviceConfig() + new() { - characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left), + characteristics = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Left, userPath = UserPaths.leftHand }, - new DeviceConfig() + new() { - characteristics = (InputDeviceCharacteristics)(InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right), + characteristics = InputDeviceCharacteristics.HeldInHand | InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | InputDeviceCharacteristics.Right, userPath = UserPaths.rightHand } }, actions = new List() { // Bumper - new ActionConfig() + new() { - name = "gripPressed", + name = nameof(MagicLeapController.gripPressed), localizedName = "Grip Pressed", type = ActionType.Binary, - usages = new List() - { - "GripButton" - }, + usages = new List(){ "GripButton" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = shoulderClick, interactionProfileName = profile, @@ -305,18 +301,15 @@ protected override void RegisterActionMapsWithRuntime() } }, // Menu - new ActionConfig() + new() { - name = "menu", + name =nameof(MagicLeapController.menu), localizedName = "Menu", type = ActionType.Binary, - usages = new List() - { - "MenuButton" - }, + usages = new List() { "MenuButton" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = menu, interactionProfileName = profile, @@ -324,18 +317,15 @@ protected override void RegisterActionMapsWithRuntime() } }, // Device Pose - new ActionConfig() + new() { - name = "devicePose", + name = nameof(MagicLeapController.devicePose), localizedName = "Device Pose", type = ActionType.Pose, - usages = new List() - { - "Device" - }, + usages = new List() { "Device" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = grip, interactionProfileName = profile, @@ -343,18 +333,15 @@ protected override void RegisterActionMapsWithRuntime() } }, // Pointer Pose - new ActionConfig() + new() { - name = "pointer", + name = nameof(MagicLeapController.pointer), localizedName = "Pointer Pose", type = ActionType.Pose, - usages = new List() - { - "Pointer" - }, + usages = new List() { "Pointer" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = aim, interactionProfileName = profile, @@ -362,18 +349,15 @@ protected override void RegisterActionMapsWithRuntime() } }, // Trigger - new ActionConfig() + new() { - name = "trigger", + name = nameof(MagicLeapController.trigger), localizedName = "Trigger", type = ActionType.Axis1D, - usages = new List() - { - "Trigger" - }, + usages = new List() { "Trigger" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = trigger, interactionProfileName = profile, @@ -381,90 +365,75 @@ protected override void RegisterActionMapsWithRuntime() } }, // Trigger Pressed - new ActionConfig() + new() { - name = "triggerPressed", + name = nameof(MagicLeapController.triggerPressed), localizedName = "Trigger Pressed", type = ActionType.Binary, - usages = new List() - { - "TriggerButton" - }, + usages = new List() { "TriggerButton" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = triggerClick, interactionProfileName = profile, } } }, - new ActionConfig() + new() { - name = "trackpad", + name = nameof(MagicLeapController.trackpad), localizedName = "Trackpad", type = ActionType.Axis2D, - usages = new List() - { - "Primary2DAxis" - }, + usages = new List() { "Primary2DAxis" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = trackpad, interactionProfileName = profile, } } }, - new ActionConfig() + new() { - name = "trackpadTouched", + name = nameof(MagicLeapController.trackpadTouched), localizedName = "Trackpad Touched", type = ActionType.Binary, - usages = new List() - { - "Primary2DAxisTouch" - }, + usages = new List() { "Primary2DAxisTouch" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = trackpadTouch, interactionProfileName = profile, } } }, - new ActionConfig() + new() { - name = "trackpadClicked", + name = nameof(MagicLeapController.trackpadClicked), localizedName = "Trackpad Clicked", type = ActionType.Binary, - usages = new List() - { - "Primary2DAxisClick" - }, + usages = new List() { "Primary2DAxisClick" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = trackpadClick, interactionProfileName = profile, } } }, - new ActionConfig() + new() { - name = "trackpadForce", + name = nameof(MagicLeapController.trackpadForce), localizedName = "Trackpad Force", type = ActionType.Axis1D, - usages = new List() - { - "Secondary2DAxisForce" - }, + usages = new List() { "Secondary2DAxisForce" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = trackpadForce, interactionProfileName = profile, @@ -472,15 +441,15 @@ protected override void RegisterActionMapsWithRuntime() } }, // Haptics - new ActionConfig() + new() { - name = "haptic", + name = nameof(MagicLeapController.haptic), localizedName = "Haptic Output", type = ActionType.Vibrate, usages = new List() { "Haptic" }, bindings = new List() { - new ActionBinding() + new() { interactionPath = haptic, interactionProfileName = profile, diff --git a/Runtime/OpenXR/MagicLeapFeature.cs b/Runtime/OpenXR/MagicLeapFeature.cs index d7e1d0d..011fe24 100644 --- a/Runtime/OpenXR/MagicLeapFeature.cs +++ b/Runtime/OpenXR/MagicLeapFeature.cs @@ -30,11 +30,7 @@ namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport BuildTargetGroups = new []{ BuildTargetGroup.Android, BuildTargetGroup.Standalone }, CustomRuntimeLoaderBuildTargets = new []{ BuildTarget.Android }, FeatureId = featureId, - OpenxrExtensionStrings = "XR_ML_compat " + - "XR_ML_frame_end_info " + - "XR_ML_global_dimmer " + - "XR_EXT_view_configuration_depth_range " + - "XR_KHR_convert_timespec_time" + OpenxrExtensionStrings = "XR_ML_compat XR_KHR_convert_timespec_time" )] #endif public partial class MagicLeapFeature : OpenXRFeature @@ -56,7 +52,6 @@ private struct xrSessionCached public bool IsMLAudioEnabled => enableMLAudio; private static List s_MeshSubsysDesc = new List(); - private static List s_PlaneSubsysDesc = new List(); private static List s_SessionSubsysDesc = new List(); public delegate void OnXRSessionStateChangeDelegate(int oldState, int newState); @@ -64,6 +59,23 @@ private struct xrSessionCached private static List xrSessionCacheList; +#if UNITY_EDITOR + // TODO :: Get actual legal justification for requiring clipping plane enforcement. + protected override void GetValidationChecks(List rules, BuildTargetGroup targetGroup) + { + base.GetValidationChecks(rules, targetGroup); + +#if !DISABLE_MAGICLEAP_CLIP_ENFORCEMENT + rules.Add(new ValidationRule(this) + { + checkPredicate = ()=> Utils.IsFeatureEnabled(targetGroup), + error = true, + message = $"[PLEASE VERIFY] {Utils.GetNiceTypeName()} is required to enforce important safety restrictions." + }); +#endif + } +#endif + protected override IntPtr HookGetInstanceProcAddr(IntPtr func) { return NativeBindings.MLOpenXRInterceptFunctions(func); @@ -104,7 +116,6 @@ protected override void OnSessionStateChange(int oldState, int newState) xrSessionCached newEvent; newEvent.oldState = oldState; newEvent.newState = newState; - xrSessionCacheList.Add(newEvent); } } @@ -114,31 +125,14 @@ protected override void OnSubsystemCreate() xrSessionCacheList = new List(); base.OnSubsystemCreate(); - - CreateSubsystem(s_PlaneSubsysDesc, MagicLeapXrProvider.PlanesSubsystemId); + CreateSubsystem(s_MeshSubsysDesc, MagicLeapXrProvider.MeshingSubsystemId); CreateSubsystem(s_SessionSubsysDesc, MagicLeapXrProvider.SessionSubsystemId); } - - protected override void OnSubsystemStart() - { - base.OnSubsystemStart(); - - StartSubsystem(); - } - - protected override void OnSubsystemStop() - { - base.OnSubsystemStop(); - - StopSubsystem(); - } - + protected override void OnSubsystemDestroy() { base.OnSubsystemDestroy(); - - DestroySubsystem(); DestroySubsystem(); DestroySubsystem(); } diff --git a/Runtime/OpenXR/MagicLeapFeatureNativeBindings.cs b/Runtime/OpenXR/MagicLeapFeatureNativeBindings.cs new file mode 100644 index 0000000..5df97ef --- /dev/null +++ b/Runtime/OpenXR/MagicLeapFeatureNativeBindings.cs @@ -0,0 +1,77 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2019-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% +#if UNITY_OPENXR_1_7_0_OR_NEWER +using System; +using System.Runtime.InteropServices; +using UnityEngine.XR.MagicLeap; +using UnityEngine.XR.MagicLeap.Native; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public partial class MagicLeapFeature + { + internal class NativeBindings : MagicLeapNativeBindings + { + public enum XrHandEXT + { + Left = 1, + Right = 2 + } + [StructLayout(LayoutKind.Sequential)] + public struct XrPosef + { + public Quaternion orientation; + public Vector3 position; + } + + [StructLayout(LayoutKind.Sequential)] + public struct XrHandJointLocationEXT + { + public UInt64 locationFlags; + public XrPosef pose; + public float radius; + } + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr MLOpenXRInterceptFunctions(IntPtr loaderFunc); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern bool MLOpenXROnInstanceCreate(IntPtr loaderFunc, ulong instance); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void MLOpenXROnInstanceDestroy(ulong instance); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void MLOpenXROnAppSpaceChange(ulong appSpace); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void MLOpenXROnSessionCreate(ulong session); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void MLOpenXROnSessionDestroy(ulong session); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOpenXRStartXRHandTracking(); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern unsafe MLResult.Code MLOpenXRGetXRHandTrackingJoints(XrHandEXT hand, XrHandJointLocationEXT* joints); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOpenXRStopXRHandTracking(); + + [DllImport(MLSdkLoaderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLZIPermissionsStart(); + + [DllImport(MLSdkLoaderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLZIPermissionsStop(); + } + } +} +#endif //UNITY_OPENXR_1_7_0_OR_NEWER diff --git a/Runtime/OpenXR/MagicLeapFeatureNativeBindings.cs.meta b/Runtime/OpenXR/MagicLeapFeatureNativeBindings.cs.meta new file mode 100644 index 0000000..7e2f9cd --- /dev/null +++ b/Runtime/OpenXR/MagicLeapFeatureNativeBindings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a09860293e75d42f4833d301bd169f14 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/MagicLeapHandTrackingFeature.cs b/Runtime/OpenXR/MagicLeapHandTrackingFeature.cs new file mode 100644 index 0000000..838d5ff --- /dev/null +++ b/Runtime/OpenXR/MagicLeapHandTrackingFeature.cs @@ -0,0 +1,96 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2019-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% +#if UNITY_OPENXR_1_7_0_OR_NEWER +using System.Collections.Generic; +using UnityEngine.XR.MagicLeap; +using UnityEngine.XR.Hands; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif +using NativeBindings = UnityEngine.XR.OpenXR.Features.MagicLeapSupport.MagicLeapFeature.NativeBindings; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + /// + /// Necessary to deploy a Magic Leap 2 compatible application with Hand Tracking. + /// +#if UNITY_EDITOR + [OpenXRFeature(UiName = "Magic Leap 2 HandTracking Support", + Desc = "Necessary to deploy a Magic Leap 2 compatible application with Hand Tracking.", + Company = "Magic Leap", + Version = "1.0.0", + Priority = -1, + BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone }, + FeatureId = featureId, + OpenxrExtensionStrings = "XR_EXT_hand_tracking " + )] +#endif + public class MagicLeapHandTrackingFeature : MagicLeapOpenXRFeatureBase + { + /// + /// The feature id string. This is used to give the feature a well known id for reference. + /// + public const string featureId = "com.magicleap.openxr.feature.ml2_handtracking"; + + private static List s_HandSubsysDesc = new List(); + + protected override bool OnInstanceCreate(ulong xrInstance) + { + if (!OpenXRRuntime.IsExtensionEnabled("XR_EXT_hand_tracking")) + { + Debug.LogWarning("XR_EXT_hand_tracking is not enabled, disabling MagicLeapHandTrackingFeature."); + return false; + } + + return base.OnInstanceCreate(xrInstance); + } + + + protected override void OnSessionCreate(ulong xrSession) + { + NativeBindings.MLOpenXRStartXRHandTracking(); + } + + protected override void OnSessionDestroy(ulong xrSession) + { + NativeBindings.MLOpenXRStopXRHandTracking(); + } + + protected override void OnSubsystemCreate() + { + + base.OnSubsystemCreate(); + + CreateSubsystem(s_HandSubsysDesc, MagicLeapXrProvider.HandSubsystemId); + } + + protected override void OnSubsystemStart() + { + base.OnSubsystemStart(); + StartSubsystem(); + } + + protected override void OnSubsystemStop() + { + base.OnSubsystemStop(); + + StopSubsystem(); + } + + protected override void OnSubsystemDestroy() + { + base.OnSubsystemDestroy(); + + DestroySubsystem(); + } + } +} +#endif diff --git a/Runtime/OpenXR/MagicLeapHandTrackingFeature.cs.meta b/Runtime/OpenXR/MagicLeapHandTrackingFeature.cs.meta new file mode 100644 index 0000000..defb773 --- /dev/null +++ b/Runtime/OpenXR/MagicLeapHandTrackingFeature.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c0fdb29d74fbdd4daf93abba8b17791 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/MagicLeapOpenXRFeatureBase.cs b/Runtime/OpenXR/MagicLeapOpenXRFeatureBase.cs new file mode 100644 index 0000000..e0ae309 --- /dev/null +++ b/Runtime/OpenXR/MagicLeapOpenXRFeatureBase.cs @@ -0,0 +1,62 @@ +#if UNITY_OPENXR_1_7_0_OR_NEWER +using System; +using System.Collections.Generic; +using System.Linq; + +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public abstract class MagicLeapOpenXRFeatureBase : OpenXRFeature + { + protected virtual IEnumerable dependsOn => Enumerable.Empty(); + + protected void CheckEnabledExtension(string extensionName, bool required = false) + { + if (OpenXRRuntime.IsExtensionEnabled(extensionName)) + return; + + if (required) + throw new Exception($"Required OpenXR extension '{extensionName}' was not enabled!"); + + Debug.LogWarning($"OpenXR extension '{extensionName}' was not enabled!"); + } + +#if UNITY_EDITOR + protected override void GetValidationChecks(List rules, BuildTargetGroup targetGroup) + { + base.GetValidationChecks(rules, targetGroup); + + rules.Add(GetDependencyRule(targetGroup)); + + foreach (var depends in dependsOn) + { + rules.Add(GetDependencyRule(targetGroup, depends)); + } + } + + protected ValidationRule GetDependencyRule(BuildTargetGroup group, Type featureType) + => new ValidationRule(this) + { + checkPredicate = ()=> Utils.IsFeatureEnabled(group, featureType), + fixIt = () => Utils.TryEnableFeature(group, featureType), + error = true, + message = $"{Utils.GetNiceTypeName(GetType())} depends on the {Utils.GetNiceTypeName(featureType)}, which is not enabled. Please enable this feature" + }; + + protected ValidationRule GetDependencyRule(BuildTargetGroup group) where TFeature : OpenXRFeature + => new ValidationRule(this) + { + checkPredicate = ()=> Utils.IsFeatureEnabled(group), + fixIt = () => Utils.TryEnableFeature(group), + error = true, + message = $"{Utils.GetNiceTypeName(GetType())} depends on the {Utils.GetNiceTypeName()}, which is not enabled. Please enable this feature" + }; + +#endif + } +} +#endif diff --git a/Runtime/OpenXR/MagicLeapOpenXRFeatureBase.cs.meta b/Runtime/OpenXR/MagicLeapOpenXRFeatureBase.cs.meta new file mode 100644 index 0000000..93db714 --- /dev/null +++ b/Runtime/OpenXR/MagicLeapOpenXRFeatureBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a984855d80c44d73987ad30935eb7aad +timeCreated: 1689100727 \ No newline at end of file diff --git a/Runtime/OpenXR/MagicLeapReferenceSpacesFeature.cs b/Runtime/OpenXR/MagicLeapReferenceSpacesFeature.cs new file mode 100644 index 0000000..6ce12b8 --- /dev/null +++ b/Runtime/OpenXR/MagicLeapReferenceSpacesFeature.cs @@ -0,0 +1,25 @@ +#if UNITY_OPENXR_1_7_0_OR_NEWER +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ +#if UNITY_EDITOR + [OpenXRFeature(UiName = "Magic Leap 2 Reference Spaces Support", + Desc="Support for additional XR reference spaces supported by Magic Leap 2", + Company = "Magic Leap", + Version = "1.0.0", + BuildTargetGroups = new []{ BuildTargetGroup.Android, BuildTargetGroup.Standalone }, + FeatureId = featureId, + OpenxrExtensionStrings = "XR_MSFT_unbounded_reference_space " + + "XR_EXT_local_floor " + )] +#endif + public class MagicLeapReferenceSpacesFeature : MagicLeapOpenXRFeatureBase + { + public const string featureId = "com.magicleap.openxr.feature.reference_spaces"; + } +} +#endif diff --git a/Runtime/OpenXR/MagicLeapReferenceSpacesFeature.cs.meta b/Runtime/OpenXR/MagicLeapReferenceSpacesFeature.cs.meta new file mode 100644 index 0000000..832ae2d --- /dev/null +++ b/Runtime/OpenXR/MagicLeapReferenceSpacesFeature.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d60f70bca23d41759964b1dda9e085b0 +timeCreated: 1689969877 \ No newline at end of file diff --git a/Runtime/OpenXR/MagicLeapRenderingExtensionsFeature.cs b/Runtime/OpenXR/MagicLeapRenderingExtensionsFeature.cs new file mode 100644 index 0000000..9d2fd99 --- /dev/null +++ b/Runtime/OpenXR/MagicLeapRenderingExtensionsFeature.cs @@ -0,0 +1,133 @@ +#if UNITY_OPENXR_1_7_0_OR_NEWER +using System; +using System.Runtime.InteropServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.XR.MagicLeap; +using UnityEngine.XR.MagicLeap.Unsafe; +using UnityEngine.XR.OpenXR.Features.MagicLeapSupport.NativeInterop; +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + using Native = MagicLeapFeature.NativeBindings; +#if UNITY_EDITOR + [OpenXRFeature(UiName = "Magic Leap 2 Rendering Extenstions Support", + Desc="Support for controlling rendering features specific to Magic Leap 2.", + Company = "Magic Leap", + Version = "1.0.0", + BuildTargetGroups = new []{ BuildTargetGroup.Android, BuildTargetGroup.Standalone }, + FeatureId = featureId, + OpenxrExtensionStrings = "XR_ML_frame_end_info " + + "XR_ML_global_dimmer " + )] +#endif + public unsafe class MagicLeapRenderingExtensionsFeature : MagicLeapOpenXRFeatureBase + { + public enum BlendMode + { + Additive, + AlphaBlend, + } + + public const string featureId = "com.magicleap.openxr.feature.rendering_extensions"; + + public float focusDistance = 0.0f; + public bool useProtectedSurface = false; + public bool useVignetteMode = false; + + public bool globalDimmerEnabled = false; + public float globalDimmerValue = 0.0f; + + public BlendMode blendMode; + + [NonSerialized] + private FrameEndInfo* m_FrameEndInfo; + + [NonSerialized] + private GlobalDimmerFrameEndInfo* m_GlobalDimmerFrameInfo; + + protected override void OnSessionBegin(ulong xrSession) + { + base.OnSessionBegin(xrSession); + + Cleanup(); + + InitializeOpenXRState(); + + Application.onBeforeRender += SynchronizeRenderState; + } + + protected override void OnSessionEnd(ulong xrSession) + { + base.OnSessionEnd(xrSession); + + Cleanup(); + } + + private void Cleanup() + { + if (m_FrameEndInfo != null) + UnsafeUtility.FreeTracked(m_FrameEndInfo, Allocator.Persistent); + + if (m_GlobalDimmerFrameInfo != null) + UnsafeUtility.FreeTracked(m_GlobalDimmerFrameInfo, Allocator.Persistent); + + Application.onBeforeRender -= SynchronizeRenderState; + } + + private void InitializeOpenXRState() + { + m_FrameEndInfo = UnsafeUtilityEx.MallocTracked(Allocator.Persistent, 1); + *m_FrameEndInfo = FrameEndInfo.Init(); + + m_GlobalDimmerFrameInfo = + UnsafeUtilityEx.MallocTracked(Allocator.Persistent, 1); + *m_GlobalDimmerFrameInfo = GlobalDimmerFrameEndInfo.Init(); + + m_FrameEndInfo->next = m_GlobalDimmerFrameInfo; + + MLOpenXRInitializeEndFrameState(m_FrameEndInfo); + } + + private NativeInterop.BlendMode ToNativeBlendMode(BlendMode blendMode) + { + switch (blendMode) + { + case BlendMode.Additive: + return NativeInterop.BlendMode.Additive; + case BlendMode.AlphaBlend: + return NativeInterop.BlendMode.AlphaBlend; + default: + throw new ArgumentException(nameof(blendMode)); + } + + } + + private void SynchronizeRenderState() + { + m_FrameEndInfo->focusDistance = focusDistance; + ref var flags = ref m_FrameEndInfo->flags; + flags = FrameInfoFlags.None; + if (useProtectedSurface) + flags &= FrameInfoFlags.Protected; + if (useVignetteMode) + flags &= FrameInfoFlags.Vignette; + + m_GlobalDimmerFrameInfo->dimmerValue = globalDimmerValue; + m_GlobalDimmerFrameInfo->flags = + (globalDimmerEnabled) ? GlobalDimmerFlags.Enabled : GlobalDimmerFlags.Disabled; + + MLOpenXRSetEnvironmentBlendMode(ToNativeBlendMode(blendMode)); + } + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + private static extern MLResult.Code MLOpenXRSetEnvironmentBlendMode(NativeInterop.BlendMode blendMode); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + private static extern void MLOpenXRInitializeEndFrameState(NativeInterop.FrameEndInfo* frameEndInfo); + } +} +#endif diff --git a/Runtime/OpenXR/MagicLeapRenderingExtensionsFeature.cs.meta b/Runtime/OpenXR/MagicLeapRenderingExtensionsFeature.cs.meta new file mode 100644 index 0000000..1879a7f --- /dev/null +++ b/Runtime/OpenXR/MagicLeapRenderingExtensionsFeature.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c6f97c49868492bb3efe751a0d9cd5d +timeCreated: 1689969264 \ No newline at end of file diff --git a/Runtime/OpenXR/NativeInteropTypes.cs b/Runtime/OpenXR/NativeInteropTypes.cs new file mode 100644 index 0000000..cce3829 --- /dev/null +++ b/Runtime/OpenXR/NativeInteropTypes.cs @@ -0,0 +1,95 @@ +#if UNITY_OPENXR_1_7_0_OR_NEWER +using System; +using System.Runtime.InteropServices; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport.NativeInterop +{ + internal enum BlendMode : ulong + { + Additive = 2, + AlphaBlend = 3 + } + + [Flags] + internal enum FrameInfoFlags : ulong + { + None = 0, + Protected = 1 << 0, + Vignette = 1 << 1, + } + + // By default, [StructLayout(LayoutKind.Sequential)] is applied to structs + internal unsafe struct FrameEndInfo + { + // OpenXR structure type constant (from OpenXR headers) + internal const ulong kStructType = 1000135000; +#pragma warning disable 0414 + private ulong type; + internal void* next; + internal float focusDistance; + internal FrameInfoFlags flags; +#pragma warning restore 0414 + + internal static FrameEndInfo Init() + => new FrameEndInfo + { + type = kStructType, + next = null, + focusDistance = 0.0f, + flags = FrameInfoFlags.None, + }; + } + + internal static class FunctionPrototypes + { +#if PLATFORM_STANDALONE_WIN || UNITY_EDITOR_WIN + private const CallingConvention kCallConv = CallingConvention.StdCall; +#else + private const CallingConvention kCallConv = CallingConvention.Cdecl; +#endif + [UnmanagedFunctionPointer(kCallConv, CharSet = CharSet.Ansi)] + public delegate Result XRGetInstanceProcAddr(ulong instance, string name, ref IntPtr function); + } + + [Flags] + internal enum GlobalDimmerFlags : ulong + { + Disabled = 0, + Enabled = 1, + } + + // By default, [StructLayout(LayoutKind.Sequential)] is applied to structs + internal unsafe struct GlobalDimmerFrameEndInfo + { + // OpenXR structure type constant (from OpenXR headers) + internal const ulong kStructType = 1000136000; + +#pragma warning disable 0414 + private ulong type; + internal void* next; + internal float dimmerValue; + internal GlobalDimmerFlags flags; +#pragma warning restore 0414 + + internal static GlobalDimmerFrameEndInfo Init() + => new GlobalDimmerFrameEndInfo() + { + type = kStructType, + next = null, + dimmerValue = 0.0f, + flags = GlobalDimmerFlags.Disabled, + }; + } + + internal struct Result + { +#pragma warning disable 0414 + private long value; +#pragma warning restore 0414 + + public bool actuallySucceeded => value == 0; + public bool failed => value < 0; + public bool succeeded => value >= 0; + } +} +#endif diff --git a/Runtime/OpenXR/NativeInteropTypes.cs.meta b/Runtime/OpenXR/NativeInteropTypes.cs.meta new file mode 100644 index 0000000..46faf47 --- /dev/null +++ b/Runtime/OpenXR/NativeInteropTypes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 37b22148b1ec46e3b693b3ceb8183501 +timeCreated: 1690389847 \ No newline at end of file diff --git a/Runtime/OpenXR/OpenXRUtils.cs b/Runtime/OpenXR/OpenXRUtils.cs new file mode 100644 index 0000000..01598c4 --- /dev/null +++ b/Runtime/OpenXR/OpenXRUtils.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +#if UNITY_OPENXR_1_7_0_OR_NEWER +using UnityEngine.XR.Management; +using UnityEngine.XR.OpenXR.Features; +#endif + +#if UNITY_EDITOR +using System; +using UnityEditor; +#endif + +namespace UnityEngine.XR.OpenXR +{ + public static class Utils + { + /// + /// Finds an appropriate Magic Leap Controller registered through OpenXR which is properly named and is valid. + /// + /// The characteristics required of the input device to be found. + /// A Magic Leap Controller InputDevice registered through OpenXR. If none is found then a new InputDevice is returned. + public static InputDevice FindMagicLeapController(InputDeviceCharacteristics inputDeviceCharacteristics) + { + List devices = new List(); + InputDevices.GetDevicesWithCharacteristics(inputDeviceCharacteristics, devices); + + // TODO: Consider changing this for loop if the controller bug is fixed. + // Currently, the input device is registered twice and the first element returns zero values. + // This loop searches the list in reverse to get the last element which should have the proper data. + for (int i = devices.Count - 1; i > 0; i--) + { + if (devices[i].name == "Magic Leap Controller OpenXR" && devices[i].isValid) + { + return devices[i]; + } + } + + return new InputDevice(); + } +#if UNITY_OPENXR_1_7_0_OR_NEWER + internal static bool TryGetOpenXRFeature(out TFeature feature) where TFeature : OpenXRFeature + { + feature = null; + + var settings = OpenXRSettings.Instance; + if (settings == null) + return false; + + feature = settings.GetFeature(); + return feature != null; + } + + internal static bool TryGetOpenXRLoader(out OpenXRLoader loader) + { + loader = null; + var settings = XRGeneralSettings.Instance; + if (settings == null) + return false; + var mgr = settings.Manager; + if (mgr == null) + return false; + loader = mgr.activeLoader as OpenXRLoader; + return loader != null; + } + + internal static bool TryGetSubsytemFromOpenXRLoader(out TSubsystem subsystem) + where TSubsystem : class, ISubsystem + { + subsystem = null; + if (!TryGetOpenXRLoader(out var loader)) + return false; + subsystem = loader.GetLoadedSubsystem(); + return subsystem != null; + } + +#if UNITY_EDITOR + internal static string GetNiceTypeName(System.Type type) + => UnityEditor.ObjectNames.NicifyVariableName(type.Name); + + internal static string GetNiceTypeName() + => GetNiceTypeName(typeof(T)); + + private static OpenXRSettings GetSettings(BuildTargetGroup group) + => OpenXRSettings.GetSettingsForBuildTargetGroup(group); + + private static OpenXRFeature GetFeatureForBuildTarget(BuildTargetGroup group, Type featureType) + { + var settings = GetSettings(group); + if (settings == null) + return null; + return settings.GetFeature(featureType); + } + + private static TFeature GetFeatureForBuildTarget(BuildTargetGroup group) where TFeature: OpenXRFeature + { + var settings = GetSettings(group); + if (settings == null) + return null; + return settings.GetFeature(); + } + + internal static bool IsFeatureEnabled(BuildTargetGroup group, Type featureType) + { + var feature = GetFeatureForBuildTarget(group, featureType); + return feature != null && feature.enabled; + } + + internal static bool IsFeatureEnabled(BuildTargetGroup group) where TFeature: OpenXRFeature + { + var feature = GetFeatureForBuildTarget(group); + return feature != null && feature.enabled; + } + + internal static bool TryEnableFeature(BuildTargetGroup group, Type featureType) + { + var feature = GetFeatureForBuildTarget(group, featureType); + return feature != null && (feature.enabled = true) || false; + } + + internal static bool TryEnableFeature(BuildTargetGroup group) where TFeature : OpenXRFeature + { + var feature = GetFeatureForBuildTarget(group); + return feature != null && (feature.enabled = true) || false; + } +#endif // UNITY_EDITOR +#endif // UNITY_OPENXR_1_7_0_OR_NEWER + } + + +} diff --git a/Runtime/OpenXR/OpenXRUtils.cs.meta b/Runtime/OpenXR/OpenXRUtils.cs.meta new file mode 100644 index 0000000..513d256 --- /dev/null +++ b/Runtime/OpenXR/OpenXRUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3057d345b04af294a8faf5633d277ceb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes.meta b/Runtime/OpenXR/Planes.meta new file mode 100644 index 0000000..0c88f62 --- /dev/null +++ b/Runtime/OpenXR/Planes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: de4e54df40817a146bd8fbdf589e66e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes/ConvexHullGenerator.cs b/Runtime/OpenXR/Planes/ConvexHullGenerator.cs new file mode 100644 index 0000000..d418775 --- /dev/null +++ b/Runtime/OpenXR/Planes/ConvexHullGenerator.cs @@ -0,0 +1,188 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2021-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% + +using System; +using System.Collections.Generic; +using Unity.Collections; +using UnityEngine.XR.MagicLeap; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public partial class MLXrPlaneSubsystem + { + internal static class ConvexHullGenerator + { + // Get a single static reference to AngleComparer to avoid additional GC allocs + private static readonly Comparison PolarAngleComparer = AngleComparer; + + // Used by AngleComparer + private static Vector2 pivot; + + // Reusable List to avoid additional GC alloc + private static readonly List Points = new(); + + /// + /// Used to sort a collection of points by the polar angle + /// made with against the +x axis. + /// + /// The first point to compare. + /// The second point to compare. + /// + /// -1 if the vector from + /// to makes a larger + /// angle against the +x axis than to , + /// +1 if the angle is smaller, and 0 if they are equal. + /// + private static int AngleComparer(Vector2 lhs, Vector2 rhs) + { + // Compute the angle against the pivot + var u = lhs - pivot; + var v = rhs - pivot; + var cross = u.x * v.y - u.y * v.x; + + // cross > 0 => lhs is more to the right than rhs + return Math.Sign(cross); + } + + /// + /// returns true if a, b, c form a clockwise turn + /// + private static bool ClockwiseTurn(Vector2 a, Vector2 b, Vector2 c) + { + var u = a - b; + var v = c - b; + return u.x * v.y - u.y * v.x > 0f; + } + + /// + /// Computes convex hull using the Graham Scan method. + /// + /// An arbitrary collection of 2D points. + /// The allocator to use for the returned array. + /// + /// A new NativeArray containing the convex hull. The allocated Length of the array will always + /// be the same as . contains the true number of + /// points in the hull, which will always be less than .Length. + /// + private static NativeFixedList GrahamScan(NativeArray points, Allocator allocator) + { + // Step 1: Find the lowest y-coordinate and leftmost point, + // called the pivot + var pivotIndex = 0; + for (var i = 1; i < points.Length; ++i) + { + var point = points[i]; + var pointPivot = points[pivotIndex]; + if (point.y < pointPivot.y) + { + pivotIndex = i; + } + else if (Mathf.Approximately(point.y, pointPivot.y) && point.x < pointPivot.x) + { + pivotIndex = i; + } + } + + pivot = points[pivotIndex]; + + // Step 2: Copy all points except the pivot into a List + Points.Clear(); + for (var i = 0; i < pivotIndex; ++i) + Points.Add(points[i]); + for (var i = pivotIndex + 1; i < points.Length; ++i) + Points.Add(points[i]); + + // Step 3: Sort points by polar angle with the pivot + Points.Sort(PolarAngleComparer); + + // Step 4: Compute the hull + var length = 0; + var hull = new NativeArray(points.Length, allocator); + hull[length++] = pivot; + foreach (var point in Points) + { + while (length > 1 && !ClockwiseTurn(hull[length - 2], hull[length - 1], point)) + { + --length; + } + + hull[length++] = point; + } + + return new NativeFixedList(hull, length); + } + + public static void GrahamScan(NativeArray points, Allocator allocator, ref NativeArray convexHullOut) + { + // We need to make a copy because GrahamScan doesn't know how big the result will be. + using var hull = GrahamScan(points, Allocator.Temp); + CreateOrResizeNativeArrayIfNecessary(hull.Length, allocator, ref convexHullOut); + hull.CopyTo(convexHullOut); + } + + private static bool IsPointLeftOfLine(Vector2 point, Vector2 lA, Vector2 lB) + { + var u = lB - lA; + var v = point - lA; + return u.x * v.y - u.y * v.x > 0f; + } + + /// + /// Computes a convex hull using the Gift Wrap method. + /// + /// + /// + /// + /// + public static void GiftWrap(NativeArray points, Allocator allocator, ref NativeArray convexHullOut) + { + if (!points.IsCreated) + throw new ArgumentException("Array has been disposed.", nameof(points)); + + // pointOnHull is initialized to the leftmost point + // which is guaranteed to be part of the convex hull + var pointOnHull = 0; + for (var i = 1; i < points.Length; ++i) + { + if (points[i].x < points[pointOnHull].x || (Mathf.Approximately(points[i].x, points[pointOnHull].x) && points[i].y < points[pointOnHull].y)) + { + pointOnHull = i; + } + } + + using var hullIndices = new NativeFixedList(points.Length, Allocator.Temp); + int endpoint; + do + { + var endpointAlreadyOnHull = false; + foreach (var t in hullIndices) + { + if (t != pointOnHull) continue; + endpointAlreadyOnHull = true; + break; + } + + if (endpointAlreadyOnHull) break; + + hullIndices.Add(pointOnHull); + endpoint = 0; // initial endpoint for a candidate edge on the hull + for (var j = 1; j < points.Length; ++j) endpoint = endpoint == pointOnHull || IsPointLeftOfLine(points[j], points[pointOnHull], points[endpoint]) ? j : endpoint; + pointOnHull = endpoint; + } while (endpoint != hullIndices[0] && hullIndices.Length < hullIndices.Capacity); // wrapped around to first hull point + + CreateOrResizeNativeArrayIfNecessary(hullIndices.Length, allocator, ref convexHullOut); + for (var i = 0; i < hullIndices.Length; ++i) + { + convexHullOut[i] = points[hullIndices[i]]; + } + } + } + } +} diff --git a/Runtime/OpenXR/Planes/ConvexHullGenerator.cs.meta b/Runtime/OpenXR/Planes/ConvexHullGenerator.cs.meta new file mode 100644 index 0000000..1d42896 --- /dev/null +++ b/Runtime/OpenXR/Planes/ConvexHullGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6d69744904f6c044b692f5f3bc98ead +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes/CopyPlaneResultsJob.cs b/Runtime/OpenXR/Planes/CopyPlaneResultsJob.cs new file mode 100644 index 0000000..1a2451f --- /dev/null +++ b/Runtime/OpenXR/Planes/CopyPlaneResultsJob.cs @@ -0,0 +1,75 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2021-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% + +using System; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine.XR.ARSubsystems; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public partial class MLXrPlaneSubsystem + { + internal unsafe struct CopyPlaneResultsJob : IJobParallelFor + { + private static readonly Quaternion MagicLeapToUnityRotation = Quaternion.AngleAxis(-90f, Vector3.right); + + [ReadOnly] public NativeArray PlaneTrackableIds; + + [ReadOnly] public XrTypes.MLXrPlaneDetectorLocation* PlanesIn; + + [WriteOnly] public NativeArray PlanesOut; + + private PlaneAlignment ToUnityAlignment(XrTypes.MLXrPlaneDetectorOrientation flag) + { + return flag switch + { + XrTypes.MLXrPlaneDetectorOrientation.Vertical => PlaneAlignment.Vertical, + XrTypes.MLXrPlaneDetectorOrientation.HorizontalUpward => PlaneAlignment.HorizontalUp, + XrTypes.MLXrPlaneDetectorOrientation.HorizontalDownward => PlaneAlignment.HorizontalDown, + XrTypes.MLXrPlaneDetectorOrientation.Arbitrary => PlaneAlignment.NotAxisAligned, + _ => PlaneAlignment.None + }; + } + + private static PlaneClassification ToUnityClassification(XrTypes.MLXrPlaneDetectorSemanticType semanticType) + { + var result = semanticType switch + { + XrTypes.MLXrPlaneDetectorSemanticType.Ceiling => PlaneClassification.Ceiling, + XrTypes.MLXrPlaneDetectorSemanticType.Floor => PlaneClassification.Floor, + XrTypes.MLXrPlaneDetectorSemanticType.Wall => PlaneClassification.Wall, + XrTypes.MLXrPlaneDetectorSemanticType.Platform => PlaneClassification.Table, + _ => PlaneClassification.None + }; + return result; + } + + public void Execute(int index) + { + var plane = PlanesIn[index]; + + var planePose = plane.pose; + var position = planePose.position; + var rotation = planePose.rotation * MagicLeapToUnityRotation; + + PlanesOut[index] = new BoundedPlane(PlaneTrackableIds[index], // trackableId + TrackableId.invalidId, // subsumedBy + new Pose(position, rotation), Vector3.zero, // center + new Vector2(plane.extents.x, plane.extents.y), // size + ToUnityAlignment(plane.orientation), // alignment + TrackingState.Tracking, // tracking state + IntPtr.Zero, // native pointer + ToUnityClassification(plane.semanticType) // classification + ); + } + } + } +} diff --git a/Runtime/OpenXR/Planes/CopyPlaneResultsJob.cs.meta b/Runtime/OpenXR/Planes/CopyPlaneResultsJob.cs.meta new file mode 100644 index 0000000..3249f41 --- /dev/null +++ b/Runtime/OpenXR/Planes/CopyPlaneResultsJob.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4e21f30772e0a04fa6d126587514b1f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes/Extensions.meta b/Runtime/OpenXR/Planes/Extensions.meta new file mode 100644 index 0000000..abd6387 --- /dev/null +++ b/Runtime/OpenXR/Planes/Extensions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5a50b2227985d5549a40fb5477236624 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes/Extensions/MLPlanesQueryFlags.cs b/Runtime/OpenXR/Planes/Extensions/MLPlanesQueryFlags.cs new file mode 100644 index 0000000..b2ece0f --- /dev/null +++ b/Runtime/OpenXR/Planes/Extensions/MLPlanesQueryFlags.cs @@ -0,0 +1,152 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2021-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% + +using System; +using System.Collections.Generic; +using UnityEngine.XR.ARSubsystems; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public partial class MLXrPlaneSubsystem + { + [Flags] + public enum MLPlanesQueryFlags : uint + { + /// + /// Include no planes. + /// + None = 0, + + /// + /// Include planes whose normal is perpendicular to gravity. + /// + Vertical = 1 << 0, + + /// + /// Include planes whose normal is parallel to gravity. + /// + Horizontal = 1 << 1, + + /// + /// Include planes with arbitrary normals. + /// + Arbitrary = 1 << 2, + + /// + /// Include all plane orientations. + /// + AllOrientations = Vertical | Horizontal | Arbitrary, + + /// + /// Include planes semantically tagged as ceiling. + /// + SemanticCeiling = 1 << 6, + + /// + /// Include planes semantically tagged as floor. + /// + SemanticFloor = 1 << 7, + + /// + /// Include planes semantically tagged as wall. + /// + SemanticWall = 1 << 8, + + /// + /// Include planes semantically tagged as platforms + /// + SemanticPlatform = 1 << 9, + + /// + /// Include all planes that are semantically tagged. + /// + SemanticAll = SemanticCeiling | SemanticFloor | SemanticWall | SemanticPlatform + } + } + + public static class MLPlanesQueryFlagsAndPlaneDetectionModeExtensions + { + public static PlaneDetectionMode ToPlaneDetectionMode(this MLXrPlaneSubsystem.MLPlanesQueryFlags planesQueryFlags) + { + var outDetectionMode = PlaneDetectionMode.None; + if ((planesQueryFlags & MLXrPlaneSubsystem.MLPlanesQueryFlags.Horizontal) != 0) + { + outDetectionMode |= PlaneDetectionMode.Horizontal; + } + + if ((planesQueryFlags & MLXrPlaneSubsystem.MLPlanesQueryFlags.Vertical) != 0) + { + outDetectionMode |= PlaneDetectionMode.Vertical; + } + + return outDetectionMode; + } + + public static MLXrPlaneSubsystem.MLPlanesQueryFlags ToMLXrQueryFlags(this PlaneDetectionMode planeDetectionMode) + { + var outFlags = MLXrPlaneSubsystem.MLPlanesQueryFlags.None; + if ((planeDetectionMode & PlaneDetectionMode.Horizontal) != 0) + { + outFlags |= MLXrPlaneSubsystem.MLPlanesQueryFlags.Horizontal; + } + + if ((planeDetectionMode & PlaneDetectionMode.Vertical) != 0) + { + outFlags |= MLXrPlaneSubsystem.MLPlanesQueryFlags.Vertical; + } + + return outFlags; + } + + public static void ToMLXrOrientationsAndSemanticTypes(this MLXrPlaneSubsystem.MLPlanesQueryFlags flags, out IList orientations, out IList semanticTypes) + { + orientations = new List(); + semanticTypes = new List(); + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.Horizontal) != 0) + { + orientations.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorOrientation.HorizontalDownward); + orientations.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorOrientation.HorizontalUpward); + } + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.Arbitrary) != 0) + { + orientations.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorOrientation.Arbitrary); + } + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.Vertical) != 0) + { + orientations.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorOrientation.Vertical); + } + + //Semantic types + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.SemanticCeiling) != 0) + { + semanticTypes.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorSemanticType.Ceiling); + } + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.SemanticFloor) != 0) + { + semanticTypes.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorSemanticType.Floor); + } + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.SemanticPlatform) != 0) + { + semanticTypes.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorSemanticType.Platform); + } + + if ((flags & MLXrPlaneSubsystem.MLPlanesQueryFlags.SemanticWall) != 0) + { + semanticTypes.Add(MLXrPlaneSubsystem.XrTypes.MLXrPlaneDetectorSemanticType.Wall); + } + } + } +} diff --git a/Runtime/OpenXR/Planes/Extensions/MLPlanesQueryFlags.cs.meta b/Runtime/OpenXR/Planes/Extensions/MLPlanesQueryFlags.cs.meta new file mode 100644 index 0000000..de1723b --- /dev/null +++ b/Runtime/OpenXR/Planes/Extensions/MLPlanesQueryFlags.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82b36c864aa43354a82b1bbcfd28dac0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes/Extensions/XRTypes.cs b/Runtime/OpenXR/Planes/Extensions/XRTypes.cs new file mode 100644 index 0000000..24e3e9b --- /dev/null +++ b/Runtime/OpenXR/Planes/Extensions/XRTypes.cs @@ -0,0 +1,93 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2021-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% + +using System.Runtime.InteropServices; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public partial class MLXrPlaneSubsystem + { + public static class XrTypes + { + public enum MLXrPlaneDetectorOrientation : uint + { + HorizontalUpward, + HorizontalDownward, + Vertical, + Arbitrary, + } + + public enum MLXrPlaneDetectorSemanticType : uint + { + Ceiling = 1U, + Floor, + Wall, + Platform, + } + + public enum MLXrPlaneDetectionState : uint + { + None = 0, + Pending, + Done, + Error, + Fatal + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MLXrPose + { + internal Vector3 position; + internal Quaternion rotation; + } + + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct MLXrPlaneDetectorBeginInfo + { + internal uint orientationCount; + internal MLXrPlaneDetectorOrientation* orientations; + internal uint semanticTypeCount; + internal MLXrPlaneDetectorSemanticType* semanticTypes; + internal uint maxPlanes; + internal float minArea; + internal MLXrPose boundingBoxPose; + internal Vector3 boundingBoxExtents; + } + + [StructLayout(LayoutKind.Sequential)] + internal readonly struct MLXrPlaneDetectorLocation + { + internal readonly ulong planeId; + internal readonly MLXrPose pose; + internal readonly Vector2 extents; + internal readonly MLXrPlaneDetectorOrientation orientation; + internal readonly MLXrPlaneDetectorSemanticType semanticType; + internal readonly uint polygonBufferCount; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct MLXrPlaneDetectorLocations + { + internal uint planeLocationCapacityInput; + internal readonly uint planeLocationCountOutput; + internal MLXrPlaneDetectorLocation* planeLocations; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct MLXrPlaneDetectorPolygonBuffer + { + internal uint vertexCapacityInput; + internal readonly uint vertexCountOutput; + internal Vector2* vertices; + } + } + } +} diff --git a/Runtime/OpenXR/Planes/Extensions/XRTypes.cs.meta b/Runtime/OpenXR/Planes/Extensions/XRTypes.cs.meta new file mode 100644 index 0000000..1509433 --- /dev/null +++ b/Runtime/OpenXR/Planes/Extensions/XRTypes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c3e207c68fbf4c2bbbafc29364cfc5db +timeCreated: 1688583586 \ No newline at end of file diff --git a/Runtime/OpenXR/Planes/MLXrPlaneSubsystem.cs b/Runtime/OpenXR/Planes/MLXrPlaneSubsystem.cs new file mode 100644 index 0000000..5e825d5 --- /dev/null +++ b/Runtime/OpenXR/Planes/MLXrPlaneSubsystem.cs @@ -0,0 +1,463 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2021-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% + +using System; +using System.Collections.Generic; +using System.Linq; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using UnityEngine.Scripting; +using UnityEngine.XR.ARSubsystems; +using UnityEngine.XR.MagicLeap; +using UnityEngine.XR.MagicLeap.Native; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + /// + /// The Magic Leap implementation of the XRPlaneSubsystem. Do not create this directly. + /// Use PlanesSubsystemDescriptor.Create() instead. + /// + [Preserve] + public sealed partial class MLXrPlaneSubsystem : XRPlaneSubsystem + { + private static PlanesQuery query; + public static PlanesQuery Query + { + get => query; + set + { + QuerySet = true; + query = value; + } + } + + private static bool QuerySet { get; set; } + + public struct PlanesQuery + { + /// + /// The flags to apply to this query. + /// + public MLPlanesQueryFlags Flags; + + /// + /// The center of the bounding box which defines where planes extraction should occur. + /// + public Vector3 BoundsCenter; + + /// + /// The rotation of the bounding box where planes extraction will occur. + /// + public Quaternion BoundsRotation; + + /// + /// The size of the bounding box where planes extraction will occur. + /// + public Vector3 BoundsExtents; + + /// + /// The maximum number of results that should be returned. + /// + public uint MaxResults; + + /// + /// The minimum area (in squared meters) of planes to be returned. This value + /// cannot be lower than 0.04 (lower values will be capped to this minimum). + /// + public float MinPlaneArea; + } + + private const ulong PlaneTrackableIdSalt = 0xf52b75076e45ad88; + + private class MagicLeapProvider : Provider + { + private readonly HashSet _currentSet = new(); + private readonly Dictionary _planes = new(); + private readonly Dictionary _boundariesTable = new(); + private MLPlanesQueryFlags _currentPlaneDetectionMode; + private MLPlanesQueryFlags _defaultQueryFlags = MLPlanesQueryFlags.AllOrientations | MLPlanesQueryFlags.SemanticAll; + private uint _lastNumResults; + private uint _maxResults = 10; + private ulong _planesTracker = MagicLeapNativeBindings.InvalidHandle; + private uint _previousLastNumResults; + private MLPlanesQueryFlags _requestedPlaneDetectionMode; + private ScanState _currentScanState = ScanState.Stopped; + + private PlanesQuery DefaultPlanesQuery + { + get + { + if (QuerySet) + { + return Query; + } + + return new PlanesQuery + { + Flags = _defaultQueryFlags, + BoundsCenter = Vector3.zero, + BoundsRotation = Quaternion.identity, + BoundsExtents = Vector3.one * 20f, + MaxResults = _maxResults, + MinPlaneArea = 10, + }; + } + } + + public override PlaneDetectionMode requestedPlaneDetectionMode + { + get => _requestedPlaneDetectionMode.ToPlaneDetectionMode(); + set + { + _requestedPlaneDetectionMode = value.ToMLXrQueryFlags(); + _defaultQueryFlags = _requestedPlaneDetectionMode | MLPlanesQueryFlags.SemanticAll; + } + } + + public override PlaneDetectionMode currentPlaneDetectionMode => _currentPlaneDetectionMode.ToPlaneDetectionMode(); + + private bool CreateClient() + { + if (_planesTracker != MagicLeapNativeBindings.InvalidHandle) + { + return true; + } + + if (!MLPermissions.CheckPermission(MLPermission.SpatialMapping).IsOk) + { + return false; + } + + var result = NativeBindings.MLOpenXRCreatePlaneTracker(out _planesTracker); + if (!MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLOpenXRCreatePlaneTracker))) + { + return false; + } + + _boundariesTable.Clear(); + return true; + } + + public override void Start() + { + SubsystemFeatures.SetFeatureRequested(Feature.PlaneTracking, true); + } + + public override void Stop() + { + if (_planesTracker != MagicLeapNativeBindings.InvalidHandle) + { + NativeBindings.MLOpenXRDestroyPlaneTracker(_planesTracker); + _planesTracker = MagicLeapNativeBindings.InvalidHandle; + _boundariesTable.Clear(); + } + + SubsystemFeatures.SetFeatureRequested(Feature.PlaneTracking, false); + } + + public override void Destroy() + { + } + + private PlaneBoundary GetBoundaryOfPlane(in TrackableId trackableId) + { + if (!_planes.TryGetValue(trackableId, out _)) + { + return default; + } + + return !_boundariesTable.TryGetValue(trackableId, out var planeBoundary) ? default : planeBoundary; + } + + public override void GetBoundary(TrackableId trackableId, Allocator allocator, ref NativeArray convexHullOut) + { + var boundary = GetBoundaryOfPlane(in trackableId); + if (boundary?.PolygonVertexCount > 0) + { + using var polygon = boundary.GetPolygon(Allocator.TempJob); + ConvexHullGenerator.GiftWrap(polygon, allocator, ref convexHullOut); + return; + } + + if (_planes.TryGetValue(trackableId, out var plane)) + { + var halfHeight = plane.height * 0.5f; + var halfWidth = plane.width * 0.5f; + + var calculatedBoundaries = new NativeArray(4, Allocator.Temp); + calculatedBoundaries[0] = new Vector2(halfWidth, halfHeight); + calculatedBoundaries[1] = new Vector2(-halfWidth, halfHeight); + calculatedBoundaries[2] = new Vector2(-halfWidth, -halfHeight); + calculatedBoundaries[3] = new Vector2(halfWidth, -halfHeight); + + ConvexHullGenerator.GiftWrap(calculatedBoundaries, allocator, ref convexHullOut); + return; + } + + CreateOrResizeNativeArrayIfNecessary(0, allocator, ref convexHullOut); + } + + public override unsafe TrackableChanges GetChanges(BoundedPlane defaultPlane, Allocator allocator) + { + if (_planesTracker == MagicLeapNativeBindings.InvalidHandle) + { + if (!CreateClient()) + { + return default; + } + } + + if (_currentScanState == ScanState.Stopped) + { + BeginNewQuery(); + } + + //If query has already begun, track the changes + if (_currentScanState != ScanState.Scanning) + { + return default; + } + + //Check the state to make sure we are done scanning + var stateResult = NativeBindings.MLGetPlaneDetectionState(_planesTracker, out var scanState); + if (!MLResult.DidNativeCallSucceed(stateResult, nameof(NativeBindings.MLGetPlaneDetectionState))) + { + return default; + } + switch (scanState) + { + case XrTypes.MLXrPlaneDetectionState.Done: + break; + case XrTypes.MLXrPlaneDetectionState.Pending: + case XrTypes.MLXrPlaneDetectionState.None: + return default; + case XrTypes.MLXrPlaneDetectionState.Error: + case XrTypes.MLXrPlaneDetectionState.Fatal: + default: + _currentScanState = ScanState.Stopped; + throw new InvalidOperationException($"An error occured when querying state of the plane detection: {scanState}"); + } + + + //Successfully scanned, so get the results + //Get the plane results first + var location = new XrTypes.MLXrPlaneDetectorLocations(); + var result = NativeBindings.MLGetPlaneDetections(_planesTracker, out location); + if (!MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLGetPlaneDetections))) + { + _currentScanState = ScanState.Stopped; + return default; + } + var planeTrackableIds = new NativeArray((int)location.planeLocationCountOutput, Allocator.TempJob); + if (location.planeLocationCountOutput > 0) + { + //Now we have the count so we can assign the locations + location.planeLocations = (XrTypes.MLXrPlaneDetectorLocation*)new NativeArray((int)location.planeLocationCountOutput, Allocator.TempJob).GetUnsafePtr(); + location.planeLocationCapacityInput = location.planeLocationCountOutput; + + result = NativeBindings.MLGetPlaneDetections(_planesTracker, out location); + + if (!MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLGetPlaneDetections))) + { + _currentScanState = ScanState.Stopped; + return default; + } + + //Now we have all the locations. So we can start creating the boundary + _boundariesTable.Clear(); + var planeCountTable = new Dictionary(); + for (var i = 0; i < location.planeLocationCountOutput; i++) + { + var planeLocation = location.planeLocations[i]; + var planeBoundary = new PlaneBoundary(); + //Get polygon + var polygonBuffer = new XrTypes.MLXrPlaneDetectorPolygonBuffer(); + result = NativeBindings.MLGetPlanePolygonBuffer(_planesTracker, planeLocation.planeId, 0, out polygonBuffer); + if (!MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLGetPlanePolygonBuffer))) + { + _currentScanState = ScanState.Stopped; + return default; + } + + polygonBuffer.vertices = (Vector2*)new NativeArray((int)polygonBuffer.vertexCountOutput, Allocator.TempJob).GetUnsafePtr(); + polygonBuffer.vertexCapacityInput = polygonBuffer.vertexCountOutput; + + result = NativeBindings.MLGetPlanePolygonBuffer(_planesTracker, planeLocation.planeId, 0, out polygonBuffer); + + if (!MLResult.DidNativeCallSucceed(result, $"{nameof(NativeBindings.MLGetPlanePolygonBuffer)} Querying Length")) + { + _currentScanState = ScanState.Stopped; + return default; + } + planeBoundary.Polygon = polygonBuffer; + if (planeLocation.polygonBufferCount > 0) + { + var holeBuffers = new List(); + for (uint holeIndex = 1; holeIndex < planeLocation.polygonBufferCount; holeIndex++) + { + var holeBuffer = new XrTypes.MLXrPlaneDetectorPolygonBuffer(); + result = NativeBindings.MLGetPlanePolygonBuffer(_planesTracker, planeLocation.planeId, holeIndex, out holeBuffer); + if (!MLResult.DidNativeCallSucceed(result, $"{nameof(NativeBindings.MLGetPlanePolygonBuffer)} Querying Holes Length")) + { + return default; + } + holeBuffer.vertices = (Vector2*)new NativeArray((int)holeBuffer.vertexCountOutput, Allocator.TempJob).GetUnsafePtr(); + holeBuffer.vertexCapacityInput = holeBuffer.vertexCountOutput; + result = NativeBindings.MLGetPlanePolygonBuffer(_planesTracker, planeLocation.planeId, holeIndex, out holeBuffer); + if (!MLResult.DidNativeCallSucceed(result, $"{nameof(NativeBindings.MLGetPlanePolygonBuffer)} Getting Holes")) + { + return default; + } + holeBuffers.Add(holeBuffer); + } + + planeBoundary.Holes = new NativeArray(holeBuffers.ToArray(), Allocator.TempJob); + } + + if (!planeCountTable.TryGetValue(planeLocation.planeId, out var count)) + { + count = 0; + } + var trackableId = new TrackableId(planeLocation.planeId, PlaneTrackableIdSalt + count); + planeCountTable[planeLocation.planeId] = ++count; + _boundariesTable[trackableId] = planeBoundary; + planeTrackableIds[i] = trackableId; + } + } + + + _previousLastNumResults = _lastNumResults; + _lastNumResults = location.planeLocationCountOutput; + _currentScanState = ScanState.Stopped; + using var uPlanes = new NativeArray((int)location.planeLocationCountOutput, Allocator.TempJob); + // Perform Unity plane conversion + new CopyPlaneResultsJob { PlaneTrackableIds = planeTrackableIds, PlanesIn = location.planeLocations, PlanesOut = uPlanes }.Schedule((int)location.planeLocationCountOutput, 1).Complete(); + planeTrackableIds.Dispose(); + + // Update plane states + var added = new NativeFixedList((int)location.planeLocationCountOutput, Allocator.Temp); + var updated = new NativeFixedList((int)location.planeLocationCountOutput, Allocator.Temp); + var removed = new NativeFixedList((int)_previousLastNumResults, Allocator.Temp); + + _currentSet.Clear(); + for (var i = 0; i < location.planeLocationCountOutput; ++i) + { + var uPlane = uPlanes[i]; + var trackableId = uPlane.trackableId; + _currentSet.Add(trackableId); + + if (_planes.ContainsKey(trackableId)) + { + updated.Add(uPlane); + } + else + { + added.Add(uPlane); + } + + _planes[trackableId] = uPlane; + } + + // Look for removed planes + foreach (var kvp in _planes) + { + var trackableId = kvp.Key; + if (!_currentSet.Contains(trackableId)) + { + removed.Add(trackableId); + } + } + + foreach (var trackableId in removed) + { + _planes.Remove(trackableId); + } + + using (added) + using (updated) + using (removed) + { + var changes = new TrackableChanges(added.Length, updated.Length, removed.Length, allocator); + added.CopyTo(changes.added); + updated.CopyTo(changes.updated); + removed.CopyTo(changes.removed); + return changes; + } + } + + private unsafe void BeginNewQuery() + { + _maxResults = QuerySet switch + { + // We hit the max, so increase for next time + false when _maxResults == _lastNumResults => _maxResults * 3 / 2, + true => DefaultPlanesQuery.MaxResults, + _ => _maxResults + }; + + DefaultPlanesQuery.Flags.ToMLXrOrientationsAndSemanticTypes(out var orientationValues, out var semanticValues); + + var orientationsArray = new NativeArray(orientationValues.Count, Allocator.TempJob); + orientationsArray.CopyFrom(orientationValues.ToArray()); + + var semanticArray = new NativeArray(semanticValues.Count, Allocator.TempJob); + semanticArray.CopyFrom(semanticValues.ToArray()); + + var beginInfo = new XrTypes.MLXrPlaneDetectorBeginInfo + { + orientationCount = (uint)orientationsArray.Length, + orientations = (XrTypes.MLXrPlaneDetectorOrientation*)orientationsArray.GetUnsafePtr(), + semanticTypeCount = (uint)semanticArray.Length, + semanticTypes = (XrTypes.MLXrPlaneDetectorSemanticType*)semanticArray.GetUnsafePtr(), + maxPlanes = _maxResults, + minArea = DefaultPlanesQuery.MinPlaneArea, + boundingBoxPose = new XrTypes.MLXrPose { position = DefaultPlanesQuery.BoundsCenter, rotation = DefaultPlanesQuery.BoundsRotation }, + boundingBoxExtents = DefaultPlanesQuery.BoundsExtents + }; + + var result = NativeBindings.MLBeginPlaneDetection(_planesTracker, in beginInfo); + + if (!MLResult.DidNativeCallSucceed(result, nameof(NativeBindings.MLBeginPlaneDetection))) + { + return; + } + + _currentScanState = ScanState.Scanning; + _currentPlaneDetectionMode = _requestedPlaneDetectionMode; + } + + private enum ScanState + { + Scanning, + Stopped + } + } + +#if UNITY_OPENXR_1_7_0_OR_NEWER + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + private static void RegisterDescriptor() + { + Debug.Log("Planes: Registering Planes Subsystem"); + XRPlaneSubsystemDescriptor.Create(new XRPlaneSubsystemDescriptor.Cinfo + { + id = MagicLeapXrProvider.PlanesSubsystemId, + providerType = typeof(MagicLeapProvider), + subsystemTypeOverride = typeof(MLXrPlaneSubsystem), + supportsVerticalPlaneDetection = true, + supportsArbitraryPlaneDetection = true, + supportsBoundaryVertices = true, + supportsClassification = true + }); + } +#endif + + } +} diff --git a/Runtime/OpenXR/Planes/MLXrPlaneSubsystem.cs.meta b/Runtime/OpenXR/Planes/MLXrPlaneSubsystem.cs.meta new file mode 100644 index 0000000..3e4d95a --- /dev/null +++ b/Runtime/OpenXR/Planes/MLXrPlaneSubsystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e80746b653db9ae4c8e42734dc079255 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/OpenXR/Planes/MagicLeapPlanesFeature.cs b/Runtime/OpenXR/Planes/MagicLeapPlanesFeature.cs new file mode 100644 index 0000000..4d62748 --- /dev/null +++ b/Runtime/OpenXR/Planes/MagicLeapPlanesFeature.cs @@ -0,0 +1,70 @@ +#if UNITY_OPENXR_1_7_0_OR_NEWER + +using System.Collections.Generic; +using UnityEngine.XR.ARSubsystems; +using UnityEngine.XR.MagicLeap; + +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.XR.OpenXR.Features; +#endif + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + /// + /// Enables the Magic Leap OpenXR Loader for Android, and modifies the AndroidManifest to be compatible with ML2. + /// +#if UNITY_EDITOR + [OpenXRFeature(UiName = "Magic Leap 2 Plane Detection Support", + Desc="Necessary to deploy a Magic Leap 2 compatible application with Planes detection", + Company = "Magic Leap", + Version = "1.0.0", + Priority = -2, + FeatureId = FeatureId, + BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone }, + OpenxrExtensionStrings = PlaneExtensionName + )] +#endif + public class MagicLeapPlanesFeature : MagicLeapOpenXRFeatureBase + { + public const string FeatureId = "com.magicleap.openxr.feature.ml2_planes"; + private const string PlaneExtensionName = "XR_EXT_plane_detection"; + + private readonly List _planeSubsystemDescriptors = new(); + + protected override bool OnInstanceCreate(ulong xrInstance) + { + if (OpenXRRuntime.IsExtensionEnabled(PlaneExtensionName)) + { + return base.OnInstanceCreate(xrInstance); + } + Debug.LogError($"{PlaneExtensionName} is not enabled. Disabling {nameof(MagicLeapPlanesFeature)}"); + return false; + } + + protected override void OnSubsystemCreate() + { + base.OnSubsystemCreate(); + CreateSubsystem(_planeSubsystemDescriptors, MagicLeapXrProvider.PlanesSubsystemId); + } + + protected override void OnSubsystemStart() + { + base.OnSubsystemStart(); + StartSubsystem(); + } + + protected override void OnSubsystemStop() + { + base.OnSubsystemStop(); + StopSubsystem(); + } + + protected override void OnSubsystemDestroy() + { + base.OnSubsystemDestroy(); + DestroySubsystem(); + } + } +} +#endif diff --git a/Runtime/OpenXR/Planes/MagicLeapPlanesFeature.cs.meta b/Runtime/OpenXR/Planes/MagicLeapPlanesFeature.cs.meta new file mode 100644 index 0000000..ad49fd8 --- /dev/null +++ b/Runtime/OpenXR/Planes/MagicLeapPlanesFeature.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4f7fbc139c144e69b4a0b0fb5139683f +timeCreated: 1690392839 \ No newline at end of file diff --git a/Runtime/OpenXR/Planes/MagicLeapXrPlanesNativeBindings.cs b/Runtime/OpenXR/Planes/MagicLeapXrPlanesNativeBindings.cs new file mode 100644 index 0000000..3ccb223 --- /dev/null +++ b/Runtime/OpenXR/Planes/MagicLeapXrPlanesNativeBindings.cs @@ -0,0 +1,30 @@ +using System.Runtime.InteropServices; +using UnityEngine.XR.MagicLeap; +using UnityEngine.XR.MagicLeap.Native; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public sealed partial class MLXrPlaneSubsystem + { + internal abstract class NativeBindings : MagicLeapNativeBindings + { + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOpenXRCreatePlaneTracker(out ulong planesTracker); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLOpenXRDestroyPlaneTracker(ulong planesTracker); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLBeginPlaneDetection(ulong planesTracker, in XrTypes.MLXrPlaneDetectorBeginInfo beginInfo); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLGetPlaneDetections(ulong planesDetector, out XrTypes.MLXrPlaneDetectorLocations planeLocations); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLGetPlaneDetectionState(ulong planesTracker, out XrTypes.MLXrPlaneDetectionState state); + + [DllImport(MagicLeapXrProviderNativeBindings.MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLGetPlanePolygonBuffer(ulong planeDetector, ulong planeId, uint polygonBufferIndex, out XrTypes.MLXrPlaneDetectorPolygonBuffer polygonBuffer); + } + } +} diff --git a/Runtime/OpenXR/Planes/MagicLeapXrPlanesNativeBindings.cs.meta b/Runtime/OpenXR/Planes/MagicLeapXrPlanesNativeBindings.cs.meta new file mode 100644 index 0000000..c88ee39 --- /dev/null +++ b/Runtime/OpenXR/Planes/MagicLeapXrPlanesNativeBindings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b48ec2b96d524b16a8810359db674723 +timeCreated: 1690759392 \ No newline at end of file diff --git a/Runtime/OpenXR/Planes/PlaneBoundary.cs b/Runtime/OpenXR/Planes/PlaneBoundary.cs new file mode 100644 index 0000000..6ca378a --- /dev/null +++ b/Runtime/OpenXR/Planes/PlaneBoundary.cs @@ -0,0 +1,179 @@ +// %BANNER_BEGIN% +// --------------------------------------------------------------------- +// %COPYRIGHT_BEGIN% +// Copyright (c) (2021-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% + +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.XR.ARSubsystems; + +namespace UnityEngine.XR.OpenXR.Features.MagicLeapSupport +{ + public partial class MLXrPlaneSubsystem + { + /// + /// Container for the boundary of a detected planar surface. This is specific + /// to Magic Leap because the polygon describing the boundary may be concave, + /// and may contain holes. + /// + public class PlaneBoundary + { + /// + /// Whether this is valid. You should check + /// for validity before invoking + /// , , or + /// + private bool Valid => Polygon.vertexCountOutput > 0; + + private static void AssignAtomicSafetyHandle(ref NativeArray array, Allocator allocator) where T : struct + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS + var safetyHandle = allocator == Allocator.Temp ? AtomicSafetyHandle.GetTempUnsafePtrSliceHandle() : AtomicSafetyHandle.Create(); + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, safetyHandle); +#endif + } + + /// + /// Gets the polygon representing a plane's boundary, and, if successful, copies it to . + /// is resized or created using if necessary. + /// The 2D vertices are in plane-space. + /// + /// + /// The Allocator to use if must be recreated. + /// Must be Allocator.TempJob or Allocator.Persistent. + /// + /// + /// A NativeArray to fill with boundary points. If the array is not the correct size, it is + /// disposed and recreated. + /// + /// Thrown if is false. + /// + /// Thrown if is Allocator.Temp or + /// Allocator.None. + /// + internal unsafe void GetPolygon(Allocator allocator, ref NativeArray polygonOut) + { + if (!Valid) + { + throw new InvalidOperationException("This plane boundary is not valid."); + } + + CreateOrResizeNativeArrayIfNecessary(PolygonVertexCount, allocator, ref polygonOut); + polygonOut = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(Polygon.vertices, PolygonVertexCount, allocator); + AssignAtomicSafetyHandle(ref polygonOut, allocator); + } + + /// + /// The number of vertices in this boundary's polygon. + /// + public int PolygonVertexCount => (int)Polygon.vertexCountOutput; + + /// + /// Gets the polygon representing this boundary. The 2D vertices are in plane-space. + /// + /// + /// The allocator to use for the returned NativeArray. Must be Allocator.TempJob or + /// Allocator.Persistent. + /// + /// + /// A new NativeArray containing a set of 2D points in plane-space representing a boundary for a plane. + /// The caller is responsible for disposing the NativeArray. + /// + /// Thrown if is false. + /// + /// Thrown if is Allocator.Temp or + /// Allocator.None. + /// + internal NativeArray GetPolygon(Allocator allocator) + { + var polygon = new NativeArray(); + GetPolygon(allocator, ref polygon); + return polygon; + } + + /// + /// The number of holes in this boundary. + /// + private int HoleCount => Holes.Length; + + /// + /// Get the polygon representing a hole in this boundary. The 2D vertices are in plane-space. + /// + /// The index of the hole. Must be less than . + /// + /// The allocator to use for the returned NativeArray. + /// Must be Allocator.TempJob or Allocator.Persistent. + /// + /// + /// A new NativeArray allocated with containing a set of 2D vertices + /// in plane-space describing the hole at . + /// + /// Thrown if is false. + /// + /// Thrown if is Allocator.Temp or + /// Allocator.None. + /// + /// + /// Thrown if is less than 0 or greater than + /// or equal to . + /// + internal NativeArray GetHole(int index, Allocator allocator) + { + var hole = new NativeArray(); + GetHole(index, allocator, ref hole); + return hole; + } + + /// + /// Get the polygon representing a hole in this boundary. The 2D vertices are in plane-space. + /// + /// The index of the hole. Must be less than . + /// + /// The allocator to use if must be resized. + /// Must be Allocator.TempJob or Allocator.Persistent. + /// + /// The resulting polygon describing the hole at . + /// Thrown if is false. + /// + /// Thrown if is Allocator.Temp or + /// Allocator.None. + /// + /// + /// Thrown if is less than 0 or greater than + /// or equal to . + /// + private unsafe void GetHole(int index, Allocator allocator, ref NativeArray polygonOut) + { + if (!Valid) + throw new InvalidOperationException("This plane boundary is not valid."); + + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index), "Hole index must be greater than zero."); + } + + if (index >= HoleCount) + { + throw new ArgumentOutOfRangeException(nameof(index), $"Hole index must be less than or equal to holeCount ({HoleCount})."); + } + + var holes = Holes[index]; + CreateOrResizeNativeArrayIfNecessary((int)holes.vertexCountOutput, allocator,ref polygonOut); + polygonOut = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(holes.vertices, (int)holes.vertexCountOutput, allocator); + AssignAtomicSafetyHandle(ref polygonOut, allocator); + } + + + internal NativeArray Holes; + internal XrTypes.MLXrPlaneDetectorPolygonBuffer Polygon; + internal Pose PlanePose; + internal TrackableId Id; + } + } +} diff --git a/Runtime/OpenXR/Planes/PlaneBoundary.cs.meta b/Runtime/OpenXR/Planes/PlaneBoundary.cs.meta new file mode 100644 index 0000000..8a69375 --- /dev/null +++ b/Runtime/OpenXR/Planes/PlaneBoundary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f4ab09ee0f41fff449e7caf466f06563 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Subsystems/Hand/HandSubsystem.cs b/Runtime/Subsystems/Hand/HandSubsystem.cs index 6fdfeec..ed23317 100644 --- a/Runtime/Subsystems/Hand/HandSubsystem.cs +++ b/Runtime/Subsystems/Hand/HandSubsystem.cs @@ -5,6 +5,8 @@ using UnityEngine.XR.Hands; using UnityEngine.XR.Hands.ProviderImplementation; using UnityEngine.XR.Management; +using Unity.Collections.LowLevel.Unsafe; +using NativeBindings = UnityEngine.XR.OpenXR.Features.MagicLeapSupport.MagicLeapFeature.NativeBindings; namespace UnityEngine.XR.MagicLeap { @@ -32,13 +34,20 @@ static void RegisterDescriptor() public class MagicLeapProvider : XRHandSubsystemProvider { private XRHandProviderUtility.SubsystemUpdater subsystemUpdater; + private List unsupportedIndices = new List() { XRHandJointID.Palm.ToIndex() }; +#if !UNITY_OPENXR_1_7_0_OR_NEWER private bool isHandTrackingStarted; private HandDevice leftHandDevice; - private HandDevice rightHandDevice; - private List unsupportedIndices = new List() { XRHandJointID.Palm.ToIndex() }; - + private HandDevice rightHandDevice; +#else + private XRHandSubsystem subsystem; + private List jointOrder; +#endif public override void GetHandLayout(NativeArray handJointsInLayout) { +#if UNITY_OPENXR_1_7_0_OR_NEWER + unsupportedIndices.Clear(); +#endif for (int i = 0; i < handJointsInLayout.Length; ++i) { if (unsupportedIndices.Contains(i)) @@ -48,20 +57,31 @@ public override void GetHandLayout(NativeArray handJointsInLayout) } } + public override void Start() { +#if !UNITY_OPENXR_1_7_0_OR_NEWER leftHandDevice = new HandDevice(InputDeviceCharacteristics.Left); - rightHandDevice = new HandDevice(InputDeviceCharacteristics.Right); - - var subsystem = XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem(); + rightHandDevice = new HandDevice(InputDeviceCharacteristics.Right); +#endif + subsystem = XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem(); subsystemUpdater = new XRHandProviderUtility.SubsystemUpdater(subsystem); subsystemUpdater.Start(); - } + jointOrder = new List + { + XRHandJointID.Palm, XRHandJointID.Wrist, XRHandJointID.ThumbMetacarpal, XRHandJointID.ThumbProximal, XRHandJointID.ThumbDistal, XRHandJointID.ThumbTip, + XRHandJointID.IndexMetacarpal, XRHandJointID.IndexProximal, XRHandJointID.IndexIntermediate, XRHandJointID.IndexDistal, XRHandJointID.IndexTip, + XRHandJointID.MiddleMetacarpal, XRHandJointID.MiddleProximal, XRHandJointID.MiddleIntermediate, XRHandJointID.MiddleDistal, XRHandJointID.MiddleTip, + XRHandJointID.RingMetacarpal, XRHandJointID.RingProximal, XRHandJointID.RingIntermediate, XRHandJointID.RingDistal, XRHandJointID.RingTip, + XRHandJointID.LittleMetacarpal, XRHandJointID.LittleProximal, XRHandJointID.LittleIntermediate, XRHandJointID.LittleDistal, XRHandJointID.LittleTip + }; + + } public override XRHandSubsystem.UpdateSuccessFlags TryUpdateHands(XRHandSubsystem.UpdateType updateType, ref Pose leftHandRootPose, NativeArray leftHandJoints, ref Pose rightHandRootPose, NativeArray rightHandJoints) { var successFlags = XRHandSubsystem.UpdateSuccessFlags.None; - +#if !UNITY_OPENXR_1_7_0_OR_NEWER if (!isHandTrackingStarted) { // Do not try to start permissions api if MLDevice has not yet been created. @@ -112,18 +132,80 @@ public override XRHandSubsystem.UpdateSuccessFlags TryUpdateHands(XRHandSubsyste rightHandDevice.FillJoints(ref rightHandJoints); successFlags |= XRHandSubsystem.UpdateSuccessFlags.RightHandJoints; } +#else + using var leftJoints = new NativeArray(26 /* Hand Joints max*/, Allocator.Temp); + using var rightJoints = new NativeArray(26 /* Hand Joints max*/, Allocator.Temp); + unsafe + { + MLResult.Code result = NativeBindings.MLOpenXRGetXRHandTrackingJoints(NativeBindings.XrHandEXT.Left, (NativeBindings.XrHandJointLocationEXT*)leftJoints.GetUnsafePtr()); + + if (result == MLResult.Code.Ok) + { + successFlags = XRHandSubsystem.UpdateSuccessFlags.LeftHandRootPose; + successFlags |= XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints; + + + for (int jointIndex = 0; jointIndex < leftJoints.Length; jointIndex++) + { + Pose currentPose = new Pose(); + currentPose.position = new Vector3(leftJoints[jointIndex].pose.position.x, leftJoints[jointIndex].pose.position.y, -leftJoints[jointIndex].pose.position.z); + currentPose.rotation = new Quaternion(-leftJoints[jointIndex].pose.orientation.x, -leftJoints[jointIndex].pose.orientation.y, leftJoints[jointIndex].pose.orientation.z, leftJoints[jointIndex].pose.orientation.w); + XRHandJointTrackingState currentState = XRHandJointTrackingState.Pose; + currentState |= XRHandJointTrackingState.Radius; + + leftHandJoints[jointOrder[jointIndex].ToIndex()] = XRHandProviderUtility.CreateJoint(Handedness.Left, currentState, XRHandJointIDUtility.FromIndex(jointOrder[jointIndex].ToIndex()), currentPose, leftJoints[jointIndex].radius); + + if (jointOrder[jointIndex] == XRHandJointID.Wrist) + { + leftHandRootPose = currentPose; + } + } + } + + leftJoints.Dispose(); + + result = NativeBindings.MLOpenXRGetXRHandTrackingJoints(NativeBindings.XrHandEXT.Right, (NativeBindings.XrHandJointLocationEXT*)rightJoints.GetUnsafePtr()); + + if (result == MLResult.Code.Ok) + { + successFlags |= XRHandSubsystem.UpdateSuccessFlags.RightHandRootPose; + successFlags |= XRHandSubsystem.UpdateSuccessFlags.RightHandJoints; + + for (int jointIndex = 0; jointIndex < rightJoints.Length; jointIndex++) + { + Pose currentPose = new Pose(); + currentPose.position = new Vector3(rightJoints[jointIndex].pose.position.x, rightJoints[jointIndex].pose.position.y, -rightJoints[jointIndex].pose.position.z); + currentPose.rotation = new Quaternion(-rightJoints[jointIndex].pose.orientation.x, -rightJoints[jointIndex].pose.orientation.y, rightJoints[jointIndex].pose.orientation.z, rightJoints[jointIndex].pose.orientation.w); + + XRHandJointTrackingState currentState = XRHandJointTrackingState.Pose; + currentState |= XRHandJointTrackingState.Radius; + + rightHandJoints[jointOrder[jointIndex].ToIndex()] = XRHandProviderUtility.CreateJoint(Handedness.Right, currentState, XRHandJointIDUtility.FromIndex(jointOrder[jointIndex].ToIndex()), currentPose, rightJoints[jointIndex].radius); + + if (jointOrder[jointIndex] == XRHandJointID.Wrist) + { + rightHandRootPose = currentPose; + } + } + } + + rightJoints.Dispose(); + } +#endif return successFlags; } public override void Stop() { +#if !UNITY_OPENXR_1_7_0_OR_NEWER InputSubsystem.Extensions.MLHandTracking.StopTracking(); +#endif subsystemUpdater.Stop(); } public override void Destroy() => subsystemUpdater.Destroy(); - +#if !UNITY_OPENXR_1_7_0_OR_NEWER private class HandDevice { public HandDevice(InputDeviceCharacteristics characteristics) @@ -189,7 +271,8 @@ private XRHandJoint BoneToJoint(XRHandJointID jointId, Bone bone) return XRHandProviderUtility.CreateJoint(XRHandJointTrackingState.Pose, jointId, new Pose(pos, rot)); } } +#endif } } -#endif \ No newline at end of file +#endif diff --git a/Runtime/Subsystems/Input/Extensions/MLEyes.cs b/Runtime/Subsystems/Input/Extensions/MLEyes.cs index 0e4acfe..f4dbb0b 100644 --- a/Runtime/Subsystems/Input/Extensions/MLEyes.cs +++ b/Runtime/Subsystems/Input/Extensions/MLEyes.cs @@ -11,6 +11,7 @@ namespace UnityEngine.XR.MagicLeap { using System; using System.Runtime.InteropServices; + using UnityEngine.XR.MagicLeap.Native; public static partial class InputSubsystem { @@ -123,6 +124,114 @@ internal State(NativeBindings.MLEyeTrackingStateEx nativeState) } } + public struct StaticData + { + public MagicLeapNativeBindings.MLCoordinateFrameUID Vergence; + + public MagicLeapNativeBindings.MLCoordinateFrameUID LeftCenter; + + public MagicLeapNativeBindings.MLCoordinateFrameUID RightCenter; + } + + public struct DeviceCenteredEyeData + { + public Pose LeftEye; + + public Pose RightEye; + + public Pose Vergence; + } + + public static void GetStaticData(out StaticData staticData) + { + staticData = new(); + var eyeHandle = MagicLeapXrProviderNativeBindings.GetEyeTrackerHandle(); + if (!MagicLeapNativeBindings.MLHandleIsValid(eyeHandle)) + { + Debug.LogError("Eye handle invalid"); + return; + } + + NativeBindings.MLEyeTrackingStaticData data = new(); + var result = MLResult.Create(NativeBindings.MLEyeTrackingGetStaticData(eyeHandle, ref data)); + if (!result.IsOk) + { + Debug.LogError($"GetStaticData failed: {result}"); + return; + } + + staticData.Vergence = data.vergence; + staticData.LeftCenter = data.left_center; + staticData.RightCenter = data.right_center; + return; + } + + public static void GetEyeDataInDeviceCoords(out DeviceCenteredEyeData deviceCenteredEyeData) + { + deviceCenteredEyeData = new(); + MagicLeapNativeBindings.MLSnapshotStaticData snapshotStaticData = MagicLeapNativeBindings.MLSnapshotStaticData.Init(); + IntPtr snap = IntPtr.Zero; + + MLResult result; + result = MLResult.Create(MagicLeapNativeBindings.MLSnapshotGetStaticData(ref snapshotStaticData)); + if (!result.IsOk) + { + Debug.LogError($"MLEyes::MLSnapshotGetStaticData failed: {result}"); + return; + } + + result = MLResult.Create(MagicLeapNativeBindings.MLPerceptionGetSnapshot(ref snap)); + if (!result.IsOk) + { + Debug.LogError($"MLEyes::MLPerceptionGetSnapshot failed: {result}"); + return; + } + + MagicLeapNativeBindings.MLPose leftEyeCenter = new(); + MagicLeapNativeBindings.MLPose rightEyeCenter = new(); + MagicLeapNativeBindings.MLTransform head = new(); + MagicLeapNativeBindings.MLTransform vergence = new(); + + MLHeadTracking.GetStaticData(out MagicLeapNativeBindings.MLCoordinateFrameUID headUID); + + result = MLResult.Create(MagicLeapNativeBindings.MLSnapshotGetTransform(snap, ref headUID, ref head)); + if (!result.IsOk) + { + Debug.LogError($"MLEyes::MLSnapshotGetTransform (Device UID) failed: {result}"); + return; + } + + StaticData eyeStaticData; + GetStaticData(out eyeStaticData); + + result = MLResult.Create(MagicLeapNativeBindings.MLSnapshotGetTransform(snap, ref eyeStaticData.Vergence, ref vergence)); + if (!result.IsOk) + { + Debug.LogError($"MLEyes::MLSnapshotGetTransform (vergence UID) failed: {result}"); + return; + } + + result = MLResult.Create(MagicLeapNativeBindings.MLSnapshotGetPoseInBase(snap, ref headUID, ref eyeStaticData.LeftCenter, ref leftEyeCenter)); + if (!result.IsOk) + { + Debug.LogError($"MLEyes::MLSnapshotGetPoseInBase(leftEye) failed: {result}"); + return; + } + + result = MLResult.Create(MagicLeapNativeBindings.MLSnapshotGetPoseInBase(snap, ref headUID, ref eyeStaticData.RightCenter, ref rightEyeCenter)); + if (!result.IsOk) + { + Debug.LogError($"MLEyes::MLSnapshotGetPoseInBase(rightEye) failed: {result}"); + return; + } + + deviceCenteredEyeData.LeftEye = new Pose(MLConvert.ToUnity(leftEyeCenter.Transform.Position), MLConvert.ToUnity(leftEyeCenter.Transform.Rotation)); + deviceCenteredEyeData.RightEye = new Pose(MLConvert.ToUnity(rightEyeCenter.Transform.Position), MLConvert.ToUnity(rightEyeCenter.Transform.Rotation)); + deviceCenteredEyeData.Vergence = new Pose(MLConvert.ToUnity(vergence.Position), MLConvert.ToUnity(vergence.Rotation)); + + MagicLeapNativeBindings.MLPerceptionReleaseSnapshot(snap); + } + internal static class NativeBindings { private static byte[] allocatedStateData = new byte[Marshal.SizeOf()]; @@ -221,6 +330,41 @@ private MLEyeTrackingStateEx(uint version) #endregion } } + + /// + /// Gets static information about the eye tracker + /// + /// A handle to an Eye Tracker retrievced from GetEyeTrackerHandle() + /// Target to populate the data about the eye tracker + /// + /// MLResult.Result will be MLResult.Code.InvalidParam if the out_data param was not valid (null). + /// MLResult.Result will be MLResult.Code.Ok if eye tracking static data was successfully received. + /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed to retrieve eye tracking static data. + /// + [DllImport(MagicLeapNativeBindings.MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLEyeTrackingGetStaticData(ulong handle, ref MLEyeTrackingStaticData outData); + + /// + /// Static information about the eye tracking. Populate with MLEyeTrackingGetStaticData + /// + [StructLayout(LayoutKind.Sequential)] + public struct MLEyeTrackingStaticData + { + /// + /// Location of the 3D vergence point. + /// + public MagicLeapNativeBindings.MLCoordinateFrameUID vergence; + + /// + /// Left center of the eye. + /// + public MagicLeapNativeBindings.MLCoordinateFrameUID left_center; + + /// + /// Right center of the eye. + /// + public MagicLeapNativeBindings.MLCoordinateFrameUID right_center; + } } } } diff --git a/Runtime/Subsystems/Input/Extensions/MLHeadTracking.cs b/Runtime/Subsystems/Input/Extensions/MLHeadTracking.cs index a49638a..cd1108e 100644 --- a/Runtime/Subsystems/Input/Extensions/MLHeadTracking.cs +++ b/Runtime/Subsystems/Input/Extensions/MLHeadTracking.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; using UnityEngine; +using UnityEngine.XR.MagicLeap.Native; +using static UnityEngine.XR.MagicLeap.InputSubsystem.Extensions.MLHeadTracking.NativeBindings; namespace UnityEngine.XR.MagicLeap { @@ -151,6 +153,28 @@ public enum MapEvents : ulong public static bool TryGetStateEx(InputDevice headDevice, out StateEx headTrackingState) => NativeBindings.TryGetStateEx(headDevice, out headTrackingState); public static bool TryGetMapEvents(InputDevice headDevice, out MapEvents mapEvents) => NativeBindings.TryGetMapEvents(headDevice, out mapEvents); + private static bool gotData = false; + private static NativeBindings.MLHeadTrackingStaticData data; + public static void GetStaticData(out MagicLeapNativeBindings.MLCoordinateFrameUID outUID) + { + outUID = MagicLeapNativeBindings.MLCoordinateFrameUID.EmptyFrame; + if (!gotData) + { + var headHandle = MagicLeapXrProviderNativeBindings.GetHeadTrackerHandle(); + data = new(); + MLResult result = MLResult.Create(MLHeadTrackingGetStaticData(headHandle, ref data)); + if (!result.IsOk) + { + Debug.LogError($"MLHeadTracking::GetStaticData failed: {result}"); + gotData = false; + return; + } + } + + outUID = data.coord_frame_head; + return; + } + /// /// A structure containing information on the current state of the /// Head Tracking system. @@ -358,6 +382,31 @@ public readonly struct StateEx /// public readonly uint Error; } + + /// + /// Returns static information about the Head Tracker + /// + /// A handle to the tracker retireved by GetHeadTrackerHandle() + /// Target to populate the data about that Head Tracker + /// + /// MLResult.Result will be MLResult.Code.InvalidParam if failed to receive static data due to an invalid input parameter. + /// MLResult.Result will be MLResult.Code.Ok if successfully received static data. + /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed to receive static data due to an unknown error. + /// + [DllImport(MagicLeapNativeBindings.MLPerceptionClientDll, CallingConvention = CallingConvention.Cdecl)] + public static extern MLResult.Code MLHeadTrackingGetStaticData(ulong handle, ref MLHeadTrackingStaticData data); + + /// + /// Static information about a Head Tracker. Populate this structure with MLHeadTrackingGetStaticData() + /// + [StructLayout(LayoutKind.Sequential)] + public struct MLHeadTrackingStaticData + { + /// + /// Coordinate frame ID of the head + /// + public MagicLeapNativeBindings.MLCoordinateFrameUID coord_frame_head; + } } } } diff --git a/Runtime/Subsystems/Input/Extensions/Utils.cs b/Runtime/Subsystems/Input/Extensions/Utils.cs index 3c2c04f..6e2ed29 100644 --- a/Runtime/Subsystems/Input/Extensions/Utils.cs +++ b/Runtime/Subsystems/Input/Extensions/Utils.cs @@ -45,6 +45,11 @@ public static InputDevice FindMagicLeapDevice(InputDeviceCharacteristics inputDe /// /// Boolean representing whether or not to predict the snapshot. public static void PredictSnapshot(long timestamp, bool predictSnapshots) => MagicLeapXrProviderNativeBindings.PredictSnapshot(timestamp, predictSnapshots); + + /// + /// Reset the snapshot state of the subsystem to before prediction. Use this to cleanup after PredictSnapshot + /// + public static void ResetSnapshotPrediction() => MagicLeapXrProviderNativeBindings.ResetSnapshotPrediction(); } } } diff --git a/Runtime/Subsystems/Meshing/MeshingSubsystemComponent.cs b/Runtime/Subsystems/Meshing/MeshingSubsystemComponent.cs index 6067936..3314b0c 100644 --- a/Runtime/Subsystems/Meshing/MeshingSubsystemComponent.cs +++ b/Runtime/Subsystems/Meshing/MeshingSubsystemComponent.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using UnityEngine.Serialization; using UnityEngine.XR.ARSubsystems; +using UnityEngine.XR.MagicLeap.Native; using UnityEngine.XR.Management; #if UNITY_EDITOR @@ -25,6 +26,8 @@ namespace UnityEngine.XR.MagicLeap [DisallowMultipleComponent] public sealed class MeshingSubsystemComponent : MonoBehaviour { + private const float SubsystemStartUpTime = 1f; + /// /// What type of mesh to generate: a triangle mesh or a point cloud /// @@ -343,6 +346,8 @@ Vector3 boundsExtents public event Action meshRemoved; private InputDevice headDevice; + private Coroutine startupRoutine = null; + private bool shouldSubsystemBeRunning = false; /// /// Retrieve the confidence values associated with a mesh. Confidence values @@ -585,12 +590,35 @@ IEnumerator Init() void StartSubsystem() { MeshingSubsystemLifecycle.StartSubsystem(); + + startupRoutine = StartCoroutine(LetSubsystemToStart()); + + MLSpace.OnLocalizationEvent += MLSpaceOnOnLocalizationChanged; + } + + private IEnumerator LetSubsystemToStart() + { + shouldSubsystemBeRunning = false; + yield return new WaitForSeconds(SubsystemStartUpTime); + shouldSubsystemBeRunning = true; + } + + private void MLSpaceOnOnLocalizationChanged(MLSpace.LocalizationResult result) + { + m_SettingsDirty = true; } void StopSubsystem() { MeshingSubsystemLifecycle.StopSubsystem(); SubsystemFeatures.SetCurrentFeatureEnabled(Feature.Meshing | Feature.PointCloud, false); + + if (startupRoutine != null) + { + StopCoroutine(startupRoutine); + } + + MLSpace.OnLocalizationEvent -= MLSpaceOnOnLocalizationChanged; } void OnEnable() @@ -630,13 +658,14 @@ void CheckHeadTrackingMapEvents() } } } - + void UpdateSettings() { DestroyAllMeshes(); UpdateBatchSize(); - + var settings = GetMeshingSettings(); + MeshingSubsystem.Extensions.MLMeshing.Config.meshingSettings = settings; MeshingSubsystem.Extensions.MLMeshing.Config.density = density; @@ -675,11 +704,17 @@ void OnApplicationPause(bool pauseStatus) // been added to the asynchronous queue, or the queue is full. void Update() { - if (MeshingSubsystemLifecycle.MeshSubsystem == null) return; + + if (!shouldSubsystemBeRunning) + return; + if (!MeshingSubsystemLifecycle.MeshSubsystem.running) + { + Debug.LogError($"MeshingSubsystemLifecycle.MeshSubsystem.running {MeshingSubsystemLifecycle.MeshSubsystem.running}"); return; + } #if UNITY_EDITOR m_SettingsDirty |= haveSettingsChanged; #endif @@ -840,6 +875,10 @@ void OnMeshGenerated(MeshGenerationResult result) } } } + else + { + m_MeshesBeingGenerated.Remove(result.MeshId); + } } bool m_SettingsDirty; diff --git a/Runtime/Subsystems/Meshing/MeshingSubsystemLifecycle.cs b/Runtime/Subsystems/Meshing/MeshingSubsystemLifecycle.cs new file mode 100644 index 0000000..7f0923a --- /dev/null +++ b/Runtime/Subsystems/Meshing/MeshingSubsystemLifecycle.cs @@ -0,0 +1,95 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.XR.Management; +#if UNITY_OPENXR_1_7_0_OR_NEWER +using UnityEngine.XR.OpenXR.Features.MagicLeapSupport; +#endif + +namespace UnityEngine.XR.MagicLeap +{ + public static class MeshingSubsystemLifecycle + { + public static XRMeshSubsystem MeshSubsystem { get; private set; } + + private static int subsysRefCount = 0; + +#if UNITY_XR_MAGICLEAP_PROVIDER + private static MagicLeapLoader mlLoader; +#endif + +#if UNITY_OPENXR_1_7_0_OR_NEWER + private static MagicLeapFeature mlOpenXrFeature; +#endif + + public static IEnumerator WaitUntilInited() + { + while (XRGeneralSettings.Instance == null) + { + yield return null; + } + while (XRGeneralSettings.Instance.Manager == null) + { + yield return null; + } + + var meshSubsystems = new List(); + SubsystemManager.GetInstances(meshSubsystems); + if (meshSubsystems.Count == 1) + { + MeshSubsystem = meshSubsystems[0]; + } + +#if UNITY_XR_MAGICLEAP_PROVIDER + mlLoader = XRGeneralSettings.Instance.Manager.ActiveLoaderAs(); +#endif +#if UNITY_OPENXR_1_7_0_OR_NEWER + mlOpenXrFeature = OpenXR.OpenXRSettings.Instance.GetFeature(); +#endif + } + + public static void StartSubsystem() + { + subsysRefCount++; + if (subsysRefCount == 1) + { +#if UNITY_XR_MAGICLEAP_PROVIDER + if (mlLoader != null) + { + mlLoader.StartMeshSubsystem(); + } +#elif UNITY_OPENXR_1_7_0_OR_NEWER + if (mlOpenXrFeature != null) + { + mlOpenXrFeature.StartMeshSubsystem(); + } +#endif + } + } + + public static void StopSubsystem() + { + if (subsysRefCount == 0) + { + return; + } + + subsysRefCount--; + + if (subsysRefCount == 0) + { +#if UNITY_XR_MAGICLEAP_PROVIDER + if (mlLoader != null) + { + mlLoader.StopMeshSubsystem(); + } +#elif UNITY_OPENXR_1_7_0_OR_NEWER + if (mlOpenXrFeature != null) + { + mlOpenXrFeature.StopMeshSubsystem(); + } +#endif + } + } + } +} diff --git a/Runtime/Subsystems/Meshing/MeshingSubsystemLifecycle.cs.meta b/Runtime/Subsystems/Meshing/MeshingSubsystemLifecycle.cs.meta new file mode 100644 index 0000000..2ce5d02 --- /dev/null +++ b/Runtime/Subsystems/Meshing/MeshingSubsystemLifecycle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f1e5a759248631e4b99d39e1553def7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Subsystems/Planes.meta b/Runtime/Subsystems/Planes.meta index 0c88f62..17aa1f9 100644 --- a/Runtime/Subsystems/Planes.meta +++ b/Runtime/Subsystems/Planes.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: de4e54df40817a146bd8fbdf589e66e4 +guid: 0f07c670b572665429ff5a7510b94930 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/Subsystems/Planes/ConvexHullGenerator.cs.meta b/Runtime/Subsystems/Planes/ConvexHullGenerator.cs.meta index 1d42896..fd3adee 100644 --- a/Runtime/Subsystems/Planes/ConvexHullGenerator.cs.meta +++ b/Runtime/Subsystems/Planes/ConvexHullGenerator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c6d69744904f6c044b692f5f3bc98ead +guid: 52a9ad0ca9c60c3438b6520bc2a38873 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/CopyPlaneResultsJob.cs.meta b/Runtime/Subsystems/Planes/CopyPlaneResultsJob.cs.meta index 3249f41..9f13198 100644 --- a/Runtime/Subsystems/Planes/CopyPlaneResultsJob.cs.meta +++ b/Runtime/Subsystems/Planes/CopyPlaneResultsJob.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d4e21f30772e0a04fa6d126587514b1f +guid: 9912d8563d99d97499f8de7f24fa1a29 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/Extensions.meta b/Runtime/Subsystems/Planes/Extensions.meta index abd6387..6c0c8e7 100644 --- a/Runtime/Subsystems/Planes/Extensions.meta +++ b/Runtime/Subsystems/Planes/Extensions.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5a50b2227985d5549a40fb5477236624 +guid: d91e667e5a684e948a25c0fc0218a428 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Runtime/Subsystems/Planes/Extensions/MLPlanesQuery.cs.meta b/Runtime/Subsystems/Planes/Extensions/MLPlanesQuery.cs.meta index 8472410..65db66c 100644 --- a/Runtime/Subsystems/Planes/Extensions/MLPlanesQuery.cs.meta +++ b/Runtime/Subsystems/Planes/Extensions/MLPlanesQuery.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1fdde03a12e3b1e469b0842afc7be947 +guid: a2c42b2f27717594995964c3fb295ac0 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/Extensions/MLPlanesQueryFlags.cs.meta b/Runtime/Subsystems/Planes/Extensions/MLPlanesQueryFlags.cs.meta index de1723b..ec075e1 100644 --- a/Runtime/Subsystems/Planes/Extensions/MLPlanesQueryFlags.cs.meta +++ b/Runtime/Subsystems/Planes/Extensions/MLPlanesQueryFlags.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 82b36c864aa43354a82b1bbcfd28dac0 +guid: 1ab1b86a1f9c9494c89f55d4b79be59e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/PlaneBoundary.cs.meta b/Runtime/Subsystems/Planes/PlaneBoundary.cs.meta index 8a69375..f6e081f 100644 --- a/Runtime/Subsystems/Planes/PlaneBoundary.cs.meta +++ b/Runtime/Subsystems/Planes/PlaneBoundary.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f4ab09ee0f41fff449e7caf466f06563 +guid: acee271b6523d404b84361589dd8d373 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/PlaneBoundaryCollection.cs.meta b/Runtime/Subsystems/Planes/PlaneBoundaryCollection.cs.meta index 3c5b69c..251ec01 100644 --- a/Runtime/Subsystems/Planes/PlaneBoundaryCollection.cs.meta +++ b/Runtime/Subsystems/Planes/PlaneBoundaryCollection.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3fe1c8684aae0454d96749ae1e033298 +guid: 8d0c7f558793b2f47b15684a55e99354 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/PlanesSubsystem.cs b/Runtime/Subsystems/Planes/PlanesSubsystem.cs index 6f483d7..0727bec 100644 --- a/Runtime/Subsystems/Planes/PlanesSubsystem.cs +++ b/Runtime/Subsystems/Planes/PlanesSubsystem.cs @@ -450,24 +450,23 @@ ulong BeginNewQuery() } } +#if !UNITY_OPENXR_1_7_0_OR_NEWER + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void RegisterDescriptor() { XRPlaneSubsystemDescriptor.Create(new XRPlaneSubsystemDescriptor.Cinfo { id = MagicLeapXrProvider.PlanesSubsystemId, -#if UNITY_2020_2_OR_NEWER - providerType = typeof(PlanesSubsystem.MagicLeapProvider), + providerType = typeof(MagicLeapProvider), subsystemTypeOverride = typeof(PlanesSubsystem), -#else - subsystemImplementationType = typeof(PlanesSubsystem), -#endif supportsVerticalPlaneDetection = true, supportsArbitraryPlaneDetection = true, supportsBoundaryVertices = true, supportsClassification = true }); } +#endif internal class NativeBindings : MagicLeapNativeBindings { diff --git a/Runtime/Subsystems/Planes/PlanesSubsystem.cs.meta b/Runtime/Subsystems/Planes/PlanesSubsystem.cs.meta index befca96..eeada78 100644 --- a/Runtime/Subsystems/Planes/PlanesSubsystem.cs.meta +++ b/Runtime/Subsystems/Planes/PlanesSubsystem.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8676eb0ac979b8b4ab368f759ff8a966 +guid: abe3ca0f0755652499eedc65538b075e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Subsystems/Planes/TransformPlaneBoundaryJob.cs.meta b/Runtime/Subsystems/Planes/TransformPlaneBoundaryJob.cs.meta index ae6b8fd..0891f55 100644 --- a/Runtime/Subsystems/Planes/TransformPlaneBoundaryJob.cs.meta +++ b/Runtime/Subsystems/Planes/TransformPlaneBoundaryJob.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f526eb9df8b740849b79349d0d4c35d7 +guid: c19af52a0c1a8cd4abe6d7dcefa7ab8c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Tools/Prefabs/Main Camera.prefab b/Runtime/Tools/Prefabs/Main Camera.prefab index 73af822..a660f1e 100644 --- a/Runtime/Tools/Prefabs/Main Camera.prefab +++ b/Runtime/Tools/Prefabs/Main Camera.prefab @@ -14,8 +14,8 @@ GameObject: - component: {fileID: 5850404675360181103} - component: {fileID: 81260711824966714} - component: {fileID: 124261382810072712} - - component: {fileID: -245835337101996341} - - component: {fileID: 2682138901704038034} + - component: {fileID: 8253148755706329020} + - component: {fileID: -2331432644492341259} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -36,7 +36,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: -1 + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &3771369551179688024 MonoBehaviour: @@ -176,7 +176,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b4f6aa8b8dab74ababa7b297602911cd, type: 3} m_Name: m_EditorClassIdentifier: - enforceNearClip: 1 enforceFarClip: 0 stereoConvergencePoint: {fileID: 0} protectedSurface: 0 @@ -198,7 +197,19 @@ Behaviour: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1011244501761250} m_Enabled: 1 ---- !u!114 &-245835337101996341 +--- !u!114 &8253148755706329020 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1011244501761250} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b122f228d3ca152448573a521107048a, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &-2331432644492341259 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -234,15 +245,3 @@ MonoBehaviour: m_RequiresDepthTexture: 0 m_RequiresColorTexture: 0 m_Version: 2 ---- !u!114 &2682138901704038034 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1011244501761250} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b122f228d3ca152448573a521107048a, type: 3} - m_Name: - m_EditorClassIdentifier: diff --git a/Runtime/Tools/Prefabs/Segmented Dimmer.prefab b/Runtime/Tools/Prefabs/Segmented Dimmer.prefab new file mode 100644 index 0000000..a0948fd --- /dev/null +++ b/Runtime/Tools/Prefabs/Segmented Dimmer.prefab @@ -0,0 +1,99 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2829901775412634616 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7316920915321141860} + - component: {fileID: 6517466774605063702} + - component: {fileID: 3718492364792726863} + m_Layer: 0 + m_Name: Segmented Dimmer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7316920915321141860 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2829901775412634616} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!20 &6517466774605063702 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2829901775412634616} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 3 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: 1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 0 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!114 &3718492364792726863 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2829901775412634616} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5ef25e1befc734d1c90ae3a499ae9677, type: 3} + m_Name: + m_EditorClassIdentifier: + shader: {fileID: 4800000, guid: b25330b849e774ec4b78d1a5c6fa6714, type: 3} diff --git a/Runtime/Tools/Prefabs/Segmented Dimmer.prefab.meta b/Runtime/Tools/Prefabs/Segmented Dimmer.prefab.meta new file mode 100644 index 0000000..689e875 --- /dev/null +++ b/Runtime/Tools/Prefabs/Segmented Dimmer.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8a61a5093a1754b1286c66eda4a2d31c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Tools/Prefabs/XR Rig.prefab b/Runtime/Tools/Prefabs/XR Rig.prefab index 1b0bd85..7595152 100644 --- a/Runtime/Tools/Prefabs/XR Rig.prefab +++ b/Runtime/Tools/Prefabs/XR Rig.prefab @@ -50,6 +50,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_TrackingType: 0 m_UpdateType: 0 + m_IgnoreTrackingState: 0 m_PositionInput: m_UseReference: 1 m_Action: @@ -61,8 +62,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: 4715436822718277786, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 971518578729158012, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationInput: m_UseReference: 1 m_Action: @@ -74,8 +74,19 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -3730266099773404857, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 2188567022586841978, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} + m_TrackingStateInput: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_PositionAction: m_Name: m_Type: 0 @@ -127,8 +138,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: 4715436822718277786, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 4715436822718277786, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationAction: m_UseReference: 0 m_Action: @@ -140,8 +150,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -3730266099773404857, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -3730266099773404857, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_TrackingStateAction: m_UseReference: 0 m_Action: @@ -153,8 +162,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -4937915811870281583, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -4937915811870281583, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_SelectAction: m_UseReference: 1 m_Action: @@ -166,8 +174,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_SelectActionValue: m_UseReference: 0 m_Action: @@ -191,8 +198,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_ActivateActionValue: m_UseReference: 0 m_Action: @@ -216,8 +222,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 1 - m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_UIPressActionValue: m_UseReference: 0 m_Action: @@ -229,8 +234,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -6123989868226725569, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -6123989868226725569, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_HapticDeviceAction: m_UseReference: 1 m_Action: @@ -242,8 +246,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 716709262026818769, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotateAnchorAction: m_UseReference: 1 m_Action: @@ -263,8 +266,19 @@ MonoBehaviour: m_Action: Rotate Anchor m_Flags: 0 m_Flags: 0 - m_Reference: {fileID: 5544746641166235747, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 5544746641166235747, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} + m_DirectionalAnchorRotationAction: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_TranslateAnchorAction: m_UseReference: 1 m_Action: @@ -284,8 +298,7 @@ MonoBehaviour: m_Action: Translate Anchor m_Flags: 0 m_Flags: 0 - m_Reference: {fileID: 5544746641166235747, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 5544746641166235747, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_ButtonPressPoint: 0.5 --- !u!114 &5589497132288526105 MonoBehaviour: @@ -307,6 +320,7 @@ MonoBehaviour: m_Bits: 4294967295 m_AttachTransform: {fileID: 0} m_KeepSelectedTargetValid: 1 + m_DisableVisualsWhenBlockedInGroup: 1 m_StartingSelectedInteractable: {fileID: 0} m_StartingTargetFilter: {fileID: 0} m_HoverEntered: @@ -321,6 +335,8 @@ MonoBehaviour: m_SelectExited: m_PersistentCalls: m_Calls: [] + m_StartingHoverFilters: [] + m_StartingSelectFilters: [] m_OnHoverEntered: m_PersistentCalls: m_Calls: [] @@ -336,6 +352,7 @@ MonoBehaviour: m_SelectActionTrigger: 0 m_HideControllerOnSelect: 0 m_AllowHoveredActivate: 0 + m_TargetPriorityMode: 0 m_PlayAudioClipOnSelectEntered: 0 m_AudioClipForOnSelectEntered: {fileID: 0} m_PlayAudioClipOnSelectExited: 0 @@ -348,6 +365,7 @@ MonoBehaviour: m_AudioClipForOnHoverExited: {fileID: 0} m_PlayAudioClipOnHoverCanceled: 0 m_AudioClipForOnHoverCanceled: {fileID: 0} + m_AllowHoverAudioWhileSelecting: 1 m_PlayHapticsOnSelectEntered: 0 m_HapticSelectEnterIntensity: 0 m_HapticSelectEnterDuration: 0 @@ -366,6 +384,7 @@ MonoBehaviour: m_PlayHapticsOnHoverCanceled: 0 m_HapticHoverCancelIntensity: 0 m_HapticHoverCancelDuration: 0 + m_AllowHoverHapticsWhileSelecting: 1 m_LineType: 0 m_BlendVisualLinePoints: 1 m_MaxRaycastDistance: 30 @@ -386,15 +405,19 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 m_RaycastTriggerInteraction: 1 + m_RaycastSnapVolumeInteraction: 1 m_HitClosestOnly: 0 m_HoverToSelect: 0 m_HoverTimeToSelect: 0.5 + m_AutoDeselect: 0 + m_TimeToAutoDeselect: 3 m_EnableUIInteraction: 1 m_AllowAnchorControl: 1 m_UseForceGrab: 1 m_RotateSpeed: 180 m_TranslateSpeed: 1 m_AnchorRotateReferenceFrame: {fileID: 0} + m_AnchorRotationMode: 0 --- !u!120 &5589497132288526104 LineRenderer: serializedVersion: 2 @@ -605,11 +628,45 @@ MonoBehaviour: m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 + m_BlockedColorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key1: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + m_TreatSelectionAsValidState: 0 m_SmoothMovement: 0 m_FollowTightness: 10 m_SnapThresholdDistance: 10 m_Reticle: {fileID: 0} + m_BlockedReticle: {fileID: 0} m_StopLineAtFirstRaycastHit: 1 + m_StopLineAtSelection: 0 + m_SnapEndpointIfAvailable: 1 --- !u!1 &5589497132891496769 GameObject: m_ObjectHideFlags: 0 @@ -714,6 +771,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_TrackingType: 0 m_UpdateType: 0 + m_IgnoreTrackingState: 0 m_PositionInput: m_UseReference: 1 m_Action: @@ -725,8 +783,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -2353999202519565437, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -2353999202519565437, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationInput: m_UseReference: 1 m_Action: @@ -738,8 +795,19 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -4607609863168903279, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -4607609863168903279, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} + m_TrackingStateInput: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_PositionAction: m_Name: m_Type: 0 @@ -791,8 +859,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -2353999202519565437, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -2353999202519565437, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationAction: m_UseReference: 0 m_Action: @@ -804,8 +871,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -4607609863168903279, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -4607609863168903279, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_TrackingStateAction: m_UseReference: 0 m_Action: @@ -914,6 +980,18 @@ MonoBehaviour: m_SingletonActionBindings: [] m_Flags: 0 m_Reference: {fileID: 0} + m_DirectionalAnchorRotationAction: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_TranslateAnchorAction: m_UseReference: 0 m_Action: @@ -947,6 +1025,7 @@ MonoBehaviour: m_Bits: 4294967295 m_AttachTransform: {fileID: 0} m_KeepSelectedTargetValid: 1 + m_DisableVisualsWhenBlockedInGroup: 1 m_StartingSelectedInteractable: {fileID: 0} m_StartingTargetFilter: {fileID: 0} m_HoverEntered: @@ -961,6 +1040,8 @@ MonoBehaviour: m_SelectExited: m_PersistentCalls: m_Calls: [] + m_StartingHoverFilters: [] + m_StartingSelectFilters: [] m_OnHoverEntered: m_PersistentCalls: m_Calls: [] @@ -976,6 +1057,7 @@ MonoBehaviour: m_SelectActionTrigger: 0 m_HideControllerOnSelect: 0 m_AllowHoveredActivate: 0 + m_TargetPriorityMode: 0 m_PlayAudioClipOnSelectEntered: 0 m_AudioClipForOnSelectEntered: {fileID: 0} m_PlayAudioClipOnSelectExited: 0 @@ -988,6 +1070,7 @@ MonoBehaviour: m_AudioClipForOnHoverExited: {fileID: 0} m_PlayAudioClipOnHoverCanceled: 0 m_AudioClipForOnHoverCanceled: {fileID: 0} + m_AllowHoverAudioWhileSelecting: 1 m_PlayHapticsOnSelectEntered: 0 m_HapticSelectEnterIntensity: 0 m_HapticSelectEnterDuration: 0 @@ -1006,6 +1089,7 @@ MonoBehaviour: m_PlayHapticsOnHoverCanceled: 0 m_HapticHoverCancelIntensity: 0 m_HapticHoverCancelDuration: 0 + m_AllowHoverHapticsWhileSelecting: 1 m_LineType: 0 m_BlendVisualLinePoints: 1 m_MaxRaycastDistance: 30 @@ -1026,15 +1110,19 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 m_RaycastTriggerInteraction: 1 + m_RaycastSnapVolumeInteraction: 1 m_HitClosestOnly: 0 m_HoverToSelect: 0 m_HoverTimeToSelect: 0.5 + m_AutoDeselect: 0 + m_TimeToAutoDeselect: 3 m_EnableUIInteraction: 1 m_AllowAnchorControl: 1 m_UseForceGrab: 1 m_RotateSpeed: 180 m_TranslateSpeed: 1 m_AnchorRotateReferenceFrame: {fileID: 0} + m_AnchorRotationMode: 0 --- !u!120 &5589497133001389823 LineRenderer: serializedVersion: 2 @@ -1245,11 +1333,45 @@ MonoBehaviour: m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 + m_BlockedColorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key1: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + m_TreatSelectionAsValidState: 0 m_SmoothMovement: 0 m_FollowTightness: 10 m_SnapThresholdDistance: 10 m_Reticle: {fileID: 0} + m_BlockedReticle: {fileID: 0} m_StopLineAtFirstRaycastHit: 1 + m_StopLineAtSelection: 0 + m_SnapEndpointIfAvailable: 1 --- !u!1 &5589497133034083132 GameObject: m_ObjectHideFlags: 0 @@ -1325,8 +1447,7 @@ MonoBehaviour: m_Action: Position m_Flags: 0 m_Flags: 0 - m_Reference: {fileID: 3736007580099224278, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 3736007580099224278, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationAction: m_UseReference: 0 m_Action: @@ -1346,8 +1467,7 @@ MonoBehaviour: m_Action: Rotation m_Flags: 0 m_Flags: 0 - m_Reference: {fileID: 3736007580099224278, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 3736007580099224278, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_TrackingStateAction: m_UseReference: 0 m_Action: @@ -1456,6 +1576,18 @@ MonoBehaviour: m_SingletonActionBindings: [] m_Flags: 0 m_Reference: {fileID: 0} + m_DirectionalAnchorRotationAction: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_TranslateAnchorAction: m_UseReference: 0 m_Action: @@ -1489,6 +1621,7 @@ MonoBehaviour: m_Bits: 4294967295 m_AttachTransform: {fileID: 0} m_KeepSelectedTargetValid: 1 + m_DisableVisualsWhenBlockedInGroup: 1 m_StartingSelectedInteractable: {fileID: 0} m_StartingTargetFilter: {fileID: 0} m_HoverEntered: @@ -1503,6 +1636,8 @@ MonoBehaviour: m_SelectExited: m_PersistentCalls: m_Calls: [] + m_StartingHoverFilters: [] + m_StartingSelectFilters: [] m_OnHoverEntered: m_PersistentCalls: m_Calls: [] @@ -1518,6 +1653,7 @@ MonoBehaviour: m_SelectActionTrigger: 0 m_HideControllerOnSelect: 0 m_AllowHoveredActivate: 0 + m_TargetPriorityMode: 0 m_PlayAudioClipOnSelectEntered: 0 m_AudioClipForOnSelectEntered: {fileID: 0} m_PlayAudioClipOnSelectExited: 0 @@ -1530,6 +1666,7 @@ MonoBehaviour: m_AudioClipForOnHoverExited: {fileID: 0} m_PlayAudioClipOnHoverCanceled: 0 m_AudioClipForOnHoverCanceled: {fileID: 0} + m_AllowHoverAudioWhileSelecting: 1 m_PlayHapticsOnSelectEntered: 0 m_HapticSelectEnterIntensity: 0 m_HapticSelectEnterDuration: 0 @@ -1548,6 +1685,7 @@ MonoBehaviour: m_PlayHapticsOnHoverCanceled: 0 m_HapticHoverCancelIntensity: 0 m_HapticHoverCancelDuration: 0 + m_AllowHoverHapticsWhileSelecting: 1 m_LineType: 0 m_BlendVisualLinePoints: 1 m_MaxRaycastDistance: 30 @@ -1568,15 +1706,19 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 m_RaycastTriggerInteraction: 1 + m_RaycastSnapVolumeInteraction: 1 m_HitClosestOnly: 0 m_HoverToSelect: 0 m_HoverTimeToSelect: 0.5 + m_AutoDeselect: 0 + m_TimeToAutoDeselect: 3 m_EnableUIInteraction: 1 m_AllowAnchorControl: 1 m_UseForceGrab: 1 m_RotateSpeed: 180 m_TranslateSpeed: 1 m_AnchorRotateReferenceFrame: {fileID: 0} + m_AnchorRotationMode: 0 --- !u!120 &5589497133034083135 LineRenderer: serializedVersion: 2 @@ -1787,11 +1929,45 @@ MonoBehaviour: m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 + m_BlockedColorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key1: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + m_TreatSelectionAsValidState: 0 m_SmoothMovement: 0 m_FollowTightness: 10 m_SnapThresholdDistance: 10 m_Reticle: {fileID: 0} + m_BlockedReticle: {fileID: 0} m_StopLineAtFirstRaycastHit: 1 + m_StopLineAtSelection: 0 + m_SnapEndpointIfAvailable: 1 --- !u!1 &5589497133593145652 GameObject: m_ObjectHideFlags: 0 @@ -1825,7 +2001,7 @@ Transform: m_Children: - {fileID: 5589497132891496770} m_Father: {fileID: 0} - m_RootOrder: -1 + m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &5589497133593145654 MonoBehaviour: @@ -1918,6 +2094,7 @@ MonoBehaviour: m_EditorClassIdentifier: m_TrackingType: 0 m_UpdateType: 0 + m_IgnoreTrackingState: 0 m_PositionInput: m_UseReference: 1 m_Action: @@ -1929,8 +2106,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -4319516118568744573, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -4319516118568744573, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationInput: m_UseReference: 1 m_Action: @@ -1942,8 +2118,19 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: 601750489641515592, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: 601750489641515592, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} + m_TrackingStateInput: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_PositionAction: m_Name: m_Type: 0 @@ -1995,8 +2182,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -2353999202519565437, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -2353999202519565437, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_RotationAction: m_UseReference: 0 m_Action: @@ -2008,8 +2194,7 @@ MonoBehaviour: m_Interactions: m_SingletonActionBindings: [] m_Flags: 0 - m_Reference: {fileID: -4607609863168903279, guid: a7b3d28b346833f4ea10936be7a9f18c, - type: 3} + m_Reference: {fileID: -4607609863168903279, guid: a7b3d28b346833f4ea10936be7a9f18c, type: 3} m_TrackingStateAction: m_UseReference: 0 m_Action: @@ -2118,6 +2303,18 @@ MonoBehaviour: m_SingletonActionBindings: [] m_Flags: 0 m_Reference: {fileID: 0} + m_DirectionalAnchorRotationAction: + m_UseReference: 0 + m_Action: + m_Name: + m_Type: 0 + m_ExpectedControlType: + m_Id: + m_Processors: + m_Interactions: + m_SingletonActionBindings: [] + m_Flags: 0 + m_Reference: {fileID: 0} m_TranslateAnchorAction: m_UseReference: 0 m_Action: @@ -2151,6 +2348,7 @@ MonoBehaviour: m_Bits: 4294967295 m_AttachTransform: {fileID: 0} m_KeepSelectedTargetValid: 1 + m_DisableVisualsWhenBlockedInGroup: 1 m_StartingSelectedInteractable: {fileID: 0} m_StartingTargetFilter: {fileID: 0} m_HoverEntered: @@ -2165,6 +2363,8 @@ MonoBehaviour: m_SelectExited: m_PersistentCalls: m_Calls: [] + m_StartingHoverFilters: [] + m_StartingSelectFilters: [] m_OnHoverEntered: m_PersistentCalls: m_Calls: [] @@ -2180,6 +2380,7 @@ MonoBehaviour: m_SelectActionTrigger: 0 m_HideControllerOnSelect: 0 m_AllowHoveredActivate: 0 + m_TargetPriorityMode: 0 m_PlayAudioClipOnSelectEntered: 0 m_AudioClipForOnSelectEntered: {fileID: 0} m_PlayAudioClipOnSelectExited: 0 @@ -2192,6 +2393,7 @@ MonoBehaviour: m_AudioClipForOnHoverExited: {fileID: 0} m_PlayAudioClipOnHoverCanceled: 0 m_AudioClipForOnHoverCanceled: {fileID: 0} + m_AllowHoverAudioWhileSelecting: 1 m_PlayHapticsOnSelectEntered: 0 m_HapticSelectEnterIntensity: 0 m_HapticSelectEnterDuration: 0 @@ -2210,6 +2412,7 @@ MonoBehaviour: m_PlayHapticsOnHoverCanceled: 0 m_HapticHoverCancelIntensity: 0 m_HapticHoverCancelDuration: 0 + m_AllowHoverHapticsWhileSelecting: 1 m_LineType: 0 m_BlendVisualLinePoints: 1 m_MaxRaycastDistance: 30 @@ -2230,15 +2433,19 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 m_RaycastTriggerInteraction: 1 + m_RaycastSnapVolumeInteraction: 1 m_HitClosestOnly: 0 m_HoverToSelect: 0 m_HoverTimeToSelect: 0.5 + m_AutoDeselect: 0 + m_TimeToAutoDeselect: 3 m_EnableUIInteraction: 1 m_AllowAnchorControl: 1 m_UseForceGrab: 1 m_RotateSpeed: 180 m_TranslateSpeed: 1 m_AnchorRotateReferenceFrame: {fileID: 0} + m_AnchorRotationMode: 0 --- !u!120 &5589497134372387906 LineRenderer: serializedVersion: 2 @@ -2449,11 +2656,45 @@ MonoBehaviour: m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 + m_BlockedColorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key1: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 0 + m_ColorSpace: -1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + m_TreatSelectionAsValidState: 0 m_SmoothMovement: 0 m_FollowTightness: 10 m_SnapThresholdDistance: 10 m_Reticle: {fileID: 0} + m_BlockedReticle: {fileID: 0} m_StopLineAtFirstRaycastHit: 1 + m_StopLineAtSelection: 0 + m_SnapEndpointIfAvailable: 1 --- !u!1001 &5589497132469688087 PrefabInstance: m_ObjectHideFlags: 0 @@ -2510,8 +2751,15 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} - - target: {fileID: 5850404675360181103, guid: 24158fb1437dc8e418d893939758764d, - type: 3} + - target: {fileID: 3771369551179688024, guid: 24158fb1437dc8e418d893939758764d, type: 3} + propertyPath: m_TrackingStateInput.m_Action.m_Id + value: c00e567b-92b6-4923-bc16-16d036db8358 + objectReference: {fileID: 0} + - target: {fileID: 3771369551179688024, guid: 24158fb1437dc8e418d893939758764d, type: 3} + propertyPath: m_TrackingStateInput.m_Action.m_Name + value: Tracking State Input + objectReference: {fileID: 0} + - target: {fileID: 5850404675360181103, guid: 24158fb1437dc8e418d893939758764d, type: 3} propertyPath: recenterXROriginAtStart value: 0 objectReference: {fileID: 0} @@ -2523,13 +2771,11 @@ PrefabInstance: m_SourcePrefab: {fileID: 100100000, guid: 24158fb1437dc8e418d893939758764d, type: 3} --- !u!4 &5593219067060734525 stripped Transform: - m_CorrespondingSourceObject: {fileID: 4214550324010282, guid: 24158fb1437dc8e418d893939758764d, - type: 3} + m_CorrespondingSourceObject: {fileID: 4214550324010282, guid: 24158fb1437dc8e418d893939758764d, type: 3} m_PrefabInstance: {fileID: 5589497132469688087} m_PrefabAsset: {fileID: 0} --- !u!20 &5609791579423962305 stripped Camera: - m_CorrespondingSourceObject: {fileID: 20294507499185110, guid: 24158fb1437dc8e418d893939758764d, - type: 3} + m_CorrespondingSourceObject: {fileID: 20294507499185110, guid: 24158fb1437dc8e418d893939758764d, type: 3} m_PrefabInstance: {fileID: 5589497132469688087} m_PrefabAsset: {fileID: 0} diff --git a/Runtime/Tools/Scripts/MagicLeapCamera.cs b/Runtime/Tools/Scripts/MagicLeapCamera.cs index f06180b..600d1f2 100644 --- a/Runtime/Tools/Scripts/MagicLeapCamera.cs +++ b/Runtime/Tools/Scripts/MagicLeapCamera.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Collections.Generic; #if UNITY_OPENXR_1_7_0_OR_NEWER +using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR.Features.MagicLeapSupport; #endif #if UNITY_XR_MAGICLEAP_PROVIDER @@ -103,6 +104,18 @@ public bool ProtectedSurface set => protectedSurface = value; } + public bool EnforceFarClip + { + get => enforceFarClip; + set => enforceFarClip = value; + } + + public bool RecenterXROriginAtStart + { + get => recenterXROriginAtStart; + set => recenterXROriginAtStart = value; + } + private void Awake() { camera = GetComponent(); @@ -115,7 +128,12 @@ private void Awake() #if UNITY_OPENXR_1_7_0_OR_NEWER if (!Application.isEditor) { - MLXrRenderSettings.SetFrameEndInfoParams(camera.stereoConvergence, vignette, protectedSurface); + var renderFeature = OpenXRSettings.Instance.GetFeature(); + if (renderFeature == null) + return; + renderFeature.focusDistance = camera.stereoConvergence; + renderFeature.useProtectedSurface = protectedSurface; + renderFeature.useVignetteMode = vignette; } #endif } @@ -150,7 +168,11 @@ private void LateUpdate() camera.stereoConvergence = CalculateFocusDistance(); #if UNITY_OPENXR_1_7_0_OR_NEWER - MLXrRenderSettings.SetFrameEndInfoParams(camera.stereoConvergence, vignette, protectedSurface); + if (!Utils.TryGetOpenXRFeature(out var renderFeature)) + return; + renderFeature.focusDistance = camera.stereoConvergence; + renderFeature.useProtectedSurface = protectedSurface; + renderFeature.useVignetteMode = vignette; #elif UNITY_XR_MAGICLEAP_PROVIDER MagicLeapXRRenderSettings.focusDistance = camera.stereoConvergence; MagicLeapXRRenderSettings.farClipDistance = camera.farClipPlane; diff --git a/Runtime/XrProvider/MagicLeapXrProvider.cs b/Runtime/XrProvider/MagicLeapXrProvider.cs index ccc2737..1f0d6b4 100644 --- a/Runtime/XrProvider/MagicLeapXrProvider.cs +++ b/Runtime/XrProvider/MagicLeapXrProvider.cs @@ -115,7 +115,14 @@ public static void AddLibrarySearchPaths(List librarySearchPaths, IEnume var resultCode = MagicLeapXrProviderNativeBindings.MLZIIsServerConfigured(out isZIRunning); if (!MLResult.DidNativeCallSucceed(resultCode, nameof(MagicLeapXrProviderNativeBindings.MLZIIsServerConfigured)) || !isZIRunning) { - Debug.LogError("Failed to detect running Magic Leap App Simulator session."); + // This is expected case: user plays a scene without an AppSim session started. + // Show a dialog with user-readable message instead of only logging errors that are not obvious nor helpful to users. + EditorUtility.DisplayDialog("Magic Leap", + "No Magic Leap App Simulator session is running. " + + "Exit Play mode and start App Simulator before entering Play mode again.", + "Ok"); + // This log message is more technical, good for for user to report bug. + Debug.LogError("Failed to detect running Magic Leap App Simulator session, or a running session if any is not compatible (e.g. protocol version is different between the frontend and the backend)."); MagicLeapXrProviderNativeBindings.MLSdkLoaderResetLibraryPaths(); } diff --git a/Runtime/XrProvider/MagicLeapXrProviderNativeBindings.cs b/Runtime/XrProvider/MagicLeapXrProviderNativeBindings.cs index fedff78..3e1d14f 100644 --- a/Runtime/XrProvider/MagicLeapXrProviderNativeBindings.cs +++ b/Runtime/XrProvider/MagicLeapXrProviderNativeBindings.cs @@ -109,6 +109,9 @@ MagicLeapXrProviderSettings GetSettings() [DllImport(MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] public static extern ulong GetHeadTrackerHandle(); + [DllImport(MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern ulong GetEyeTrackerHandle(); + [DllImport(MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] public static extern ulong GetInputHandle(); @@ -124,6 +127,9 @@ MagicLeapXrProviderSettings GetSettings() [DllImport(MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] public static extern void PredictSnapshot(long predictionTimestamp, bool enableSnapshotPrediction); + [DllImport(MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] + public static extern void ResetSnapshotPrediction(); + [DllImport(MagicLeapXrProviderDll, CallingConvention = CallingConvention.Cdecl)] internal static extern void InputSetOnPerceptionShutdownCallback(CallOnPerceptionShutdownDelegate createOnPerceptionShutdown); diff --git a/Tests/Runtime/Common.meta b/Tests/Runtime/Common.meta new file mode 100644 index 0000000..e1b6b54 --- /dev/null +++ b/Tests/Runtime/Common.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7b6cb19a9ef949c9a57aaee2711a8750 +timeCreated: 1691189090 \ No newline at end of file diff --git a/Tests/Runtime/Common/NativeSyncBufferTests.cs b/Tests/Runtime/Common/NativeSyncBufferTests.cs new file mode 100644 index 0000000..c30de28 --- /dev/null +++ b/Tests/Runtime/Common/NativeSyncBufferTests.cs @@ -0,0 +1,187 @@ +using NUnit.Framework; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine.XR.MagicLeap; + +namespace UnitySDKPlayTests +{ + public class NativeSyncBufferTests + { + [Test] + public void DefaultNativeSyncBufferIsInvalid() + { + var buffer = new NativeSyncBuffer(); + Assert.That(buffer.IsValid, Is.False); + } + + [Test] + public void CreateBufferWithDifferentAllocatorsWorks() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.Temp); + Assert.That(buffer.IsValid, Is.True); + buffer.Dispose(); + Assert.That(buffer.IsValid, Is.False); + + buffer = new NativeSyncBuffer(Allocator.TempJob); + Assert.That(buffer.IsValid, Is.True); + buffer.Dispose(); + Assert.That(buffer.IsValid, Is.False); + + buffer = new NativeSyncBuffer(Allocator.Persistent); + Assert.That(buffer.IsValid, Is.True); + buffer.Dispose(); + Assert.That(buffer.IsValid, Is.False); + } + + [Test] + public void NewBufferHasDefaultValuesUnlessSpecified() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.Temp); + AssertBufferHasValues(buffer, 0, 0); + + buffer.Dispose(); + + buffer = new NativeSyncBuffer(Allocator.Temp, 5); + AssertBufferHasValues(buffer, 5, 5); + + buffer.Dispose(); + } + + [Test] + public void UpdatingTheBufferChangesTheInputSideButNotTheOutputSide() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.Temp); + buffer.UpdateInput(5); + AssertBufferHasValues(buffer, 5, 0); + + buffer.Dispose(); + } + + [Test] + public void SynchronizingTheBufferActuallyUpdatesTheOutputSide() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.Temp); + buffer.UpdateInput(5); + buffer.Sync(); + AssertBufferHasValues(buffer, 5, 5); + + buffer.Dispose(); + } + + private struct SyncBufferJob : IJob + { + public NativeSyncBuffer Buffer; + + public void Execute() => Buffer.Sync(); + } + + private struct UpdateBufferJob : IJob + { + public NativeSyncBuffer Buffer; + public int Value; + + public void Execute() => Buffer.UpdateInput(Value); + } + + [Test] + public void CanUpdateBufferInputFromJob() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.TempJob); + new UpdateBufferJob + { + Buffer = buffer, + Value = 5, + }.Schedule().Complete(); + AssertBufferHasValues(buffer, 5, 0); + + buffer.Dispose(); + } + + [Test] + public void BufferAsyncUpdateWorks() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.TempJob); + buffer.UpdateInputAsync(5, default).Complete(); + AssertBufferHasValues(buffer, 5, 0); + buffer.Dispose(); + } + + [Test] + public void CanSyncBufferOutputFromJob() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.TempJob); + buffer.UpdateInput(5); + AssertBufferHasValues(buffer,5,0); + new SyncBufferJob { Buffer = buffer}.Schedule().Complete(); + AssertBufferHasValues(buffer,5,5); + + buffer.Dispose(); + } + + [Test] + public void BufferAsnycSyncWorks() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.TempJob); + buffer.UpdateInput(5); + buffer.Sync(default).Complete(); + AssertBufferHasValues(buffer, 5, 5); + + buffer.Dispose(); + } + + [Test] + public void BothAsyncUpdateAndSyncWork() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.TempJob); + + var handle = buffer.UpdateInputAsync(5, default); + buffer.Sync(handle).Complete(); + + AssertBufferHasValues(buffer, 5,5); + + buffer.Dispose(); + } + + [Test] + public void TryingToUpdateAndSyncAtTheSameTimeFails() + { + NativeSyncBuffer buffer; + + buffer = new NativeSyncBuffer(Allocator.TempJob); + var handle1 = buffer.UpdateInputAsync(5, default); + Assert.That(() => buffer.Sync(), Throws.InvalidOperationException); + handle1.Complete(); + AssertBufferHasValues(buffer, 5, 0); + + buffer.Dispose(); + + } + + private void AssertBufferHasValues(NativeSyncBuffer buffer, in T input, in T output) where T : unmanaged + { + unsafe + { + Assert.That(*buffer.GetInputPointer(), Is.EqualTo(input)); + Assert.That(buffer.Output, Is.EqualTo(output)); + } + } + } +} diff --git a/Tests/Runtime/Common/NativeSyncBufferTests.cs.meta b/Tests/Runtime/Common/NativeSyncBufferTests.cs.meta new file mode 100644 index 0000000..0b9cf71 --- /dev/null +++ b/Tests/Runtime/Common/NativeSyncBufferTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3708a26f25cb42d2bc5a1f5051a19532 +timeCreated: 1691189114 \ No newline at end of file diff --git a/Tests/Runtime/MLFacialExpression.meta b/Tests/Runtime/MLFacialExpression.meta new file mode 100644 index 0000000..82e44e5 --- /dev/null +++ b/Tests/Runtime/MLFacialExpression.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4c5cff0c0a794a840a0ccbb814498ffe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLFacialExpression/MLFacialExpressionTests.cs b/Tests/Runtime/MLFacialExpression/MLFacialExpressionTests.cs new file mode 100644 index 0000000..3168877 --- /dev/null +++ b/Tests/Runtime/MLFacialExpression/MLFacialExpressionTests.cs @@ -0,0 +1,34 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; + +namespace UnitySDKPlayTests +{ + public class MLFacialExpressionTests + { + MLResult result; + + [Test] + public void MLFacialExpression_UpdateSettingsEnable() + { + result = MLResult.Create(MLFacialExpression.UpdateSettings(new MLFacialExpression.Settings { EnableEyeExpression = true})); + Assert.IsTrue(result.IsOk, string.Format("UpdateSettingsEnable failed: {0}", result.ToString())); + } + + [Test] + public void MLFacialExpression_UpdateSettingsDisable() + { + result = MLResult.Create(MLFacialExpression.UpdateSettings(new MLFacialExpression.Settings { EnableEyeExpression = true })); + Assert.IsTrue(result.IsOk, string.Format("UpdateSettingsEnable failed: {0}", result.ToString())); + } + + [Test] + public void MLFacialExpression_GetEyeData() + { + Assert.Pass("Must be tested with manual scene at this time"); + } + } +} diff --git a/Tests/Runtime/MLFacialExpression/MLFacialExpressionTests.cs.meta b/Tests/Runtime/MLFacialExpression/MLFacialExpressionTests.cs.meta new file mode 100644 index 0000000..b405d1d --- /dev/null +++ b/Tests/Runtime/MLFacialExpression/MLFacialExpressionTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3141b727b35c4d9429c7bedacde7c356 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLGlobalDimmer.meta b/Tests/Runtime/MLGlobalDimmer.meta new file mode 100644 index 0000000..fcee48e --- /dev/null +++ b/Tests/Runtime/MLGlobalDimmer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 23a53c118785566468cbccb7e671064a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLGlobalDimmer/MLGlobalDimmerTests.cs b/Tests/Runtime/MLGlobalDimmer/MLGlobalDimmerTests.cs new file mode 100644 index 0000000..63e332b --- /dev/null +++ b/Tests/Runtime/MLGlobalDimmer/MLGlobalDimmerTests.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; + +namespace UnitySDKPlayTests +{ + public class MLGlobalDimmerTests + { + MLResult result; + + [Test] + public void MLGlobalDimmer_SetValue() + { +#if UNITY_OPENXR_1_7_0_OR_NEWER + result = MLGlobalDimmer.SetValue(1); + Assert.IsTrue(result.IsOk, string.Format("SetValue failed: {0}", result.ToString())); +#endif + } + } +} + diff --git a/Tests/Runtime/MLGlobalDimmer/MLGlobalDimmerTests.cs.meta b/Tests/Runtime/MLGlobalDimmer/MLGlobalDimmerTests.cs.meta new file mode 100644 index 0000000..5b99290 --- /dev/null +++ b/Tests/Runtime/MLGlobalDimmer/MLGlobalDimmerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10b66166161a9f543b0dc74b07197b61 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLMediaRecorder.meta b/Tests/Runtime/MLMediaRecorder.meta new file mode 100644 index 0000000..026ae24 --- /dev/null +++ b/Tests/Runtime/MLMediaRecorder.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5627d2c44aaa52b4288951a10fcc88b6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLMediaRecorder/MLMediaRecorderTests.cs b/Tests/Runtime/MLMediaRecorder/MLMediaRecorderTests.cs new file mode 100644 index 0000000..49da49e --- /dev/null +++ b/Tests/Runtime/MLMediaRecorder/MLMediaRecorderTests.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.Playables; +using UnityEngine.Profiling; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; +using static UnityEngine.XR.MagicLeap.MeshingSubsystem.Extensions.MLMeshing; + +namespace UnitySDKPlayTests +{ + // We separate this from camera because its large enough, even if its not used + // solely by itself + public class MLMediaRecorderTests + { + MLMediaRecorder recorder; + MLMediaFormat format; + MLResult result; + string filePath = Path.Combine(Application.temporaryCachePath,"out_file_test"); + + #region Test Parameters + private static IEnumerable OutputFormatsToTest = new[] + { + MLMediaRecorder.OutputFormat.Default, + MLMediaRecorder.OutputFormat.THREE_GPP, + MLMediaRecorder.OutputFormat.MPEG_4, + MLMediaRecorder.OutputFormat.AMR_NB, + MLMediaRecorder.OutputFormat.AMR_WB, + MLMediaRecorder.OutputFormat.AAC_ADIF, + MLMediaRecorder.OutputFormat.AAC_ADTS, + MLMediaRecorder.OutputFormat.RTP_AVP, + MLMediaRecorder.OutputFormat.MPEG2TS, + MLMediaRecorder.OutputFormat.WEBM, + MLMediaRecorder.OutputFormat.HEIF, + MLMediaRecorder.OutputFormat.OGG, + }; + + private static IEnumerable AudioSourcesToTest = new[] + { + MLMediaRecorder.AudioSource.Voice, + MLMediaRecorder.AudioSource.World, + MLMediaRecorder.AudioSource.Virtual, + MLMediaRecorder.AudioSource.Mixed, + }; + + private static IEnumerable VideoEncodersToTest = new[] + { + MLMediaRecorder.VideoEncoder.Default, + MLMediaRecorder.VideoEncoder.H263, + MLMediaRecorder.VideoEncoder.H264, + MLMediaRecorder.VideoEncoder.MPEG_4_SP, + MLMediaRecorder.VideoEncoder.VP8, + MLMediaRecorder.VideoEncoder.HEVC, + }; + + private static IEnumerable AudioEncodersToTest = new[] + { + MLMediaRecorder.AudioEncoder.Default, + MLMediaRecorder.AudioEncoder.AMR_NB, + MLMediaRecorder.AudioEncoder.AMR_WB, + MLMediaRecorder.AudioEncoder.AAC, + MLMediaRecorder.AudioEncoder.HE_AAC, + MLMediaRecorder.AudioEncoder.AAC_ELD, + MLMediaRecorder.AudioEncoder.VORBIS, + MLMediaRecorder.AudioEncoder.OPUS, + + }; + #endregion + + [SetUp] + public void SetUp() + { + //Condensing create and setvideosource into this for later tests convenience + recorder = MLMediaRecorder.Create(); + Assert.IsNotNull(recorder, "Setup:MLMediaRecorder.Create failed; recorder null"); + result = recorder.SetVideoSource(MLMediaRecorder.VideoSource.Camera); + Assert.IsTrue(result.IsOk, "Setup:SetVideoSource fail"); + format = MLMediaFormat.CreateEmpty(); + + //gently lifted from CameraRecorder.cs, used for prepare and start tests + //some later tests may overwrite these specific configs; this is a general setup + format.SetValue(MLMediaFormatKey.Width, 1920); + format.SetValue(MLMediaFormatKey.Height, 1080); + format.SetValue(MLMediaFormatKey.Frame_Rate, 30); + format.SetValue(MLMediaFormatKey.Parameter_Video_Bitrate, 20000000); + format.SetValue(MLMediaFormatKey.Bit_Rate, 96000); + format.SetValue(MLMediaFormatKey.Channel_Count, 1); + format.SetValue(MLMediaFormatKey.Sample_Rate, 16000); + } + + [UnityTest] + public IEnumerator MLMediaRecorder_SetAudioSource( + [ValueSource(nameof(AudioSourcesToTest))] MLMediaRecorder.AudioSource source) + { + result = recorder.SetAudioSource(source); + Assert.IsTrue(result.IsOk, string.Format("SetAudioSource fail: {0}", source.ToString())); + yield return null; + } + + [UnityTest] + public IEnumerator MLMediaRecorder_SetOutputFormat( + [ValueSource(nameof(OutputFormatsToTest))] MLMediaRecorder.OutputFormat outputFormat) + { + result = recorder.SetOutputFormat(outputFormat); + Assert.IsTrue(result.IsOk, string.Format("SetOutputFormat fail: {0}", outputFormat.ToString())); + yield return null; + } + + [Test] + public void MLMediaRecorder_SetOutputFileForFD() + { + // Not really sure how we're supposed to get an unmanaged FD for this method, so + // we have it present but leave it as unimplemented. + Assert.Ignore("Not implemented"); + } + + [UnityTest] + public IEnumerator MLMediaRecorder_SetVideoEncoder( + [ValueSource(nameof(VideoEncodersToTest))] MLMediaRecorder.VideoEncoder encoder) + { + result = recorder.SetOutputFormat(MLMediaRecorder.OutputFormat.Default); + Assert.IsTrue(result.IsOk, "SetOutputFormat fail"); + result = recorder.SetVideoEncoder(encoder); + Assert.IsTrue(result.IsOk, string.Format("SetVideoEncoder fail: {0}", encoder.ToString())); + yield return null; + } + + [UnityTest] + public IEnumerator MLMediaRecorder_SetAudioEncoder( + [ValueSource(nameof(AudioEncodersToTest))] MLMediaRecorder.AudioEncoder encoder) + { + result = recorder.SetOutputFormat(MLMediaRecorder.OutputFormat.THREE_GPP); + Assert.IsTrue(result.IsOk, "SetOutputFormat fail"); + result = recorder.SetVideoEncoder(MLMediaRecorder.VideoEncoder.Default); + Assert.IsTrue(result.IsOk, "SetVideoEncoder fail"); + result = recorder.SetAudioEncoder(encoder); + Assert.IsTrue(result.IsOk, string.Format("SetAudioEncoder fail: {0}", encoder.ToString())); + yield return null; + } + + [Test] + public void MLMediaRecorder_SetMaxDuration() + { + recorder.SetOutputFormat(MLMediaRecorder.OutputFormat.Default); + result = recorder.SetMaxDuration(0); + Assert.IsTrue(result.IsOk, string.Format("SetMaxDuration fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_SetOutputFilePath() + { + recorder.SetOutputFormat(MLMediaRecorder.OutputFormat.Default); + result = recorder.SetOutputFileForPath(filePath); + Assert.IsTrue(result.IsOk, string.Format("SetOutputFormat fail: {0}; path is {1}", result, filePath)); + } + + [Test] + public void MLMediaRecorder_SetMaxFileSize() + { + recorder.SetOutputFormat(MLMediaRecorder.OutputFormat.Default); + result = recorder.SetMaxFileSize(0); + Assert.IsTrue(result.IsOk, string.Format("SetMaxFileSize fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_SetGeoLocation() + { + result = recorder.SetGeoLocation(0, 0); //Null island -- I had to make sure this existed + Assert.IsTrue(result.IsOk, "SetGeoLocation failed with 0, 0"); + } + + [Test] + public void MLMediaRecorder_Prepare() + { + ExtraSetup(); + result = recorder.Prepare(format); + Assert.IsTrue(result.IsOk, string.Format("Prepare fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_Start() + { + ExtraSetup(); + Assert.IsTrue(result.IsOk, string.Format("SetOutputFileForPath fail: {0}, path is {1}", result, filePath)); + result = recorder.Prepare(format); + Assert.IsTrue(result.IsOk, string.Format("Prepare fail: {0}", result)); + result = recorder.Start(); + Assert.IsTrue(result.IsOk, string.Format("Start fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_GetInputSurface() + { + ExtraSetup(); + result = recorder.Prepare(format); + Assert.IsTrue(result.IsOk, string.Format("Prepare fail: {0}", result)); + result = recorder.Start(); + Assert.IsTrue(result.IsOk, string.Format("Start fail: {0}", result)); + result = recorder.GetInputSurface(); + Assert.IsTrue(result.IsOk, string.Format("GetInputSurface fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_GetMaxAmplitude() + { + ExtraSetup(); + result = recorder.Prepare(format); + Assert.IsTrue(result.IsOk, string.Format("Prepare fail: {0}", result)); + result = recorder.Start(); + Assert.IsTrue(result.IsOk, string.Format("Start fail: {0}", result)); + result = recorder.GetMaxAmplitude(out int _); + Assert.IsTrue(result.IsOk, string.Format("GetMaxAmplitude fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_Stop() + { + ExtraSetup(); + result = recorder.Prepare(format); + Assert.IsTrue(result.IsOk, string.Format("Prepare fail: {0}", result)); + result = recorder.Start(); + Assert.IsTrue(result.IsOk, string.Format("Start fail: {0}", result)); + result = recorder.Stop(); + Assert.IsTrue(result.IsOk, string.Format("Stop fail: {0}", result)); + } + + [Test] + public void MLMediaRecorder_Reset() + { + MLResult result = recorder.Reset(); + Assert.IsTrue(result.IsOk); + } + + private void ExtraSetup() + { + // This method is specifically used when doing tests that need to call + // MLMediaRecorder.Start() + result = recorder.SetOutputFormat(MLMediaRecorder.OutputFormat.Default); + Assert.IsTrue(result.IsOk, string.Format("SetOutputFormat fail: {0}", result)); + result = recorder.SetVideoEncoder(MLMediaRecorder.VideoEncoder.Default); + Assert.IsTrue(result.IsOk, string.Format("SetVideoEncoder fail: {0}", result)); + result = recorder.SetOutputFileForPath(filePath); + Assert.IsTrue(result.IsOk, string.Format("SetOutputFileForPath fail: {0}, path is {1}", result, filePath)); + } + + } + +} diff --git a/Tests/Runtime/MLMediaRecorder/MLMediaRecorderTests.cs.meta b/Tests/Runtime/MLMediaRecorder/MLMediaRecorderTests.cs.meta new file mode 100644 index 0000000..4946836 --- /dev/null +++ b/Tests/Runtime/MLMediaRecorder/MLMediaRecorderTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4bbb0762cb9184e4dadacd6b2cb464b8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLNotifications.meta b/Tests/Runtime/MLNotifications.meta new file mode 100644 index 0000000..47f8df0 --- /dev/null +++ b/Tests/Runtime/MLNotifications.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7531aa4c7426c0f418c21a604ff13408 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLNotifications/MLNotificationsTests.cs b/Tests/Runtime/MLNotifications/MLNotificationsTests.cs new file mode 100644 index 0000000..10046f6 --- /dev/null +++ b/Tests/Runtime/MLNotifications/MLNotificationsTests.cs @@ -0,0 +1,72 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; + + +namespace UnitySDKPlayTests +{ + public class MLNotificationsTests + { + + MLResult result; + bool hasPermission = false; + static MLPermissions.Callbacks permissionCallbacks = new MLPermissions.Callbacks(); + + [Test] + public void MLNotifications_SetNotifications_True() + { + CheckPermissions(); + if (!hasPermission) + { + Assert.Fail("Could not get System notification permission"); + } + result = MLNotifications.SetNotifications(true); + if (!result.IsOk) + { + switch (result.Result) + { + case MLResult.Code.IncompatibleSKU: + Assert.Ignore("Feature not supported on current device; requires medical device"); + break; + default: + Assert.Fail(string.Format("SetNotifications_True failed: {0}", result.ToString())); + break; + } + } + } + + [Test] + public void MLNotifications_SetNotifications_False() + { + CheckPermissions(); + if (!hasPermission) + { + Assert.Fail("Could not get System notification permission"); + } + result = MLNotifications.SetNotifications(false); + if (!result.IsOk) + { + switch (result.Result) + { + case MLResult.Code.IncompatibleSKU: + Assert.Ignore("Feature not supported on current device; requires medical device"); + break; + default: + Assert.Fail(string.Format("SetNotifications_False failed: {0}", result.ToString())); + break; + } + } + } + + private void CheckPermissions() + { + //Need system notification permissions for this + MLPermissions.RequestPermission(MLPermission.SystemNotification, permissionCallbacks); + hasPermission = MLPermissions.CheckPermission(MLPermission.SystemNotification).IsOk; + } + + } +} diff --git a/Tests/Runtime/MLNotifications/MLNotificationsTests.cs.meta b/Tests/Runtime/MLNotifications/MLNotificationsTests.cs.meta new file mode 100644 index 0000000..1f25ea8 --- /dev/null +++ b/Tests/Runtime/MLNotifications/MLNotificationsTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 43792189527758248991fa2306e965e1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLPlanes/MLPlanesTests.cs b/Tests/Runtime/MLPlanes/MLPlanesTests.cs index 034b46a..d305c2f 100644 --- a/Tests/Runtime/MLPlanes/MLPlanesTests.cs +++ b/Tests/Runtime/MLPlanes/MLPlanesTests.cs @@ -4,6 +4,7 @@ using UnityEngine; using UnityEngine.TestTools; using UnityEngine.XR.MagicLeap; +using UnityEngine.XR.OpenXR.Features.MagicLeapSupport; namespace UnitySDKPlayTests { diff --git a/Tests/Runtime/MLPowerManager.meta b/Tests/Runtime/MLPowerManager.meta new file mode 100644 index 0000000..e25b382 --- /dev/null +++ b/Tests/Runtime/MLPowerManager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 244df9c7a201f314c86059d0405c99dd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLPowerManager/MLPowerManagerTests.cs b/Tests/Runtime/MLPowerManager/MLPowerManagerTests.cs new file mode 100644 index 0000000..bee715b --- /dev/null +++ b/Tests/Runtime/MLPowerManager/MLPowerManagerTests.cs @@ -0,0 +1,62 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; + +namespace UnitySDKPlayTests +{ + public class MLPowerManagerTests + { + MLResult result; + + #region Test Parameters + // Some states can't be tested; None will be returned for + // invalid/no power, DisabledWhileCharging is returned when the + // controller is charging, and Sleep is not supported + private static readonly IEnumerable PowerStatesToTest = new[] + { + MLPowerManager.PowerState.Normal, + MLPowerManager.PowerState.Standby, + }; + #endregion + + // Ensure that controller is connected to avoid PowerManagerNotConnect error + [Test] + public void MLPowerManager_SetPowerState( + [ValueSource(nameof(PowerStatesToTest))] MLPowerManager.PowerState state) + { + result = MLResult.Create(MLPowerManager.SetPowerState(new MLPowerManager.Settings { State = state })); + Assert.IsTrue(result.IsOk, string.Format("SetPowerState failed: {0} on state {1}", result.ToString(), state.ToString())); + } + + [Test] + public void MLPowerManager_GetComponentProperties() + { + result = MLResult.Create(MLPowerManager.GetComponentProperties(out _)); + Assert.IsTrue(result.IsOk, string.Format("GetComponentProperties failed: {0}", result.ToString())); + } + + [Test] + public void MLPowerManager_GetAvailblePowerStates() + { + result = MLResult.Create(MLPowerManager.GetAvailablePowerStates(out _)); + Assert.IsTrue(result.IsOk, string.Format("GetAvailablePowerStates failed: {0}", result.ToString())); + } + + [Test] + public void MLPowerManager_GetPowerState() + { + result = MLResult.Create(MLPowerManager.GetPowerState(out _)); + Assert.IsTrue(result.IsOk, string.Format("GetPowerState failed: {0}", result.ToString())); + } + + [Test] + public void MLPowerManager_GetAvailableProperties() + { + result = MLResult.Create(MLPowerManager.GetAvailableProperties(out _)); + Assert.IsTrue(result.IsOk, string.Format("GetAvailableProperties failed: {0}", result.ToString())); + } + } +} diff --git a/Tests/Runtime/MLPowerManager/MLPowerManagerTests.cs.meta b/Tests/Runtime/MLPowerManager/MLPowerManagerTests.cs.meta new file mode 100644 index 0000000..f3fb4e4 --- /dev/null +++ b/Tests/Runtime/MLPowerManager/MLPowerManagerTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62f89cee21e860649b3ff25cd7720d8e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLSpace.meta b/Tests/Runtime/MLSpace.meta new file mode 100644 index 0000000..e915eac --- /dev/null +++ b/Tests/Runtime/MLSpace.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 84eeda6010ee70e4cb3507692ddbbdb5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLSpace/MLSpaceTests.cs b/Tests/Runtime/MLSpace/MLSpaceTests.cs new file mode 100644 index 0000000..abbdc39 --- /dev/null +++ b/Tests/Runtime/MLSpace/MLSpaceTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; + +namespace UnitySDKPlayTests +{ + public class MLSpaceTests + { + // If you don't have any spaces on the device these tests will fail + MLResult result; + + [Test] + public void MLSpace_GetSpaceList() + { + result = MLResult.Create(MLSpace.GetSpaceList(out _)); + Assert.IsTrue(result.IsOk, string.Format("GetSpaceList failed: {0}", result.ToString())); + } + + [Test] + public void MLSpace_ExportSpace() + { + MLSpace.Space space = GetSpace(); + result = MLResult.Create(MLSpace.ExportSpace(new MLSpace.SpaceInfo {SpaceId = space.SpaceId} , out MLSpace.SpaceData outData)); + Assert.IsTrue(result.IsOk, string.Format("ExportSpace failed: {0}", result.ToString())); + } + + [Test] + public void MLSpace_ImportSpace() + { + MLSpace.Space space = GetSpace(); + result = MLResult.Create(MLSpace.ExportSpace(new MLSpace.SpaceInfo{ SpaceId = space.SpaceId }, out MLSpace.SpaceData spaceData)); + Assert.IsTrue(result.IsOk, string.Format("ExportSpace failed: {0}", result.ToString())); + + MLSpace.SpaceInfo spaceInfo; + + // Can't import an existing space, expect SpaceAlreadyExists + result = MLResult.Create(MLSpace.ImportSpace(in spaceData, out spaceInfo)); + if(!result.IsOk) + { + switch (result.Result) + { + case MLResult.Code.SpaceAlreadyExists: + Assert.Pass("Cannot import an existing space (expected result)"); + break; + default: + Assert.Fail(string.Format("ImportSpace failed: {0}", result.ToString())); + break; + } + } + } + + [Test] + public void MLSpace_RequestLocalization() + { + MLSpace.Space space = GetSpace(); + MLSpace.SpaceInfo spaceInfo = new MLSpace.SpaceInfo { SpaceId = space.SpaceId }; + result = MLResult.Create(MLSpace.RequestLocalization(ref spaceInfo)); + Assert.IsTrue(result.IsOk, string.Format("RequestLocalization failed: {0}", result.ToString())); + } + + [Test] + public void MLSpace_GetLocalizationResult() + { + MLSpace.Space space = GetSpace(); + MLSpace.SpaceInfo spaceInfo = new MLSpace.SpaceInfo { SpaceId = space.SpaceId }; + result = MLResult.Create(MLSpace.RequestLocalization(ref spaceInfo)); + Assert.IsTrue(result.IsOk, string.Format("RequestLocalization failed: {0}", result.ToString())); + result = MLResult.Create(MLSpace.GetLocalizationResult(out MLSpace.LocalizationResult res)); + Assert.IsTrue(result.IsOk, string.Format("GetLocalizationResult failed: {0}", result.ToString())); + } + + // Used for methods that need a space to query + private MLSpace.Space GetSpace() + { + MLSpace.GetSpaceList(out MLSpace.Space[] spaces); + Assert.IsTrue(spaces.Length > 0, "GetSpace: no spaces retrieved from device; please create a space or import from AR Cloud"); + return spaces[0]; + } + + } +} + diff --git a/Tests/Runtime/MLSpace/MLSpaceTests.cs.meta b/Tests/Runtime/MLSpace/MLSpaceTests.cs.meta new file mode 100644 index 0000000..cecab67 --- /dev/null +++ b/Tests/Runtime/MLSpace/MLSpaceTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98d0fe115c35d8f478235f3615425e00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLTime.meta b/Tests/Runtime/MLTime.meta new file mode 100644 index 0000000..267ffb3 --- /dev/null +++ b/Tests/Runtime/MLTime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 255103eec111a724e9d580e1d9ed180e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MLTime/MLTimeTests.cs b/Tests/Runtime/MLTime/MLTimeTests.cs new file mode 100644 index 0000000..20c61b0 --- /dev/null +++ b/Tests/Runtime/MLTime/MLTimeTests.cs @@ -0,0 +1,29 @@ +using System.Collections; +using System.Collections.Generic; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using UnityEngine.XR.MagicLeap; + +namespace UnitySDKPlayTests +{ + public class MLTimeTests + { + MLResult result; + MLTime time; + + [Test, Order(1)] + public void MLTime_ConvertSystemTimeToMLTimeTest() + { + result = MLTime.ConvertSystemTimeToMLTime(0, out time); + Assert.IsTrue(result.IsOk, string.Format("ConvertSystemTimeToMLTime failed: {0}", result.ToString())); + } + + [Test, Order(2)] + public void MLTime_ConvertMLTimeToSystemTimeTest() + { + result = MLTime.ConvertMLTimeToSystemTime(time, out long _); + Assert.IsTrue(result.IsOk, string.Format("ConvertMLTimeToSystemTime failed: {0}", result.ToString())); + } + } +} diff --git a/Tests/Runtime/MLTime/MLTimeTests.cs.meta b/Tests/Runtime/MLTime/MLTimeTests.cs.meta new file mode 100644 index 0000000..51cff69 --- /dev/null +++ b/Tests/Runtime/MLTime/MLTimeTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ae97c33e08b78043ae33d0662b6347b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tests/Runtime/MagicLeap.SDK.Tests.asmdef b/Tests/Runtime/MagicLeap.SDK.Tests.asmdef index 5d2bbd5..6104118 100644 --- a/Tests/Runtime/MagicLeap.SDK.Tests.asmdef +++ b/Tests/Runtime/MagicLeap.SDK.Tests.asmdef @@ -11,7 +11,7 @@ ], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": true, "precompiledReferences": [ "nunit.framework.dll" @@ -22,4 +22,4 @@ ], "versionDefines": [], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/package.json b/package.json index 383b67f..4462f20 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,34 @@ { - "name": "com.magicleap.unitysdk", - "displayName": "Magic Leap SDK", - "version": "1.9.0", - "unity": "2022.2", - "description": "Magic Leap Unity SDK", - "keywords": [ - "magicleap", - "ar", - "augmented", - "xr", - "reality", - "xreditorsubsystem" - ], - "dependencies": { - "com.unity.xr.magicleap": "7.0.0", - "com.unity.xr.arfoundation": "5.0.0-pre.12", - "com.unity.xr.interaction.toolkit": "2.1.0-pre.1", - "com.unity.inputsystem": "1.3.0", - "com.unity.modules.androidjni": "1.0.0", - "com.unity.xr.interactionsubsystems": "2.0.0" - }, - "author": { - "name": "Magic Leap", - "url": "https://developer.magicleap.com/" - } -} + "name": "com.magicleap.unitysdk", + "displayName": "Magic Leap SDK", + "version": "1.10.0", + "unity": "2022.2", + "description": "Magic Leap Unity SDK", + "keywords": [ + "magicleap", + "ar", + "augmented", + "xr", + "reality", + "xreditorsubsystem" + ], + "dependencies": { + "com.unity.xr.magicleap": "7.0.0", + "com.unity.xr.arfoundation": "5.0.0-pre.12", + "com.unity.xr.interaction.toolkit": "2.1.0-pre.1", + "com.unity.inputsystem": "1.3.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.xr.interactionsubsystems": "2.0.0" + }, + "author": { + "name": "Magic Leap", + "url": "https://developer.magicleap.com/" + }, + "samples": [ + { + "displayName": "XR Rig & Inputs", + "description": "Contains a ready-made ML Rig prefab for your XR Origin and device tracking, as well as a sample InputActionsAsset with ML Controller Actions.", + "path": "Samples~/RigAndInputs" + } + ] +} \ No newline at end of file