TaiZhouCangChu_VRanime/Assets/HTC.UnityPlugin/VRModule/Modules/UnityXRModuleBase.cs

454 lines
19 KiB
C#

//========= Copyright 2016-2023, HTC Corporation. All rights reserved. ===========
#pragma warning disable 0649
using HTC.UnityPlugin.Utility;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#endif
#if VIU_XR_GENERAL_SETTINGS
using UnityEngine.XR.Management;
#endif
namespace HTC.UnityPlugin.VRModuleManagement
{
public enum VRModuleKnownXRLoader
{
Unknown,
OpenVR,
Oculus,
WindowsXR,
MagicLeap,
WaveXR,
OpenXR,
}
public enum VRModuleKnownXRInputSubsystem
{
Unknown,
OpenVR,
Oculus,
WindowsXR,
MagicLeap,
WaveXR,
OpenXR,
}
public abstract partial class UnityXRModuleBase : VRModule.ModuleBase
{
#if UNITY_2019_3_OR_NEWER && VIU_XR_GENERAL_SETTINGS
private struct XRLoaderProfile
{
public VRModuleKnownXRLoader loader;
public Regex matchNameRgx;
public string fixedName;
}
private struct XRInputSubsystemProfile
{
public VRModuleKnownXRInputSubsystem subsystem;
public Regex matchNameRgx;
public string fixedName;
}
private static List<XRLoaderProfile> loaderProfiles = new List<XRLoaderProfile>()
{
new XRLoaderProfile() { loader = VRModuleKnownXRLoader.OpenVR, matchNameRgx = new Regex("openvr", REGEX_OPTIONS) },
new XRLoaderProfile() { loader = VRModuleKnownXRLoader.Oculus, matchNameRgx = new Regex("oculus", REGEX_OPTIONS) },
new XRLoaderProfile() { loader = VRModuleKnownXRLoader.WindowsXR, matchNameRgx = new Regex("windows", REGEX_OPTIONS) },
new XRLoaderProfile() { loader = VRModuleKnownXRLoader.MagicLeap, matchNameRgx = new Regex("magicleap", REGEX_OPTIONS) },
new XRLoaderProfile() { loader = VRModuleKnownXRLoader.WaveXR, matchNameRgx = new Regex("wave", REGEX_OPTIONS) },
new XRLoaderProfile() { loader = VRModuleKnownXRLoader.OpenXR, matchNameRgx = new Regex("open xr", REGEX_OPTIONS) },
};
private static List<XRInputSubsystemProfile> inputSubsystemProfiles = new List<XRInputSubsystemProfile>()
{
new XRInputSubsystemProfile() { subsystem = VRModuleKnownXRInputSubsystem.OpenVR, matchNameRgx = new Regex("openvr", REGEX_OPTIONS) },
new XRInputSubsystemProfile() { subsystem = VRModuleKnownXRInputSubsystem.Oculus, matchNameRgx = new Regex("oculus", REGEX_OPTIONS) },
new XRInputSubsystemProfile() { subsystem = VRModuleKnownXRInputSubsystem.WindowsXR, matchNameRgx = new Regex("windows", REGEX_OPTIONS) },
new XRInputSubsystemProfile() { subsystem = VRModuleKnownXRInputSubsystem.MagicLeap, matchNameRgx = new Regex("magicleap", REGEX_OPTIONS) },
new XRInputSubsystemProfile() { subsystem = VRModuleKnownXRInputSubsystem.WaveXR, matchNameRgx = new Regex("wave", REGEX_OPTIONS) },
new XRInputSubsystemProfile() { subsystem = VRModuleKnownXRInputSubsystem.OpenXR, matchNameRgx = new Regex("openxr", REGEX_OPTIONS) },
};
private VRModuleKnownXRLoader knownActiveLoader;
private VRModuleKnownXRInputSubsystem knownActiveInputSubsystem;
private IndexMap indexMap = new IndexMap();
private uint uxrRightIndex = INVALID_DEVICE_INDEX;
private uint uxrLeftIndex = INVALID_DEVICE_INDEX;
private uint moduleRightIndex = INVALID_DEVICE_INDEX;
private uint moduleLeftIndex = INVALID_DEVICE_INDEX;
private VRModule.SubmoduleBase.Collection submodules = new VRModule.SubmoduleBase.Collection(
new ViveHandTrackingSubmodule(),
new WaveHandTrackingSubmodule(),
new WaveTrackerSubmodule()
);
private bool[] prevDeviceConnected = new bool[VRModule.MAX_DEVICE_COUNT];
private bool[] currDeviceConnected = new bool[VRModule.MAX_DEVICE_COUNT];
private void FlushDeviceConnectedState()
{
var temp = prevDeviceConnected;
prevDeviceConnected = currDeviceConnected;
currDeviceConnected = temp;
Array.Clear(currDeviceConnected, 0, (int)VRModule.MAX_DEVICE_COUNT);
}
protected VRModuleKnownXRLoader KnownActiveLoader { get { return knownActiveLoader; } }
protected VRModuleKnownXRInputSubsystem KnownActiveInputSubsystem { get { return knownActiveInputSubsystem; } }
public override void OnActivated()
{
knownActiveLoader = GetKnownActiveLoader();
knownActiveInputSubsystem = GetKnownActiveInputSubsystem();
EnsureDeviceStateLength(8);
UpdateTrackingSpaceType();
submodules.ActivateAllModules();
Debug.Log("Activated XRLoader Name: " + XRGeneralSettings.Instance.Manager.activeLoader.name);
}
public override void OnDeactivated()
{
submodules.DeactivateAllModules();
indexMap.Clear();
}
public override void UpdateTrackingSpaceType()
{
TrackingOriginModeFlags originFlag;
switch (VRModule.trackingSpaceType)
{
case VRModuleTrackingSpaceType.Stationary: originFlag = TrackingOriginModeFlags.Device; break;
case VRModuleTrackingSpaceType.RoomScale: originFlag = TrackingOriginModeFlags.Floor; break;
default: return;
}
SetAllXRInputSubsystemTrackingOriginMode(originFlag);
}
private List<InputDevice> connectedDevices = new List<InputDevice>();
public sealed override void BeforeRenderUpdate()
{
if (knownActiveInputSubsystem == VRModuleKnownXRInputSubsystem.Unknown)
{
knownActiveInputSubsystem = GetKnownActiveInputSubsystem();
}
// update device connection and poses
IVRModuleDeviceState prevState;
IVRModuleDeviceStateRW currState;
uint deviceIndex;
FlushDeviceState();
InputDevices.GetDevices(connectedDevices);
foreach (var device in connectedDevices)
{
if (!indexMap.TryGetIndex(device, out deviceIndex))
{
string deviceName;
if (indexMap.TryMapAsHMD(device))
{
deviceIndex = VRModule.HMD_DEVICE_INDEX;
EnsureValidDeviceState(deviceIndex, out prevState, out currState);
deviceName = device.name;
}
else
{
// this function will skip VRModule.HMD_DEVICE_INDEX (preserved index for HMD)
deviceIndex = FindAndEnsureUnusedNotHMDDeviceState(out prevState, out currState);
indexMap.MapNonHMD(device, deviceIndex);
deviceName = device.name;
}
currState.deviceClass = GetDeviceClass(device.name, device.characteristics);
currState.serialNumber = deviceName + " " + device.serialNumber + " " + (int)device.characteristics;
currState.modelNumber = deviceName + " (" + device.characteristics + ")";
currState.renderModelName = deviceName + " (" + device.characteristics + ")";
SetupKnownDeviceModel(currState);
Debug.LogFormat("Device connected: {0} / {1} / {2} / {3} / {4} / {5} ({6})",
currState.deviceIndex,
currState.deviceClass,
currState.deviceModel,
currState.modelNumber,
currState.serialNumber,
device.name,
device.characteristics);
if ((device.characteristics & InputDeviceCharacteristics.Right) > 0u)
{
uxrRightIndex = deviceIndex;
}
else if ((device.characteristics & InputDeviceCharacteristics.Left) > 0u)
{
uxrLeftIndex = deviceIndex;
}
UpdateNewConnectedInputDevice(currState, device);
}
else
{
EnsureValidDeviceState(deviceIndex, out prevState, out currState);
}
currState.isConnected = true;
// update device Poses
currState.isPoseValid = GetDeviceFeatureValueOrDefault(device, CommonUsages.isTracked);
if (Vive.VIUSettings.preferUnityXRPointerPose)
{
currState.position = GetDeviceFeatureValueOrDefault(device, pointerPositionFeature, CommonUsages.devicePosition);
currState.rotation = GetDeviceFeatureValueOrDefault(device, pointerRotationFeature, CommonUsages.deviceRotation);
currState.velocity = GetDeviceFeatureValueOrDefault(device, pointerVelocityFeature, CommonUsages.deviceVelocity);
currState.angularVelocity = GetDeviceFeatureValueOrDefault(device, pointerAngularVelocityFeature, CommonUsages.deviceAngularVelocity);
}
else
{
currState.position = GetDeviceFeatureValueOrDefault(device, CommonUsages.devicePosition);
currState.rotation = GetDeviceFeatureValueOrDefault(device, CommonUsages.deviceRotation);
currState.velocity = GetDeviceFeatureValueOrDefault(device, CommonUsages.deviceVelocity);
currState.angularVelocity = GetDeviceFeatureValueOrDefault(device, CommonUsages.deviceAngularVelocity);
}
currDeviceConnected[deviceIndex] = true;
// TODO: update hand skeleton pose
}
// unmap index for disconnected device state
for (uint i = 0u, imax = VRModule.MAX_DEVICE_COUNT; i < imax; ++i)
{
if (prevDeviceConnected[i] && !currDeviceConnected[i])
{
if (indexMap.IsMapped(i))
{
indexMap.UnmapByIndex(i);
}
else
{
Debug.LogWarning("[UnityXRModule] Disconnected device[" + i + "] already unmapped");
}
if (TryGetValidDeviceState(i, out prevState, out currState) && currState.isConnected)
{
currState.Reset();
if (uxrRightIndex == i) { uxrRightIndex = INVALID_DEVICE_INDEX; }
if (uxrLeftIndex == i) { uxrLeftIndex = INVALID_DEVICE_INDEX; }
}
else
{
Debug.LogWarning("[UnityXRModule] Disconnected device[" + i + "] already been reset");
}
}
}
FlushDeviceConnectedState();
submodules.UpdateModulesDeviceConnectionAndPoses();
// process hand role
var subRightIndex = submodules.GetFirstRightHandedIndex();
var currentRight = (subRightIndex == INVALID_DEVICE_INDEX || (TryGetValidDeviceState(uxrRightIndex, out prevState, out currState) && currState.isPoseValid && currState.deviceClass == VRModuleDeviceClass.Controller)) ? uxrRightIndex : subRightIndex;
var subLeftIndex = submodules.GetFirstLeftHandedIndex();
var currentLeft = (subLeftIndex == INVALID_DEVICE_INDEX || (TryGetValidDeviceState(uxrLeftIndex, out prevState, out currState) && currState.isPoseValid && currState.deviceClass == VRModuleDeviceClass.Controller)) ? uxrLeftIndex : subLeftIndex;
var roleChanged = ChangeProp.Set(ref moduleRightIndex, currentRight);
roleChanged |= ChangeProp.Set(ref moduleLeftIndex, currentLeft);
if (roleChanged)
{
InvokeControllerRoleChangedEvent();
}
ProcessConnectedDeviceChanged();
ProcessDevicePoseChanged();
}
public override void Update()
{
UpdateLockPhysicsUpdateRate();
for (uint deviceIndex = 0u, len = GetDeviceStateLength(); deviceIndex < len; ++deviceIndex)
{
InputDevice device;
if (indexMap.TryGetDevice(deviceIndex, out device))
{
if ((device.characteristics & InputDeviceCharacteristics.Controller) > 0
|| (device.characteristics & InputDeviceCharacteristics.TrackedDevice) > 0)
{
IVRModuleDeviceState prevState;
IVRModuleDeviceStateRW currState;
EnsureValidDeviceState(deviceIndex, out prevState, out currState);
UpdateInputDevicesControllerState(currState, device);
}
}
}
submodules.UpdateAllModulesActivity();
submodules.UpdateModulesDeviceInput();
UpdateHapticVibration();
ProcessDeviceInputChanged();
}
public override uint GetRightControllerDeviceIndex() { return moduleRightIndex; }
public override uint GetLeftControllerDeviceIndex() { return moduleLeftIndex; }
protected virtual void UpdateNewConnectedInputDevice(IVRModuleDeviceStateRW state, InputDevice device) { }
protected virtual void UpdateInputDevicesControllerState(IVRModuleDeviceStateRW state, InputDevice device) { }
protected static VRModuleDeviceClass GetDeviceClass(string name, InputDeviceCharacteristics characteristics)
{
if ((characteristics & InputDeviceCharacteristics.HeadMounted) != 0)
{
return VRModuleDeviceClass.HMD;
}
if ((characteristics & InputDeviceCharacteristics.Controller) != 0)
{
return VRModuleDeviceClass.Controller;
}
if ((characteristics & InputDeviceCharacteristics.TrackingReference) != 0)
{
return VRModuleDeviceClass.TrackingReference;
}
if ((characteristics & InputDeviceCharacteristics.TrackedDevice) != 0)
{
return VRModuleDeviceClass.GenericTracker;
}
return VRModuleDeviceClass.Invalid;
}
private static void SetAllXRInputSubsystemTrackingOriginMode(TrackingOriginModeFlags value)
{
var activeSubsys = ListPool<XRInputSubsystem>.Get();
try
{
SubsystemManager.GetInstances(activeSubsys);
foreach (var subsys in activeSubsys)
{
if (!subsys.running) { continue; }
if (!subsys.TrySetTrackingOriginMode(value))
{
Debug.LogWarning("Failed to set TrackingOriginModeFlags(" + value + ") to XRInputSubsystem: " + subsys.SubsystemDescriptor.id);
}
}
}
finally { ListPool<XRInputSubsystem>.Release(activeSubsys); }
}
private struct HapticState
{
public float amplitude;
public float startTime;
public float endTime;
public HapticState(float amp, float startTime, float endTime)
{
this.amplitude = amp;
this.endTime = endTime;
this.startTime = startTime;
}
}
private uint maxHapticStateIndex = 0u;
private HapticState[] hapticStates = new HapticState[VRModule.MAX_DEVICE_COUNT];
public override void TriggerViveControllerHaptic(uint deviceIndex, ushort durationMicroSec = 500)
{
TriggerHapticVibration(deviceIndex, durationMicroSec * 1000000f);
}
// NOTE: Frequency not supported
public override void TriggerHapticVibration(uint deviceIndex, float durationSeconds = 0.01f, float frequency = 85.0f, float amplitude = 0.125f, float startSecondsFromNow = 0.0f)
{
InputDevice device;
if (indexMap.TryGetDevice(deviceIndex, out device))
{
HapticCapabilities capabilities;
if (device.TryGetHapticCapabilities(out capabilities))
{
if (capabilities.supportsImpulse)
{
var now = Time.unscaledTime;
hapticStates[deviceIndex] = new HapticState()
{
amplitude = amplitude,
startTime = now + startSecondsFromNow,
endTime = now + startSecondsFromNow + durationSeconds,
};
if (deviceIndex > maxHapticStateIndex) { maxHapticStateIndex = deviceIndex; }
}
}
}
}
protected void UpdateHapticVibration()
{
if (maxHapticStateIndex > 0u)
{
var now = Time.unscaledTime;
var newMaxIndex = 0u;
for (uint i = 0, imax = maxHapticStateIndex; i <= imax; ++i)
{
InputDevice device;
if (now <= hapticStates[i].endTime)
{
if (now >= hapticStates[i].startTime && indexMap.TryGetDevice(i, out device))
{
device.SendHapticImpulse(0u, hapticStates[i].amplitude);
}
if (i > newMaxIndex) { newMaxIndex = i; }
}
}
maxHapticStateIndex = newMaxIndex;
}
}
protected void UpdateLockPhysicsUpdateRate()
{
if (VRModule.lockPhysicsUpdateRateToRenderFrequency && Time.timeScale > 0.0f)
{
var displaySystems = ListPool<XRDisplaySubsystem>.Get();
try
{
SubsystemManager.GetInstances(displaySystems);
var minRefreshRate = float.MaxValue;
foreach (XRDisplaySubsystem system in displaySystems)
{
float rate = 60.0f;
if (system.TryGetDisplayRefreshRate(out rate))
{
if (rate < minRefreshRate)
{
minRefreshRate = rate;
}
}
}
if (minRefreshRate > 0 && minRefreshRate < float.MaxValue)
{
Time.fixedDeltaTime = 1.0f / minRefreshRate;
}
}
finally { ListPool<XRDisplaySubsystem>.Release(displaySystems); }
}
}
#endif
}
}