Spelunx Cavern SDK
 
Loading...
Searching...
No Matches
ViveController.cs
Go to the documentation of this file.
1using System;
2using System.Reflection;
3using System.Runtime.InteropServices;
4using UnityEngine;
5using UnityEngine.Events;
6using Valve.VR;
8
9namespace Spelunx.Vive
10{
11 public sealed class ViveController : OVRT_TrackedDevice
12 {
13 public enum Role
14 {
17 }
18
19 public Role role;
20
21 // Events for tracker buttons being pressed
22 public UnityEvent<ViveControllerButton> onButtonPressed = new();
23 public UnityEvent<ViveControllerButton> onButtonReleased = new();
24 public UnityEvent<ViveControllerButton> onButtonHeld = new();
25 public UnityEvent<ViveControllerButton> onButtonTouchPressed = new();
26 public UnityEvent<ViveControllerButton> onButtonTouchReleased = new();
27 public UnityEvent<ViveControllerButton> onButtonTouchHeld = new();
28 // The previous controller state, used for determining press, hold, and release
29 private VRControllerState_t previousControllerState = new();
30 private VRControllerState_t currentControllerState = new();
31
32 private UnityAction<TrackedDevicePose_t[]> _onNewPosesAction;
33 private UnityAction<int, EVRButtonId, bool> _onButtonPressedAction;
34 private UnityAction<int> _onTrackedDeviceRoleChangedAction;
35
36 public static string GetReadableRoleName(Role r)
37 {
38 return r switch
39 {
40 Role.LeftHand => "Left Hand",
41 Role.RightHand => "Right Hand",
42 _ => "None",
43 };
44 }
45
46 public void TriggerHapticPulse(ushort durationMicroSec = 500, EVRButtonId buttonId = EVRButtonId.k_EButton_SteamVR_Touchpad)
47 {
48 if (DeviceIndex == -1)
49 return;
50
51 if (OpenVR.System == null) return;
52
53 var axisId = (uint)buttonId - (uint)EVRButtonId.k_EButton_Axis0;
54 OpenVR.System.TriggerHapticPulse((uint)DeviceIndex, axisId, (char)durationMicroSec);
55 }
56
57
58 private void OnDeviceConnected(int index, bool connected)
59 {
60 if (OpenVR.System == null) return;
61
62 var roleIndex = FindIndexForRole();
63
64 if (roleIndex > -1)
65 {
66 IsConnected = connected;
67 UpdateIndex();
68 }
69 }
70
71 private void OnTrackedDeviceRoleChanged(int index)
72 {
73 UpdateIndex();
74 }
75
76 private int FindIndexForRole()
77 {
78 if (OpenVR.System == null) return -1;
79
80 ETrackedControllerRole trackedRole = ETrackedControllerRole.Invalid;
81 switch (role)
82 {
83 case Role.LeftHand:
84 trackedRole = ETrackedControllerRole.LeftHand; break;
85 case Role.RightHand:
86 trackedRole = ETrackedControllerRole.RightHand; break;
87 }
88
89 if (trackedRole == ETrackedControllerRole.LeftHand || trackedRole == ETrackedControllerRole.RightHand)
90 {
91 return (int)OpenVR.System.GetTrackedDeviceIndexForControllerRole(trackedRole);
92 }
93 else
94 {
95 return -1;
96 }
97 }
98
99 void Update()
100 {
101 if (DeviceIndex == -1 || OpenVR.System == null || !IsConnected)
102 return;
103
104 var deviceClass = OpenVR.System.GetTrackedDeviceClass((uint)DeviceIndex);
105 if (deviceClass != ETrackedDeviceClass.Controller && deviceClass != ETrackedDeviceClass.GenericTracker) return;
106
107
108 // Optimization to avoid allocations we want (previous, current) to become (current, next)
109 // so write to previous and then swap
110 if (OpenVR.System.GetControllerState((uint)DeviceIndex, ref previousControllerState, (uint)Marshal.SizeOf(typeof(VRControllerState_t))))
111 {
112 // Optimization to avoid allocations - move current state to previous, previous to current, and then overwrite
113 (previousControllerState, currentControllerState) = (currentControllerState, previousControllerState);
114 Debug.Log($"Device {DeviceIndex}: Buttons pressed = {currentControllerState.ulButtonPressed}, touched = {currentControllerState.ulButtonTouched}");
115 foreach (ViveControllerButton buttonMask in Enum.GetValues(typeof(ViveControllerButton)))
116 {
117 // press events
118 if ((currentControllerState.ulButtonPressed & (ulong)buttonMask) != 0)
119 {
120 // button is pressed
121 if ((previousControllerState.ulButtonPressed & (ulong)buttonMask) == 0)
122 {
123 // button wasn't pressed previous frame
124 onButtonPressed.Invoke(buttonMask);
125 }
126 else
127 {
128 // button was pressed previous frame, so now it's a hold
129 onButtonHeld.Invoke(buttonMask);
130 }
131 }
132 else if ((previousControllerState.ulButtonPressed & (ulong)buttonMask) != 0)
133 {
134 // Button was pressed last frame, and is no longer pressed
135 onButtonReleased.Invoke(buttonMask);
136 }
137
138 // touch events
139 if ((currentControllerState.ulButtonTouched & (ulong)buttonMask) != 0)
140 {
141 // button is touched
142 if ((previousControllerState.ulButtonTouched & (ulong)buttonMask) == 0)
143 {
144 // button wasn't touched previous frame
145 onButtonTouchPressed.Invoke(buttonMask);
146 }
147 else
148 {
149 // button was touched previous frame, so now it's a hold
150 onButtonTouchHeld.Invoke(buttonMask);
151 }
152 }
153 else if ((previousControllerState.ulButtonTouched & (ulong)buttonMask) != 0)
154 {
155 // Button was touched last frame, and is no longer touched
156 onButtonTouchReleased.Invoke(buttonMask);
157 }
158 }
159 }
160 }
161
162 // TODO: check that these axes are actually correct
163 public Vector2 GetInputAxis0()
164 {
165 return currentControllerState.rAxis0.ToVector2();
166 }
167
168 public Vector2 GetTrackpadAxis()
169 {
170 return currentControllerState.rAxis1.ToVector2();
171 }
172
173 public Vector2 GetJoystickAxis()
174 {
175 return currentControllerState.rAxis2.ToVector2();
176 }
177
178 public Vector2 GetTriggerAxis()
179 {
180 return currentControllerState.rAxis3.ToVector2();
181 }
182
183 public Vector2 GetInputAxis4()
184 {
185 return currentControllerState.rAxis4.ToVector2();
186 }
187
188
189 private void OnNewPoses(TrackedDevicePose_t[] poses)
190 {
191 if (DeviceIndex == -1)
192 return;
193
194 var i = DeviceIndex;
195
196 IsValid = false;
197
198 if (i < 0 || poses.Length <= i)
199 return;
200
201 IsConnected = poses[i].bDeviceIsConnected;
202 if (!poses[i].bDeviceIsConnected)
203 return;
204
205 if (!poses[i].bPoseIsValid)
206 return;
207
208 IsValid = true;
209
210 var pose = new OVRT_Utils.RigidTransform(poses[i].mDeviceToAbsoluteTracking);
211
212 if (origin != null)
213 {
214 transform.position = origin.transform.TransformPoint(pose.pos);
215 transform.rotation = origin.rotation * pose.rot;
216 }
217 else
218 {
219 transform.localPosition = pose.pos;
220 transform.localRotation = pose.rot;
221 }
222 }
223
224 private void Awake()
225 {
226 _onNewPosesAction += OnNewPoses;
227 _onDeviceConnectedAction += OnDeviceConnected;
228 _onTrackedDeviceRoleChangedAction += OnTrackedDeviceRoleChanged;
229 }
230
231 private void Start()
232 {
233 UpdateIndex();
234 SetOrigin(FindFirstObjectByType<Vive_Manager>().transform);
235 }
236 // t is treated as the (0,0,0) point
237 public void SetOrigin(Transform t)
238 {
239 origin = t;
240 }
241
242 private void OnEnable()
243 {
244 UpdateIndex();
245
246 OVRT_Events.NewPoses.AddListener(_onNewPosesAction);
247 OVRT_Events.ButtonPressed.AddListener(_onButtonPressedAction);
249 OVRT_Events.TrackedDeviceRoleChanged.AddListener(_onTrackedDeviceRoleChangedAction);
250 }
251
252 private void OnDisable()
253 {
254 OVRT_Events.NewPoses.RemoveListener(_onNewPosesAction);
255 OVRT_Events.ButtonPressed.RemoveListener(_onButtonPressedAction);
256 OVRT_Events.TrackedDeviceConnected.RemoveListener(_onDeviceConnectedAction);
257 IsValid = false;
258 IsConnected = false;
259 }
260
261 private void UpdateIndex()
262 {
263 DeviceIndex = FindIndexForRole();
265 }
266
267#if UNITY_EDITOR
268 // A gizmo, which can be enabled or disabled through the gizmos menu
269 // This shows the position, size, and rotation of the vive tracker.
270 private void OnDrawGizmos()
271 {
272 if (role == Role.LeftHand)
273 {
274
275 Gizmos.DrawMesh(ViveDebugRenderer.indexControllerLeftMesh, transform.position, transform.rotation);
276 }
277 else
278 {
279 Gizmos.DrawMesh(ViveDebugRenderer.indexControllerRightMesh, transform.position, transform.rotation);
280 }
281 }
282#endif
283
284 // Masks for the controller buttons
285 public enum ViveControllerButton : ulong
286 {
287 System = 1ul << (int)EVRButtonId.k_EButton_System,
288 ApplicationMenu = 1ul << (int)EVRButtonId.k_EButton_ApplicationMenu,
289 Grip = 1ul << (int)EVRButtonId.k_EButton_Grip,
290 Axis0 = 1ul << (int)EVRButtonId.k_EButton_Axis0,
291 Axis1 = 1ul << (int)EVRButtonId.k_EButton_Axis1,
292 Axis2 = 1ul << (int)EVRButtonId.k_EButton_Axis2,
293 Axis3 = 1ul << (int)EVRButtonId.k_EButton_Axis3,
294 Axis4 = 1ul << (int)EVRButtonId.k_EButton_Axis4,
295 Touchpad = 1ul << (int)EVRButtonId.k_EButton_SteamVR_Touchpad,
296 Trigger = 1ul << (int)EVRButtonId.k_EButton_SteamVR_Trigger
297 }
298 }
299}
static UnityEvent< int > TrackedDeviceRoleChanged
Definition: OVRT_Events.cs:12
static UnityEvent< int, bool > TrackedDeviceConnected
Definition: OVRT_Events.cs:10
static UnityEvent< TrackedDevicePose_t[]> NewPoses
Definition: OVRT_Events.cs:14
static UnityEvent< int, EVRButtonId, bool > ButtonPressed
Definition: OVRT_Events.cs:17
UnityAction< int, bool > _onDeviceConnectedAction
Maps tracked OpenVR poses to transform by device index.
void SetOrigin(Transform t)
UnityEvent< ViveControllerButton > onButtonTouchHeld
UnityEvent< ViveControllerButton > onButtonPressed
UnityEvent< ViveControllerButton > onButtonTouchReleased
UnityEvent< ViveControllerButton > onButtonHeld
void TriggerHapticPulse(ushort durationMicroSec=500, EVRButtonId buttonId=EVRButtonId.k_EButton_SteamVR_Touchpad)
UnityEvent< ViveControllerButton > onButtonReleased
static string GetReadableRoleName(Role r)
UnityEvent< ViveControllerButton > onButtonTouchPressed