18using System.Collections.Generic;
22#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
23using UnityEngine.InputSystem;
24using UnityEngine.InputSystem.Users;
25using UnityEngine.InputSystem.Controls;
57 #region Private Fields
62 private Dictionary<GlassesHandle, WandPair>
wandCores =
new Dictionary<GlassesHandle, WandPair>();
89 private HashSet<GlassesHandle>
lostHandles =
new HashSet<GlassesHandle>();
91 private HashSet<WandCore>
lostWands =
new HashSet<WandCore>();
106 #endregion Private Fields
109 #region Private Structs
126 switch (controllerIndex)
141 wandCore =
this[controllerIndex];
142 return wandCore !=
null;
146 #endregion Private Structs
149 #region Public Functions
155 if (
Player.TryGetGlassesHandle(playerIndex, out var glassesHandle))
157 Update(glassesHandle, wandSettings, scaleSettings, gameBoardSettings);
163 if(
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
166 wandCore.Update(wandSettings, scaleSettings, gameBoardSettings);
172 var currentTime = System.DateTime.Now;
186 catch (System.DllNotFoundException e)
189 "Could not connect to Tilt Five plugin to scan for wands: {0}",
198 return (0 == result);
210 var connectedGlassesHandles =
Glasses.GetAllConnectedGlassesHandles();
224 for (
int i = 0; i < connectedGlassesHandles.Length; i++)
226 var glassesHandle = connectedGlassesHandles[i];
239 foreach(var glassesHandle
in wandCores.Keys)
249 if(
wandCores.TryGetValue(glassesHandle, out var wandPair))
267 var lostWandPair =
wandCores[lostHandle];
287 internal static void OnDisable()
289 foreach (var wandPair
in Instance.wandCores.Values)
291 wandPair.RightWand?.Dispose();
292 wandPair.LeftWand?.Dispose();
309 if(!
Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
310 || !
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
311 || !wandPair.TryGet(controllerIndex, out var wandCore))
316 switch (controllerPosition)
319 return wandCore.fingertipsPose_UnityWorldSpace.position;
321 return wandCore.aimPose_UnityWorldSpace.position;
323 return wandCore.Pose_UnityWorldSpace.position;
337 if (!
Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
338 || !
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
339 || !wandPair.TryGet(controllerIndex, out var wandCore))
341 return Quaternion.identity;
344 return wandCore.Pose_UnityWorldSpace.rotation;
349 if (!
Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
350 || !
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
351 || !wandPair.TryGet(controllerIndex, out var wandCore))
356 return wandCore.IsTracked;
368 if(!
Player.TryGetGlassesHandle(playerIndex, out var glassesHandle)
369 || !
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
370 || !wandPair.TryGet(controllerIndex, out var wandCore))
376 connected = wandCore.IsConnected;
380#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
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))
390 wandDevice = wandDeviceCore.wandDevice;
406 connected = wandAvailable;
410 catch (DllNotFoundException e)
412 Log.
Info(
"Could not connect to Tilt Five plugin for wand: {0}", e.Message);
418 "Failed to connect to Tilt Five plugin for wand availability: {0}",
428 #endregion Public Functions
431 #region Internal Functions
433 internal static void GetLatestInputs()
444 foreach (var wandPair
in Instance.wandCores.Values)
446 wandPair.RightWand?.GetLatestInputs();
447 wandPair.LeftWand?.GetLatestInputs();
451 internal static bool GetButton(
WandButton button, GlassesHandle glassesHandle,
454 if(!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
455 || !wandPair.TryGet(controllerIndex, out var wandCore))
459 return wandCore.GetButton(button);
462 internal static bool TryGetButton(
WandButton button, out
bool pressed,
465 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
466 || !wandPair.TryGet(controllerIndex, out var wandCore))
472 return wandCore.TryGetButton(button, out pressed);
475 internal static bool GetButtonDown(
WandButton button,
478 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
479 || !wandPair.TryGet(controllerIndex, out var wandCore))
484 return wandCore.GetButtonDown(button);
487 internal static bool TryGetButtonDown(
WandButton button, out
bool buttonDown,
490 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
491 || !wandPair.TryGet(controllerIndex, out var wandCore))
497 return wandCore.TryGetButtonDown(button, out buttonDown);
500 internal static bool GetButtonUp(
WandButton button,
503 if (
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
504 || !wandPair.TryGet(controllerIndex, out var wandCore))
511 return wandCore.GetButtonUp(button);
514 internal static bool TryGetButtonUp(
WandButton button, out
bool buttonUp,
517 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
518 || !wandPair.TryGet(controllerIndex, out var wandCore))
526 return wandCore.TryGetButtonUp(button, out buttonUp);
531 if (
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
532 || !wandPair.TryGet(controllerIndex, out var wandCore))
537 return wandCore.GetStickTilt();
540 internal static bool TryGetStickTilt(out Vector2 stickTilt,
543 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
544 || !wandPair.TryGet(controllerIndex, out var wandCore))
546 stickTilt = Vector2.zero;
550 return wandCore.TryGetStickTilt(out stickTilt);
555 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
556 || !wandPair.TryGet(controllerIndex, out var wandCore))
561 return wandCore.GetTrigger();
564 internal static bool TryGetTrigger(out
float triggerDisplacement,
567 if (!
Instance.wandCores.TryGetValue(glassesHandle, out var wandPair)
568 || !wandPair.TryGet(controllerIndex, out var wandCore))
570 triggerDisplacement = 0f;
574 return wandCore.TryGetTrigger(out triggerDisplacement);
577 internal static bool TryGetWandControlsState(GlassesHandle glassesHandle, out
T5_ControllerState? controllerState,
586 controllerState = (result == 0)
592 controllerState =
null;
596 return (0 == result);
599 #endregion Internal Functions
602 #region Private Functions
607 var glassesAlreadyMonitored =
wandCores.TryGetValue(glassesHandle, out var wandPair);
613 if (!glassesAlreadyMonitored || !wandPair.TryGet(controllerIndex, out wandCore))
615#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
616 wandCore =
new WandDeviceCore(glassesHandle, controllerIndex);
618 wandCore =
new WandCore(glassesHandle, controllerIndex);
624 else if(glassesAlreadyMonitored && wandPair.TryGet(controllerIndex, out var lostWandCore))
632 #endregion Private Functions
635 #region Private Classes
642 #region Public Fields
657 #endregion Public Fields
660 #region Public Functions
668 Log.
Info($
"Glasses {glassesHandle} (\"{friendlyName}\") {Enum.GetName(typeof(ControllerIndex), controllerIndex)} Wand connected");
715 var previouslyPressed =
previousState?.GetButton(button) ??
false;
723 return pressed && !previouslyPressed;
741 var previouslyPressed =
previousState?.GetButton(button) ??
false;
744 return previousState.HasValue
745 ? !pressed && previouslyPressed
781 #endregion Public Functions
788 base.Reset(wandSettings);
793 if (wandSettings ==
null)
795 Log.
Error(
"WandSettings configuration required for Wand tracking updates.");
799 base.Update(wandSettings, scaleSettings, gameBoardSettings);
815 * (settings.controllerIndex == ControllerIndex.Right ? 1f : -1f);
838 connected = wandAvailable;
842 catch (DllNotFoundException e)
844 Log.
Info(
"Could not connect to Tilt Five plugin for wand: {0}", e.Message);
850 "Failed to connect to Tilt Five plugin for wand availability: {0}",
871 controllerState = controllerStateResult.Value;
900 rotation_UGBD, wandSettings, scaleSettings, gameboardSettings,
936 Log.
Info($
"Glasses {glassesHandle} {controllerIndex} Wand disconnected");
942 #region Private Helper Functions
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;
953 protected void ProcessTrackingData(Vector3 gripPosition_UGBD, Vector3 fingertipsPosition_UGBD, Vector3 aimPosition_UGBD, Quaternion rotToUGBD_WND,
955 out Pose gripPose_UGBD, out Pose fingertipsPose_UGBD, out Pose aimPose_UGBD)
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);
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);
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);
986 Pose newTrackingPointPose,
float trackingPointOffsetDistance,
WandSettings settings)
998 var extrapolatedPosition = staleGripPointPose.position +
999 Quaternion.Inverse(newTrackingPointPose.rotation) * Vector3.forward * trackingPointOffsetDistance;
1000 return new Pose(extrapolatedPosition, newTrackingPointPose.rotation);
1002 return staleTrackingPointPose;
1009 return newTrackingPointPose;
1013 #endregion Private Helper Functions
1016#if UNITY_2019_1_OR_NEWER && INPUTSYSTEM_AVAILABLE
1017 private class WandDeviceCore : WandCore
1019 internal WandDevice wandDevice;
1021 private enum TrackingState :
int
1028 public WandDeviceCore(GlassesHandle glassesHandle,
ControllerIndex controllerIndex) : base(glassesHandle, controllerIndex)
1030 if(
Glasses.TryGetGlassesDevice(glassesHandle, out var glassesDevice))
1032 wandDevice =
Input.GetWandDevice(glassesDevice.PlayerIndex, controllerIndex);
1033 InputSystem.QueueConfigChangeEvent(wandDevice);
1034 InputSystem.EnableDevice(wandDevice);
1036 var inputUserCount = InputUser.all.Count;
1037 var playerIndex = (int)glassesDevice.PlayerIndex;
1040 if (headPoseRoot !=
null && inputUserCount >= playerIndex)
1042 var playerInput = headPoseRoot.GetComponentInChildren<PlayerInput>();
1044 if(playerInput !=
null)
1046 InputUser.PerformPairingWithDevice(wandDevice, playerInput.user);
1052 public override void Dispose()
1055 InputSystem.QueueConfigChangeEvent(wandDevice);
1056 InputSystem.DisableDevice(wandDevice);
1059 public override void GetLatestInputs()
1061 base.GetLatestInputs();
1065 if (!wandDevice.added || !wandDevice.enabled)
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));
1079 InputSystem.QueueDeltaStateEvent(wandDevice.Stick, currentState.HasValue ? currentState.Value.TryGetStick() : Vector2.zero);
1080 InputSystem.QueueDeltaStateEvent(wandDevice.Trigger, currentState.HasValue ? currentState.Value.TryGetTrigger() : 0f);
1085 base.SetDrivenObjectTransform(wandSettings, scaleSettings, gameBoardSettings);
1089 if (!wandDevice.added || !wandDevice.enabled)
1095 QueueDeltaStateEvent(wandDevice.devicePosition, pose_UWRLD.position);
1096 QueueDeltaStateEvent(wandDevice.FingertipsPosition, fingertipsPose_UnityWorldSpace.position);
1097 QueueDeltaStateEvent(wandDevice.AimPosition, aimPose_UnityWorldSpace.position);
1099 QueueDeltaStateEvent(wandDevice.RawGripPosition, pose_UGBD.position);
1100 QueueDeltaStateEvent(wandDevice.RawFingertipsPosition, fingertipsPose_GameboardSpace.position);
1101 QueueDeltaStateEvent(wandDevice.RawAimPosition, aimPose_GameboardSpace.position);
1103 QueueDeltaStateEvent(wandDevice.deviceRotation, pose_UWRLD.rotation);
1104 QueueDeltaStateEvent(wandDevice.RawRotation, pose_UGBD.rotation);
1106 InputSystem.QueueDeltaStateEvent(wandDevice.isTracked, isTracked);
1108 var trackingState = TrackingState.Tracking;
1111 trackingState = wandSettings.FailureMode == TrackableSettings.TrackingFailureMode.FreezePosition
1112 ? TrackingState.Limited
1113 : TrackingState.None;
1116 InputSystem.QueueDeltaStateEvent(wandDevice.trackingState, (
int)trackingState);
1119 private static void QueueDeltaStateEvent(Vector3Control vector3Control, Vector3 delta)
1121 InputSystem.QueueDeltaStateEvent(vector3Control.x, delta.x);
1122 InputSystem.QueueDeltaStateEvent(vector3Control.y, delta.y);
1123 InputSystem.QueueDeltaStateEvent(vector3Control.z, delta.z);
1128 private static void QueueDeltaStateEvent(QuaternionControl quaternionControl, Quaternion delta)
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);
1138 #endregion Private Classes
TiltFive.Input.WandButton WandButton
The Glasses API and runtime.
static bool IsTracked(PlayerIndex playerIndex=PlayerIndex.One)
Indicate if the specified glasses are tracked.
static bool TryGetFriendlyName(PlayerIndex playerIndex, out string friendlyName)
static GameObject GetPoseRoot(PlayerIndex playerIndex)
static void Info(string m, params object[] list)
INFO logging function call.
static void Error(string m, params object[] list)
ERROR logging function call.
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.
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)
static Vector3 ConvertPosGBDToUGBD(Vector3 pos_GBD)
bool RejectUntrackedPositionData
TrackingFailureMode FailureMode
Internal Wand core runtime.
Pose FilterTrackingPointPose(Pose staleGripPointPose, Pose staleTrackingPointPose, Pose newTrackingPointPose, float trackingPointOffsetDistance, WandSettings settings)
bool TryGetButtonDown(WandButton button, out bool buttonDown)
Pose aimPose_UnityWorldSpace
bool TryGetStickTilt(out Vector2 stickTilt)
T5_ControllerState? currentState
T5_ControllerState? previousState
bool TryGetButtonUp(WandButton button, out bool buttonUp)
override void SetPoseUnityWorldSpace(ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
Sets the pose values of the tracked object in Unity World Space.
new void Reset(WandSettings wandSettings)
bool TryGetButton(WandButton button, out bool pressed)
bool GetButton(WandButton button)
override bool TryCheckConnected(out bool connected)
Determines whether the tracked object is still connected.
override bool TryGetStateFromPlugin(out T5_ControllerState controllerState, out bool poseIsValid, GameBoardSettings gameBoardSettings)
bool TryGetTrigger(out float triggerDisplacement)
override void SetPoseGameboardSpace(in T5_ControllerState controllerState, WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameboardSettings)
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)
Pose fingertipsPose_UnityWorldSpace
readonly GlassesHandle glassesHandle
Pose aimPose_GameboardSpace
WandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
Pose fingertipsPose_GameboardSpace
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.
bool GetButtonDown(WandButton button)
bool GetButtonUp(WandButton button)
virtual new void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
override void SetDefaultPoseGameboardSpace(WandSettings settings)
Pose gripPose_UnityWorldSpace
override void SetDrivenObjectTransform(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings)
readonly ControllerIndex controllerIndex
Quaternion CalculateRotation(Quaternion rotToWND_GBD)
static Pose GetDefaultPoseGameboardSpace(WandSettings settings)
virtual void GetLatestInputs()
The Wand API and runtime.
static bool IsTracked(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
static bool TryCheckConnected(out bool connected, PlayerIndex playerIndex, ControllerIndex controllerIndex=ControllerIndex.Right)
Gets the connection status of the indicated wand.
static readonly double wandScanRate
static readonly Vector3 DEFAULT_WAND_HANDEDNESS_OFFSET_GAME_BOARD_SPACE
A left/right offset to the default wand position, depending on handedness.
HashSet< GlassesHandle > incomingHandles
static readonly Quaternion DEFAULT_WAND_ROTATION_GAME_BOARD_SPACE
The default rotation of the wand relative to the board.
static void ScanForWands()
static readonly Vector3 DEFAULT_WAND_POSITION_GAME_BOARD_SPACE
The default position of the wand relative to the board.
static bool TryScanForWands()
static void Update(WandSettings wandSettings, ScaleSettings scaleSettings, GameBoardSettings gameBoardSettings, PlayerIndex playerIndex=PlayerIndex.One)
static bool wandAvailabilityErroredOnce
static bool TryGetWandAvailability(out bool connected, GlassesHandle glassesHandle, ControllerIndex controllerIndex)
WandCore ObtainWandCore(GlassesHandle glassesHandle, ControllerIndex controllerIndex)
static DateTime lastScanAttempt
Dictionary< GlassesHandle, WandPair > wandCores
The collection of WandCores. GlassesHandles are mapped to pairs of right/left WandCores.
static Quaternion GetRotation(ControllerIndex controllerIndex=ControllerIndex.Right, PlayerIndex playerIndex=PlayerIndex.One)
Gets the rotation of the wand in world space.
HashSet< GlassesHandle > lostHandles
static Vector3 GetPosition(ControllerIndex controllerIndex=ControllerIndex.Right, ControllerPosition controllerPosition=ControllerPosition.Grip, PlayerIndex playerIndex=PlayerIndex.One)
Gets the position of the wand in world space.
HashSet< WandCore > lostWands
Wand Settings encapsulates all configuration data used by the Wand's tracking runtime to compute the ...
ControllerIndex controllerIndex
GameObject FingertipPoint
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)
bool TryGet(ControllerIndex controllerIndex, out WandCore wandCore)
WandPair(WandCore right, WandCore left)