//========= Copyright 2016-2023, HTC Corporation. All rights reserved. =========== using HTC.UnityPlugin.Utility; using System.Collections.Generic; using UnityEngine; #if UNITY_2017_2_OR_NEWER using UnityEngine.XR; #endif #if VIU_XR_GENERAL_SETTINGS using UnityEngine.XR.Management; using System; #endif namespace HTC.UnityPlugin.VRModuleManagement { public abstract partial class UnityXRModuleBase : VRModule.ModuleBase { #if UNITY_2019_3_OR_NEWER && VIU_XR_GENERAL_SETTINGS private class IndexMap { private Dictionary hashID2index = new Dictionary(); private InputDevice[] index2Device = new InputDevice[VRModule.MAX_DEVICE_COUNT]; private int[] index2DeviceHashID = new int[VRModule.MAX_DEVICE_COUNT]; public IndexMap() { Clear(); } private bool IsValidDevice(InputDevice device) { return device != default(InputDevice); } private bool IsValidIndex(uint index) { return index < index2Device.Length; } public uint Device2Index(InputDevice device) { if (IsValidDevice(device)) { uint index; if (hashID2index.TryGetValue(HashID(device), out index)) { return index; } } return VRModule.INVALID_DEVICE_INDEX; } public bool TryGetIndex(InputDevice device, out uint index) { index = Device2Index(device); return IsValidIndex(index); } public InputDevice Index2Device(uint index) { return IsValidIndex(index) ? index2Device[index] : default(InputDevice); } public bool TryGetDevice(uint index, out InputDevice device) { device = Index2Device(index); return IsValidDevice(device); } public bool IsMapped(int hashID) { uint index; return hashID2index.TryGetValue(hashID, out index) && IsValidIndex(index); } public bool IsMapped(uint index) { return IsValidIndex(index) && index2DeviceHashID[index] != 0; } public static bool IsHMD(InputDevice device) { return (device.characteristics & InputDeviceCharacteristics.HeadMounted) > 0u; } public bool TryMapAsHMD(InputDevice device) { if (!IsValidDevice(device)) { throw new ArgumentException("Invalid device", "device"); } var hashID = HashID(device); if (IsMapped(hashID)) { throw new Exception("device(" + device.ToString() + ") already mapped"); } if (!IsHMD(device)) { return false; } hashID2index[hashID] = VRModule.HMD_DEVICE_INDEX; index2Device[VRModule.HMD_DEVICE_INDEX] = device; index2DeviceHashID[VRModule.HMD_DEVICE_INDEX] = hashID; return true; } public void MapNonHMD(InputDevice device, uint index) { if (!IsValidDevice(device)) { throw new ArgumentException("Invalid device", "device"); } if (!IsValidIndex(index)) { throw new ArgumentException("index larger then VRModule.MAX_DEVICE_COUNT(" + VRModule.MAX_DEVICE_COUNT + ")", "index"); } var hashID = HashID(device); if (IsMapped(hashID)) { throw new Exception("device(" + device.ToString() + ") already mapped"); } if (IsMapped(index)) { throw new Exception("index(" + index + ") already mapped"); } if (IsHMD(device)) { throw new Exception("device(" + device.ToString() + ") is hmd"); } if (index == VRModule.HMD_DEVICE_INDEX) { throw new Exception("index cannot be VRModule.HMD_DEVICE_INDEX(" + VRModule.HMD_DEVICE_INDEX + ")"); } hashID2index[hashID] = index; index2Device[index] = device; index2DeviceHashID[index] = hashID; } public void UnmapByDevice(InputDevice device) { if (!IsValidDevice(device)) { throw new ArgumentException("Invalid device", "device"); } var hashID = HashID(device); uint index; if (!hashID2index.TryGetValue(hashID, out index)) { return; } hashID2index.Remove(hashID); index2Device[index] = default(InputDevice); index2DeviceHashID[index] = 0; } public void UnmapByIndex(uint index) { if (!IsValidIndex(index)) { throw new ArgumentException("Invalid index", "index"); } hashID2index.Remove(index2DeviceHashID[index]); index2Device[index] = default(InputDevice); index2DeviceHashID[index] = 0; } public void Clear() { hashID2index.Clear(); for (int i = index2Device.Length - 1; i >= 0; --i) { index2Device[i] = default(InputDevice); } } public static int HashID(InputDevice device) { #if CSHARP_7_OR_LATER return (device, device.name, device.characteristics).GetHashCode(); #else return new { device, device.name, device.characteristics }.GetHashCode(); #endif } } public static bool HasActiveLoader() { var instance = XRGeneralSettings.Instance; if (instance == null) { return false; } var manager = instance.Manager; if (manager == null) { return false; } return manager.activeLoader != null; } public static bool HasActiveLoader(string loaderName) { string activeLoaderName; return TryGetActiveLoaderName(out activeLoaderName) && activeLoaderName == loaderName; } public static bool HasActiveLoader(VRModuleKnownXRLoader knownLoader) { string activeLoaderName; return TryGetActiveLoaderName(out activeLoaderName) && knownLoader == ToKnownXRLoader(activeLoaderName); } public static VRModuleKnownXRLoader GetKnownActiveLoader() { var instance = XRGeneralSettings.Instance; if (instance == null) { return VRModuleKnownXRLoader.Unknown; } var manager = instance.Manager; if (manager == null) { return VRModuleKnownXRLoader.Unknown; } var loader = manager.activeLoader; if (loader == null) { return VRModuleKnownXRLoader.Unknown; } var loaderName = loader.name; if (string.IsNullOrEmpty(loaderName)) { return VRModuleKnownXRLoader.Unknown; } foreach (var profile in loaderProfiles) { if (string.IsNullOrEmpty(profile.fixedName) || profile.fixedName != loaderName) { continue; } if (profile.matchNameRgx == null || !profile.matchNameRgx.IsMatch(loaderName)) { continue; } return profile.loader; } return VRModuleKnownXRLoader.Unknown; } public static VRModuleKnownXRLoader ToKnownXRLoader(string loaderName) { foreach (var profile in loaderProfiles) { if (string.IsNullOrEmpty(profile.fixedName) || profile.fixedName != loaderName) { continue; } if (profile.matchNameRgx == null || !profile.matchNameRgx.IsMatch(loaderName)) { continue; } return profile.loader; } return VRModuleKnownXRLoader.Unknown; } public static bool TryGetActiveLoaderName(out string loaderName) { loaderName = default(string); var instance = XRGeneralSettings.Instance; if (instance == null) { return false; } var manager = instance.Manager; if (manager == null) { return false; } var loader = manager.activeLoader; if (loader == null) { return false; } loaderName = loader.name; return true; } protected void LogDeviceFeatureUsages(InputDevice device) { List usages = new List(); if (device.TryGetFeatureUsages(usages)) { string strUsages = ""; foreach (var usage in usages) { strUsages += "[" + usage.type.Name + "] " + usage.name + "\n"; } Debug.Log(device.name + " feature usages:\n\n" + strUsages); } } protected static string CharacteristicsToString(InputDeviceCharacteristics ch) { if (ch == 0u) { return " No Characteristic"; } var chu = (uint)ch; var str = string.Empty; for (var i = 1u; chu > 0u; i <<= 1) { if ((chu & i) == 0u) { continue; } str += " " + (InputDeviceCharacteristics)i; chu &= ~i; } return str; } public static VRModuleKnownXRInputSubsystem GetKnownActiveInputSubsystem() { var activeSubsys = ListPool.Get(); try { SubsystemManager.GetInstances(activeSubsys); if (activeSubsys.Count == 0) { Debug.LogWarning("No XRInputSubsystem detected."); } else { foreach (var subsys in activeSubsys) { if (!subsys.running) { continue; } var subsysName = subsys.SubsystemDescriptor.id; if (string.IsNullOrEmpty(subsysName)) { continue; } foreach (var profile in inputSubsystemProfiles) { if (!string.IsNullOrEmpty(profile.fixedName) && profile.fixedName != subsysName) { continue; } if (profile.matchNameRgx == null || !profile.matchNameRgx.IsMatch(subsysName)) { continue; } return profile.subsystem; } } } return VRModuleKnownXRInputSubsystem.Unknown; } finally { ListPool.Release(activeSubsys); } } public static InputFeatureUsage pointerPositionFeature = new InputFeatureUsage("PointerPosition"); public static InputFeatureUsage pointerRotationFeature = new InputFeatureUsage("PointerRotation"); public static InputFeatureUsage pointerVelocityFeature = new InputFeatureUsage("PointerVelocity"); public static InputFeatureUsage pointerAngularVelocityFeature = new InputFeatureUsage("PointerAngularVelocity"); #if UNITY_EDITOR public static bool GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, bool defaultValue = default(bool)) { bool value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static uint GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, uint defaultValue = default(uint)) { uint value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static float GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, float defaultValue = default(float)) { float value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static Vector2 GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Vector2 defaultValue = default(Vector2)) { Vector2 value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static Vector3 GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Vector3 defaultValue = default(Vector3)) { Vector3 value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static Vector3 GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, InputFeatureUsage fallbackFeature, Vector3 defaultValue = default(Vector3)) { Vector3 value; if (device.TryGetFeatureValue(feature, out value)) { return value; } if (device.TryGetFeatureValue(fallbackFeature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); LogWarningFeatureNotFound(device, fallbackFeature); return defaultValue; } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature) { return GetDeviceFeatureValueOrDefault(device, feature, Quaternion.identity); } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Quaternion defaultValue) { Quaternion value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, InputFeatureUsage fallbackFeature) { return GetDeviceFeatureValueOrDefault(device, feature, fallbackFeature, Quaternion.identity); } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, InputFeatureUsage fallbackFeature, Quaternion defaultValue) { Quaternion value; if (device.TryGetFeatureValue(feature, out value)) { return value; } if (device.TryGetFeatureValue(fallbackFeature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); LogWarningFeatureNotFound(device, fallbackFeature); return defaultValue; } public static UnityEngine.XR.Hand GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, UnityEngine.XR.Hand defaultValue = default(UnityEngine.XR.Hand)) { UnityEngine.XR.Hand value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static Bone GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Bone defaultValue = default(Bone)) { Bone value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } public static Eyes GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Eyes defaultValue = default(Eyes)) { Eyes value; if (device.TryGetFeatureValue(feature, out value)) { return value; } LogWarningFeatureNotFound(device, feature); return defaultValue; } private static HashSet warnedFeatures = new HashSet(); private static void LogWarningFeatureNotFound(InputDevice device, InputFeatureUsage feature) { #if CSHARP_7_OR_LATER var hashCode = (device, feature).GetHashCode(); #else var hashCode = new { device, feature }.GetHashCode(); #endif if (warnedFeatures.Add(hashCode)) { Debug.LogWarningFormat("Device {0} doesn't have {1} feature {2}.", device.name, typeof(T).Name, feature.name); } } #else public static bool GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, bool defaultValue = default(bool)) { bool value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static uint GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, uint defaultValue = default(uint)) { uint value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static float GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, float defaultValue = default(float)) { float value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static Vector2 GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Vector2 defaultValue = default(Vector2)) { Vector2 value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static Vector3 GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Vector3 defaultValue = default(Vector3)) { Vector3 value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static Vector3 GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, InputFeatureUsage fallbackFeature, Vector3 defaultValue = default(Vector3)) { Vector3 value; if (device.TryGetFeatureValue(feature, out value)) { return value; } if (device.TryGetFeatureValue(fallbackFeature, out value)) { return value; } return defaultValue; } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature) { return GetDeviceFeatureValueOrDefault(device, feature, Quaternion.identity); } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Quaternion defaultValue) { Quaternion value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, InputFeatureUsage fallbackFeature) { return GetDeviceFeatureValueOrDefault(device, feature, fallbackFeature, Quaternion.identity); } public static Quaternion GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, InputFeatureUsage fallbackFeature, Quaternion defaultValue) { Quaternion value; if (device.TryGetFeatureValue(feature, out value)) { return value; } if (device.TryGetFeatureValue(fallbackFeature, out value)) { return value; } return defaultValue; } public static Hand GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Hand defaultValue = default(Hand)) { Hand value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static Bone GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Bone defaultValue = default(Bone)) { Bone value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } public static Eyes GetDeviceFeatureValueOrDefault(InputDevice device, InputFeatureUsage feature, Eyes defaultValue = default(Eyes)) { Eyes value; if (device.TryGetFeatureValue(feature, out value)) { return value; } return defaultValue; } #endif #else public static bool HasActiveLoader() { return false; } public static bool HasActiveLoader(string loaderName) { return false; } public static bool HasActiveLoader(VRModuleKnownXRLoader knownLoader) { return false; } public static VRModuleKnownXRLoader GetKnownActiveLoader() { return VRModuleKnownXRLoader.Unknown; } public static VRModuleKnownXRLoader ToKnownXRLoader(string loaderName) { return VRModuleKnownXRLoader.Unknown; } public static bool TryGetActiveLoaderName(out string loaderName) { loaderName = default(string); return false; } public static VRModuleKnownXRInputSubsystem GetKnownActiveInputSubsystem() { return VRModuleKnownXRInputSubsystem.Unknown; } #endif } }