Skip to content

Backport hands subsystem project validation rules from XRI3 branch to main #991

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using MixedReality.Toolkit.Subsystems;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;

namespace MixedReality.Toolkit
{
Expand Down
14 changes: 3 additions & 11 deletions org.mixedrealitytoolkit.core/Editor/MRTKProjectValidation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// Licensed under the BSD 3-Clause

using MixedReality.Toolkit.Editor;
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.XR.CoreUtils.Editor;
using UnityEditor;
using UnityEngine;
Expand All @@ -16,12 +14,6 @@ namespace MixedReality.Toolkit
/// </summary>
public static class MRTKProjectValidation
{
#pragma warning disable 618
private static readonly BuildTargetGroup[] excludedBuildTargetGroups = new BuildTargetGroup[]
// Need to cast int back to BuildTargetGroup because BuildTargetGroup.WebPlayer is marked as obsolete and treated as an error
{ (BuildTargetGroup)2, BuildTargetGroup.PS3, BuildTargetGroup.XBOX360, BuildTargetGroup.WP8, BuildTargetGroup.BlackBerry, BuildTargetGroup.Tizen, BuildTargetGroup.PSP2,
BuildTargetGroup.PSM, BuildTargetGroup.SamsungTV, BuildTargetGroup.N3DS, BuildTargetGroup.WiiU, BuildTargetGroup.Facebook, BuildTargetGroup.Switch };
#pragma warning restore 618
private const string XRProjectValidationSettingsPath = "Project/XR Plug-in Management/Project Validation";
private const string DefaultMRTKProfileGuid = "c677e5c4eb85b7849a8da406775c299d";
private static readonly Dictionary<BuildTargetGroup, List<BuildValidationRule>> validationRulesDictionary = new Dictionary<BuildTargetGroup, List<BuildValidationRule>>();
Expand All @@ -32,7 +24,7 @@ public static class MRTKProjectValidation
/// <remarks>
/// Build targets currently not supported by MRTK will be filtered out.
/// </remarks>
public static readonly BuildTargetGroup[] BuildTargetGroups = ((BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup))).Distinct().Except(excludedBuildTargetGroups).ToArray();
public static readonly BuildTargetGroup[] BuildTargetGroups = { BuildTargetGroup.Standalone, BuildTargetGroup.Android, BuildTargetGroup.WSA };

[MenuItem("Mixed Reality/MRTK3/Utilities/Project Validation", priority = 0)]
private static void MenuItem()
Expand All @@ -50,7 +42,7 @@ private static void MRTKProjectValidationCheck()

private static void AddMRTKValidationRules()
{
foreach (var buildTargetGroup in validationRulesDictionary.Keys)
foreach (BuildTargetGroup buildTargetGroup in validationRulesDictionary.Keys)
{
BuildValidator.AddRules(buildTargetGroup, validationRulesDictionary[buildTargetGroup]);
}
Expand All @@ -70,7 +62,7 @@ private static void AddMRTKCoreValidationRules()
AddTargetIndependentRules(mrtkCoreTargetIndependentRules);

// Add target-specific rules
foreach (var buildTargetGroup in BuildTargetGroups)
foreach (BuildTargetGroup buildTargetGroup in BuildTargetGroups)
{
// Skip the standalone target as the profile rule for it is already present for all build targets
if (buildTargetGroup != BuildTargetGroup.Standalone)
Expand Down
1 change: 1 addition & 0 deletions org.mixedrealitytoolkit.input/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Added

* Added a project validation rule to ensure the Unity XR Hands subsystem is enabled in the OpenXR settings when the corresponding MRTK subsystem is enabled. [PR #973](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/973)
* Added support for Unity's com.unity.cloud.gltfast and com.unity.cloud.ktx packages when loading controller models. [PR #631](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/631)

### Fixed
Expand Down
100 changes: 97 additions & 3 deletions org.mixedrealitytoolkit.input/Editor/InputValidation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@
using UnityEditor.PackageManager;
using UnityEngine;

#if UNITY_OPENXR_PRESENT
using MixedReality.Toolkit.Editor;
using UnityEngine.XR.Hands.OpenXR;
using UnityEngine.XR.OpenXR;
#endif

namespace MixedReality.Toolkit.Input.Editor
{
/// <summary>
/// A class adding input related rule(s) to the validator
/// A class adding input related rule(s) to the validator.
/// </summary>
internal static class InputValidation
{
Expand All @@ -22,11 +28,27 @@ private static void AddInputValidationRule()
foreach (var buildTargetGroup in MRTKProjectValidation.BuildTargetGroups)
{
MRTKProjectValidation.AddTargetDependentRules(new List<BuildValidationRule>() { GenerateSpeechInteractorRule(buildTargetGroup) }, buildTargetGroup);

#if UNITY_OPENXR_PRESENT
// Skip the standalone target as the hand subsystem rule for it is already present for all build targets
if (buildTargetGroup != BuildTargetGroup.Standalone)
{
MRTKProjectValidation.AddTargetDependentRules(new List<BuildValidationRule>() { GenerateUnityHandsRule(buildTargetGroup) }, buildTargetGroup);
}
#endif
}
MRTKProjectValidation.AddTargetIndependentRules(new List<BuildValidationRule>() { GenerateSkinWeightsRule(), GenerateGLTFastRule() });
MRTKProjectValidation.AddTargetIndependentRules(new List<BuildValidationRule>() { GenerateSkinWeightsRule(), GenerateGLTFastRule(),
#if UNITY_OPENXR_PRESENT
GenerateUnityHandsRule(BuildTargetGroup.Standalone),
#endif
});

// Only generate the KTX rule for platforms related to Meta
MRTKProjectValidation.AddTargetDependentRules(new List<BuildValidationRule>() { GenerateKTXRule() }, BuildTargetGroup.Android);
MRTKProjectValidation.AddTargetDependentRules(new List<BuildValidationRule>() { GenerateKTXRule(),
#if UNITY_OPENXR_PRESENT
GenerateAndroidHandsRule(),
#endif
}, BuildTargetGroup.Android);
MRTKProjectValidation.AddTargetDependentRules(new List<BuildValidationRule>() { GenerateKTXRule() }, BuildTargetGroup.Standalone);
}

Expand Down Expand Up @@ -104,5 +126,77 @@ private static BuildValidationRule GenerateKTXRule()
Error = false
};
}

#if UNITY_OPENXR_PRESENT
private static BuildValidationRule GenerateUnityHandsRule(BuildTargetGroup buildTargetGroup)
{
return new BuildValidationRule()
{
IsRuleEnabled = () => MRTKProjectValidation.GetLoadedSubsystemsForBuildTarget(buildTargetGroup)?.Contains(typeof(UnityHandsSubsystem)) ?? false,
Category = "MRTK3",
Message = $"When {nameof(UnityHandsSubsystem)} is enabled for the {buildTargetGroup} build target, " +
$"{nameof(HandTracking)} must also be enabled in the OpenXR settings for {buildTargetGroup}.",
CheckPredicate = () =>
{
OpenXRSettings settings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup);
if (settings == null)
{
return false;
}

HandTracking handFeature = settings.GetFeature<HandTracking>();
return handFeature != null && handFeature.enabled;
},
FixIt = () =>
{
OpenXRSettings settings = OpenXRSettings.GetSettingsForBuildTargetGroup(buildTargetGroup);
if (settings == null)
{
return;
}

HandTracking handFeature = settings.GetFeature<HandTracking>();
if (handFeature != null)
{
handFeature.enabled = true;
EditorUtility.SetDirty(settings);
}
},
FixItMessage = $"Enable {nameof(HandTracking)} in the OpenXR settings.",
Error = true
};
}

private static BuildValidationRule GenerateAndroidHandsRule()
{
// Disable this warning because this rule's purpose is to help migrate away from the obsolete type
#pragma warning disable CS0618 // Type or member is obsolete
return new BuildValidationRule()
{
Category = "MRTK3",
Message = "Hand tracking on Android with the Mixed Reality OpenXR Plugin has been deprecated. " +
$"Please turn off {nameof(OpenXRHandsSubsystem)} in the MRTK profile and use {nameof(UnityHandsSubsystem)} instead.",
CheckPredicate = () => !MRTKProjectValidation.GetLoadedSubsystemsForBuildTarget(BuildTargetGroup.Android)?.Contains(typeof(OpenXRHandsSubsystem)) ?? true,
FixIt = () =>
{
MRTKProfile profile = MRTKSettings.ProfileForBuildTarget(BuildTargetGroup.Android);
if (profile == null)
{
return;
}

profile.LoadedSubsystems.Remove(typeof(OpenXRHandsSubsystem));
if (!profile.LoadedSubsystems.Contains(typeof(UnityHandsSubsystem)))
{
profile.LoadedSubsystems.Add(typeof(UnityHandsSubsystem));
}
EditorUtility.SetDirty(profile);
},
FixItMessage = $"Turn off {nameof(OpenXRHandsSubsystem)} in the MRTK profile and ensure {typeof(UnityHandsSubsystem)} is enabled instead.",
Error = false
};
#pragma warning restore CS0618 // Type or member is obsolete
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
"MixedReality.Toolkit.Input",
"Unity.XR.CoreUtils",
"Unity.XR.CoreUtils.Editor",
"Unity.XR.Hands",
"Unity.XR.Interaction.Toolkit",
"Unity.XR.Interaction.Toolkit.Editor",
"Unity.XR.Management"
"Unity.XR.Management",
"Unity.XR.OpenXR"
],
"includePlatforms": [
"Editor"
Expand Down Expand Up @@ -45,6 +47,11 @@
"name": "com.unity.xr.management",
"expression": "4.2",
"define": "UNITYXR_MANAGEMENT_PRESENT"
},
{
"name": "com.unity.xr.openxr",
"expression": "",
"define": "UNITY_OPENXR_PRESENT"
}
],
"noEngineReferences": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ namespace MixedReality.Toolkit.Input
SubsystemTypeOverride = typeof(OpenXRHandsSubsystem),
ConfigType = typeof(BaseSubsystemConfig))]
#endif // MROPENXR_PRESENT
#if UNITY_ANDROID
[System.Obsolete("Hand tracking on Android with the Mixed Reality OpenXR Plugin has been deprecated. Please use " + nameof(UnityHandsSubsystem) + " instead.")]
#endif
public class OpenXRHandsSubsystem : HandsSubsystem
{
#if MROPENXR_PRESENT && (UNITY_EDITOR_WIN || UNITY_WSA || UNITY_STANDALONE_WIN || UNITY_ANDROID)
Expand Down