AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
GestureInteractive.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.Collections;
5 using HoloToolkit.Unity;
7 using UnityEngine;
9 
10 #if UNITY_WSA || UNITY_STANDALONE_WIN
11 using UnityEngine.Windows.Speech;
12 #endif
13 
14 namespace HoloToolkit.Examples.InteractiveElements
15 {
23  {
27  public enum GestureManipulationState { None, Start, Update, Lost }
28  public GestureManipulationState GestureState { get; protected set; }
29 
30  private IInputSource mCurrentInputSource;
31  private uint mCurrentInputSourceId;
32 
33  [Tooltip("Sets the time before the gesture starts after a press has occurred, handy when a select event is also being used")]
34  public float StartDelay;
35 
36  [Tooltip("The GestureInteractiveControl to send gesture updates to")]
38 
42  [Tooltip("Should this control hide the cursor during this manipulation?")]
44 
48  private Vector3 mStartHeadPosition;
49  private Vector3 mStartHeadRay;
50  private Vector3 mStartHandPosition;
51  private Vector3 mCurrentHandPosition;
52  private Cursor mCursor;
53 
54  private Coroutine mTicker;
55  private IInputSource mTempInputSource;
56  private uint mTempInputSourceId;
57 
58  protected override void Awake()
59  {
60  base.Awake();
61 
62  // get the gestureInteractiveControl if not previously set
63  // This could reside on another GameObject, so we will not require this to exist on this game object.
64  if (Control == null)
65  {
66  Control = GetComponent<GestureInteractiveControl>();
67  }
68  }
69 
75  {
76  Control = newControl;
77  }
78 
82  public override void OnInputDown(InputEventData eventData)
83  {
84  base.OnInputDown(eventData);
85 
86  mTempInputSource = eventData.InputSource;
87  mTempInputSourceId = eventData.SourceId;
88 
89  if (StartDelay > 0)
90  {
91  if (mTicker == null)
92  {
93  mTicker = StartCoroutine(Ticker(StartDelay));
94  }
95  }
96  else
97  {
98  HandleStartGesture();
99  }
100  }
101 
102  // Makes sure when a gesture interactive gets cleared the input source gets the gesture lost event.
103  public static void ClearGestureModalInput(GameObject source)
104  {
105  // Stack could hold a reference that's been removed.
106  if (source == null)
107  {
108  return;
109  }
110 
111  GestureInteractive gesture = source.GetComponent<GestureInteractive>();
112  if (gesture == null)
113  {
114  return;
115  }
116 
117  gesture.HandleRelease(false);
118  gesture.CleanUpTicker();
119  }
120 
121  private IEnumerator Ticker(float seconds)
122  {
123  yield return new WaitForSeconds(seconds);
124  HandleStartGesture();
125  }
126 
130  private void HandleStartGesture()
131  {
132  InputManager.Instance.ClearModalInputStack();
133 
134  // Add self as a modal input handler, to get all inputs during the manipulation
135  InputManager.Instance.PushModalInputHandler(gameObject);
136 
137  mCurrentInputSource = mTempInputSource;
138  mCurrentInputSourceId = mTempInputSourceId;
139 
140  mStartHeadPosition = CameraCache.Main.transform.position;
141  mStartHeadRay = CameraCache.Main.transform.forward;
142 
143  Vector3 handPosition;
144  mCurrentInputSource.TryGetGripPosition(mCurrentInputSourceId, out handPosition);
145 
146  mStartHandPosition = handPosition;
147  mCurrentHandPosition = handPosition;
148  Control.ManipulationUpdate(mStartHandPosition, mStartHandPosition, mStartHeadPosition, mStartHeadRay, GestureManipulationState.Start);
149  HandleCursor(true);
150  }
151 
156  public override void OnInputUp(InputEventData eventData)
157  {
158  //base.OnInputUp(eventData);
159  if (mCurrentInputSource != null && (eventData == null || eventData.SourceId == mCurrentInputSourceId))
160  {
161  HandleRelease(false);
162  }
163 
164  CleanUpTicker();
165  }
166 
171  public void OnSourceDetected(SourceStateEventData eventData)
172  {
173  // Nothing to do
174  }
175 
180  public void OnSourceLost(SourceStateEventData eventData)
181  {
182  if (mCurrentInputSource != null && eventData.SourceId == mCurrentInputSourceId)
183  {
184  HandleRelease(true);
185  }
186 
187  CleanUpTicker();
188  }
189 
193  private void CleanUpTicker()
194  {
195  if (mTicker != null)
196  {
197  StopCoroutine(mTicker);
198  mTicker = null;
199  }
200  }
201 
205  private void HandleRelease(bool lost)
206  {
207  mTempInputSource = null;
208 
209  Vector3 handPosition = GetCurrentHandPosition();
210 
211  mCurrentHandPosition = handPosition;
212  Control.ManipulationUpdate(
213  mStartHandPosition,
214  mCurrentHandPosition,
215  mStartHeadPosition,
216  mStartHeadRay,
218 
219  InputManager.Instance.ClearModalInputStack();
220 
221  if (HasGaze)
222  {
223  base.OnInputUp(null);
224  }
225  else
226  {
227  base.OnInputUp(null);
228  base.OnFocusExit();
229  }
230 
231  mCurrentInputSource = null;
232 
233  HandleCursor(false);
234  }
235 
239  public override void OnFocusExit()
240  {
241  //base.OnGazeLeave();
242  if (mCurrentInputSource == null)
243  {
244  base.OnFocusExit();
245  }
246  }
247 
251  public override void OnFocusEnter()
252  {
253  if (mCurrentInputSource == null)
254  {
255  base.OnFocusEnter();
256  }
257  }
258 
263  private Vector3 GetCurrentHandPosition()
264  {
265  Vector3 handPosition;
266 #if UNITY_2017_2_OR_NEWER
267  mCurrentInputSource.TryGetGripPosition(mCurrentInputSourceId, out handPosition);
268 #else
269  mCurrentInputSource.TryGetPointerPosition(mCurrentInputSourceId, out handPosition);
270 #endif
271  return handPosition;
272  }
273 
278  private void HandleCursor(bool state)
279  {
280  // Hack for now.
281  // TODO: Update Cursor Modifier to handle HideOnGesture, then calculate visibility so cursors can handle this correctly
282  if (state)
283  {
284  mCursor = FindObjectOfType<Cursor>();
285  }
286 
287  if (HideCursorOnManipulation && mCursor != null)
288  {
289  mCursor.SetVisibility(!state);
290  }
291  }
292 
296  protected override void Update()
297  {
298  base.Update();
299 
300  if (mCurrentInputSource != null)
301  {
302  mCurrentHandPosition = GetCurrentHandPosition();
303  Control.ManipulationUpdate(mStartHandPosition, mCurrentHandPosition, mStartHeadPosition, mStartHeadRay, GestureManipulationState.Update);
304  }
305  }
306 
307 #if UNITY_WSA || UNITY_STANDALONE_WIN
308  protected override void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
314  {
315  base.KeywordRecognizer_OnPhraseRecognized(args);
316 
317  // Check to make sure the recognized keyword matches, then invoke the corresponding method.
318  if ((!KeywordRequiresGaze || HasGaze) && mKeywordDictionary != null)
319  {
320  int index;
321  if (mKeywordDictionary.TryGetValue(args.text, out index))
322  {
323  Control.setGestureValue(index);
324  }
325  }
326  }
327 #endif
328 
332  protected override void OnDestroy()
333  {
334  if (mTicker != null)
335  {
336  StopCoroutine(mTicker);
337  mTicker = null;
338  }
339 
340  base.OnDestroy();
341  }
342  }
343 }
void SetGestureControl(GestureInteractiveControl newControl)
Change the control in code or in a UnityEvent inspector.
virtual void SetVisibility(bool visible)
Updates the visual representation of the cursor.
Definition: Cursor.cs:386
Input Manager is responsible for managing input sources and dispatching relevant events to the approp...
Definition: InputManager.cs:19
GestureInteractive extends Interactive and handles more advanced gesture events. On Press a gesture b...
uint SourceId
The id of the source the event is from, for instance the hand id.
bool TryGetGripPosition(uint sourceId, out Vector3 position)
Returns the position of the input source, if available. Not all input sources support positional info...
IInputSource InputSource
The source the input event originates from.
The purpose of this class is to provide a cached reference to the main camera. Calling Camera...
Definition: CameraCache.cs:12
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
virtual void setGestureValue(int gestureValue)
a place holder function for taking value and settings a gesture direction. Used by the keyword gestur...
static Camera Main
Returns a cached reference to the main camera and uses Camera.main if it hasn&#39;t been cached yet...
Definition: CameraCache.cs:20
bool HideCursorOnManipulation
Provide additional UI for gesture feedback.
Interface for an input source. An input source can be anything that a user can use to interact with a...
Definition: IInputSource.cs:12
Interface to implement to react to source state changes, such as when an input source is detected or ...
virtual void ManipulationUpdate(Vector3 startGesturePosition, Vector3 currentGesturePosition, Vector3 startHeadOrigin, Vector3 startHeadRay, GestureInteractive.GestureManipulationState gestureState)
Gesture updates called by GestureInteractive
override void OnFocusExit()
Works like an Interactive if no manipulation has begun
Object that represents a cursor in 3D space controlled by gaze.
Definition: Cursor.cs:11
Describes an source state event that has a source id.
bool TryGetPointerPosition(uint sourceId, out Vector3 position)
Returns the position of the input source, if available. Not all input sources support positional info...
Describes an input event that has a source id and a press kind.
Useful for releasing external override. See CursorStateEnum.Contextual
override void Update()
Update gestures and send gesture data to GestureInteractiveController
override void OnInputDown(InputEventData eventData)
The press event runs before all other gesture based events, so it&#39;s safe to register Manipulation eve...
override void OnInputUp(InputEventData eventData)
ignore this event at face value, the user may roll off the interactive while performing a gesture...
void OnSourceLost(SourceStateEventData eventData)
Stops the gesture when the source is lost
Interactive exposes basic button type events to the Unity Editor and receives messages from the Gestu...
Definition: Interactive.cs:22
void OnSourceDetected(SourceStateEventData eventData)
required by ISourceStateHandler