Skip to content

Commit 3fcb4ee

Browse files
authored
Merge pull request #9721 from keveleigh/openxr-controller-model
Add support for OpenXR controller model
2 parents a191589 + 81d3af0 commit 3fcb4ee

15 files changed

+391
-79
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.MixedReality.Toolkit.Utilities;
5+
using System;
6+
using UnityEngine;
7+
8+
namespace Microsoft.MixedReality.Toolkit.Input
9+
{
10+
/// <summary>
11+
/// Provides helpers for setting up the controller model with a visualization script.
12+
/// </summary>
13+
public static class MixedRealityControllerModelHelpers
14+
{
15+
private static MixedRealityControllerVisualizationProfile visualizationProfile = null;
16+
17+
/// <summary>
18+
/// Tries to read the controller visualization profile to apply a visualization script to the passed-in controller model.
19+
/// </summary>
20+
/// <remarks>Automatically disables DestroyOnSourceLost to encourage controller model creators to manage their life-cycle themselves.</remarks>
21+
/// <param name="controllerModel">The GameObject to modify.</param>
22+
/// <param name="controllerType">The type of controller this model represents.</param>
23+
/// <param name="handedness">The handedness of this controller.</param>
24+
/// <returns>True if a visualization script could be loaded and applied.</returns>
25+
public static bool TryAddVisualizationScript(GameObject controllerModel, Type controllerType, Handedness handedness)
26+
{
27+
if (controllerModel != null)
28+
{
29+
if (visualizationProfile == null && CoreServices.InputSystem?.InputSystemProfile != null)
30+
{
31+
visualizationProfile = CoreServices.InputSystem.InputSystemProfile.ControllerVisualizationProfile;
32+
}
33+
34+
if (visualizationProfile != null)
35+
{
36+
var visualizationType = visualizationProfile.GetControllerVisualizationTypeOverride(controllerType, handedness);
37+
if (visualizationType != null)
38+
{
39+
// Set the platform controller model to not be destroyed when the source is lost. It'll be disabled instead,
40+
// and re-enabled when the same controller is re-detected.
41+
if (controllerModel.EnsureComponent(visualizationType.Type) is IMixedRealityControllerPoseSynchronizer visualizer)
42+
{
43+
visualizer.DestroyOnSourceLost = false;
44+
}
45+
46+
return true;
47+
}
48+
else
49+
{
50+
Debug.LogError("Controller visualization type not defined for controller visualization profile");
51+
}
52+
}
53+
else
54+
{
55+
Debug.LogError("Failed to obtain a controller visualization profile");
56+
}
57+
}
58+
59+
return false;
60+
}
61+
}
62+
}

Assets/MRTK/Core/Definitions/Devices/MixedRealityControllerModelHelpers.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/MRTK/Providers/OpenXR/MRTK.OpenXR.asmdef

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"references": [
55
"Microsoft.MixedReality.OpenXR",
66
"Microsoft.MixedReality.Toolkit",
7+
"Microsoft.MixedReality.Toolkit.Gltf",
78
"Microsoft.MixedReality.Toolkit.Providers.XRSDK",
89
"Unity.XR.Management",
910
"Unity.XR.OpenXR"
@@ -28,6 +29,11 @@
2829
"expression": "",
2930
"define": "MSFT_OPENXR"
3031
},
32+
{
33+
"name": "com.microsoft.mixedreality.openxr",
34+
"expression": "0.9.4",
35+
"define": "MSFT_OPENXR_0_9_4_OR_NEWER"
36+
},
3137
{
3238
"name": "com.microsoft.mixedreality.openxr",
3339
"expression": "0.2.0",

Assets/MRTK/Providers/OpenXR/Profiles/ObsoleteOpenXRControllerVisualizationProfile.asset

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,28 @@ MonoBehaviour:
2626
type: 3}
2727
globalLeftHandVisualizer: {fileID: 1227372834072826, guid: 378f91dbbff9e2343b27a467467750bf,
2828
type: 3}
29-
globalRightHandVisualizer: {fileID: 1788309537287834, guid: 132c9402d5843aa4f94380840186fd41,
29+
globalRightHandVisualizer: {fileID: 1227372834072826, guid: 378f91dbbff9e2343b27a467467750bf,
3030
type: 3}
31-
controllerVisualizationSettings: []
31+
controllerVisualizationSettings:
32+
- description: Microsoft Motion Controller
33+
controllerType:
34+
reference: Microsoft.MixedReality.Toolkit.XRSDK.OpenXR.MicrosoftMotionController,
35+
Microsoft.MixedReality.Toolkit.Providers.OpenXR
36+
handedness: 3
37+
usePlatformModels: 1
38+
platformModelMaterial: {fileID: 0}
39+
overrideModel: {fileID: 0}
40+
controllerVisualizationType:
41+
reference: Microsoft.MixedReality.Toolkit.Input.WindowsMixedRealityControllerVisualizer,
42+
Microsoft.MixedReality.Toolkit.SDK
43+
- description: HP Reverb G2 Controller
44+
controllerType:
45+
reference: Microsoft.MixedReality.Toolkit.XRSDK.OpenXR.HPReverbG2Controller,
46+
Microsoft.MixedReality.Toolkit.Providers.OpenXR
47+
handedness: 3
48+
usePlatformModels: 1
49+
platformModelMaterial: {fileID: 0}
50+
overrideModel: {fileID: 0}
51+
controllerVisualizationType:
52+
reference: Microsoft.MixedReality.Toolkit.Input.WindowsMixedRealityControllerVisualizer,
53+
Microsoft.MixedReality.Toolkit.SDK

Assets/MRTK/Providers/OpenXR/Scripts/HPReverbG2Controller.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public class HPReverbG2Controller : GenericXRSDKController
2020
/// </summary>
2121
public HPReverbG2Controller(TrackingState trackingState, Handedness controllerHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null)
2222
: base(trackingState, controllerHandedness, inputSource, interactions, new HPMotionControllerDefinition(controllerHandedness))
23-
{
24-
}
23+
{ }
2524

2625
private Vector3 currentPointerPosition = Vector3.zero;
2726
private Quaternion currentPointerRotation = Quaternion.identity;
@@ -67,5 +66,54 @@ protected override void UpdatePoseData(MixedRealityInteractionMapping interactio
6766
}
6867
}
6968
}
69+
70+
#if MSFT_OPENXR_0_9_4_OR_NEWER
71+
private MicrosoftControllerModelProvider controllerModelProvider;
72+
73+
/// <inheritdoc />
74+
protected override bool TryRenderControllerModel(System.Type controllerType, InputSourceType inputSourceType)
75+
{
76+
if (GetControllerVisualizationProfile() == null ||
77+
!GetControllerVisualizationProfile().GetUsePlatformModelsOverride(GetType(), ControllerHandedness))
78+
{
79+
return base.TryRenderControllerModel(controllerType, inputSourceType);
80+
}
81+
else
82+
{
83+
TryRenderControllerModelWithModelProvider();
84+
return true;
85+
}
86+
}
87+
88+
private async void TryRenderControllerModelWithModelProvider()
89+
{
90+
if (controllerModelProvider == null)
91+
{
92+
controllerModelProvider = new MicrosoftControllerModelProvider(ControllerHandedness);
93+
}
94+
95+
GameObject controllerModel = await controllerModelProvider.TryGenerateControllerModelFromPlatformSDK();
96+
97+
if (this != null)
98+
{
99+
if (controllerModel != null
100+
&& MixedRealityControllerModelHelpers.TryAddVisualizationScript(controllerModel, GetType(), ControllerHandedness)
101+
&& TryAddControllerModelToSceneHierarchy(controllerModel))
102+
{
103+
controllerModel.SetActive(true);
104+
return;
105+
}
106+
107+
Debug.LogWarning("Failed to create controller model from driver; defaulting to BaseController behavior.");
108+
base.TryRenderControllerModel(GetType(), InputSource.SourceType);
109+
}
110+
111+
if (controllerModel != null)
112+
{
113+
// If we didn't successfully set up the model and add it to the hierarchy (which returns early), set it inactive.
114+
controllerModel.SetActive(false);
115+
}
116+
}
117+
#endif // MSFT_OPENXR_0_9_4_OR_NEWER
70118
}
71119
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Threading.Tasks;
5+
using UnityEngine;
6+
7+
#if MSFT_OPENXR_0_9_4_OR_NEWER
8+
using Microsoft.MixedReality.OpenXR;
9+
using Microsoft.MixedReality.Toolkit.Utilities.Gltf.Serialization;
10+
using System.Collections.Generic;
11+
#endif // MSFT_OPENXR_0_9_4_OR_NEWER
12+
13+
namespace Microsoft.MixedReality.Toolkit.XRSDK.OpenXR
14+
{
15+
/// <summary>
16+
/// Queries the OpenXR APIs for a renderable controller model.
17+
/// </summary>
18+
internal class MicrosoftControllerModelProvider
19+
{
20+
public MicrosoftControllerModelProvider(Utilities.Handedness handedness)
21+
{
22+
#if MSFT_OPENXR_0_9_4_OR_NEWER
23+
controllerModelProvider = handedness == Utilities.Handedness.Left ? ControllerModel.Left : ControllerModel.Right;
24+
#endif // MSFT_OPENXR_0_9_4_OR_NEWER
25+
}
26+
27+
#if MSFT_OPENXR_0_9_4_OR_NEWER
28+
private static readonly Dictionary<ulong, GameObject> ControllerModelDictionary = new Dictionary<ulong, GameObject>(2);
29+
private readonly ControllerModel controllerModelProvider;
30+
#endif // MSFT_OPENXR_0_9_4_OR_NEWER
31+
32+
// Disables "This async method lacks 'await' operators and will run synchronously." when the correct OpenXR package isn't installed
33+
#pragma warning disable CS1998
34+
/// <summary>
35+
/// Attempts to load the glTF controller model from OpenXR.
36+
/// </summary>
37+
/// <returns>The controller model as a GameObject or null if it was unobtainable.</returns>
38+
public async Task<GameObject> TryGenerateControllerModelFromPlatformSDK()
39+
{
40+
GameObject gltfGameObject = null;
41+
42+
#if MSFT_OPENXR_0_9_4_OR_NEWER
43+
if (!controllerModelProvider.TryGetControllerModelKey(out ulong modelKey))
44+
{
45+
Debug.LogError("Failed to obtain controller model key from platform.");
46+
return null;
47+
}
48+
49+
if (ControllerModelDictionary.TryGetValue(modelKey, out gltfGameObject))
50+
{
51+
gltfGameObject.SetActive(true);
52+
return gltfGameObject;
53+
}
54+
55+
byte[] modelStream = await controllerModelProvider.TryGetControllerModel(modelKey);
56+
57+
if (modelStream == null || modelStream.Length == 0)
58+
{
59+
Debug.LogError("Failed to obtain controller model from platform.");
60+
return null;
61+
}
62+
63+
Utilities.Gltf.Schema.GltfObject gltfObject = GltfUtility.GetGltfObjectFromGlb(modelStream);
64+
gltfGameObject = await gltfObject.ConstructAsync();
65+
66+
if (gltfGameObject != null)
67+
{
68+
// After all the awaits, double check that another task didn't finish earlier
69+
if (ControllerModelDictionary.TryGetValue(modelKey, out GameObject existingGameObject))
70+
{
71+
Object.Destroy(gltfGameObject);
72+
return existingGameObject;
73+
}
74+
else
75+
{
76+
ControllerModelDictionary.Add(modelKey, gltfGameObject);
77+
}
78+
}
79+
#endif // MSFT_OPENXR_0_9_4_OR_NEWER
80+
81+
return gltfGameObject;
82+
}
83+
#pragma warning restore CS1998
84+
}
85+
}

Assets/MRTK/Providers/OpenXR/Scripts/MicrosoftControllerModelProvider.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/MRTK/Providers/OpenXR/Scripts/MicrosoftMotionController.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ public class MicrosoftMotionController : GenericXRSDKController
2424
/// </summary>
2525
public MicrosoftMotionController(TrackingState trackingState, Handedness controllerHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null)
2626
: base(trackingState, controllerHandedness, inputSource, interactions, new WindowsMixedRealityControllerDefinition(controllerHandedness))
27-
{
28-
}
27+
{ }
2928

3029
private Vector3 currentPointerPosition = Vector3.zero;
3130
private Quaternion currentPointerRotation = Quaternion.identity;
@@ -71,5 +70,54 @@ protected override void UpdatePoseData(MixedRealityInteractionMapping interactio
7170
}
7271
}
7372
}
73+
74+
#if MSFT_OPENXR_0_9_4_OR_NEWER
75+
private MicrosoftControllerModelProvider controllerModelProvider;
76+
77+
/// <inheritdoc />
78+
protected override bool TryRenderControllerModel(System.Type controllerType, InputSourceType inputSourceType)
79+
{
80+
if (GetControllerVisualizationProfile() == null ||
81+
!GetControllerVisualizationProfile().GetUsePlatformModelsOverride(GetType(), ControllerHandedness))
82+
{
83+
return base.TryRenderControllerModel(controllerType, inputSourceType);
84+
}
85+
else
86+
{
87+
TryRenderControllerModelWithModelProvider();
88+
return true;
89+
}
90+
}
91+
92+
private async void TryRenderControllerModelWithModelProvider()
93+
{
94+
if (controllerModelProvider == null)
95+
{
96+
controllerModelProvider = new MicrosoftControllerModelProvider(ControllerHandedness);
97+
}
98+
99+
GameObject controllerModel = await controllerModelProvider.TryGenerateControllerModelFromPlatformSDK();
100+
101+
if (this != null)
102+
{
103+
if (controllerModel != null
104+
&& MixedRealityControllerModelHelpers.TryAddVisualizationScript(controllerModel, GetType(), ControllerHandedness)
105+
&& TryAddControllerModelToSceneHierarchy(controllerModel))
106+
{
107+
controllerModel.SetActive(true);
108+
return;
109+
}
110+
111+
Debug.LogWarning("Failed to create controller model from driver; defaulting to BaseController behavior.");
112+
base.TryRenderControllerModel(GetType(), InputSource.SourceType);
113+
}
114+
115+
if (controllerModel != null)
116+
{
117+
// If we didn't successfully set up the model and add it to the hierarchy (which returns early), set it inactive.
118+
controllerModel.SetActive(false);
119+
}
120+
}
121+
#endif // MSFT_OPENXR_0_9_4_OR_NEWER
74122
}
75123
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
[assembly: InternalsVisibleTo("Microsoft.MixedReality.Toolkit.Providers.WindowsMixedReality")]
7+
[assembly: InternalsVisibleTo("Microsoft.MixedReality.Toolkit.Providers.XRSDK.WindowsMixedReality")]

Assets/MRTK/Providers/WindowsMixedReality/Shared/AssemblyInfo.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)