IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
SteamVR_ControllerManager.cs
1 //======= Copyright (c) Valve Corporation, All rights reserved. ===============
2 //
3 // Purpose: Enables/disables objects based on connectivity and assigned roles.
4 //
5 //=============================================================================
6 
7 using UnityEngine;
8 using Valve.VR;
9 
10 public class SteamVR_ControllerManager : MonoBehaviour
11 {
12  public GameObject left, right;
13 
14  [Tooltip("Populate with objects you want to assign to additional controllers")]
15  public GameObject[] objects;
16 
17  [Tooltip("Set to true if you want objects arbitrarily assigned to controllers before their role (left vs right) is identified")]
18  public bool assignAllBeforeIdentified;
19 
20  uint[] indices; // assigned
21  bool[] connected = new bool[OpenVR.k_unMaxTrackedDeviceCount]; // controllers only
22 
23  // cached roles - may or may not be connected
24  uint leftIndex = OpenVR.k_unTrackedDeviceIndexInvalid;
25  uint rightIndex = OpenVR.k_unTrackedDeviceIndexInvalid;
26 
27  // Helper function to avoid adding duplicates to object array.
28  void SetUniqueObject(GameObject o, int index)
29  {
30  for (int i = 0; i < index; i++)
31  if (objects[i] == o)
32  return;
33 
34  objects[index] = o;
35  }
36 
37  // This needs to be called if you update left, right or objects at runtime (e.g. when dyanmically spawned).
38  public void UpdateTargets()
39  {
40  // Add left and right entries to the head of the list so we only have to operate on the list itself.
41  var objects = this.objects;
42  var additional = (objects != null) ? objects.Length : 0;
43  this.objects = new GameObject[2 + additional];
44  SetUniqueObject(right, 0);
45  SetUniqueObject(left, 1);
46  for (int i = 0; i < additional; i++)
47  SetUniqueObject(objects[i], 2 + i);
48 
49  // Reset assignments.
50  indices = new uint[2 + additional];
51  for (int i = 0; i < indices.Length; i++)
52  indices[i] = OpenVR.k_unTrackedDeviceIndexInvalid;
53  }
54 
55  SteamVR_Events.Action inputFocusAction, deviceConnectedAction, trackedDeviceRoleChangedAction;
56 
57  void Awake()
58  {
59  UpdateTargets();
60  }
61 
63  {
64  inputFocusAction = SteamVR_Events.InputFocusAction(OnInputFocus);
65  deviceConnectedAction = SteamVR_Events.DeviceConnectedAction(OnDeviceConnected);
66  trackedDeviceRoleChangedAction = SteamVR_Events.SystemAction(EVREventType.VREvent_TrackedDeviceRoleChanged, OnTrackedDeviceRoleChanged);
67  }
68 
69  void OnEnable()
70  {
71  for (int i = 0; i < objects.Length; i++)
72  {
73  var obj = objects[i];
74  if (obj != null)
75  obj.SetActive(false);
76 
77  indices[i] = OpenVR.k_unTrackedDeviceIndexInvalid;
78  }
79 
80  Refresh();
81 
82  for (int i = 0; i < SteamVR.connected.Length; i++)
83  if (SteamVR.connected[i])
84  OnDeviceConnected(i, true);
85 
86  inputFocusAction.enabled = true;
87  deviceConnectedAction.enabled = true;
88  trackedDeviceRoleChangedAction.enabled = true;
89  }
90 
91  void OnDisable()
92  {
93  inputFocusAction.enabled = false;
94  deviceConnectedAction.enabled = false;
95  trackedDeviceRoleChangedAction.enabled = false;
96  }
97 
98  static string hiddenPrefix = "hidden (", hiddenPostfix = ")";
99  static string[] labels = { "left", "right" };
100 
101  // Hide controllers when the dashboard is up.
102  private void OnInputFocus(bool hasFocus)
103  {
104  if (hasFocus)
105  {
106  for (int i = 0; i < objects.Length; i++)
107  {
108  var obj = objects[i];
109  if (obj != null)
110  {
111  var label = (i < 2) ? labels[i] : (i - 1).ToString();
112  ShowObject(obj.transform, hiddenPrefix + label + hiddenPostfix);
113  }
114  }
115  }
116  else
117  {
118  for (int i = 0; i < objects.Length; i++)
119  {
120  var obj = objects[i];
121  if (obj != null)
122  {
123  var label = (i < 2) ? labels[i] : (i - 1).ToString();
124  HideObject(obj.transform, hiddenPrefix + label + hiddenPostfix);
125  }
126  }
127  }
128  }
129 
130  // Reparents to a new object and deactivates that object (this allows
131  // us to call SetActive in OnDeviceConnected independently.
132  private void HideObject(Transform t, string name)
133  {
134  if (t.gameObject.name.StartsWith(hiddenPrefix))
135  {
136  Debug.Log("Ignoring double-hide.");
137  return;
138  }
139  var hidden = new GameObject(name).transform;
140  hidden.parent = t.parent;
141  t.parent = hidden;
142  hidden.gameObject.SetActive(false);
143  }
144  private void ShowObject(Transform t, string name)
145  {
146  var hidden = t.parent;
147  if (hidden.gameObject.name != name)
148  return;
149  t.parent = hidden.parent;
150  Destroy(hidden.gameObject);
151  }
152 
153  private void SetTrackedDeviceIndex(int objectIndex, uint trackedDeviceIndex)
154  {
155  // First make sure no one else is already using this index.
156  if (trackedDeviceIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
157  {
158  for (int i = 0; i < objects.Length; i++)
159  {
160  if (i != objectIndex && indices[i] == trackedDeviceIndex)
161  {
162  var obj = objects[i];
163  if (obj != null)
164  obj.SetActive(false);
165 
166  indices[i] = OpenVR.k_unTrackedDeviceIndexInvalid;
167  }
168  }
169  }
170 
171  // Only set when changed.
172  if (trackedDeviceIndex != indices[objectIndex])
173  {
174  indices[objectIndex] = trackedDeviceIndex;
175 
176  var obj = objects[objectIndex];
177  if (obj != null)
178  {
179  if (trackedDeviceIndex == OpenVR.k_unTrackedDeviceIndexInvalid)
180  obj.SetActive(false);
181  else
182  {
183  obj.SetActive(true);
184  obj.BroadcastMessage("SetDeviceIndex", (int)trackedDeviceIndex, SendMessageOptions.DontRequireReceiver);
185  }
186  }
187  }
188  }
189 
190  // Keep track of assigned roles.
191  private void OnTrackedDeviceRoleChanged(VREvent_t vrEvent)
192  {
193  Refresh();
194  }
195 
196  // Keep track of connected controller indices.
197  private void OnDeviceConnected(int index, bool connected)
198  {
199  bool changed = this.connected[index];
200  this.connected[index] = false;
201 
202  if (connected)
203  {
204  var system = OpenVR.System;
205  if (system != null)
206  {
207  var deviceClass = system.GetTrackedDeviceClass((uint)index);
208  if (deviceClass == ETrackedDeviceClass.Controller ||
209  deviceClass == ETrackedDeviceClass.GenericTracker)
210  {
211  this.connected[index] = true;
212  changed = !changed; // if we clear and set the same index, nothing has changed
213  }
214  }
215  }
216 
217  if (changed)
218  Refresh();
219  }
220 
221  public void Refresh()
222  {
223  int objectIndex = 0;
224 
225  var system = OpenVR.System;
226  if (system != null)
227  {
228  leftIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand);
229  rightIndex = system.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand);
230  }
231 
232  // If neither role has been assigned yet, try hooking up at least the right controller.
233  if (leftIndex == OpenVR.k_unTrackedDeviceIndexInvalid && rightIndex == OpenVR.k_unTrackedDeviceIndexInvalid)
234  {
235  for (uint deviceIndex = 0; deviceIndex < connected.Length; deviceIndex++)
236  {
237  if (objectIndex >= objects.Length)
238  break;
239 
240  if (!connected[deviceIndex])
241  continue;
242 
243  SetTrackedDeviceIndex(objectIndex++, deviceIndex);
244 
245  if (!assignAllBeforeIdentified)
246  break;
247  }
248  }
249  else
250  {
251  SetTrackedDeviceIndex(objectIndex++, (rightIndex < connected.Length && connected[rightIndex]) ? rightIndex : OpenVR.k_unTrackedDeviceIndexInvalid);
252  SetTrackedDeviceIndex(objectIndex++, (leftIndex < connected.Length && connected[leftIndex]) ? leftIndex : OpenVR.k_unTrackedDeviceIndexInvalid);
253 
254  // Assign out any additional controllers only after both left and right have been assigned.
255  if (leftIndex != OpenVR.k_unTrackedDeviceIndexInvalid && rightIndex != OpenVR.k_unTrackedDeviceIndexInvalid)
256  {
257  for (uint deviceIndex = 0; deviceIndex < connected.Length; deviceIndex++)
258  {
259  if (objectIndex >= objects.Length)
260  break;
261 
262  if (!connected[deviceIndex])
263  continue;
264 
265  if (deviceIndex != leftIndex && deviceIndex != rightIndex)
266  {
267  SetTrackedDeviceIndex(objectIndex++, deviceIndex);
268  }
269  }
270  }
271  }
272 
273  // Reset the rest.
274  while (objectIndex < objects.Length)
275  {
276  SetTrackedDeviceIndex(objectIndex++, OpenVR.k_unTrackedDeviceIndexInvalid);
277  }
278  }
279 }
280