Tilt Five™ Unity API  1.3.0
 
Loading...
Searching...
No Matches
Wand.cs
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020-2022 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
17using System;
18using System.Collections.Generic;
19using UnityEngine;
21
22#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
23using UnityEngine.InputSystem;
24using UnityEngine.InputSystem.Users;
25using UnityEngine.InputSystem.Controls;
26#endif
27
29
30namespace 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 wandScanRate = 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
151 // Update is called once per frame
152 public static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex = PlayerIndex.One)
153 {
154 // Update the relevant WandCore
155 if (Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
156 {
157 Update(glassesHandle, wandSettings, scaleSettings, gameBoardSettings);
158 }
159 }
160
161 internal static void Update(GlassesHandle glassesHandle, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
162 {
163 if(Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
164 && wandPair.TryGet(wandSettings.controllerIndex, out var wandCore))
165 {
166 wandCore.Update(wandSettings, scaleSettings, gameBoardSettings);
167 }
168 }
169
170 private static bool TryScanForWands()
171 {
172 var currentTime = System.DateTime.Now;
173 var timeSinceLastScan = currentTime - lastScanAttempt;
174
175 // Scan for wands if necessary.
176 // TODO: Implement more robust disconnect detection, communicate wand availability events to users, offer user option to swap wands.
177 if (timeSinceLastScan.TotalSeconds >= wandScanRate
179 {
180 int result = 1;
181
182 try
183 {
184 result = NativePlugin.ScanForWands();
185 }
186 catch (System.DllNotFoundException e)
187 {
188 Log.Info(
189 "Could not connect to Tilt Five plugin to scan for wands: {0}",
190 e);
191 }
192 catch (Exception e)
193 {
194 Log.Error(e.Message);
195 }
196
197 lastScanAttempt = currentTime;
198 return (0 == result);
199 }
200
201 return false;
202 }
203
204 public static void ScanForWands()
205 {
206 // Tell the native plugin to search for wands.
208
209 // Obtain the latest set of connected glasses
210 var connectedGlassesHandles = Glasses.GetAllConnectedGlassesHandles();
211
212 // Add/Remove entries from the wandCores dictionary depending on which glasses appeared/disappeared
213 // ---------------------------------------------------
214 var wandCores = Instance.wandCores;
215 var incomingHandles = Instance.incomingHandles;
216 var lostHandles = Instance.lostHandles;
217 var lostWands = Instance.lostWands;
218
219 incomingHandles.Clear();
220 lostHandles.Clear();
221 lostWands.Clear();
222
223 // Add newly connected wands
224 for (int i = 0; i < connectedGlassesHandles.Length; i++)
225 {
226 var glassesHandle = connectedGlassesHandles[i];
227 incomingHandles.Add(glassesHandle);
228
229 var rightWandCore = Instance.ObtainWandCore(glassesHandle, ControllerIndex.Right);
230 var leftWandCore = Instance.ObtainWandCore(glassesHandle, ControllerIndex.Left);
231
232 // Obtain and store a pair of WandCores to associate with this GlassesHandle.
233 // If either wand is unavailable, we'll just store null.
234 // This will also clear any WandCores that represented a now-disconnected wand.
235 wandCores[glassesHandle] = new WandPair(rightWandCore, leftWandCore);
236 }
237
238 // Prune disconnected wands
239 foreach(var glassesHandle in wandCores.Keys)
240 {
241 // If a pair of glasses disconnects, save its handle for the next foreach loop below
242 if(!incomingHandles.Contains(glassesHandle))
243 {
244 lostHandles.Add(glassesHandle);
245 }
246 // otherwise, check if its wands are still connected.
247 else
248 {
249 if(wandCores.TryGetValue(glassesHandle, out var wandPair))
250 {
251 if(wandPair.TryGet(ControllerIndex.Left, out var leftWandCore)
252 && (!TryGetWandAvailability(out bool leftWandConnected, glassesHandle, ControllerIndex.Left) || !leftWandConnected))
253 {
254 lostWands.Add(leftWandCore);
255 }
256 if (wandPair.TryGet(ControllerIndex.Right, out var rightWandCore)
257 && (!TryGetWandAvailability(out bool rightWandConnected, glassesHandle, ControllerIndex.Right) || !rightWandConnected))
258 {
259 lostWands.Add(rightWandCore);
260 }
261 }
262 }
263 }
264
265 foreach(var lostHandle in lostHandles)
266 {
267 var lostWandPair = wandCores[lostHandle];
268
269 if (lostWandPair.TryGet(ControllerIndex.Right, out var lostRightWand))
270 {
271 lostWands.Add(lostRightWand);
272 }
273
274 if (lostWandPair.TryGet(ControllerIndex.Left, out var lostLeftWand))
275 {
276 lostWands.Add(lostLeftWand);
277 }
278 wandCores.Remove(lostHandle);
279 }
280
281 foreach (var lostWand in lostWands)
282 {
283 lostWand.Dispose();
284 }
285 }
286
287 internal static void OnDisable()
288 {
289 foreach (var wandPair in Instance.wandCores.Values)
290 {
291 wandPair.RightWand?.Dispose();
292 wandPair.LeftWand?.Dispose();
293 }
294 Instance.wandCores.Clear();
295 }
296
304 public static Vector3 GetPosition(
305 ControllerIndex controllerIndex = ControllerIndex.Right,
306 ControllerPosition controllerPosition = ControllerPosition.Grip,
307 PlayerIndex playerIndex = PlayerIndex.One)
308 {
309 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
310 || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
311 || !wandPair.TryGet(controllerIndex, out var wandCore))
312 {
313 return Vector3.zero;
314 }
315
316 switch (controllerPosition)
317 {
318 case ControllerPosition.Fingertips:
319 return wandCore.fingertipsPose_UnityWorldSpace.position;
320 case ControllerPosition.Aim:
321 return wandCore.aimPose_UnityWorldSpace.position;
322 case ControllerPosition.Grip:
323 return wandCore.Pose_UnityWorldSpace.position;
324 default:
325 return Vector3.zero;
326 }
327 }
328
335 public static Quaternion GetRotation(ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
336 {
337 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
338 || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
339 || !wandPair.TryGet(controllerIndex, out var wandCore))
340 {
341 return Quaternion.identity;
342 }
343
344 return wandCore.Pose_UnityWorldSpace.rotation;
345 }
346
347 public static bool IsTracked(ControllerIndex controllerIndex = ControllerIndex.Right, PlayerIndex playerIndex = PlayerIndex.One)
348 {
349 if (!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
350 || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
351 || !wandPair.TryGet(controllerIndex, out var wandCore))
352 {
353 return false;
354 }
355
356 return wandCore.IsTracked;
357 }
358
366 public static bool TryCheckConnected(out bool connected, PlayerIndex playerIndex, ControllerIndex controllerIndex = ControllerIndex.Right)
367 {
368 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
369 || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
370 || !wandPair.TryGet(controllerIndex, out var wandCore))
371 {
372 connected = false;
373 return false;
374 }
375
376 connected = wandCore.IsConnected;
377 return true;
378 }
379
380#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
381 public static bool TryGetWandDevice(PlayerIndex playerIndex, ControllerIndex controllerIndex, out WandDevice wandDevice)
382 {
383 if(!Player.TryGetGlassesHandle(playerIndex, out var glassesHandle) || !Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
384 || !wandPair.TryGet(controllerIndex, out var wandCore) || !(wandCore is WandDeviceCore wandDeviceCore))
385 {
386 wandDevice = null;
387 return false;
388 }
389
390 wandDevice = wandDeviceCore.wandDevice;
391 return true;
392 }
393#endif
394
395 private static bool TryGetWandAvailability(out bool connected, GlassesHandle glassesHandle, ControllerIndex controllerIndex)
396 {
398 {
399 try
400 {
401 T5_Bool wandAvailable = false;
402 int result = NativePlugin.GetWandAvailability(glassesHandle, ref wandAvailable, controllerIndex);
403
404 if (result == 0)
405 {
406 connected = wandAvailable;
407 return true;
408 }
409 }
410 catch (DllNotFoundException e)
411 {
412 Log.Info("Could not connect to Tilt Five plugin for wand: {0}", e.Message);
414 }
415 catch (Exception e)
416 {
417 Log.Error(
418 "Failed to connect to Tilt Five plugin for wand availability: {0}",
419 e.ToString());
421 }
422 }
423
424 connected = false;
425 return false;
426 }
427
428 #endregion Public Functions
429
430
431 #region Internal Functions
432
433 internal static void GetLatestInputs()
434 {
435 // If the previous/current wand states are shuffled more than once per frame,
436 // our button rising/falling edge detection breaks.
437 // Detect this scenario and return early to prevent this.
438 if (Time.frameCount == currentFrame)
439 {
440 return;
441 }
442 currentFrame = Time.frameCount;
443
444 foreach (var wandPair in Instance.wandCores.Values)
445 {
446 wandPair.RightWand?.GetLatestInputs();
447 wandPair.LeftWand?.GetLatestInputs();
448 }
449 }
450
451 internal static bool GetButton(WandButton button, GlassesHandle glassesHandle,
452 ControllerIndex controllerIndex = ControllerIndex.Right)
453 {
454 if(!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
455 || !wandPair.TryGet(controllerIndex, out var wandCore))
456 {
457 return false;
458 }
459 return wandCore.GetButton(button);
460 }
461
462 internal static bool TryGetButton(WandButton button, out bool pressed,
463 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
464 {
465 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
466 || !wandPair.TryGet(controllerIndex, out var wandCore))
467 {
468 pressed = false;
469 return false;
470 }
471
472 return wandCore.TryGetButton(button, out pressed);
473 }
474
475 internal static bool GetButtonDown(WandButton button,
476 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
477 {
478 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
479 || !wandPair.TryGet(controllerIndex, out var wandCore))
480 {
481 return false;
482 }
483
484 return wandCore.GetButtonDown(button);
485 }
486
487 internal static bool TryGetButtonDown(WandButton button, out bool buttonDown,
488 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
489 {
490 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
491 || !wandPair.TryGet(controllerIndex, out var wandCore))
492 {
493 buttonDown = false;
494 return false;
495 }
496
497 return wandCore.TryGetButtonDown(button, out buttonDown);
498 }
499
500 internal static bool GetButtonUp(WandButton button,
501 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
502 {
503 if (Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
504 || !wandPair.TryGet(controllerIndex, out var wandCore))
505 {
506 // TODO: Handle corner case in which wandCore existed during the previous frame, and the button was pressed
507 // We'd want to return true in that scenario.
508 return false;
509 }
510
511 return wandCore.GetButtonUp(button);
512 }
513
514 internal static bool TryGetButtonUp(WandButton button, out bool buttonUp,
515 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
516 {
517 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
518 || !wandPair.TryGet(controllerIndex, out var wandCore))
519 {
520 // TODO: Handle corner case in which wandCore existed during the previous frame, and the button was pressed.
521 // We'd want to return true in that scenario.
522 buttonUp = false;
523 return false;
524 }
525
526 return wandCore.TryGetButtonUp(button, out buttonUp);
527 }
528
529 internal static Vector2 GetStickTilt(GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
530 {
531 if (Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
532 || !wandPair.TryGet(controllerIndex, out var wandCore))
533 {
534 return Vector2.zero;
535 }
536
537 return wandCore.GetStickTilt();
538 }
539
540 internal static bool TryGetStickTilt(out Vector2 stickTilt,
541 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
542 {
543 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
544 || !wandPair.TryGet(controllerIndex, out var wandCore))
545 {
546 stickTilt = Vector2.zero;
547 return false;
548 }
549
550 return wandCore.TryGetStickTilt(out stickTilt);
551 }
552
553 internal static float GetTrigger(GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
554 {
555 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
556 || !wandPair.TryGet(controllerIndex, out var wandCore))
557 {
558 return 0f;
559 }
560
561 return wandCore.GetTrigger();
562 }
563
564 internal static bool TryGetTrigger(out float triggerDisplacement,
565 GlassesHandle glassesHandle, ControllerIndex controllerIndex = ControllerIndex.Right)
566 {
567 if (!Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
568 || !wandPair.TryGet(controllerIndex, out var wandCore))
569 {
570 triggerDisplacement = 0f;
571 return false;
572 }
573
574 return wandCore.TryGetTrigger(out triggerDisplacement);
575 }
576
577 internal static bool TryGetWandControlsState(GlassesHandle glassesHandle, out T5_ControllerState? controllerState,
578 ControllerIndex controllerIndex = ControllerIndex.Right)
579 {
580 int result = 1;
581
582 try
583 {
585 result = NativePlugin.GetControllerState(glassesHandle, controllerIndex, ref state);
586 controllerState = (result == 0)
587 ? state
588 : (T5_ControllerState?)null;
589 }
590 catch (Exception e)
591 {
592 controllerState = null;
593 Log.Error(e.Message);
594 }
595
596 return (0 == result);
597 }
598
599 #endregion Internal Functions
600
601
602 #region Private Functions
603
604 private WandCore ObtainWandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
605 {
606 WandCore wandCore = null;
607 var glassesAlreadyMonitored = wandCores.TryGetValue(glassesHandle, out var wandPair);
608
609 // Ask the native plugin whether this wand is currently connected.
610 if (TryGetWandAvailability(out var wandConnected, glassesHandle, controllerIndex) && wandConnected)
611 {
612 // If we're not already monitoring this wand, go ahead and create a corresponding WandCore.
613 if (!glassesAlreadyMonitored || !wandPair.TryGet(controllerIndex, out wandCore))
614 {
615#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
616 wandCore = new WandDeviceCore(glassesHandle, controllerIndex);
617#else
618 wandCore = new WandCore(glassesHandle, controllerIndex);
619#endif
620 }
621 return wandCore;
622 }
623 // If this wand is disconnected and we were previously monitoring it, mark it as disconnected
624 else if(glassesAlreadyMonitored && wandPair.TryGet(controllerIndex, out var lostWandCore))
625 {
626 lostWands.Add(lostWandCore);
627 }
628
629 return wandCore;
630 }
631
632 #endregion Private Functions
633
634
635 #region Private Classes
636
640 private class WandCore : TrackableCore<WandSettings, T5_ControllerState>, IDisposable
641 {
642 #region Public Fields
643
644 public readonly GlassesHandle glassesHandle;
646
647 public Pose fingertipsPose_GameboardSpace = new Pose(DEFAULT_WAND_POSITION_GAME_BOARD_SPACE, Quaternion.identity);
648 public Pose aimPose_GameboardSpace = new Pose(DEFAULT_WAND_POSITION_GAME_BOARD_SPACE, Quaternion.identity);
649
653
656
657 #endregion Public Fields
658
659
660 #region Public Functions
661
663 {
664 this.glassesHandle = glassesHandle;
665 this.controllerIndex = controllerIndex;
666
667 Glasses.TryGetFriendlyName(glassesHandle, out var friendlyName);
668 Log.Info($"Glasses {glassesHandle} (\"{friendlyName}\") {Enum.GetName(typeof(ControllerIndex), controllerIndex)} Wand connected");
669 }
670
671 public virtual void GetLatestInputs()
672 {
674
675 try
676 {
678
680
681 currentState = (result == 0)
682 ? state
683 : (T5_ControllerState?)null;
684 }
685 catch (Exception e)
686 {
687 currentState = null;
688 Log.Error(e.Message);
689 }
690 }
691
692 public bool GetButton(WandButton button)
693 {
694 // If the wand isn't connected, GetButton() should return a default value of false.
695 return currentState?.GetButton(button) ?? false;
696 }
697
698 public bool TryGetButton(WandButton button, out bool pressed)
699 {
700 pressed = currentState?.GetButton(button) ?? false;
701
702 // If the wand isn't connected, TryGetButton() should fail.
703 return currentState.HasValue;
704 }
705
706 public bool GetButtonDown(WandButton button)
707 {
708
709 // If the current wand state is null, the wand isn't connected.
710 // If so, let the application assume the user isn't pressing the button currently.
711 var pressed = currentState?.GetButton(button) ?? false;
712
713 // If the previous wand state is null, the wand wasn't connected.
714 // If so, let the application assume the user wasn't pressing the button last frame.
715 var previouslyPressed = previousState?.GetButton(button) ?? false;
716
717 // The wand could potentially connect while the user is holding a button, so just report the button state.
718 if (!previousState.HasValue && currentState.HasValue)
719 {
720 return pressed;
721 }
722 // Return true if the button is currently pressed, but was unpressed on the previous frame.
723 return pressed && !previouslyPressed;
724 }
725
726 public bool TryGetButtonDown(WandButton button, out bool buttonDown)
727 {
728 // Even if this operation fails, give buttonDown a default value.
729 buttonDown = GetButtonDown(button);
730 return currentState.HasValue;
731 }
732
733 public bool GetButtonUp(WandButton button)
734 {
735 // If the current wand state is null, the wand isn't connected.
736 // If so, let the application assume the user isn't pressing the button currently.
737 var pressed = currentState?.GetButton(button) ?? false;
738
739 // If the previous wand state is null, the wand wasn't connected.
740 // If so, let the application assume the user wasn't pressing the button last frame.
741 var previouslyPressed = previousState?.GetButton(button) ?? false;
742
743 // Return true if the button is currently released, but was pressed on the previous frame.
744 return previousState.HasValue
745 ? !pressed && previouslyPressed
746 // If the current state exists but the previous state was null, the wand has just connected.
747 // There's no way for the button to be pressed during the previous frame,
748 // so there's no way for the button to have been released this frame. Always return false.
749 : false;
750 }
751
752 public bool TryGetButtonUp(WandButton button, out bool buttonUp)
753 {
754 // Even if this operation fails, give buttonUp a default value.
755 buttonUp = GetButtonUp(button);
756 return currentState.HasValue;
757 }
758
759 public Vector2 GetStickTilt()
760 {
761 return currentState?.Stick ?? Vector2.zero;
762 }
763
764 public bool TryGetStickTilt(out Vector2 stickTilt)
765 {
766 stickTilt = GetStickTilt();
767 return currentState.HasValue;
768 }
769
770 public float GetTrigger()
771 {
772 return currentState?.Trigger ?? 0.0f;
773 }
774
775 public bool TryGetTrigger(out float triggerDisplacement)
776 {
777 triggerDisplacement = GetTrigger();
778 return currentState.HasValue;
779 }
780
781 #endregion Public Functions
782
783
784 #region Overrides
785
786 public new void Reset(WandSettings wandSettings)
787 {
788 base.Reset(wandSettings);
789 }
790
791 public new virtual void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
792 {
793 if (wandSettings == null)
794 {
795 Log.Error("WandSettings configuration required for Wand tracking updates.");
796 return;
797 }
798
799 base.Update(wandSettings, scaleSettings, gameBoardSettings);
800 }
801
802 protected override void SetDefaultPoseGameboardSpace(WandSettings settings)
803 {
805 // We don't have a good offset that we can use for default fingertips/aim poses, so just use the default pose for everything
808 }
809
810 protected static Pose GetDefaultPoseGameboardSpace(WandSettings settings)
811 {
812 Vector3 defaultPosition = DEFAULT_WAND_POSITION_GAME_BOARD_SPACE;
813
815 * (settings.controllerIndex == ControllerIndex.Right ? 1f : -1f);
816 return new Pose(defaultPosition, DEFAULT_WAND_ROTATION_GAME_BOARD_SPACE);
817 }
818
819 protected override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
820 {
821 pose_UWRLD = GameboardToWorldSpace(pose_UGBD, scaleSettings, gameBoardSettings);
823 aimPose_UnityWorldSpace = GameboardToWorldSpace(aimPose_GameboardSpace, scaleSettings, gameBoardSettings);
824 }
825
826 protected override bool TryCheckConnected(out bool connected)
827 {
829 {
830 try
831 {
832 T5_Bool wandAvailable = false;
833 int result = NativePlugin.GetWandAvailability(glassesHandle, ref wandAvailable, controllerIndex);
834
835 if (result == 0)
836 {
837 isConnected = wandAvailable;
838 connected = wandAvailable;
839 return true;
840 }
841 }
842 catch (DllNotFoundException e)
843 {
844 Log.Info("Could not connect to Tilt Five plugin for wand: {0}", e.Message);
846 }
847 catch (Exception e)
848 {
849 Log.Error(
850 "Failed to connect to Tilt Five plugin for wand availability: {0}",
851 e.ToString());
853 }
854 }
855
856 isConnected = false;
857 connected = false;
858 return false;
859 }
860
861 protected override bool TryGetStateFromPlugin(out T5_ControllerState controllerState, out bool poseIsValid, GameBoardSettings gameBoardSettings)
862 {
863 if (!TryGetWandControlsState(glassesHandle, out var controllerStateResult, controllerIndex))
864 {
865 poseIsValid = false;
866 controllerState = new T5_ControllerState();
867
868 return false;
869 }
870
871 controllerState = controllerStateResult.Value;
872 poseIsValid = Glasses.IsTracked(glassesHandle) && controllerState.PoseValid;
873
874 return true;
875 }
876
877 protected override void SetPoseGameboardSpace(in T5_ControllerState controllerState,
878 WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
879 {
880 // Unity reference frames:
881 //
882 // UWND - Unity WaND local space.
883 // +x right, +y up, +z forward
884 // UGBD - Unity Gameboard space.
885 // +x right, +y up, +z forward
886 //
887 // Tilt Five reference frames:
888 //
889 // DW - Our right-handed version of Unity's default wand space.
890 // +x right, +y down, +z forward
891 // GBD - Gameboard space.
892 // +x right, +y forward, +z up
893
894 Vector3 gripPosition_UGBD = ConvertPosGBDToUGBD(controllerState.GripPos_GBD);
895 Vector3 fingertipsPosition_UGBD = ConvertPosGBDToUGBD(controllerState.FingertipsPos_GBD);
896 Vector3 aimPosition_UGBD = ConvertPosGBDToUGBD(controllerState.AimPos_GBD);
897 var rotation_UGBD = CalculateRotation(controllerState.RotToWND_GBD);
898
899 ProcessTrackingData(gripPosition_UGBD, fingertipsPosition_UGBD, aimPosition_UGBD,
900 rotation_UGBD, wandSettings, scaleSettings, gameboardSettings,
902 }
903
911 protected override void SetInvalidPoseGameboardSpace(in T5_ControllerState t5_ControllerState, WandSettings settings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
912 {
913 SetPoseGameboardSpace(t5_ControllerState, settings, scaleSettings, gameboardSettings);
914 }
915
916 protected override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
917 {
918 if (wandSettings.GripPoint != null)
919 {
920 wandSettings.GripPoint.transform.SetPositionAndRotation(gripPose_UnityWorldSpace.position, gripPose_UnityWorldSpace.rotation);
921 }
922
923 if (wandSettings.FingertipPoint != null)
924 {
925 wandSettings.FingertipPoint.transform.SetPositionAndRotation(fingertipsPose_UnityWorldSpace.position, fingertipsPose_UnityWorldSpace.rotation);
926 }
927
928 if (wandSettings.AimPoint != null)
929 {
930 wandSettings.AimPoint.transform.SetPositionAndRotation(aimPose_UnityWorldSpace.position, aimPose_UnityWorldSpace.rotation);
931 }
932 }
933
934 public virtual void Dispose()
935 {
936 Log.Info($"Glasses {glassesHandle} {controllerIndex} Wand disconnected");
937 }
938
939 #endregion Overrides
940
941
942 #region Private Helper Functions
943
944 protected Quaternion CalculateRotation(Quaternion rotToWND_GBD)
945 {
946 Quaternion rotToDW_GBD = Quaternion.AngleAxis(90f, Vector3.right);
947 Quaternion rotToGBD_DW = Quaternion.Inverse(rotToDW_GBD);
948 Quaternion rotToWND_DW = rotToWND_GBD * rotToGBD_DW;
949 Quaternion rotToUGBD_UWND = new Quaternion(rotToWND_DW.x, -rotToWND_DW.y, rotToWND_DW.z, rotToWND_DW.w);
950 return rotToUGBD_UWND;
951 }
952
953 protected void ProcessTrackingData(Vector3 gripPosition_UGBD, Vector3 fingertipsPosition_UGBD, Vector3 aimPosition_UGBD, Quaternion rotToUGBD_WND,
954 WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings,
955 out Pose gripPose_UGBD, out Pose fingertipsPose_UGBD, out Pose aimPose_UGBD)
956 {
957 var incomingGripPose_UGBD = new Pose(gripPosition_UGBD, rotToUGBD_WND);
958 var incomingFingertipsPose_UGBD = new Pose(fingertipsPosition_UGBD, rotToUGBD_WND);
959 var incomingAimPose_UGBD = new Pose(aimPosition_UGBD, rotToUGBD_WND);
960
961 // Get the distance between the tracking points and the grip point.
962 // Currently, when a pose is considered invalid, the position and rotation reported by the native plugin are completely zero'd out.
963 // This crunches the tracking points together unless we consider the stale positions from the previous frame.
964 // It also means that we don't get the desired behavior for TrackingFailureMode.FreezePosition,
965 // in which the wand position freezes while still showing rotation values reported by the IMU.
966 // TODO: In the native plugin, even for invalid poses (both wand and glasses, which are also affected),
967 // include an offset for the tracking points, and pass through rotation data.
968 var staleGripPose_UGBD = pose_UGBD;
969 var staleFingertipsPose_UGBD = fingertipsPose_GameboardSpace;
970 var staleAimPose_UGBD = aimPose_GameboardSpace;
971
972 var gripPointOffsetDistance = 0f;
973 var fingertipsPointOffsetDistance = Mathf.Max((fingertipsPosition_UGBD - gripPosition_UGBD).magnitude,
974 (staleFingertipsPose_UGBD.position - staleGripPose_UGBD.position).magnitude);
975 var aimPointOffsetDistance = Mathf.Max((aimPosition_UGBD - gripPosition_UGBD).magnitude,
976 (staleAimPose_UGBD.position - staleGripPose_UGBD.position).magnitude);
977
978 // Handle invalid poses
979 gripPose_UGBD = FilterTrackingPointPose(staleGripPose_UGBD, staleGripPose_UGBD, incomingGripPose_UGBD, gripPointOffsetDistance, wandSettings);
980 fingertipsPose_UGBD = FilterTrackingPointPose(staleGripPose_UGBD, staleFingertipsPose_UGBD, incomingFingertipsPose_UGBD, fingertipsPointOffsetDistance, wandSettings);
981 aimPose_UGBD = FilterTrackingPointPose(staleGripPose_UGBD, staleAimPose_UGBD, incomingAimPose_UGBD, aimPointOffsetDistance, wandSettings);
982
983 }
984
985 protected Pose FilterTrackingPointPose(Pose staleGripPointPose, Pose staleTrackingPointPose,
986 Pose newTrackingPointPose, float trackingPointOffsetDistance, WandSettings settings)
987 {
988 if (!isTracked && settings.RejectUntrackedPositionData)
989 {
990 switch (settings.FailureMode)
991 {
992 default:
993 // If we have an undefined FailureMode for some reason, fall through to FreezePosition
994 case TrackableSettings.TrackingFailureMode.FreezePosition:
995 // We need to determine where to put each tracking point.
996 // We only want to freeze the position of the grip point while allowing the other
997 // points to rotate around it, colinear to the grip point's forward vector.
998 var extrapolatedPosition = staleGripPointPose.position +
999 Quaternion.Inverse(newTrackingPointPose.rotation) * Vector3.forward * trackingPointOffsetDistance;
1000 return new Pose(extrapolatedPosition, newTrackingPointPose.rotation);
1001 case TrackableSettings.TrackingFailureMode.FreezePositionAndRotation:
1002 return staleTrackingPointPose;
1003 case TrackableSettings.TrackingFailureMode.SnapToDefault:
1004 return GetDefaultPoseGameboardSpace(settings);
1005 }
1006 }
1007 else
1008 {
1009 return newTrackingPointPose;
1010 }
1011 }
1012
1013 #endregion Private Helper Functions
1014 }
1015
1016#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1017 private class WandDeviceCore : WandCore
1018 {
1019 internal WandDevice wandDevice;
1020
1021 private enum TrackingState : int
1022 {
1023 None,
1024 Limited,
1025 Tracking
1026 }
1027
1028 public WandDeviceCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex) : base(glassesHandle, controllerIndex)
1029 {
1030 if(Glasses.TryGetGlassesDevice(glassesHandle, out var glassesDevice))
1031 {
1032 wandDevice = Input.GetWandDevice(glassesDevice.PlayerIndex, controllerIndex);
1033 InputSystem.QueueConfigChangeEvent(wandDevice);
1034 InputSystem.EnableDevice(wandDevice);
1035
1036 var inputUserCount = InputUser.all.Count;
1037 var playerIndex = (int)glassesDevice.PlayerIndex;
1038 var headPoseRoot = Glasses.GetPoseRoot(glassesDevice.PlayerIndex);
1039
1040 if (headPoseRoot != null && inputUserCount >= playerIndex)
1041 {
1042 var playerInput = headPoseRoot.GetComponentInChildren<PlayerInput>();
1043
1044 if(playerInput != null)
1045 {
1046 InputUser.PerformPairingWithDevice(wandDevice, playerInput.user);
1047 }
1048 }
1049 }
1050 }
1051
1052 public override void Dispose()
1053 {
1054 base.Dispose();
1055 InputSystem.QueueConfigChangeEvent(wandDevice);
1056 InputSystem.DisableDevice(wandDevice);
1057 }
1058
1059 public override void GetLatestInputs()
1060 {
1061 base.GetLatestInputs();
1062
1063 // If the wandDevice isn't added to the input system for any reason,
1064 // don't bother queueing any delta state events.
1065 if (!wandDevice.added || !wandDevice.enabled)
1066 {
1067 return;
1068 }
1069
1070 InputSystem.QueueDeltaStateEvent(wandDevice.TiltFive, currentState.HasValue && currentState.Value.TryGetButton(WandButton.T5));
1071 InputSystem.QueueDeltaStateEvent(wandDevice.One, currentState.HasValue && currentState.Value.TryGetButton(WandButton.One));
1072 InputSystem.QueueDeltaStateEvent(wandDevice.Two, currentState.HasValue && currentState.Value.TryGetButton(WandButton.Two));
1073 InputSystem.QueueDeltaStateEvent(wandDevice.Three, currentState.HasValue && currentState.Value.TryGetButton(WandButton.Three));
1074 InputSystem.QueueDeltaStateEvent(wandDevice.A, currentState.HasValue && currentState.Value.TryGetButton(WandButton.A));
1075 InputSystem.QueueDeltaStateEvent(wandDevice.B, currentState.HasValue && currentState.Value.TryGetButton(WandButton.B));
1076 InputSystem.QueueDeltaStateEvent(wandDevice.X, currentState.HasValue && currentState.Value.TryGetButton(WandButton.X));
1077 InputSystem.QueueDeltaStateEvent(wandDevice.Y, currentState.HasValue && currentState.Value.TryGetButton(WandButton.Y));
1078
1079 InputSystem.QueueDeltaStateEvent(wandDevice.Stick, currentState.HasValue ? currentState.Value.TryGetStick() : Vector2.zero);
1080 InputSystem.QueueDeltaStateEvent(wandDevice.Trigger, currentState.HasValue ? currentState.Value.TryGetTrigger() : 0f);
1081 }
1082
1083 protected override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
1084 {
1085 base.SetDrivenObjectTransform(wandSettings, scaleSettings, gameBoardSettings);
1086
1087 // If the wandDevice isn't added to the input system for any reason,
1088 // don't bother queueing any delta state events.
1089 if (!wandDevice.added || !wandDevice.enabled)
1090 {
1091 return;
1092 }
1093
1094 // Time to inject our wand state into the Input System.
1095 QueueDeltaStateEvent(wandDevice.devicePosition, pose_UWRLD.position);
1096 QueueDeltaStateEvent(wandDevice.FingertipsPosition, fingertipsPose_UnityWorldSpace.position);
1097 QueueDeltaStateEvent(wandDevice.AimPosition, aimPose_UnityWorldSpace.position);
1098
1099 QueueDeltaStateEvent(wandDevice.RawGripPosition, pose_UGBD.position);
1100 QueueDeltaStateEvent(wandDevice.RawFingertipsPosition, fingertipsPose_GameboardSpace.position);
1101 QueueDeltaStateEvent(wandDevice.RawAimPosition, aimPose_GameboardSpace.position);
1102
1103 QueueDeltaStateEvent(wandDevice.deviceRotation, pose_UWRLD.rotation);
1104 QueueDeltaStateEvent(wandDevice.RawRotation, pose_UGBD.rotation);
1105
1106 InputSystem.QueueDeltaStateEvent(wandDevice.isTracked, isTracked);
1107
1108 var trackingState = TrackingState.Tracking;
1109 if (!isTracked)
1110 {
1111 trackingState = wandSettings.FailureMode == TrackableSettings.TrackingFailureMode.FreezePosition
1112 ? TrackingState.Limited
1113 : TrackingState.None;
1114 }
1115
1116 InputSystem.QueueDeltaStateEvent(wandDevice.trackingState, (int)trackingState);
1117 }
1118
1119 private static void QueueDeltaStateEvent(Vector3Control vector3Control, Vector3 delta)
1120 {
1121 InputSystem.QueueDeltaStateEvent(vector3Control.x, delta.x);
1122 InputSystem.QueueDeltaStateEvent(vector3Control.y, delta.y);
1123 InputSystem.QueueDeltaStateEvent(vector3Control.z, delta.z);
1124 }
1125
1126 // For some reason, using QueueDeltaStateEvent on a QuaternionControl with a Quaternion as the delta state doesn't work.
1127 // As a workaround, let's do it component-wise, since we know floats seem fine.
1128 private static void QueueDeltaStateEvent(QuaternionControl quaternionControl, Quaternion delta)
1129 {
1130 InputSystem.QueueDeltaStateEvent(quaternionControl.w, delta.w);
1131 InputSystem.QueueDeltaStateEvent(quaternionControl.x, delta.x);
1132 InputSystem.QueueDeltaStateEvent(quaternionControl.y, delta.y);
1133 InputSystem.QueueDeltaStateEvent(quaternionControl.z, delta.z);
1134 }
1135 }
1136#endif
1137
1138 #endregion Private Classes
1139 }
1140
1141}
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:220
static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
Definition: Glasses.cs:250
static GameObject GetPoseRoot(PlayerIndex playerIndex)
Definition: Glasses.cs:296
Provides access to Wand inputs.
Definition: Input.cs:31
static bool GetWandAvailability(ControllerIndex controllerIndex=ControllerIndex.Right)
Gets the connection status of the indicated wand.
Definition: Input.cs:247
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
static int GetWandAvailability(UInt64 glassesHandle, ref T5_Bool wandAvailable, [MarshalAs(UnmanagedType.I4)] ControllerIndex wandTarget)
static int GetControllerState(UInt64 glassesHandle, [MarshalAs(UnmanagedType.I4)] ControllerIndex controllerIndex, ref T5_ControllerState controllerState)
static int ScanForWands()
Provides access to player settings and functionality.
Definition: Player.cs:16
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
Internal Wand core runtime.
Definition: Wand.cs:641
Pose FilterTrackingPointPose(Pose staleGripPointPose, Pose staleTrackingPointPose, Pose newTrackingPointPose, float trackingPointOffsetDistance, WandSettings settings)
Definition: Wand.cs:985
virtual void Dispose()
Definition: Wand.cs:934
bool TryGetButtonDown(WandButton button, out bool buttonDown)
Definition: Wand.cs:726
Pose aimPose_UnityWorldSpace
Definition: Wand.cs:652
bool TryGetStickTilt(out Vector2 stickTilt)
Definition: Wand.cs:764
float GetTrigger()
Definition: Wand.cs:770
T5_ControllerState? currentState
Definition: Wand.cs:654
T5_ControllerState? previousState
Definition: Wand.cs:655
bool TryGetButtonUp(WandButton button, out bool buttonUp)
Definition: Wand.cs:752
override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Sets the pose values of the tracked object in Unity World Space.
Definition: Wand.cs:819
new void Reset(WandSettings wandSettings)
Definition: Wand.cs:786
bool TryGetButton(WandButton button, out bool pressed)
Definition: Wand.cs:698
bool GetButton(WandButton button)
Definition: Wand.cs:692
override bool TryCheckConnected(out bool connected)
Determines whether the tracked object is still connected.
Definition: Wand.cs:826
override bool TryGetStateFromPlugin(out T5_ControllerState controllerState, out bool poseIsValid, GameBoardSettings gameBoardSettings)
Definition: Wand.cs:861
bool TryGetTrigger(out float triggerDisplacement)
Definition: Wand.cs:775
override void SetPoseGameboardSpace(in T5_ControllerState controllerState, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
Definition: Wand.cs:877
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:953
Pose fingertipsPose_UnityWorldSpace
Definition: Wand.cs:651
readonly GlassesHandle glassesHandle
Definition: Wand.cs:644
Pose aimPose_GameboardSpace
Definition: Wand.cs:648
WandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Definition: Wand.cs:662
Pose fingertipsPose_GameboardSpace
Definition: Wand.cs:647
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:911
bool GetButtonDown(WandButton button)
Definition: Wand.cs:706
bool GetButtonUp(WandButton button)
Definition: Wand.cs:733
virtual new void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Wand.cs:791
override void SetDefaultPoseGameboardSpace(WandSettings settings)
Definition: Wand.cs:802
Pose gripPose_UnityWorldSpace
Definition: Wand.cs:650
Vector2 GetStickTilt()
Definition: Wand.cs:759
override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Definition: Wand.cs:916
readonly ControllerIndex controllerIndex
Definition: Wand.cs:645
Quaternion CalculateRotation(Quaternion rotToWND_GBD)
Definition: Wand.cs:944
static Pose GetDefaultPoseGameboardSpace(WandSettings settings)
Definition: Wand.cs:810
virtual void GetLatestInputs()
Definition: Wand.cs:671
The Wand API and runtime.
Definition: Wand.cs:56
static bool IsTracked(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Definition: Wand.cs:347
static bool TryCheckConnected(out bool connected, PlayerIndex playerIndex, ControllerIndex controllerIndex=ControllerIndex.Right)
Gets the connection status of the indicated wand.
Definition: Wand.cs:366
static readonly double wandScanRate
Definition: Wand.cs:98
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 void ScanForWands()
Definition: Wand.cs:204
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:170
static int currentFrame
Definition: Wand.cs:104
static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex=PlayerIndex.One)
Definition: Wand.cs:152
static bool wandAvailabilityErroredOnce
Definition: Wand.cs:100
static bool TryGetWandAvailability(out bool connected, GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Definition: Wand.cs:395
WandCore ObtainWandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Definition: Wand.cs:604
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:335
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:304
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)
Represents a boolean value.
Contains wand related information (Pose, Buttons, Trigger, Stick, Battery)
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