// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) (2024) 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.Runtime.InteropServices; using MagicLeap.OpenXR.Constants; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.XR; using UnityEngine.XR.MagicLeap; using UnityEngine.XR.Management; using UnityEngine.XR.OpenXR; using UnityEngine.XR.OpenXR.Features; using UnityEngine.XR.OpenXR.NativeTypes; #if UNITY_EDITOR using UnityEditor; #endif namespace MagicLeap.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(); } 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; } internal static bool DidXrCallSucceed(XrResult result, string functionName = "A native function", Predicate successCase = null, bool showError = true) { var success = successCase?.Invoke(result) ?? result == XrResult.Success; if (success || !showError) { return success; } var logResult = result.ToString(); if (!Enum.IsDefined(typeof(XrResult), result)) { logResult = ((MLXrResult)result).ToString(); } MLPluginLog.ErrorFormat($"{functionName} in the Magic Leap API failed. Reason: {logResult} "); return false; } internal static Vector3 InvertZ(this Vector3 input) { return new Vector3(input.x, input.y, -input.z); } internal static Quaternion InvertXY(this Quaternion quaternion) { return new Quaternion(-quaternion.x, -quaternion.y, quaternion.z, quaternion.w); } /// /// An OpenXRStruct helper class /// /// The type of the OpenXR struct internal static class OpenXRStructHelpers where T : unmanaged { /// /// Create an OpenXR struct with the type initialized /// /// The type of the OpenXr struct (that should be written to the Type field) /// The resulting openxr struct /// The name of the Type field (default is "Type") /// The value to be initialized with for the field /// The underlying type of the field (can be ulong or an enum for example) internal static void Create(TType type, out T result, string typeFieldName = "Type", T defaultValue = default) where TType: unmanaged { var nativeResult = defaultValue; unsafe { var nativePtr = new IntPtr(&nativeResult); var offset = Marshal.OffsetOf(typeFieldName); var sourceType = type; UnsafeUtility.MemCpy((nativePtr + offset.ToInt32()).ToPointer(), &sourceType, sizeof(TType)); result = nativeResult; } } } #if UNITY_EDITOR internal static string GetNiceTypeName(Type type) => ObjectNames.NicifyVariableName(type.Name); internal static OpenXRFeature.ValidationRule GetDepthSubmissionValidationRule(OpenXRFeature feature, BuildTargetGroup targetGroup, OpenXRSettings.DepthSubmissionMode validDepthSubmissionMode = OpenXRSettings.DepthSubmissionMode.Depth16Bit) { var depthSubmissionModeValidationRule = new OpenXRFeature.ValidationRule(feature) { message = $"{nameof(OpenXRSettings.DepthSubmissionMode)} must be set to {validDepthSubmissionMode} to support {GetNiceTypeName(feature.GetType())}", checkPredicate = () => { var settings = GetSettings(targetGroup); return settings.depthSubmissionMode == validDepthSubmissionMode; }, fixIt = () => { var settings = GetSettings(targetGroup); settings.depthSubmissionMode = validDepthSubmissionMode; }, error = true, }; return depthSubmissionModeValidationRule; } 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); } internal static bool TryEnableFeature(BuildTargetGroup group) where TFeature : OpenXRFeature { var feature = GetFeatureForBuildTarget(group); return feature != null && (feature.enabled = true); } #endif // UNITY_EDITOR } }