Skip to content

Commit 32a4c3f

Browse files
authored
Merge pull request #4707 from keveleigh/wmr-controller-base
Fix runtime controller model loading and fix WMRController inheritance
2 parents ffb4004 + dce7f22 commit 32a4c3f

File tree

6 files changed

+452
-407
lines changed

6 files changed

+452
-407
lines changed
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using Microsoft.MixedReality.Toolkit.Input;
5+
using Microsoft.MixedReality.Toolkit.Utilities;
6+
7+
#if UNITY_WSA
8+
using UnityEngine;
9+
using UnityEngine.XR.WSA.Input;
10+
#endif
11+
12+
namespace Microsoft.MixedReality.Toolkit.WindowsMixedReality.Input
13+
{
14+
/// <summary>
15+
/// A Windows Mixed Reality Source Instance.
16+
/// </summary>
17+
public abstract class BaseWindowsMixedRealitySource : BaseController
18+
{
19+
/// <summary>
20+
/// Constructor.
21+
/// </summary>
22+
/// <param name="trackingState"></param>
23+
/// <param name="sourceHandedness"></param>
24+
/// <param name="inputSource"></param>
25+
/// <param name="interactions"></param>
26+
public BaseWindowsMixedRealitySource(TrackingState trackingState, Handedness sourceHandedness, IMixedRealityInputSource inputSource = null, MixedRealityInteractionMapping[] interactions = null)
27+
: base(trackingState, sourceHandedness, inputSource, interactions)
28+
{
29+
}
30+
31+
/// <inheritdoc />
32+
public override MixedRealityInteractionMapping[] DefaultLeftHandedInteractions => DefaultInteractions;
33+
34+
/// <inheritdoc />
35+
public override MixedRealityInteractionMapping[] DefaultRightHandedInteractions => DefaultInteractions;
36+
37+
/// <inheritdoc />
38+
public override void SetupDefaultInteractions(Handedness controllerHandedness)
39+
{
40+
AssignControllerMappings(DefaultInteractions);
41+
}
42+
43+
#if UNITY_WSA
44+
45+
/// <summary>
46+
/// The last updated source state reading for this Windows Mixed Reality Source.
47+
/// </summary>
48+
public InteractionSourceState LastSourceStateReading { get; protected set; }
49+
50+
private Vector3 currentSourcePosition = Vector3.zero;
51+
private Quaternion currentSourceRotation = Quaternion.identity;
52+
private MixedRealityPose lastSourcePose = MixedRealityPose.ZeroIdentity;
53+
private MixedRealityPose currentSourcePose = MixedRealityPose.ZeroIdentity;
54+
55+
private Vector3 currentPointerPosition = Vector3.zero;
56+
private Quaternion currentPointerRotation = Quaternion.identity;
57+
private MixedRealityPose currentPointerPose = MixedRealityPose.ZeroIdentity;
58+
59+
private Vector3 currentGripPosition = Vector3.zero;
60+
private Quaternion currentGripRotation = Quaternion.identity;
61+
private MixedRealityPose currentGripPose = MixedRealityPose.ZeroIdentity;
62+
63+
#region Update data functions
64+
65+
/// <summary>
66+
/// Update the source data from the provided platform state.
67+
/// </summary>
68+
/// <param name="interactionSourceState">The InteractionSourceState retrieved from the platform.</param>
69+
public virtual void UpdateController(InteractionSourceState interactionSourceState)
70+
{
71+
if (!Enabled) { return; }
72+
73+
UpdateSourceData(interactionSourceState);
74+
75+
if (Interactions == null)
76+
{
77+
Debug.LogError($"No interaction configuration for Windows Mixed Reality {ControllerHandedness} Source");
78+
Enabled = false;
79+
}
80+
81+
for (int i = 0; i < Interactions?.Length; i++)
82+
{
83+
switch (Interactions[i].InputType)
84+
{
85+
case DeviceInputType.None:
86+
break;
87+
case DeviceInputType.SpatialPointer:
88+
UpdatePointerData(interactionSourceState, Interactions[i]);
89+
break;
90+
case DeviceInputType.Select:
91+
case DeviceInputType.Trigger:
92+
case DeviceInputType.TriggerTouch:
93+
case DeviceInputType.TriggerPress:
94+
UpdateTriggerData(interactionSourceState, Interactions[i]);
95+
break;
96+
case DeviceInputType.SpatialGrip:
97+
UpdateGripData(interactionSourceState, Interactions[i]);
98+
break;
99+
}
100+
}
101+
102+
LastSourceStateReading = interactionSourceState;
103+
}
104+
105+
/// <summary>
106+
/// Update the source input from the device.
107+
/// </summary>
108+
/// <param name="interactionSourceState">The InteractionSourceState retrieved from the platform.</param>
109+
private void UpdateSourceData(InteractionSourceState interactionSourceState)
110+
{
111+
var lastState = TrackingState;
112+
var sourceKind = interactionSourceState.source.kind;
113+
114+
lastSourcePose = currentSourcePose;
115+
116+
if (sourceKind == InteractionSourceKind.Hand ||
117+
(sourceKind == InteractionSourceKind.Controller && interactionSourceState.source.supportsPointing))
118+
{
119+
// The source is either a hand or a controller that supports pointing.
120+
// We can now check for position and rotation.
121+
IsPositionAvailable = interactionSourceState.sourcePose.TryGetPosition(out currentSourcePosition);
122+
123+
if (IsPositionAvailable)
124+
{
125+
IsPositionApproximate = (interactionSourceState.sourcePose.positionAccuracy == InteractionSourcePositionAccuracy.Approximate);
126+
}
127+
else
128+
{
129+
IsPositionApproximate = false;
130+
}
131+
132+
IsRotationAvailable = interactionSourceState.sourcePose.TryGetRotation(out currentSourceRotation);
133+
134+
// Devices are considered tracked if we receive position OR rotation data from the sensors.
135+
TrackingState = (IsPositionAvailable || IsRotationAvailable) ? TrackingState.Tracked : TrackingState.NotTracked;
136+
}
137+
else
138+
{
139+
// The input source does not support tracking.
140+
TrackingState = TrackingState.NotApplicable;
141+
}
142+
143+
currentSourcePose.Position = currentSourcePosition;
144+
currentSourcePose.Rotation = currentSourceRotation;
145+
146+
// Raise input system events if it is enabled.
147+
if (lastState != TrackingState)
148+
{
149+
InputSystem?.RaiseSourceTrackingStateChanged(InputSource, this, TrackingState);
150+
}
151+
152+
if (TrackingState == TrackingState.Tracked && lastSourcePose != currentSourcePose)
153+
{
154+
if (IsPositionAvailable && IsRotationAvailable)
155+
{
156+
InputSystem?.RaiseSourcePoseChanged(InputSource, this, currentSourcePose);
157+
}
158+
else if (IsPositionAvailable && !IsRotationAvailable)
159+
{
160+
InputSystem?.RaiseSourcePositionChanged(InputSource, this, currentSourcePosition);
161+
}
162+
else if (!IsPositionAvailable && IsRotationAvailable)
163+
{
164+
InputSystem?.RaiseSourceRotationChanged(InputSource, this, currentSourceRotation);
165+
}
166+
}
167+
}
168+
169+
/// <summary>
170+
/// Update the spatial pointer input from the device.
171+
/// </summary>
172+
/// <param name="interactionSourceState">The InteractionSourceState retrieved from the platform.</param>
173+
/// <param name="interactionMapping"></param>
174+
private void UpdatePointerData(InteractionSourceState interactionSourceState, MixedRealityInteractionMapping interactionMapping)
175+
{
176+
if (interactionSourceState.source.supportsPointing)
177+
{
178+
interactionSourceState.sourcePose.TryGetPosition(out currentPointerPosition, InteractionSourceNode.Pointer);
179+
interactionSourceState.sourcePose.TryGetRotation(out currentPointerRotation, InteractionSourceNode.Pointer);
180+
181+
// We want the source to follow the Playspace, so fold in the playspace transform here to
182+
// put the source pose into world space.
183+
currentPointerPose.Position = MixedRealityPlayspace.TransformPoint(currentPointerPosition);
184+
currentPointerPose.Rotation = MixedRealityPlayspace.Rotation * currentPointerRotation;
185+
}
186+
187+
// Update the interaction data source
188+
interactionMapping.PoseData = currentPointerPose;
189+
190+
// If our value changed raise it.
191+
if (interactionMapping.Changed)
192+
{
193+
// Raise input system Event if it enabled
194+
InputSystem?.RaisePoseInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, currentPointerPose);
195+
}
196+
}
197+
198+
/// <summary>
199+
/// Update the spatial grip input from the device.
200+
/// </summary>
201+
/// <param name="interactionSourceState">The InteractionSourceState retrieved from the platform.</param>
202+
/// <param name="interactionMapping"></param>
203+
private void UpdateGripData(InteractionSourceState interactionSourceState, MixedRealityInteractionMapping interactionMapping)
204+
{
205+
switch (interactionMapping.AxisType)
206+
{
207+
case AxisType.SixDof:
208+
{
209+
interactionSourceState.sourcePose.TryGetPosition(out currentGripPosition, InteractionSourceNode.Grip);
210+
interactionSourceState.sourcePose.TryGetRotation(out currentGripRotation, InteractionSourceNode.Grip);
211+
212+
currentGripPose.Position = MixedRealityPlayspace.TransformPoint(currentGripPosition);
213+
currentGripPose.Rotation = Quaternion.Euler(MixedRealityPlayspace.TransformDirection(currentGripRotation.eulerAngles));
214+
215+
// Update the interaction data source
216+
interactionMapping.PoseData = currentGripPose;
217+
218+
// If our value changed raise it.
219+
if (interactionMapping.Changed)
220+
{
221+
// Raise input system Event if it enabled
222+
InputSystem?.RaisePoseInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, currentGripPose);
223+
}
224+
}
225+
break;
226+
}
227+
}
228+
229+
/// <summary>
230+
/// Update the trigger and grasped input from the device.
231+
/// </summary>
232+
/// <param name="interactionSourceState">The InteractionSourceState retrieved from the platform.</param>
233+
/// <param name="interactionMapping"></param>
234+
private void UpdateTriggerData(InteractionSourceState interactionSourceState, MixedRealityInteractionMapping interactionMapping)
235+
{
236+
switch (interactionMapping.InputType)
237+
{
238+
case DeviceInputType.TriggerPress:
239+
{
240+
// Update the interaction data source
241+
interactionMapping.BoolData = interactionSourceState.grasped;
242+
243+
// If our value changed raise it.
244+
if (interactionMapping.Changed)
245+
{
246+
// Raise input system Event if it enabled
247+
if (interactionMapping.BoolData)
248+
{
249+
InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
250+
}
251+
else
252+
{
253+
InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
254+
}
255+
}
256+
break;
257+
}
258+
case DeviceInputType.Select:
259+
{
260+
// Get the select pressed state, factoring in a workaround for Unity issue #1033526.
261+
// When that issue is fixed, it should be possible change the line below to:
262+
// interactionMapping.BoolData = interactionSourceState.selectPressed;
263+
interactionMapping.BoolData = GetSelectPressedWorkaround(interactionSourceState);
264+
265+
// If our value changed raise it.
266+
if (interactionMapping.Changed)
267+
{
268+
// Raise input system Event if it enabled
269+
if (interactionMapping.BoolData)
270+
{
271+
InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
272+
}
273+
else
274+
{
275+
InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
276+
}
277+
}
278+
break;
279+
}
280+
case DeviceInputType.Trigger:
281+
{
282+
// Update the interaction data source
283+
interactionMapping.FloatData = interactionSourceState.selectPressedAmount;
284+
285+
// If our value changed raise it.
286+
if (interactionMapping.Changed)
287+
{
288+
// Raise input system Event if it enabled
289+
InputSystem?.RaiseFloatInputChanged(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction, interactionSourceState.selectPressedAmount);
290+
}
291+
break;
292+
}
293+
case DeviceInputType.TriggerTouch:
294+
{
295+
// Update the interaction data source
296+
interactionMapping.BoolData = interactionSourceState.selectPressedAmount > 0;
297+
298+
// If our value changed raise it.
299+
if (interactionMapping.Changed)
300+
{
301+
// Raise input system Event if it enabled
302+
if (interactionSourceState.selectPressedAmount > 0)
303+
{
304+
InputSystem?.RaiseOnInputDown(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
305+
}
306+
else
307+
{
308+
InputSystem?.RaiseOnInputUp(InputSource, ControllerHandedness, interactionMapping.MixedRealityInputAction);
309+
}
310+
}
311+
break;
312+
}
313+
}
314+
}
315+
316+
/// <summary>
317+
/// Gets whether or not 'select' has been pressed.
318+
/// </summary>
319+
/// <remarks>
320+
/// This includes a workaround to fix air-tap gestures in HoloLens 1 remoting, to work around the following Unity issue:
321+
/// https://issuetracker.unity3d.com/issues/hololens-interactionsourcestate-dot-selectpressed-is-false-when-air-tap-and-hold
322+
/// Bug was discovered May 2018 and still exists as of May 2019 in version 2018.3.11f1. This workaround is scoped to only
323+
/// cases where remoting is active.
324+
/// </remarks>
325+
private bool GetSelectPressedWorkaround(InteractionSourceState interactionSourceState)
326+
{
327+
bool selectPressed = interactionSourceState.selectPressed;
328+
if (interactionSourceState.source.kind == InteractionSourceKind.Hand &&
329+
UnityEngine.XR.WSA.HolographicRemoting.ConnectionState == UnityEngine.XR.WSA.HolographicStreamerConnectionState.Connected)
330+
{
331+
// This workaround is safe as long as all these assumptions hold:
332+
Debug.Assert(!interactionSourceState.selectPressed, "Unity issue #1033526 seems to have been resolved. Please remove this workaround!");
333+
Debug.Assert(!interactionSourceState.source.supportsGrasp);
334+
Debug.Assert(!interactionSourceState.source.supportsMenu);
335+
Debug.Assert(!interactionSourceState.source.supportsPointing);
336+
Debug.Assert(!interactionSourceState.source.supportsThumbstick);
337+
Debug.Assert(!interactionSourceState.source.supportsTouchpad);
338+
339+
selectPressed = interactionSourceState.anyPressed;
340+
}
341+
return selectPressed;
342+
}
343+
344+
#endregion Update data functions
345+
346+
#endif // UNITY_WSA
347+
}
348+
}

Assets/MixedRealityToolkit.Providers/WindowsMixedReality/BaseWindowsMixedRealitySource.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)