Tilt Five™ Unity API  1.4.1
Wand.cs
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2020-2023 Tilt Five, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 using System;
18 using System.Collections.Generic;
19 using UnityEngine;
20 using TiltFive.Logging;
21 
22 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
23 using UnityEngine.InputSystem;
24 using UnityEngine.InputSystem.Users;
25 using UnityEngine.InputSystem.Controls;
26 #endif
27 
29 
30 namespace TiltFive
31 {
36  [System.Serializable]
38  {
40 
41  public GameObject GripPoint;
42  public GameObject FingertipPoint;
43  public GameObject AimPoint;
44 
45  // TODO: Think about some accessors for physical attributes of the wand (length, distance to tip, etc)?
46  internal WandSettings Copy()
47  {
48  return (WandSettings)MemberwiseClone();
49  }
50  }
51 
55  public class Wand : Singleton<Wand>
56  {
57  #region Private Fields
58 
62  private Dictionary<GlassesHandle, WandPair> wandCores = new Dictionary<GlassesHandle, WandPair>();
63 
70  private static readonly Vector3 DEFAULT_WAND_POSITION_GAME_BOARD_SPACE = new Vector3(0f, 0.25f, -0.25f);
74  private static readonly Vector3 DEFAULT_WAND_HANDEDNESS_OFFSET_GAME_BOARD_SPACE = new Vector3(0.125f, 0f, 0f);
75 
83  private static readonly Quaternion DEFAULT_WAND_ROTATION_GAME_BOARD_SPACE = Quaternion.Euler(new Vector3(-33f, 0f, 0f));
84 
85 
86  // Handles for Glasses that have just connected
87  private HashSet<GlassesHandle> incomingHandles = new HashSet<GlassesHandle>();
88  // Handles for Glasses that have just disconnected
89  private HashSet<GlassesHandle> lostHandles = new HashSet<GlassesHandle>();
90 
91  private HashSet<WandCore> lostWands = new HashSet<WandCore>();
92 
93 
94  // Scan for new wands every half second.
95  private static DateTime lastScanAttempt = System.DateTime.MinValue;
96 
97  // This should likely become a query into the native library.
98  private static readonly double wandScanInterval = 0.5d;
99 
100  private static bool wandAvailabilityErroredOnce = false;
101 
102 
103  // Used to guard against certain functions executing more than once per frame
104  private static int currentFrame = -1;
105 
106  #endregion Private Fields
107 
108 
109  #region Private Structs
110 
111  private struct WandPair
112  {
115 
116  public WandPair(WandCore right, WandCore left)
117  {
118  RightWand = right;
119  LeftWand = left;
120  }
121 
122  public WandCore this[ControllerIndex controllerIndex]
123  {
124  get
125  {
126  switch (controllerIndex)
127  {
128  case ControllerIndex.Right:
129  return RightWand;
130  case ControllerIndex.Left:
131  return LeftWand;
132  default:
133  // TODO: If we get an unexpected value here, should we fail silently or throw an exception?
134  return null;
135  }
136  }
137  }
138 
139  public bool TryGet(ControllerIndex controllerIndex, out WandCore wandCore)
140  {
141  wandCore = this[controllerIndex];
142  return wandCore != null;
143  }
144  }
145 
146  #endregion Private Structs
147 
148 
149  #region Public Functions
150 
158  public static Vector3 GetPosition(
159  ControllerIndex controllerIndex = ControllerIndex.Right,
160  ControllerPosition controllerPosition = ControllerPosition.Grip,
161  PlayerIndex playerIndex = PlayerIndex.One)
162  {
163  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
164  || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
165  || !wandPair.TryGet(controllerIndex, out var wandCore))
166  {
167  return Vector3.zero;
168  }
169 
170  switch (controllerPosition)
171  {
172  case ControllerPosition.Fingertips:
173  return wandCore.fingertipsPose_UnityWorldSpace.position;
174  case ControllerPosition.Aim:
175  return wandCore.aimPose_UnityWorldSpace.position;
176  case ControllerPosition.Grip:
177  return wandCore.Pose_UnityWorldSpace.position;
178  default:
179  return Vector3.zero;
180  }
181  }
182 
189  public static Quaternion GetRotation(ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
190  {
191  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
192  || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
193  || !wandPair.TryGet(controllerIndex, out var wandCore))
194  {
195  return Quaternion.identity;
196  }
197 
198  return wandCore.Pose_UnityWorldSpace.rotation;
199  }
200 
201  public static bool IsTracked(ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
202  {
203  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
204  || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
205  || !wandPair.TryGet(controllerIndex, out var wandCore))
206  {
207  return false;
208  }
209 
210  return wandCore.IsTracked;
211  }
212 
220  public static bool TryCheckConnected(out bool connected, PlayerIndex playerIndex, ControllerIndex controllerIndex = ControllerIndex.Right)
221  {
222  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
223  || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
224  || !wandPair.TryGet(controllerIndex, out var wandCore))
225  {
226  connected = false;
227  return true;
228  }
229 
230  connected = wandCore.IsConnected;
231  return true;
232  }
233 
234 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
245  public static bool TryGetWandDevice(PlayerIndex playerIndex, ControllerIndex controllerIndex, out WandDevice wandDevice)
246  {
247  if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
248  || !wandPair.TryGet(controllerIndex, out var wandCore) || !(wandCore is WandDeviceCore wandDeviceCore))
249  {
250  wandDevice = null;
251  return false;
252  }
253 
254  wandDevice = wandDeviceCore.wandDevice;
255  return true;
256  }
257 #endif
258 
267  public static bool TrySendImpulse(float amplitude, float duration, PlayerIndex playerIndex = PlayerIndex.One, ControllerIndex controllerIndex = ControllerIndex.Right)
268  {
269  if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
270  || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
271  || !wandPair.TryGet(controllerIndex, out var wandCore))
272  {
273  return false;
274  }
275 
276  try
277  {
278  if (NativePlugin.SendImpulse(glassesHandle, controllerIndex, amplitude, (UInt16)(Mathf.Clamp(duration, 0.0f, 0.320f) * 1000)) == 0)
279  {
280  return true;
281  }
282  else
283  {
284  return false;
285  }
286  }
287  catch (DllNotFoundException e)
288  {
289  Log.Info("Tilt Five library missing. Could not connect to wand: {0}", e.Message);
290  return false;
291  }
292  catch (Exception e)
293  {
294  Log.Error(
295  "Failed to send impulse to wand: {0}",
296  e.ToString());
297  return false;
298  }
299  }
300 
301  #region Deprecated Public Functions
302 
303  // Update is called once per frame
304  [Obsolete("Converted to be an internal function.")]
305  public static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex = PlayerIndex.One)
306  {
307  Update(playerIndex, wandSettings, scaleSettings, gameBoardSettings);
308  }
309 
310  [Obsolete("Converted to be an internal function.")]
311  public static void ScanForWands()
312  {
313  Scan();
314  }
315 
316  #endregion Deprecated Public Functions
317 
318  #endregion Public Functions
319 
320 
321  #region Internal Functions
322 
323  internal static void Update(PlayerIndex playerIndex, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
324  {
325  // Update the relevant WandCore
326  if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
327  {
328  Update(glassesHandle, wandSettings, scaleSettings, gameBoardSettings);
329  }
330  }
331 
332  internal static void Update(GlassesHandle glassesHandle, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
333  {
334  if (Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
335  && wandPair.TryGet(wandSettings.controllerIndex, out var wandCore))
336  {
337  wandCore.Update(wandSettings, scaleSettings, gameBoardSettings);
338  }
339  }
340 
341  internal static void OnDisable()
342  {
343  foreach (var wandPair in Instance.wandCores.Values)
344  {
345  wandPair.RightWand?.Dispose();
346  wandPair.LeftWand?.Dispose();
347  }
348  Instance.wandCores.Clear();
349  }
350 
351  internal static void Scan()
352  {
353  // Tell the native plugin to search for wands.
354  TryScanForWands();
355 
356  // Obtain the latest set of connected glasses
357  var connectedGlassesHandles = Glasses.GetAllConnectedGlassesHandles();
358 
359  // Add/Remove entries from the wandCores dictionary depending on which glasses appeared/disappeared
360  // ---------------------------------------------------
361  var wandCores = Instance.wandCores;
362  var incomingHandles = Instance.incomingHandles;
363  var lostHandles = Instance.lostHandles;
364  var lostWands = Instance.lostWands;
365 
366  incomingHandles.Clear();
367  lostHandles.Clear();
368  lostWands.Clear();
369 
370  // Add newly connected wands
371  for (int i = 0; i < connectedGlassesHandles.Length; i++)
372  {
373  var glassesHandle = connectedGlassesHandles[i];
374  incomingHandles.Add(glassesHandle);
375 
376  var rightWandCore = Instance.ObtainWandCore(glassesHandle, ControllerIndex.Right);
377  var leftWandCore = Instance.ObtainWandCore(glassesHandle, ControllerIndex.Left);
378 
379  // Obtain and store a pair of WandCores to associate with this GlassesHandle.
380  // If either wand is unavailable, we'll just store null.
381  // This will also clear any WandCores that represented a now-disconnected wand.
382  wandCores[glassesHandle] = new WandPair(rightWandCore, leftWandCore);
383  }
384 
385  // Prune disconnected wands
386  foreach (var glassesHandle in wandCores.Keys)
387  {
388  // If a pair of glasses disconnects, save its handle for the next foreach loop below
389  if (!incomingHandles.Contains(glassesHandle))
390  {
391  lostHandles.Add(glassesHandle);
392  }
393  // otherwise, check if its wands are still connected.
394  else
395  {
396  if (wandCores.TryGetValue(glassesHandle, out var wandPair))
397  {
398  if (wandPair.TryGet(ControllerIndex.Left, out var leftWandCore)
399  && (!TryGetWandAvailability(out bool leftWandConnected, glassesHandle, ControllerIndex.Left) || !leftWandConnected))
400  {
401  lostWands.Add(leftWandCore);
402  }
403  if (wandPair.TryGet(ControllerIndex.Right, out var rightWandCore)
404  && (!TryGetWandAvailability(out bool rightWandConnected, glassesHandle, ControllerIndex.Right) || !rightWandConnected))
405  {
406  lostWands.Add(rightWandCore);
407  }
408  }
409  }
410  }
411 
412  foreach (var lostHandle in lostHandles)
413  {
414  var lostWandPair = wandCores[lostHandle];
415 
416  if (lostWandPair.TryGet(ControllerIndex.Right, out var lostRightWand))
417  {
418  lostWands.Add(lostRightWand);
419  }
420 
421  if (lostWandPair.TryGet(ControllerIndex.Left, out var lostLeftWand))
422  {
423  lostWands.Add(lostLeftWand);
424  }
425  wandCores.Remove(lostHandle);
426  }
427 
428  foreach (var lostWand in lostWands)
429  {
430  lostWand.Dispose();
431  }
432  }
433 
434  internal static void GetLatestInputs()
435  {
436  // If the previous/current wand states are shuffled more than once per frame,
437  // our button rising/falling edge detection breaks.
438  // Detect this scenario and return early to prevent this.
439  if (Time.frameCount == currentFrame)
440  {
441  return;
442  }
443  currentFrame = Time.frameCount;
444 
445  foreach (var wandPair in Instance.wandCores.Values)
446  {
447  wandPair.RightWand?.GetLatestInputs();
448  wandPair.LeftWand?.GetLatestInputs();
449  }
450  }
451 
452  internal static bool GetButton(WandButton button, GlassesHandle glassesHandle,
453  ControllerIndex controllerIndex = ControllerIndex.Right)
454  {
455  if(!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
456  || !wandPair.TryGet(controllerIndex, out var wandCore))
457  {
458  return false;
459  }
460  return wandCore.GetButton(button);
461  }
462 
463  internal static bool TryGetButton(WandButton button, out bool pressed,
464  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
465  {
466  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
467  || !wandPair.TryGet(controllerIndex, out var wandCore))
468  {
469  pressed = false;
470  return false;
471  }
472 
473  return wandCore.TryGetButton(button, out pressed);
474  }
475 
476  internal static bool GetButtonDown(WandButton button,
477  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
478  {
479  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
480  || !wandPair.TryGet(controllerIndex, out var wandCore))
481  {
482  return false;
483  }
484 
485  return wandCore.GetButtonDown(button);
486  }
487 
488  internal static bool TryGetButtonDown(WandButton button, out bool buttonDown,
489  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
490  {
491  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
492  || !wandPair.TryGet(controllerIndex, out var wandCore))
493  {
494  buttonDown = false;
495  return false;
496  }
497 
498  return wandCore.TryGetButtonDown(button, out buttonDown);
499  }
500 
501  internal static bool GetButtonUp(WandButton button,
502  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
503  {
504  if (Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
505  || !wandPair.TryGet(controllerIndex, out var wandCore))
506  {
507  // TODO: Handle corner case in which wandCore existed during the previous frame, and the button was pressed
508  // We'd want to return true in that scenario.
509  return false;
510  }
511 
512  return wandCore.GetButtonUp(button);
513  }
514 
515  internal static bool TryGetButtonUp(WandButton button, out bool buttonUp,
516  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
517  {
518  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
519  || !wandPair.TryGet(controllerIndex, out var wandCore))
520  {
521  // TODO: Handle corner case in which wandCore existed during the previous frame, and the button was pressed.
522  // We'd want to return true in that scenario.
523  buttonUp = false;
524  return false;
525  }
526 
527  return wandCore.TryGetButtonUp(button, out buttonUp);
528  }
529 
530  internal static Vector2 GetStickTilt(GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
531  {
532  if (Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
533  || !wandPair.TryGet(controllerIndex, out var wandCore))
534  {
535  return Vector2.zero;
536  }
537 
538  return wandCore.GetStickTilt();
539  }
540 
541  internal static bool TryGetStickTilt(out Vector2 stickTilt,
542  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
543  {
544  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
545  || !wandPair.TryGet(controllerIndex, out var wandCore))
546  {
547  stickTilt = Vector2.zero;
548  return false;
549  }
550 
551  return wandCore.TryGetStickTilt(out stickTilt);
552  }
553 
554  internal static float GetTrigger(GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
555  {
556  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
557  || !wandPair.TryGet(controllerIndex, out var wandCore))
558  {
559  return 0f;
560  }
561 
562  return wandCore.GetTrigger();
563  }
564 
565  internal static bool TryGetTrigger(out float triggerDisplacement,
566  GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
567  {
568  if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
569  || !wandPair.TryGet(controllerIndex, out var wandCore))
570  {
571  triggerDisplacement = 0f;
572  return false;
573  }
574 
575  return wandCore.TryGetTrigger(out triggerDisplacement);
576  }
577 
578  internal static bool TryGetWandControlsState(GlassesHandle glassesHandle, out T5_ControllerState? controllerState,
579  ControllerIndex controllerIndex = ControllerIndex.Right)
580  {
581  int result = NativePlugin.T5_RESULT_UNKNOWN_ERROR;
582 
583  try
584  {
585  T5_ControllerState state = new T5_ControllerState();
586  result = NativePlugin.GetControllerState(glassesHandle, controllerIndex, ref state);
587  controllerState = (result == NativePlugin.T5_RESULT_SUCCESS)
588  ? state
589  : (T5_ControllerState?)null;
590  }
591  catch (Exception e)
592  {
593  controllerState = null;
594  Log.Error(e.Message);
595  }
596 
597  return result == NativePlugin.T5_RESULT_SUCCESS;
598  }
599 
600  #endregion Internal Functions
601 
602 
603  #region Private Functions
604 
605  private static bool TryScanForWands()
606  {
607  var currentTime = System.DateTime.Now;
608  var timeSinceLastScan = currentTime - lastScanAttempt;
609 
610  // Scan for wands if the scan interval has elapsed to catch newly connected wands.
611  if (timeSinceLastScan.TotalSeconds >= wandScanInterval)
612  {
613  int result = NativePlugin.T5_RESULT_UNKNOWN_ERROR;
614 
615  try
616  {
617  result = NativePlugin.ScanForWands();
618  }
619  catch (System.DllNotFoundException e)
620  {
621  Log.Info(
622  "Could not connect to Tilt Five plugin to scan for wands: {0}",
623  e);
624  }
625  catch (Exception e)
626  {
627  Log.Error(e.Message);
628  }
629 
630  lastScanAttempt = currentTime;
631  return (result == NativePlugin.T5_RESULT_SUCCESS);
632  }
633 
634  return false;
635  }
636 
637  private static bool TryGetWandAvailability(out bool connected, GlassesHandle glassesHandle, ControllerIndex controllerIndex)
638  {
640  {
641  try
642  {
643  T5_Bool wandAvailable = false;
644  int result = NativePlugin.GetWandAvailability(glassesHandle, ref wandAvailable, controllerIndex);
645 
646  if (result == NativePlugin.T5_RESULT_SUCCESS)
647  {
648  connected = wandAvailable;
649  return true;
650  }
651  }
652  catch (DllNotFoundException e)
653  {
654  Log.Info("Could not connect to Tilt Five plugin for wand: {0}", e.Message);
656  }
657  catch (Exception e)
658  {
659  Log.Error(
660  "Failed to connect to Tilt Five plugin for wand availability: {0}",
661  e.ToString());
663  }
664  }
665 
666  connected = false;
667  return false;
668  }
669 
670  private WandCore ObtainWandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
671  {
672  WandCore wandCore = null;
673  var glassesAlreadyMonitored = wandCores.TryGetValue(glassesHandle, out var wandPair);
674 
675  // Ask the native plugin whether this wand is currently connected.
676  if (TryGetWandAvailability(out var wandConnected, glassesHandle, controllerIndex) && wandConnected)
677  {
678  // If we're not already monitoring this wand, go ahead and create a corresponding WandCore.
679  if (!glassesAlreadyMonitored || !wandPair.TryGet(controllerIndex, out wandCore))
680  {
681 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
682  wandCore = new WandDeviceCore(glassesHandle, controllerIndex);
683 #else
684  wandCore = new WandCore(glassesHandle, controllerIndex);
685 #endif
686  }
687  return wandCore;
688  }
689  // If this wand is disconnected and we were previously monitoring it, mark it as disconnected
690  else if(glassesAlreadyMonitored && wandPair.TryGet(controllerIndex, out var lostWandCore))
691  {
692  lostWands.Add(lostWandCore);
693  }
694 
695  return wandCore;
696  }
697 
698  #endregion Private Functions
699 
700 
701  #region Private Classes
702 
706  private class WandCore : TrackableCore<WandSettings, T5_ControllerState>, IDisposable
707  {
708  #region Public Fields
709 
710  public readonly GlassesHandle glassesHandle;
712 
713  public Pose fingertipsPose_GameboardSpace = new Pose(DEFAULT_WAND_POSITION_GAME_BOARD_SPACE, Quaternion.identity);
714  public Pose aimPose_GameboardSpace = new Pose(DEFAULT_WAND_POSITION_GAME_BOARD_SPACE, Quaternion.identity);
715 
719 
720  protected T5_ControllerState? currentState;
721  protected T5_ControllerState? previousState;
722 
723  #endregion Public Fields
724 
725 
726  #region Public Functions
727 
729  {
730  this.glassesHandle = glassesHandle;
731  this.controllerIndex = controllerIndex;
732 
733  Glasses.TryGetFriendlyName(glassesHandle, out var friendlyName);
734  Log.Info($"Glasses {glassesHandle} (\"{friendlyName}\") {Enum.GetName(typeof(ControllerIndex), controllerIndex)} Wand connected");
735  }
736 
737  public virtual void GetLatestInputs()
738  {
740 
741  try
742  {
743  T5_ControllerState state = new T5_ControllerState();
744 
745  var result = NativePlugin.GetControllerState(glassesHandle, controllerIndex, ref state);
746 
747  currentState = (result == NativePlugin.T5_RESULT_SUCCESS)
748  ? state
749  : (T5_ControllerState?)null;
750  }
751  catch (Exception e)
752  {
753  currentState = null;
754  Log.Error(e.Message);
755  }
756  }
757 
758  public bool GetButton(WandButton button)
759  {
760  // If the wand isn't connected, GetButton() should return a default value of false.
761  return currentState?.GetButton(button) ?? false;
762  }
763 
764  public bool TryGetButton(WandButton button, out bool pressed)
765  {
766  pressed = currentState?.GetButton(button) ?? false;
767 
768  // If the wand isn't connected, TryGetButton() should fail.
769  return currentState.HasValue;
770  }
771 
772  public bool GetButtonDown(WandButton button)
773  {
774 
775  // If the current wand state is null, the wand isn't connected.
776  // If so, let the application assume the user isn't pressing the button currently.
777  var pressed = currentState?.GetButton(button) ?? false;
778 
779  // If the previous wand state is null, the wand wasn't connected.
780  // If so, let the application assume the user wasn't pressing the button last frame.
781  var previouslyPressed = previousState?.GetButton(button) ?? false;
782 
783  // The wand could potentially connect while the user is holding a button, so just report the button state.
784  if (!previousState.HasValue && currentState.HasValue)
785  {
786  return pressed;
787  }
788  // Return true if the button is currently pressed, but was unpressed on the previous frame.
789  return pressed && !previouslyPressed;
790  }
791 
792  public bool TryGetButtonDown(WandButton button, out bool buttonDown)
793  {
794  // Even if this operation fails, give buttonDown a default value.
795  buttonDown = GetButtonDown(button);
796  return currentState.HasValue;
797  }
798 
799  public bool GetButtonUp(WandButton button)
800  {
801  // If the current wand state is null, the wand isn't connected.
802  // If so, let the application assume the user isn't pressing the button currently.
803  var pressed = currentState?.GetButton(button) ?? false;
804 
805  // If the previous wand state is null, the wand wasn't connected.
806  // If so, let the application assume the user wasn't pressing the button last frame.
807  var previouslyPressed = previousState?.GetButton(button) ?? false;
808 
809  // Return true if the button is currently released, but was pressed on the previous frame.
810  return previousState.HasValue
811  ? !pressed && previouslyPressed
812  // If the current state exists but the previous state was null, the wand has just connected.
813  // There's no way for the button to be pressed during the previous frame,
814  // so there's no way for the button to have been released this frame. Always return false.
815  : false;
816  }
817 
818  public bool TryGetButtonUp(WandButton button, out bool buttonUp)
819  {
820  // Even if this operation fails, give buttonUp a default value.
821  buttonUp = GetButtonUp(button);
822  return currentState.HasValue;
823  }
824 
825  public Vector2 GetStickTilt()
826  {
827  return currentState?.Stick ?? Vector2.zero;
828  }
829 
830  public bool TryGetStickTilt(out Vector2 stickTilt)
831  {
832  stickTilt = GetStickTilt();
833  return currentState.HasValue;
834  }
835 
836  public float GetTrigger()
837  {
838  return currentState?.Trigger ?? 0.0f;
839  }
840 
841  public bool TryGetTrigger(out float triggerDisplacement)
842  {
843  triggerDisplacement = GetTrigger();
844  return currentState.HasValue;
845  }
846 
847  #endregion Public Functions
848 
849 
850  #region Overrides
851 
852  public new void Reset(WandSettings wandSettings)
853  {
854  base.Reset(wandSettings);
855  }
856 
857  public new virtual void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
858  {
859  if (wandSettings == null)
860  {
861  Log.Error("WandSettings configuration required for Wand tracking updates.");
862  return;
863  }
864 
865  base.Update(wandSettings, scaleSettings, gameBoardSettings);
866  }
867 
868  protected override void SetDefaultPoseGameboardSpace(WandSettings settings)
869  {
871  // We don't have a good offset that we can use for default fingertips/aim poses, so just use the default pose for everything
874  }
875 
876  protected static Pose GetDefaultPoseGameboardSpace(WandSettings settings)
877  {
878  Vector3 defaultPosition = DEFAULT_WAND_POSITION_GAME_BOARD_SPACE;
879 
881  * (settings.controllerIndex == ControllerIndex.Right ? 1f : -1f);
882  return new Pose(defaultPosition, DEFAULT_WAND_ROTATION_GAME_BOARD_SPACE);
883  }
884 
885  protected override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
886  {
887  pose_UWRLD = GameboardToWorldSpace(pose_UGBD, scaleSettings, gameBoardSettings);
889  aimPose_UnityWorldSpace = GameboardToWorldSpace(aimPose_GameboardSpace, scaleSettings, gameBoardSettings);
890  }
891 
892  protected override bool TryCheckConnected(out bool connected)
893  {
895  {
896  try
897  {
898  T5_Bool wandAvailable = false;
899  int result = NativePlugin.GetWandAvailability(glassesHandle, ref wandAvailable, controllerIndex);
900 
901  if (result == NativePlugin.T5_RESULT_SUCCESS)
902  {
903  isConnected = wandAvailable;
904  connected = wandAvailable;
905  return true;
906  }
907  }
908  catch (DllNotFoundException e)
909  {
910  Log.Info("Could not connect to Tilt Five plugin for wand: {0}", e.Message);
912  }
913  catch (Exception e)
914  {
915  Log.Error(
916  "Failed to connect to Tilt Five plugin for wand availability: {0}",
917  e.ToString());
919  }
920  }
921 
922  isConnected = false;
923  connected = false;
924  return false;
925  }
926 
927  protected override bool TryGetStateFromPlugin(out T5_ControllerState controllerState, out bool poseIsValid, GameBoardSettings gameBoardSettings)
928  {
929  if (!TryGetWandControlsState(glassesHandle, out var controllerStateResult, controllerIndex))
930  {
931  poseIsValid = false;
932  controllerState = new T5_ControllerState();
933 
934  return false;
935  }
936 
937  controllerState = controllerStateResult.Value;
938  poseIsValid = Glasses.IsTracked(glassesHandle) && controllerState.PoseValid;
939 
940  return true;
941  }
942 
943  protected override void SetPoseGameboardSpace(in T5_ControllerState controllerState,
944  WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
945  {
946  // Unity reference frames:
947  //
948  // UWND - Unity WaND local space.
949  // +x right, +y up, +z forward
950  // UGBD - Unity Gameboard space.
951  // +x right, +y up, +z forward
952  //
953  // Tilt Five reference frames:
954  //
955  // DW - Our right-handed version of Unity's default wand space.
956  // +x right, +y down, +z forward
957  // GBD - Gameboard space.
958  // +x right, +y forward, +z up
959 
960  Vector3 gripPosition_UGBD = ConvertPosGBDToUGBD(controllerState.GripPos_GBD);
961  Vector3 fingertipsPosition_UGBD = ConvertPosGBDToUGBD(controllerState.FingertipsPos_GBD);
962  Vector3 aimPosition_UGBD = ConvertPosGBDToUGBD(controllerState.AimPos_GBD);
963  var rotation_UGBD = CalculateRotation(controllerState.RotToWND_GBD);
964 
965  ProcessTrackingData(gripPosition_UGBD, fingertipsPosition_UGBD, aimPosition_UGBD,
966  rotation_UGBD, wandSettings, scaleSettings, gameboardSettings,
968  }
969 
977  protected override void SetInvalidPoseGameboardSpace(in T5_ControllerState t5_ControllerState, WandSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
978  {
979  SetPoseGameboardSpace(t5_ControllerState, settings, scaleSettings, gameboardSettings);
980  }
981 
982  protected override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
983  {
984  if (wandSettings.GripPoint != null)
985  {
986  wandSettings.GripPoint.transform.SetPositionAndRotation(gripPose_UnityWorldSpace.position, gripPose_UnityWorldSpace.rotation);
987  }
988 
989  if (wandSettings.FingertipPoint != null)
990  {
991  wandSettings.FingertipPoint.transform.SetPositionAndRotation(fingertipsPose_UnityWorldSpace.position, fingertipsPose_UnityWorldSpace.rotation);
992  }
993 
994  if (wandSettings.AimPoint != null)
995  {
996  wandSettings.AimPoint.transform.SetPositionAndRotation(aimPose_UnityWorldSpace.position, aimPose_UnityWorldSpace.rotation);
997  }
998  }
999 
1000  public virtual void Dispose()
1001  {
1002  Log.Info($"Glasses {glassesHandle} {controllerIndex} Wand disconnected");
1003  }
1004 
1005  #endregion Overrides
1006 
1007 
1008  #region Private Helper Functions
1009 
1010  protected Quaternion CalculateRotation(Quaternion rotToWND_GBD)
1011  {
1012  Quaternion rotToDW_GBD = Quaternion.AngleAxis(90f, Vector3.right);
1013  Quaternion rotToGBD_DW = Quaternion.Inverse(rotToDW_GBD);
1014  Quaternion rotToWND_DW = rotToWND_GBD * rotToGBD_DW;
1015  Quaternion rotToUGBD_UWND = new Quaternion(rotToWND_DW.x, -rotToWND_DW.y, rotToWND_DW.z, rotToWND_DW.w);
1016  return rotToUGBD_UWND;
1017  }
1018 
1019  protected void ProcessTrackingData(Vector3 gripPosition_UGBD, Vector3 fingertipsPosition_UGBD, Vector3 aimPosition_UGBD, Quaternion rotToUGBD_WND,
1020  WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings,
1021  out Pose gripPose_UGBD, out Pose fingertipsPose_UGBD, out Pose aimPose_UGBD)
1022  {
1023  var incomingGripPose_UGBD = new Pose(gripPosition_UGBD, rotToUGBD_WND);
1024  var incomingFingertipsPose_UGBD = new Pose(fingertipsPosition_UGBD, rotToUGBD_WND);
1025  var incomingAimPose_UGBD = new Pose(aimPosition_UGBD, rotToUGBD_WND);
1026 
1027  // Get the distance between the tracking points and the grip point.
1028  // Currently, when a pose is considered invalid, the position and rotation reported by the native plugin are completely zero'd out.
1029  // This crunches the tracking points together unless we consider the stale positions from the previous frame.
1030  // It also means that we don't get the desired behavior for TrackingFailureMode.FreezePosition,
1031  // in which the wand position freezes while still showing rotation values reported by the IMU.
1032  // TODO: In the native plugin, even for invalid poses (both wand and glasses, which are also affected),
1033  // include an offset for the tracking points, and pass through rotation data.
1034  var staleGripPose_UGBD = pose_UGBD;
1035  var staleFingertipsPose_UGBD = fingertipsPose_GameboardSpace;
1036  var staleAimPose_UGBD = aimPose_GameboardSpace;
1037 
1038  var gripPointOffsetDistance = 0f;
1039  var fingertipsPointOffsetDistance = Mathf.Max((fingertipsPosition_UGBD - gripPosition_UGBD).magnitude,
1040  (staleFingertipsPose_UGBD.position - staleGripPose_UGBD.position).magnitude);
1041  var aimPointOffsetDistance = Mathf.Max((aimPosition_UGBD - gripPosition_UGBD).magnitude,
1042  (staleAimPose_UGBD.position - staleGripPose_UGBD.position).magnitude);
1043 
1044  // Handle invalid poses
1045  gripPose_UGBD = FilterTrackingPointPose(staleGripPose_UGBD, staleGripPose_UGBD, incomingGripPose_UGBD, gripPointOffsetDistance, wandSettings);
1046  fingertipsPose_UGBD = FilterTrackingPointPose(staleGripPose_UGBD, staleFingertipsPose_UGBD, incomingFingertipsPose_UGBD, fingertipsPointOffsetDistance, wandSettings);
1047  aimPose_UGBD = FilterTrackingPointPose(staleGripPose_UGBD, staleAimPose_UGBD, incomingAimPose_UGBD, aimPointOffsetDistance, wandSettings);
1048 
1049  }
1050 
1051  protected Pose FilterTrackingPointPose(Pose staleGripPointPose, Pose staleTrackingPointPose,
1052  Pose newTrackingPointPose, float trackingPointOffsetDistance, WandSettings settings)
1053  {
1054  if (!isTracked && settings.RejectUntrackedPositionData)
1055  {
1056  switch (settings.FailureMode)
1057  {
1058  default:
1059  // If we have an undefined FailureMode for some reason, fall through to FreezePosition
1060  case TrackableSettings.TrackingFailureMode.FreezePosition:
1061  // We need to determine where to put each tracking point.
1062  // We only want to freeze the position of the grip point while allowing the other
1063  // points to rotate around it, colinear to the grip point's forward vector.
1064  var extrapolatedPosition = staleGripPointPose.position +
1065  Quaternion.Inverse(newTrackingPointPose.rotation) * Vector3.forward * trackingPointOffsetDistance;
1066  return new Pose(extrapolatedPosition, newTrackingPointPose.rotation);
1067  case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
1068  return staleTrackingPointPose;
1069  case TrackableSettings.TrackingFailureMode.SnapToDefault:
1070  return GetDefaultPoseGameboardSpace(settings);
1071  }
1072  }
1073  else
1074  {
1075  return newTrackingPointPose;
1076  }
1077  }
1078 
1079  #endregion Private Helper Functions
1080  }
1081 
1082 #if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1083  private class WandDeviceCore : WandCore
1084  {
1085  internal WandDevice wandDevice;
1086 
1087  private enum TrackingState : int
1088  {
1089  None,
1090  Limited,
1091  Tracking
1092  }
1093 
1094  public WandDeviceCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex) : base(glassesHandle, controllerIndex)
1095  {
1096  if(Glasses.TryGetGlassesDevice(glassesHandle, out var glassesDevice))
1097  {
1098  //Add the new Wand Device to the Input System only when a new Wand Core is being created
1099  Input.AddWandDevice(glassesDevice.PlayerIndex, controllerIndex);
1100  wandDevice = Input.GetWandDevice(glassesDevice.PlayerIndex, controllerIndex);
1101  InputSystem.QueueConfigChangeEvent(wandDevice);
1102  if(controllerIndex == ControllerIndex.Right)
1103  {
1104  glassesDevice.RightWand = wandDevice;
1105  }
1106  else
1107  {
1108  glassesDevice.LeftWand = wandDevice;
1109  }
1110  if(TiltFiveManager2.IsInstantiated){
1111  TiltFiveManager2.Instance.RefreshInputDevicePairings();
1112  }
1113  }
1114  }
1115 
1116  public override void Dispose()
1117  {
1118  base.Dispose();
1119 
1120  if (Player.TryGetPlayerIndex(glassesHandle, out var playerIndex))
1121  {
1122  Input.RemoveWandDevice(playerIndex, controllerIndex);
1123  }
1124  }
1125 
1126  public override void GetLatestInputs()
1127  {
1128  base.GetLatestInputs();
1129 
1130  // If the wandDevice isn't added to the input system for any reason,
1131  // don't bother queueing any delta state events.
1132  if (!wandDevice.added || !wandDevice.enabled)
1133  {
1134  return;
1135  }
1136 
1137  InputSystem.QueueDeltaStateEvent(wandDevice.TiltFive, currentState.HasValue && currentState.Value.TryGetButton(WandButton.T5));
1138  InputSystem.QueueDeltaStateEvent(wandDevice.One, currentState.HasValue && currentState.Value.TryGetButton(WandButton.One));
1139  InputSystem.QueueDeltaStateEvent(wandDevice.Two, currentState.HasValue && currentState.Value.TryGetButton(WandButton.Two));
1140  InputSystem.QueueDeltaStateEvent(wandDevice.Three, currentState.HasValue && currentState.Value.TryGetButton(WandButton.Three));
1141  InputSystem.QueueDeltaStateEvent(wandDevice.A, currentState.HasValue && currentState.Value.TryGetButton(WandButton.A));
1142  InputSystem.QueueDeltaStateEvent(wandDevice.B, currentState.HasValue && currentState.Value.TryGetButton(WandButton.B));
1143  InputSystem.QueueDeltaStateEvent(wandDevice.X, currentState.HasValue && currentState.Value.TryGetButton(WandButton.X));
1144  InputSystem.QueueDeltaStateEvent(wandDevice.Y, currentState.HasValue && currentState.Value.TryGetButton(WandButton.Y));
1145 
1146  InputSystem.QueueDeltaStateEvent(wandDevice.Stick, currentState.HasValue ? currentState.Value.TryGetStick() : Vector2.zero);
1147  InputSystem.QueueDeltaStateEvent(wandDevice.Trigger, currentState.HasValue ? currentState.Value.TryGetTrigger() : 0f);
1148  }
1149 
1150  protected override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
1151  {
1152  base.SetDrivenObjectTransform(wandSettings, scaleSettings, gameBoardSettings);
1153 
1154  // If the wandDevice isn't added to the input system for any reason,
1155  // don't bother queueing any delta state events.
1156  if (!wandDevice.added || !wandDevice.enabled)
1157  {
1158  return;
1159  }
1160 
1161  // Time to inject our wand state into the Input System.
1162  QueueDeltaStateEvent(wandDevice.devicePosition, pose_UWRLD.position);
1163  QueueDeltaStateEvent(wandDevice.FingertipsPosition, fingertipsPose_UnityWorldSpace.position);
1164  QueueDeltaStateEvent(wandDevice.AimPosition, aimPose_UnityWorldSpace.position);
1165 
1166  QueueDeltaStateEvent(wandDevice.RawGripPosition, pose_UGBD.position);
1167  QueueDeltaStateEvent(wandDevice.RawFingertipsPosition, fingertipsPose_GameboardSpace.position);
1168  QueueDeltaStateEvent(wandDevice.RawAimPosition, aimPose_GameboardSpace.position);
1169 
1170  QueueDeltaStateEvent(wandDevice.deviceRotation, pose_UWRLD.rotation);
1171  QueueDeltaStateEvent(wandDevice.RawRotation, pose_UGBD.rotation);
1172 
1173  InputSystem.QueueDeltaStateEvent(wandDevice.isTracked, isTracked);
1174 
1175  var trackingState = TrackingState.Tracking;
1176  if (!isTracked)
1177  {
1178  trackingState = wandSettings.FailureMode == TrackableSettings.TrackingFailureMode.FreezePosition
1179  ? TrackingState.Limited
1180  : TrackingState.None;
1181  }
1182 
1183  InputSystem.QueueDeltaStateEvent(wandDevice.trackingState, (int)trackingState);
1184  }
1185 
1186  private static void QueueDeltaStateEvent(Vector3Control vector3Control, Vector3 delta)
1187  {
1188  InputSystem.QueueDeltaStateEvent(vector3Control.x, delta.x);
1189  InputSystem.QueueDeltaStateEvent(vector3Control.y, delta.y);
1190  InputSystem.QueueDeltaStateEvent(vector3Control.z, delta.z);
1191  }
1192 
1193  // For some reason, using QueueDeltaStateEvent on a QuaternionControl with a Quaternion as the delta state doesn't work.
1194  // As a workaround, let's do it component-wise, since we know floats seem fine.
1195  private static void QueueDeltaStateEvent(QuaternionControl quaternionControl, Quaternion delta)
1196  {
1197  InputSystem.QueueDeltaStateEvent(quaternionControl.w, delta.w);
1198  InputSystem.QueueDeltaStateEvent(quaternionControl.x, delta.x);
1199  InputSystem.QueueDeltaStateEvent(quaternionControl.y, delta.y);
1200  InputSystem.QueueDeltaStateEvent(quaternionControl.z, delta.z);
1201  }
1202  }
1203 #endif
1204 
1205  #endregion Private Classes
1206 
1207 
1208  #region Public Classes
1209 
1245  public class WaitUntilWandConnected : CustomYieldInstruction
1246  {
1247  public override bool keepWaiting => !Player.IsConnected(playerIndex) // Keep waiting if the player isn't connected,
1248  || !Wand.TryCheckConnected(out bool wandConnected, playerIndex, controllerIndex) // or if their wand can't be checked,
1249  || !wandConnected; // or if their wand isn't connected.
1252 
1254  {
1255  this.playerIndex = playerIndex;
1256  this.controllerIndex = controllerIndex;
1257  }
1258  }
1259 
1260  #endregion
1261  }
1262 
1263 }
TiltFive.Input.WandButton WandButton
Definition: Wand.cs:28
The Glasses API and runtime.
Definition: Glasses.cs:35
static bool IsTracked(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are tracked.
Definition: Glasses.cs:142
static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
Gets the friendly name associated with the specified player's glasses in the Tilt Five Control Panel.
Definition: Glasses.cs:178
Provides access to Wand inputs
Definition: Input.cs:31
The Logger.
Definition: Log.cs:42
static void Info(string m, params object[] list)
INFO logging function call.
Definition: Log.cs:140
static void Error(string m, params object[] list)
ERROR logging function call.
Definition: Log.cs:127
Provides access to player settings and functionality.
Definition: Player.cs:31
static bool IsConnected(PlayerIndex playerIndex)
Determines whether the specified player has an associated pair of glasses connected.
Definition: Player.cs:48
ScaleSettings contains the scale data used to translate between Unity units and the user's physical s...
static Pose GameboardToWorldSpace(Pose pose_GameboardSpace, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
TrackingFailureMode FailureMode
Suspends coroutine execution until the provided player's wand has connected.
Definition: Wand.cs:1246
WaitUntilWandConnected(PlayerIndex playerIndex, ControllerIndex controllerIndex)
Definition: Wand.cs:1253
ControllerIndex controllerIndex
Definition: Wand.cs:1251
Internal Wand core runtime.
Definition: Wand.cs:707
Pose FilterTrackingPointPose(Pose staleGripPointPose, Pose staleTrackingPointPose, Pose newTrackingPointPose, float trackingPointOffsetDistance, WandSettings settings)
Definition: Wand.cs:1051
virtual void Dispose()
Definition: Wand.cs:1000
bool TryGetButtonDown(WandButton button, out bool buttonDown)
Definition: Wand.cs:792
Pose aimPose_UnityWorldSpace
Definition: Wand.cs:718
bool TryGetStickTilt(out Vector2 stickTilt)
Definition: Wand.cs:830
float GetTrigger()
Definition: Wand.cs:836
T5_ControllerState? currentState
Definition: Wand.cs:720
T5_ControllerState? previousState
Definition: Wand.cs:721
bool TryGetButtonUp(WandButton button, out bool buttonUp)
Definition: Wand.cs:818
override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Sets the pose values of the tracked object in Unity World Space
Definition: Wand.cs:885
new void Reset(WandSettings wandSettings)
Definition: Wand.cs:852
bool TryGetButton(WandButton button, out bool pressed)
Definition: Wand.cs:764
bool GetButton(WandButton button)
Definition: Wand.cs:758
override bool TryCheckConnected(out bool connected)
Determines whether the tracked object is still connected.
Definition: Wand.cs:892
override bool TryGetStateFromPlugin(out T5_ControllerState controllerState, out bool poseIsValid, GameBoardSettings gameBoardSettings)
Definition: Wand.cs:927
bool TryGetTrigger(out float triggerDisplacement)
Definition: Wand.cs:841
override void SetPoseGameboardSpace(in T5_ControllerState controllerState, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Definition: Wand.cs:943
void ProcessTrackingData(Vector3 gripPosition_UGBD, Vector3 fingertipsPosition_UGBD, Vector3 aimPosition_UGBD, Quaternion rotToUGBD_WND, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, out Pose gripPose_UGBD, out Pose fingertipsPose_UGBD, out Pose aimPose_UGBD)
Definition: Wand.cs:1019
Pose fingertipsPose_UnityWorldSpace
Definition: Wand.cs:717
readonly GlassesHandle glassesHandle
Definition: Wand.cs:710
Pose aimPose_GameboardSpace
Definition: Wand.cs:714
WandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Definition: Wand.cs:728
Pose fingertipsPose_GameboardSpace
Definition: Wand.cs:713
override void SetInvalidPoseGameboardSpace(in T5_ControllerState t5_ControllerState, WandSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Handle setting the wand pose when we know the controller state isn't valid.
Definition: Wand.cs:977
bool GetButtonDown(WandButton button)
Definition: Wand.cs:772
bool GetButtonUp(WandButton button)
Definition: Wand.cs:799
virtual new void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Wand.cs:857
override void SetDefaultPoseGameboardSpace(WandSettings settings)
Definition: Wand.cs:868
Pose gripPose_UnityWorldSpace
Definition: Wand.cs:716
Vector2 GetStickTilt()
Definition: Wand.cs:825
override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Wand.cs:982
readonly ControllerIndex controllerIndex
Definition: Wand.cs:711
Quaternion CalculateRotation(Quaternion rotToWND_GBD)
Definition: Wand.cs:1010
static Pose GetDefaultPoseGameboardSpace(WandSettings settings)
Definition: Wand.cs:876
virtual void GetLatestInputs()
Definition: Wand.cs:737
The Wand API and runtime.
Definition: Wand.cs:56
static bool IsTracked(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Definition: Wand.cs:201
static bool TryCheckConnected(out bool connected, PlayerIndex playerIndex, ControllerIndex controllerIndex=ControllerIndex.Right)
Gets the connection status of the indicated wand.
Definition: Wand.cs:220
static readonly Vector3 DEFAULT_WAND_HANDEDNESS_OFFSET_GAME_BOARD_SPACE
A left/right offset to the default wand position, depending on handedness.
Definition: Wand.cs:74
HashSet< GlassesHandle > incomingHandles
Definition: Wand.cs:87
static readonly Quaternion DEFAULT_WAND_ROTATION_GAME_BOARD_SPACE
The default rotation of the wand relative to the board.
Definition: Wand.cs:83
static readonly double wandScanInterval
Definition: Wand.cs:98
static void ScanForWands()
Definition: Wand.cs:311
static readonly Vector3 DEFAULT_WAND_POSITION_GAME_BOARD_SPACE
The default position of the wand relative to the board.
Definition: Wand.cs:70
static bool TryScanForWands()
Definition: Wand.cs:605
static int currentFrame
Definition: Wand.cs:104
static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex=PlayerIndex.One)
Definition: Wand.cs:305
static bool wandAvailabilityErroredOnce
Definition: Wand.cs:100
static bool TryGetWandAvailability(out bool connected, GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Definition: Wand.cs:637
WandCore ObtainWandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Definition: Wand.cs:670
static DateTime lastScanAttempt
Definition: Wand.cs:95
Dictionary< GlassesHandle, WandPair > wandCores
The collection of WandCores. GlassesHandles are mapped to pairs of right/left WandCores.
Definition: Wand.cs:62
static Quaternion GetRotation(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Gets the rotation of the wand in world space.
Definition: Wand.cs:189
static bool TrySendImpulse(float amplitude, float duration, PlayerIndex playerIndex=PlayerIndex.One, ControllerIndex controllerIndex=ControllerIndex.Right)
Try to send a haptics impulse to the specified wand
Definition: Wand.cs:267
HashSet< GlassesHandle > lostHandles
Definition: Wand.cs:89
static Vector3 GetPosition(ControllerIndex controllerIndex=ControllerIndex.Right, ControllerPosition controllerPosition=ControllerPosition.Grip, PlayerIndex playerIndex=PlayerIndex.One)
Gets the position of the wand in world space.
Definition: Wand.cs:158
HashSet< WandCore > lostWands
Definition: Wand.cs:91
Wand Settings encapsulates all configuration data used by the Wand's tracking runtime to compute the ...
Definition: Wand.cs:38
ControllerIndex controllerIndex
Definition: Wand.cs:39
GameObject AimPoint
Definition: Wand.cs:43
GameObject FingertipPoint
Definition: Wand.cs:42
GameObject GripPoint
Definition: Wand.cs:41
Definition: Log.cs:21
ControllerPosition
Points of interest along the wand controller, such as the handle position or wand tip.
ControllerIndex
Since wands are all physically identical (they have no "handedness"), it doesn't make sense to addres...
PlayerIndex
The Player index (e.g. Player One, Player Two, etc)
WandCore RightWand
Definition: Wand.cs:113
bool TryGet(ControllerIndex controllerIndex, out WandCore wandCore)
Definition: Wand.cs:139
WandPair(WandCore right, WandCore left)
Definition: Wand.cs:116
WandCore LeftWand
Definition: Wand.cs:114