mixedreality/com.microsoft.mixedreality..../Providers/Oculus/XRSDK/OculusXRSDKDeviceManager.cs

380 lines
13 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using Microsoft.MixedReality.Toolkit.XRSDK.Input;
using System;
using UnityEngine;
using UnityEngine.XR;
#if OCULUS_ENABLED
using Unity.XR.Oculus;
#endif // OCULUS_ENABLED
#if OCULUSINTEGRATION_PRESENT
using System.Collections.Generic;
#endif // OCULUSINTEGRATION_PRESENT
namespace Microsoft.MixedReality.Toolkit.XRSDK.Oculus.Input
{
/// <summary>
/// Manages XR SDK devices on the Oculus platform.
/// </summary>
[MixedRealityDataProvider(
typeof(IMixedRealityInputSystem),
SupportedPlatforms.WindowsStandalone | SupportedPlatforms.Android,
"XR SDK Oculus Device Manager",
"Oculus/XRSDK/Profiles/DefaultOculusXRSDKDeviceManagerProfile.asset",
"MixedRealityToolkit.Providers",
true,
SupportedUnityXRPipelines.XRSDK)]
public class OculusXRSDKDeviceManager : XRSDKDeviceManager
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="inputSystem">The <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputSystem"/> instance that receives data from this provider.</param>
/// <param name="name">Friendly name of the service.</param>
/// <param name="priority">Service priority. Used to determine order of instantiation.</param>
/// <param name="profile">The service's configuration profile.</param>
public OculusXRSDKDeviceManager(
IMixedRealityInputSystem inputSystem,
string name = null,
uint priority = DefaultPriority,
BaseMixedRealityProfile profile = null) : base(inputSystem, name, priority, profile) { }
#if !OCULUSINTEGRATION_PRESENT && UNITY_EDITOR && UNITY_ANDROID
public override void Initialize()
{
base.Initialize();
#if !UNITY_2020_1_OR_NEWER
UnityEngine.Debug.Log(@"Detected a potential deployment issue for the Oculus Quest. In order to use hand tracking with the Oculus Quest, download the Oculus Integration Package from the Unity Asset Store and run the Integration tool before deploying.
The tool can be found under <i>Mixed Reality > Toolkit > Utilities > Oculus > Integrate Oculus Integration Unity Modules</i>");
#endif
}
#endif
#if OCULUSINTEGRATION_PRESENT
private readonly Dictionary<Handedness, OculusHand> trackedHands = new Dictionary<Handedness, OculusHand>();
private OVRCameraRig cameraRig;
internal OVRControllerHelper leftControllerHelper;
internal OVRControllerHelper rightControllerHelper;
private OVRHand rightHand;
private OVRSkeleton rightSkeleton;
private OVRHand leftHand;
private OVRSkeleton leftSkeleton;
/// <summary>
/// The profile that contains settings for the Oculus XRSDK Device Manager input data provider. This profile is nested under
/// Input > Input Data Providers > Oculus XRSDK Device Manager in the MixedRealityToolkit object in the hierarchy.
/// </summary>
private OculusXRSDKDeviceManagerProfile SettingsProfile => ConfigurationProfile as OculusXRSDKDeviceManagerProfile;
#endif
#region IMixedRealityCapabilityCheck Implementation
/// <inheritdoc />
public override bool CheckCapability(MixedRealityCapability capability)
{
#if OCULUSINTEGRATION_PRESENT
if (capability == MixedRealityCapability.ArticulatedHand)
{
return true;
}
#endif
return capability == MixedRealityCapability.MotionController;
}
#endregion IMixedRealityCapabilityCheck Implementation
#region Controller Utilities
/// <inheritdoc />
protected override Type GetControllerType(SupportedControllerType supportedControllerType)
{
switch (supportedControllerType)
{
case SupportedControllerType.ArticulatedHand:
return typeof(OculusHand);
case SupportedControllerType.OculusTouch:
return typeof(OculusXRSDKTouchController);
default:
return base.GetControllerType(supportedControllerType);
}
}
/// <inheritdoc />
protected override InputSourceType GetInputSourceType(SupportedControllerType supportedControllerType)
{
switch (supportedControllerType)
{
case SupportedControllerType.OculusTouch:
return InputSourceType.Controller;
case SupportedControllerType.ArticulatedHand:
return InputSourceType.Hand;
default:
return base.GetInputSourceType(supportedControllerType);
}
}
/// <inheritdoc />
protected override SupportedControllerType GetCurrentControllerType(InputDevice inputDevice)
{
if (inputDevice.characteristics.IsMaskSet(InputDeviceCharacteristics.HandTracking))
{
if (inputDevice.characteristics.IsMaskSet(InputDeviceCharacteristics.Left) ||
inputDevice.characteristics.IsMaskSet(InputDeviceCharacteristics.Right))
{
// If it's a hand with a reported handedness, assume articulated hand
return SupportedControllerType.ArticulatedHand;
}
}
if (inputDevice.characteristics.IsMaskSet(InputDeviceCharacteristics.Controller))
{
return SupportedControllerType.OculusTouch;
}
return base.GetCurrentControllerType(inputDevice);
}
#endregion Controller Utilities
private bool? IsActiveLoader =>
#if OCULUS_ENABLED
LoaderHelpers.IsLoaderActive<OculusLoader>();
#else
false;
#endif // OCULUS_ENABLED
/// <inheritdoc/>
public override void Enable()
{
if (!IsActiveLoader.HasValue)
{
IsEnabled = false;
EnableIfLoaderBecomesActive();
return;
}
else if (!IsActiveLoader.Value)
{
IsEnabled = false;
return;
}
#if OCULUSINTEGRATION_PRESENT
SetupInput();
ConfigurePerformancePreferences();
#endif // OCULUSINTEGRATION_PRESENT
base.Enable();
}
private async void EnableIfLoaderBecomesActive()
{
await new WaitUntil(() => IsActiveLoader.HasValue);
if (IsActiveLoader.Value)
{
Enable();
}
}
#if OCULUSINTEGRATION_PRESENT
/// <inheritdoc/>
public override void Update()
{
if (!IsEnabled)
{
return;
}
base.Update();
if (OVRPlugin.GetHandTrackingEnabled())
{
UpdateHands();
}
else
{
RemoveAllHandDevices();
}
}
private void SetupInput()
{
cameraRig = GameObject.FindObjectOfType<OVRCameraRig>();
if (cameraRig == null)
{
var mainCamera = CameraCache.Main;
// Instantiate camera rig as a child of the MixedRealityPlayspace
var cameraRigObject = GameObject.Instantiate(SettingsProfile.OVRCameraRigPrefab);
cameraRig = cameraRigObject.GetComponent<OVRCameraRig>();
// Ensure all related game objects are configured
cameraRig.EnsureGameObjectIntegrity();
if (mainCamera != null)
{
// We already had a main camera MRTK probably started using, let's replace the CenterEyeAnchor MainCamera with it
GameObject prefabMainCamera = cameraRig.trackingSpace.Find("CenterEyeAnchor").gameObject;
prefabMainCamera.SetActive(false);
mainCamera.transform.SetParent(cameraRig.trackingSpace.transform);
mainCamera.name = prefabMainCamera.name;
GameObject.Destroy(prefabMainCamera);
}
cameraRig.transform.SetParent(MixedRealityPlayspace.Transform);
}
else
{
// Ensure all related game objects are configured
cameraRig.EnsureGameObjectIntegrity();
}
bool useAvatarHands = SettingsProfile.RenderAvatarHandsWithControllers;
// If using Avatar hands, initialize the local avatar controller
if (useAvatarHands)
{
GameObject.Instantiate(SettingsProfile.LocalAvatarPrefab, cameraRig.trackingSpace);
}
var ovrControllerHelpers = cameraRig.GetComponentsInChildren<OVRControllerHelper>();
foreach (var ovrControllerHelper in ovrControllerHelpers)
{
switch (ovrControllerHelper.m_controller)
{
case OVRInput.Controller.LTouch:
leftControllerHelper = ovrControllerHelper;
break;
case OVRInput.Controller.RTouch:
rightControllerHelper = ovrControllerHelper;
break;
default:
break;
}
}
var ovrHands = cameraRig.GetComponentsInChildren<OVRHand>();
foreach (var ovrHand in ovrHands)
{
// Manage Hand skeleton data
var skeletonDataProvider = ovrHand as OVRSkeleton.IOVRSkeletonDataProvider;
var skeletonType = skeletonDataProvider.GetSkeletonType();
var ovrSkeleton = ovrHand.GetComponent<OVRSkeleton>();
if (ovrSkeleton == null)
{
continue;
}
switch (skeletonType)
{
case OVRSkeleton.SkeletonType.HandLeft:
leftHand = ovrHand;
leftSkeleton = ovrSkeleton;
break;
case OVRSkeleton.SkeletonType.HandRight:
rightHand = ovrHand;
rightSkeleton = ovrSkeleton;
break;
}
}
}
private void ConfigurePerformancePreferences()
{
SettingsProfile.ApplyConfiguredPerformanceSettings();
}
#region Hand Utilities
protected void UpdateHands()
{
UpdateHand(rightHand, rightSkeleton, Handedness.Right);
UpdateHand(leftHand, leftSkeleton, Handedness.Left);
}
protected void UpdateHand(OVRHand ovrHand, OVRSkeleton ovrSkeleton, Handedness handedness)
{
if (ovrHand.IsTracked)
{
var hand = GetOrAddHand(handedness, ovrHand);
hand.UpdateController(ovrHand, ovrSkeleton, cameraRig.trackingSpace);
}
else
{
RemoveHandDevice(handedness);
}
}
private OculusHand GetOrAddHand(Handedness handedness, OVRHand ovrHand)
{
if (trackedHands.ContainsKey(handedness))
{
return trackedHands[handedness];
}
// Add new hand
var pointers = RequestPointers(SupportedControllerType.ArticulatedHand, handedness);
var inputSourceType = InputSourceType.Hand;
var inputSource = Service?.RequestNewGenericInputSource($"Oculus Quest {handedness} Hand", pointers, inputSourceType);
OculusHand handDevice = new OculusHand(TrackingState.Tracked, handedness, inputSource);
handDevice.InitializeHand(ovrHand, SettingsProfile);
for (int i = 0; i < handDevice.InputSource?.Pointers?.Length; i++)
{
handDevice.InputSource.Pointers[i].Controller = handDevice;
}
Service?.RaiseSourceDetected(handDevice.InputSource, handDevice);
trackedHands.Add(handedness, handDevice);
return handDevice;
}
private void RemoveHandDevice(Handedness handedness)
{
if (trackedHands.TryGetValue(handedness, out OculusHand hand))
{
RemoveHandDevice(hand);
}
}
private void RemoveAllHandDevices()
{
if (trackedHands.Count == 0) return;
// Create a new list to avoid causing an error removing items from a list currently being iterated on.
foreach (var hand in new List<OculusHand>(trackedHands.Values))
{
RemoveHandDevice(hand);
}
trackedHands.Clear();
}
private void RemoveHandDevice(OculusHand handDevice)
{
if (handDevice == null) return;
CoreServices.InputSystem?.RaiseSourceLost(handDevice.InputSource, handDevice);
trackedHands.Remove(handDevice.ControllerHandedness);
RecyclePointers(handDevice.InputSource);
}
#endregion
#endif // OCULUSINTEGRATION_PRESENT
}
}