AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
CustomInputSource.cs
Go to the documentation of this file.
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 System;
5 using UnityEngine;
6 using Random = UnityEngine.Random;
7 
8 namespace HoloToolkit.Unity.InputModule
9 {
13  [RequireComponent(typeof(CustomInputControl))]
15  {
16  // TODO: add thumbstick, touchpad, and trigger axis support.
17  [Serializable]
18  private class ButtonStates
19  {
20  public ButtonStates()
21  {
22  IsSelectButtonDown = false;
23  SelectButtonStateChanged = false;
24 
25  IsMenuButtonDown = false;
26  MenuButtonStateChanged = false;
27 
28  IsGrasped = false;
29  GraspStateChanged = false;
30 
31  ManipulationInProgress = false;
32  HoldInProgress = false;
33  CumulativeDelta = Vector3.zero;
34  }
35 
36  public bool IsSelectButtonDown;
37  public bool SelectButtonStateChanged;
38  public float SelectDownStartTime;
39 
40  public bool IsMenuButtonDown;
41  public bool MenuButtonStateChanged;
42 
43  public bool IsGrasped;
44  public bool GraspStateChanged;
45 
46  public bool ManipulationInProgress;
47  public bool HoldInProgress;
48  public Vector3 CumulativeDelta;
49  public Vector3 CumulativeGripDelta;
50  }
51 
52  [Tooltip("This property now represents Pointer position (contrast with Grip position)")]
53  public bool SupportsPosition;
54  [Tooltip("This property now represents Pointer rotation (contrast with Grip rotation)")]
55  public bool SupportsRotation;
56  public bool SupportsGripPosition;
57  public bool SupportsGripRotation;
58  public bool SupportsRay;
59  public bool SupportsMenuButton;
60  public bool SupportsGrasp;
63 
64  [Tooltip("This property now represents controller's Pointer position (contrast with controller Grip position)")]
65  public Vector3 ControllerPosition;
66  [Tooltip("This property now represents controller's Pointer rotation (contrast with controller Grip rotation)")]
67  public Quaternion ControllerRotation;
68 
69  //Navigation Gesture Emulation vars
70  Vector3 NavigatorValues = Vector3.zero; //holds the navigation gesture values [-1,1]
71  Vector2 railUsedCurrently = Vector2.one;
72  bool isNavigatorUsingRails = false;
73 
74  public Vector3 ControllerGripPosition;
75  public Quaternion ControllerGripRotation;
76 
77  public Ray? PointingRay;
78 
79  [SerializeField]
80  private ButtonStates currentButtonStates;
81 
82  private uint controllerId;
83 
84  private CustomInputControl manualController;
85 
86  private bool currentlyVisible;
87  private bool visibilityChanged;
88 
92  private const float MaxClickDuration = 0.5f;
93 
94  [SerializeField]
95  [Tooltip("The total amount of input source movement that needs to happen to signal intent to start a manipulation. This is a distance, but not a distance in any one direction.")]
96  private float manipulationStartMovementThreshold = 0.03f;
97 
98  public override SupportedInputInfo GetSupportedInputInfo(uint sourceId)
99  {
100  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
101 
102  var supportedInputInfo = SupportedInputInfo.None;
103 
104  if (SupportsPosition)
105  {
106  supportedInputInfo |= SupportedInputInfo.PointerPosition;
107  }
108 
109  if (SupportsRotation)
110  {
111  supportedInputInfo |= SupportedInputInfo.PointerRotation;
112  }
113 
114  if (SupportsRay)
115  {
116  supportedInputInfo |= SupportedInputInfo.Pointing;
117  }
118 
119  if (SupportsGripPosition)
120  {
121  supportedInputInfo |= SupportedInputInfo.GripPosition;
122  }
123 
124  if (SupportsGripRotation)
125  {
126  supportedInputInfo |= SupportedInputInfo.GripRotation;
127  }
128 
129  if (SupportsMenuButton)
130  {
131  supportedInputInfo |= SupportedInputInfo.Menu;
132  }
133 
134  if (SupportsGrasp)
135  {
136  supportedInputInfo |= SupportedInputInfo.Grasp;
137  }
138 
139  return supportedInputInfo;
140  }
141 
142  public override bool TryGetSourceKind(uint sourceId, out InteractionSourceInfo sourceKind)
143  {
144  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
145 
146  sourceKind = SourceKind;
147  return true;
148  }
149 
150  public override bool TryGetPointerPosition(uint sourceId, out Vector3 position)
151  {
152  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
153 
154  if (SupportsPosition)
155  {
156  position = ControllerPosition;
157  return true;
158  }
159 
160  position = Vector3.zero;
161  return false;
162  }
163 
164  public override bool TryGetPointerRotation(uint sourceId, out Quaternion rotation)
165  {
166  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
167 
168  if (SupportsRotation)
169  {
170  rotation = ControllerRotation;
171  return true;
172  }
173 
174  rotation = Quaternion.identity;
175  return false;
176  }
177 
178  public override bool TryGetPointingRay(uint sourceId, out Ray pointingRay)
179  {
180  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
181 
182  if (SupportsRay && (PointingRay != null))
183  {
184  pointingRay = (Ray)PointingRay;
185  return true;
186  }
187 
188  pointingRay = default(Ray);
189  return false;
190  }
191 
192  public override bool TryGetGripPosition(uint sourceId, out Vector3 position)
193  {
194  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
195 
196  if (SupportsGripPosition)
197  {
198  position = ControllerGripPosition;
199  return true;
200  }
201 
202  position = Vector3.zero;
203  return false;
204  }
205 
206  public override bool TryGetGripRotation(uint sourceId, out Quaternion rotation)
207  {
208  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
209 
210  if (SupportsGripRotation)
211  {
212  rotation = ControllerGripRotation;
213  return true;
214  }
215 
216  rotation = Quaternion.identity;
217  return false;
218  }
219 
220  public override bool TryGetThumbstick(uint sourceId, out bool isPressed, out Vector2 position)
221  {
222  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
223 
224  isPressed = false;
225  position = Vector2.zero;
226  return false;
227  }
228 
229  public override bool TryGetTouchpad(uint sourceId, out bool isPressed, out bool isTouched, out Vector2 position)
230  {
231  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
232 
233  isPressed = false;
234  isTouched = false;
235  position = Vector2.zero;
236  return false;
237  }
238 
239  public override bool TryGetSelect(uint sourceId, out bool isPressed, out double pressedAmount)
240  {
241  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
242 
243  isPressed = false;
244  pressedAmount = 0;
245  return false;
246  }
247 
248  public override bool TryGetGrasp(uint sourceId, out bool isPressed)
249  {
250  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
251 
252  if (SupportsGrasp)
253  {
254  isPressed = currentButtonStates.IsGrasped;
255  return true;
256  }
257 
258  isPressed = false;
259  return false;
260  }
261 
262  public override bool TryGetMenu(uint sourceId, out bool isPressed)
263  {
264  Debug.Assert(sourceId == controllerId, "Controller data requested for a mismatched source ID.");
265 
266  if (SupportsMenuButton)
267  {
268  isPressed = currentButtonStates.IsMenuButtonDown;
269  return true;
270  }
271 
272  isPressed = false;
273  return false;
274  }
275 
276  private void Awake()
277  {
278  if (!Application.isEditor)
279  {
280  Destroy(gameObject);
281  return;
282  }
283 
284  manualController = GetComponent<CustomInputControl>();
285 
286  currentButtonStates = new ButtonStates();
287  currentlyVisible = false;
288  visibilityChanged = false;
289  controllerId = (uint)Random.value;
290 
291  InteractionInputSource inputSource = FindObjectOfType<InteractionInputSource>();
292  if (inputSource != null)
293  {
294  isNavigatorUsingRails = inputSource.UseRailsNavigation;
295  }
296  }
297 
298  private void Update()
299  {
300  if (!Application.isEditor) { return; }
301 
302  UpdateControllerData();
303  SendControllerVisibilityEvents();
304  }
305 
306  private void OnEnable()
307  {
308  if (!Application.isEditor) { return; }
309 
310  ConnectController();
311  }
312 
313  private void OnDisable()
314  {
315  if (!Application.isEditor) { return; }
316 
317  DisconnectController();
318  }
319 
320  private void ConnectController()
321  {
322  if (!RaiseEventsBasedOnVisibility)
323  {
324  InputManager.Instance.RaiseSourceDetected(this, controllerId);
325  }
326  }
327 
328  private void DisconnectController()
329  {
330  if (!RaiseEventsBasedOnVisibility)
331  {
332  InputManager.Instance.RaiseSourceLost(this, controllerId);
333  }
334  }
335 
339  private void UpdateControllerData()
340  {
341  bool doUpdateState = !RaiseEventsBasedOnVisibility;
342 
343  if (manualController.ControllerInView)
344  {
345  if (!currentlyVisible)
346  {
347  visibilityChanged = true;
348  }
349 
350  currentlyVisible = true;
351  doUpdateState = true;
352  }
353  else
354  {
355  if (currentlyVisible)
356  {
357  visibilityChanged = true;
358  }
359 
360  currentlyVisible = false;
361  }
362 
363  if (doUpdateState)
364  {
365  UpdateControllerState(manualController.ControllerSourceState);
366  }
367  }
368 
373  private void UpdateControllerState(DebugInteractionSourceState source)
374  {
375  float time = manualController.UseUnscaledTime ? Time.unscaledTime : Time.time;
376 
377  currentButtonStates.SelectButtonStateChanged = (currentButtonStates.IsSelectButtonDown != source.SelectPressed);
378  currentButtonStates.IsSelectButtonDown = source.SelectPressed;
379 
380  if (currentButtonStates.SelectButtonStateChanged && source.SelectPressed)
381  {
382  currentButtonStates.SelectDownStartTime = time;
383  currentButtonStates.CumulativeDelta = Vector3.zero;
384  }
385 
386  if (SupportsPosition)
387  {
388  Vector3 controllerPosition;
389  if (source.SourcePose.TryGetPosition(out controllerPosition))
390  {
391  currentButtonStates.CumulativeDelta += controllerPosition - ControllerPosition;
392  ControllerPosition = controllerPosition;
393  }
394  }
395 
396  if (SupportsRotation)
397  {
398  Quaternion controllerRotation;
399  if (source.SourcePose.TryGetRotation(out controllerRotation))
400  {
401  ControllerRotation = controllerRotation;
402  }
403  }
404 
405  if (SupportsRay)
406  {
407  PointingRay = source.SourcePose.PointerRay;
408  }
409 
410  if (SupportsGripPosition)
411  {
412  Vector3 controllerGripPosition;
413  if (source.SourcePose.TryGetGripPosition(out controllerGripPosition))
414  {
415  currentButtonStates.CumulativeGripDelta += controllerGripPosition - ControllerGripPosition;
416  ControllerGripPosition = controllerGripPosition;
417  }
418  }
419 
420  if (SupportsGripRotation)
421  {
422  Quaternion controllerGripRotation;
423  if (source.SourcePose.TryGetGripRotation(out controllerGripRotation))
424  {
425  ControllerGripRotation = controllerGripRotation;
426  }
427  }
428 
429  if (SupportsMenuButton)
430  {
431  currentButtonStates.MenuButtonStateChanged = (currentButtonStates.IsMenuButtonDown != source.MenuPressed);
432  currentButtonStates.IsMenuButtonDown = source.MenuPressed;
433  }
434 
435  if (SupportsGrasp)
436  {
437  currentButtonStates.GraspStateChanged = (currentButtonStates.IsGrasped != source.Grasped);
438  currentButtonStates.IsGrasped = source.Grasped;
439  }
440 
441  SendControllerStateEvents(time);
442  }
443 
447  private void SendControllerStateEvents(float time)
448  {
449  // TODO: Send other new input manager events relating to source updates.
450  if (currentButtonStates.SelectButtonStateChanged)
451  {
452  if (currentButtonStates.IsSelectButtonDown)
453  {
454  InputManager.Instance.RaiseSourceDown(this, controllerId, InteractionSourcePressInfo.Select);
455  }
456  // New up presses require sending different events depending on whether it's also a click, hold, or manipulation.
457  else
458  {
459  // A gesture is always either a click, a hold or a manipulation.
460  if (currentButtonStates.ManipulationInProgress)
461  {
462  InputManager.Instance.RaiseManipulationCompleted(this, controllerId, currentButtonStates.CumulativeDelta);
463  currentButtonStates.ManipulationInProgress = false;
464 
465  //Navigation Gesture Emulation
466  InputManager.Instance.RaiseNavigationCompleted(this, controllerId, NavigatorValues);
467  NavigatorValues = Vector3.zero;
468  railUsedCurrently = Vector2.one;
469  }
470  // Clicks and holds are based on time, and both are overruled by manipulations.
471  else if (currentButtonStates.HoldInProgress)
472  {
473  InputManager.Instance.RaiseHoldCompleted(this, controllerId);
474  currentButtonStates.HoldInProgress = false;
475  }
476  else
477  {
478  // We currently only support single taps in editor.
479  InputManager.Instance.RaiseInputClicked(this, controllerId, InteractionSourcePressInfo.Select, 1);
480  }
481 
482  InputManager.Instance.RaiseSourceUp(this, controllerId, InteractionSourcePressInfo.Select);
483  }
484  }
485  // If the select state hasn't changed, but it's down, that means it might
486  // trigger a hold or a manipulation (or a hold and then a manipulation).
487  else if (currentButtonStates.IsSelectButtonDown)
488  {
489  if (!currentButtonStates.ManipulationInProgress)
490  {
491  // Manipulations are triggered by the amount of movement since select was pressed down.
492  if (currentButtonStates.CumulativeDelta.magnitude > manipulationStartMovementThreshold)
493  {
494  // Starting a manipulation will cancel an existing hold.
495  if (currentButtonStates.HoldInProgress)
496  {
497  InputManager.Instance.RaiseHoldCanceled(this, controllerId);
498  currentButtonStates.HoldInProgress = false;
499  }
500 
501  InputManager.Instance.RaiseManipulationStarted(this, controllerId);
502  currentButtonStates.ManipulationInProgress = true;
503 
504  //Navigation Gesture Emulation
505  InputManager.Instance.RaiseNavigationStarted(this, controllerId);
506  NavigatorValues = Vector3.zero;
507  if (isNavigatorUsingRails)
508  {
509  railUsedCurrently = (currentButtonStates.CumulativeDelta.x >= manipulationStartMovementThreshold) ? new Vector2(1, 0) : new Vector2(0, 1);
510  }
511  }
512  // Holds are triggered by time.
513  else if (!currentButtonStates.HoldInProgress && (time - currentButtonStates.SelectDownStartTime >= MaxClickDuration))
514  {
515  InputManager.Instance.RaiseHoldStarted(this, controllerId);
516  currentButtonStates.HoldInProgress = true;
517  }
518  }
519  else
520  {
521  InputManager.Instance.RaiseManipulationUpdated(this, controllerId, currentButtonStates.CumulativeDelta);
522 
523  //Navigation Gesture Emulation
524  NavigatorValues.x = Mathf.Clamp(currentButtonStates.CumulativeDelta.x*5, -1.0f, 1.0f) * railUsedCurrently.x;
525  NavigatorValues.y = Mathf.Clamp(currentButtonStates.CumulativeDelta.y*5, -1.0f, 1.0f) * railUsedCurrently.y;
526  InputManager.Instance.RaiseNavigationUpdated(this, controllerId, NavigatorValues);
527  }
528  }
529 
530  if (currentButtonStates.MenuButtonStateChanged)
531  {
532  if (currentButtonStates.IsMenuButtonDown)
533  {
534  InputManager.Instance.RaiseSourceDown(this, controllerId, InteractionSourcePressInfo.Menu);
535  }
536  else
537  {
538  InputManager.Instance.RaiseSourceUp(this, controllerId, InteractionSourcePressInfo.Menu);
539  }
540  }
541 
542  if (currentButtonStates.GraspStateChanged)
543  {
544  if (currentButtonStates.IsGrasped)
545  {
546  InputManager.Instance.RaiseSourceDown(this, controllerId, InteractionSourcePressInfo.Grasp);
547  }
548  else
549  {
550  InputManager.Instance.RaiseSourceUp(this, controllerId, InteractionSourcePressInfo.Grasp);
551  }
552  }
553  }
554 
558  private void SendControllerVisibilityEvents()
559  {
560  // Send event for new hands that were added
561  if (RaiseEventsBasedOnVisibility && visibilityChanged)
562  {
563  if (currentlyVisible)
564  {
565  InputManager.Instance.RaiseSourceDetected(this, controllerId);
566  }
567  else
568  {
569  InputManager.Instance.RaiseSourceLost(this, controllerId);
570  }
571 
572  visibilityChanged = false;
573  }
574  }
575  }
576 }
Input source for gestures and interaction source information from the WSA APIs, which gives access to...
Input Manager is responsible for managing input sources and dispatching relevant events to the approp...
Definition: InputManager.cs:19
override bool TryGetSourceKind(uint sourceId, out InteractionSourceInfo sourceKind)
override bool TryGetGripPosition(uint sourceId, out Vector3 position)
Returns the position of the input source, if available. Not all input sources support positional info...
SupportedInputInfo
Flags used to indicate which input information is supported by an input source.
override bool TryGetThumbstick(uint sourceId, out bool isPressed, out Vector2 position)
override bool TryGetTouchpad(uint sourceId, out bool isPressed, out bool isTouched, out Vector2 position)
override bool TryGetGrasp(uint sourceId, out bool isPressed)
override bool TryGetMenu(uint sourceId, out bool isPressed)
override SupportedInputInfo GetSupportedInputInfo(uint sourceId)
Returns the input info that the input source can provide.
override bool TryGetSelect(uint sourceId, out bool isPressed, out double pressedAmount)
static T Instance
Returns the Singleton instance of the classes type. If no instance is found, then we search for an in...
Definition: Singleton.cs:26
override bool TryGetGripRotation(uint sourceId, out Quaternion rotation)
Returns the rotation of the input source, if available. Not all input sources support rotation inform...
Since the InteractionSourceState is internal to UnityEngine.VR.WSA.Input, this is a fake SourceState ...
override bool TryGetPointerRotation(uint sourceId, out Quaternion rotation)
Returns the rotation of the input source, if available. Not all input sources support rotation inform...
override bool TryGetPointingRay(uint sourceId, out Ray pointingRay)
Returns the pointing ray of the input source, if available. Not all input sources support pointing in...
Base class for an input source.
override bool TryGetPointerPosition(uint sourceId, out Vector3 position)
Returns the position of the input source, if available. Not all input sources support positional info...
Class for manually controlling inputs when running in the Unity editor.
Input source for fake input source information, which gives details about current source state and po...