Skip to content

Commit 9a03d96

Browse files
author
lawwong
committed
Add ability to handle multiple right/left handed devices
1 parent 23da4e2 commit 9a03d96

4 files changed

Lines changed: 158 additions & 39 deletions

File tree

Assets/HTC.UnityPlugin/VRModule/Modules/UnityXRModuleBase.cs

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#pragma warning disable 0649
44
using HTC.UnityPlugin.Utility;
5+
using HTC.UnityPlugin.Vive;
56
using System;
67
using System.Collections.Generic;
78
using System.Text.RegularExpressions;
@@ -78,10 +79,55 @@ private struct XRInputSubsystemProfile
7879
private VRModuleKnownXRLoader knownActiveLoader;
7980
private VRModuleKnownXRInputSubsystem knownActiveInputSubsystem;
8081
private IndexMap indexMap = new IndexMap();
81-
private uint uxrRightIndex = INVALID_DEVICE_INDEX;
82-
private uint uxrLeftIndex = INVALID_DEVICE_INDEX;
8382
private uint moduleRightIndex = INVALID_DEVICE_INDEX;
8483
private uint moduleLeftIndex = INVALID_DEVICE_INDEX;
84+
85+
private enum RolePriority
86+
{
87+
Controller,
88+
TrackedHand,
89+
Tracker,
90+
Other,
91+
}
92+
private struct RoleIdx
93+
{
94+
public IVRModuleDeviceStateRW ds;
95+
public RolePriority pri;
96+
public uint index { get { return ds.deviceIndex; } }
97+
public RoleIdx(IVRModuleDeviceStateRW deviceState)
98+
{
99+
ds = deviceState;
100+
switch (deviceState.deviceClass)
101+
{
102+
case VRModuleDeviceClass.Controller: pri = RolePriority.Controller; break;
103+
case VRModuleDeviceClass.TrackedHand: pri = RolePriority.TrackedHand; break;
104+
case VRModuleDeviceClass.GenericTracker: pri = RolePriority.Tracker; break;
105+
default: pri = RolePriority.Other; break;
106+
}
107+
}
108+
}
109+
private List<RoleIdx> rightIndice = new List<RoleIdx>();
110+
private List<RoleIdx> leftIndice = new List<RoleIdx>();
111+
private static int CompareRoleIdx(RoleIdx a, RoleIdx b)
112+
{
113+
var c = a.ds.isPoseValid.CompareTo(b.ds.isPoseValid);
114+
if (c != 0) { return -c; }
115+
116+
c = a.pri - b.pri;
117+
if (c != 0) { return c; }
118+
119+
if (a.pri == RolePriority.TrackedHand)
120+
{
121+
foreach (var j in JointEnumArray.StaticEnums)
122+
{
123+
c = a.ds.handJoints[j].isValid.CompareTo(b.ds.handJoints[j].isValid);
124+
if (c != 0) { return -c; }
125+
}
126+
}
127+
128+
return a.ds.deviceIndex.CompareTo(b.ds.deviceIndex);
129+
}
130+
85131
private VRModule.SubmoduleBase.Collection submodules = new VRModule.SubmoduleBase.Collection(
86132
new ViveHandTrackingSubmodule(),
87133
new WaveHandTrackingSubmodule(),
@@ -90,11 +136,15 @@ private struct XRInputSubsystemProfile
90136

91137
private bool[] prevDeviceConnected = new bool[VRModule.MAX_DEVICE_COUNT];
92138
private bool[] currDeviceConnected = new bool[VRModule.MAX_DEVICE_COUNT];
139+
private int prevMaxConnectedIndex = -1;
140+
private int currMaxConnectedIndex = -1;
93141
private void FlushDeviceConnectedState()
94142
{
95143
var temp = prevDeviceConnected;
96144
prevDeviceConnected = currDeviceConnected;
97145
currDeviceConnected = temp;
146+
prevMaxConnectedIndex = currMaxConnectedIndex;
147+
currMaxConnectedIndex = -1;
98148
Array.Clear(currDeviceConnected, 0, (int)VRModule.MAX_DEVICE_COUNT);
99149
}
100150

@@ -144,6 +194,9 @@ public sealed override void BeforeRenderUpdate()
144194
uint deviceIndex;
145195

146196
FlushDeviceState();
197+
FlushDeviceConnectedState();
198+
rightIndice.Clear();
199+
leftIndice.Clear();
147200

148201
InputDevices.GetDevices(connectedDevices);
149202

@@ -174,7 +227,7 @@ public sealed override void BeforeRenderUpdate()
174227

175228
SetupKnownDeviceModel(currState);
176229

177-
Debug.LogFormat("Device connected: {0} / {1} / {2} / {3} / {4} / {5} ({6})",
230+
Debug.LogFormat("[UnityXRModule] Device connected: {0} / {1} / {2} / {3} / {4} / {5} ({6})",
178231
currState.deviceIndex,
179232
currState.deviceClass,
180233
currState.deviceModel,
@@ -183,15 +236,6 @@ public sealed override void BeforeRenderUpdate()
183236
device.name,
184237
device.characteristics);
185238

186-
if ((device.characteristics & InputDeviceCharacteristics.Right) > 0u)
187-
{
188-
uxrRightIndex = deviceIndex;
189-
}
190-
else if ((device.characteristics & InputDeviceCharacteristics.Left) > 0u)
191-
{
192-
uxrLeftIndex = deviceIndex;
193-
}
194-
195239
UpdateNewConnectedInputDevice(currState, device);
196240
}
197241
else
@@ -202,7 +246,10 @@ public sealed override void BeforeRenderUpdate()
202246
currState.isConnected = true;
203247
// update device Poses
204248
currState.isPoseValid = GetDeviceFeatureValueOrDefault(device, CommonUsages.isTracked);
205-
if (Vive.VIUSettings.preferUnityXRPointerPose)
249+
if ((device.characteristics & InputDeviceCharacteristics.Right) != 0u) { rightIndice.Add(new RoleIdx(currState)); }
250+
else if ((device.characteristics & InputDeviceCharacteristics.Left) != 0u) { leftIndice.Add(new RoleIdx(currState)); }
251+
252+
if (VIUSettings.preferUnityXRPointerPose)
206253
{
207254
currState.position = GetDeviceFeatureValueOrDefault(device, pointerPositionFeature, CommonUsages.devicePosition);
208255
currState.rotation = GetDeviceFeatureValueOrDefault(device, pointerRotationFeature, CommonUsages.deviceRotation);
@@ -218,12 +265,13 @@ public sealed override void BeforeRenderUpdate()
218265
}
219266

220267
currDeviceConnected[deviceIndex] = true;
268+
if (currMaxConnectedIndex < (int)deviceIndex) { currMaxConnectedIndex = (int)deviceIndex; }
221269

222270
// TODO: update hand skeleton pose
223271
}
224272

225273
// unmap index for disconnected device state
226-
for (uint i = 0u, imax = VRModule.MAX_DEVICE_COUNT; i < imax; ++i)
274+
for (uint i = 0u, imax = (uint)(prevMaxConnectedIndex + 1); i < imax; ++i)
227275
{
228276
if (prevDeviceConnected[i] && !currDeviceConnected[i])
229277
{
@@ -233,35 +281,55 @@ public sealed override void BeforeRenderUpdate()
233281
}
234282
else
235283
{
236-
Debug.LogWarning("[UnityXRModule] Disconnected device[" + i + "] already unmapped");
284+
Debug.LogWarning("[UnityXRModule] Device disconnected: [" + i + "] already unmapped");
237285
}
238286

239287
if (TryGetValidDeviceState(i, out prevState, out currState) && currState.isConnected)
240288
{
241289
currState.Reset();
242-
if (uxrRightIndex == i) { uxrRightIndex = INVALID_DEVICE_INDEX; }
243-
if (uxrLeftIndex == i) { uxrLeftIndex = INVALID_DEVICE_INDEX; }
244290
}
245291
else
246292
{
247-
Debug.LogWarning("[UnityXRModule] Disconnected device[" + i + "] already been reset");
293+
Debug.LogWarning("[UnityXRModule] Device disconnected: [" + i + "] already been reset");
248294
}
295+
296+
Debug.LogFormat("[UnityXRModule] Device disconnected: {0} / {1} / {2} / {3} / {4}",
297+
prevState.deviceIndex,
298+
prevState.deviceClass,
299+
prevState.deviceModel,
300+
prevState.modelNumber,
301+
prevState.serialNumber);
249302
}
250303
}
251304

252-
FlushDeviceConnectedState();
253-
254305
submodules.UpdateModulesDeviceConnectionAndPoses();
306+
for (int i = 0, imax = submodules.ModuleCount; i < imax; ++i)
307+
{
308+
if (!submodules[i].isActivated) { continue; }
309+
if (TryGetValidDeviceState(submodules[i].GetRightHandedIndex(), out prevState, out currState) && currState.isConnected)
310+
{
311+
rightIndice.Add(new RoleIdx(currState));
312+
}
313+
if (TryGetValidDeviceState(submodules[i].GetLeftHandedIndex(), out prevState, out currState) && currState.isConnected)
314+
{
315+
leftIndice.Add(new RoleIdx(currState));
316+
}
317+
}
255318

256319
// process hand role
257-
var subRightIndex = submodules.GetFirstRightHandedIndex();
258-
var currentRight = (subRightIndex == INVALID_DEVICE_INDEX || (TryGetValidDeviceState(uxrRightIndex, out prevState, out currState) && currState.isPoseValid && currState.deviceClass == VRModuleDeviceClass.Controller)) ? uxrRightIndex : subRightIndex;
259-
var subLeftIndex = submodules.GetFirstLeftHandedIndex();
260-
var currentLeft = (subLeftIndex == INVALID_DEVICE_INDEX || (TryGetValidDeviceState(uxrLeftIndex, out prevState, out currState) && currState.isPoseValid && currState.deviceClass == VRModuleDeviceClass.Controller)) ? uxrLeftIndex : subLeftIndex;
261-
var roleChanged = ChangeProp.Set(ref moduleRightIndex, currentRight);
262-
roleChanged |= ChangeProp.Set(ref moduleLeftIndex, currentLeft);
263-
264-
if (roleChanged)
320+
var newRightIndex = INVALID_DEVICE_INDEX;
321+
if (rightIndice.Count != 0)
322+
{
323+
if (rightIndice.Count != 1) { rightIndice.Sort(CompareRoleIdx); }
324+
newRightIndex = rightIndice[0].index;
325+
}
326+
var newLeftIndex = INVALID_DEVICE_INDEX;
327+
if (leftIndice.Count != 0)
328+
{
329+
if (leftIndice.Count != 1) { leftIndice.Sort(CompareRoleIdx); }
330+
newLeftIndex = leftIndice[0].index;
331+
}
332+
if (ChangeProp.Set(ref moduleRightIndex, newRightIndex) | ChangeProp.Set(ref moduleLeftIndex, newLeftIndex))
265333
{
266334
InvokeControllerRoleChangedEvent();
267335
}
@@ -327,6 +395,11 @@ protected static VRModuleDeviceClass GetDeviceClass(string name, InputDeviceChar
327395
return VRModuleDeviceClass.GenericTracker;
328396
}
329397

398+
if ((characteristics & InputDeviceCharacteristics.HandTracking) != 0)
399+
{
400+
return VRModuleDeviceClass.TrackedHand;
401+
}
402+
330403
return VRModuleDeviceClass.Invalid;
331404
}
332405

@@ -335,13 +408,22 @@ private static void SetAllXRInputSubsystemTrackingOriginMode(TrackingOriginModeF
335408
var activeSubsys = ListPool<XRInputSubsystem>.Get();
336409
try
337410
{
411+
#if UNITY_6000_0_OR_NEWER
412+
SubsystemManager.GetSubsystems(activeSubsys);
413+
#else
338414
SubsystemManager.GetInstances(activeSubsys);
415+
#endif
339416
foreach (var subsys in activeSubsys)
340417
{
341418
if (!subsys.running) { continue; }
342419
if (!subsys.TrySetTrackingOriginMode(value))
343420
{
344-
Debug.LogWarning("Failed to set TrackingOriginModeFlags(" + value + ") to XRInputSubsystem: " + subsys.SubsystemDescriptor.id);
421+
#if UNITY_6000_0_OR_NEWER
422+
var subsysId = subsys.subsystemDescriptor.id;
423+
#else
424+
var subsysId = subsys.SubsystemDescriptor.id;
425+
#endif
426+
Debug.LogWarning("Failed to set TrackingOriginModeFlags(" + value + ") to XRInputSubsystem: " + subsysId);
345427
}
346428
}
347429
}
@@ -425,7 +507,11 @@ protected void UpdateLockPhysicsUpdateRate()
425507
var displaySystems = ListPool<XRDisplaySubsystem>.Get();
426508
try
427509
{
510+
#if UNITY_6000_0_OR_NEWER
511+
SubsystemManager.GetSubsystems(displaySystems);
512+
#else
428513
SubsystemManager.GetInstances(displaySystems);
514+
#endif
429515

430516
var minRefreshRate = float.MaxValue;
431517
foreach (XRDisplaySubsystem system in displaySystems)

Assets/HTC.UnityPlugin/VRModule/VRModuleBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,10 @@ public Collection(params SubmoduleBase[] modules)
449449
}
450450
}
451451

452+
public int ModuleCount { get { return modules.Count; } }
453+
454+
public SubmoduleBase this[int i] { get { return modules[i]; } }
455+
452456
public void AddModule(SubmoduleBase module, params SubmoduleBase[] modules)
453457
{
454458
if (module != null) { this.modules.Add(module); }

Assets/HTC.UnityPlugin/ViveInputUtility/Scripts/ViveRole/RoleMaps/HandRole.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ private void MappingLeftRightHands()
174174
{
175175
if (i == rightIndex || i == leftIndex) { continue; }
176176
var state = VRModule.GetCurrentDeviceState(i);
177-
if (!state.isConnected || state.deviceClass != VRModuleDeviceClass.Controller) { continue; }
177+
if (!state.isConnected || !state.isPoseValid || state.deviceClass != VRModuleDeviceClass.Controller) { continue; }
178178
if (RoleMap.IsDeviceBound(state.serialNumber)) { continue; }
179179
m_sortedDeviceList.Add(i);
180180
}

Assets/HTC.UnityPlugin/ViveInputUtility/Scripts/ViveRole/RoleMaps/TrackedHandRole.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,30 +40,59 @@ public override void OnBindingChanged(string deviceSN, bool previousIsBound, Tra
4040

4141
Refresh();
4242
}
43+
private static int CompareDevice(IVRModuleDeviceState a, IVRModuleDeviceState b)
44+
{
45+
var c = a.isPoseValid.CompareTo(b.isPoseValid);
46+
if (c != 0) { return -c; }
47+
48+
foreach (var j in JointEnumArray.StaticEnums)
49+
{
50+
c = a.readOnlyHandJoints[j].isValid.CompareTo(b.readOnlyHandJoints[j].isValid);
51+
if (c != 0) { return -c; }
52+
}
4353

54+
return a.deviceIndex.CompareTo(b.deviceIndex);
55+
}
56+
57+
private List<IVRModuleDeviceState> rightTrackedHandDevices = new List<IVRModuleDeviceState>();
58+
private List<IVRModuleDeviceState> leftTrackedHandDevices = new List<IVRModuleDeviceState>();
4459
public void Refresh()
4560
{
4661
// find tracked right/left hand index
4762
var deviceCount = VRModule.GetDeviceStateCount();
4863
var rightIndex = VRModule.INVALID_DEVICE_INDEX;
4964
var leftIndex = VRModule.INVALID_DEVICE_INDEX;
5065

66+
// find proper left/right tracked hand
5167
for (uint deviceIndex = 0u; deviceIndex < deviceCount; ++deviceIndex)
5268
{
5369
var deviceState = VRModule.GetDeviceState(deviceIndex);
54-
if (deviceState.isConnected && deviceState.deviceClass == VRModuleDeviceClass.TrackedHand)
70+
if (deviceState.isConnected == false) { continue; }
71+
if (deviceState.deviceClass != VRModuleDeviceClass.TrackedHand) { continue; }
72+
if (deviceState.deviceModel.IsRight())
5573
{
56-
if (deviceState.deviceModel.IsRight())
57-
{
58-
rightIndex = deviceIndex;
59-
}
60-
else if (deviceState.deviceModel.IsLeft())
61-
{
62-
leftIndex = deviceIndex;
63-
}
74+
rightTrackedHandDevices.Add(deviceState);
6475
}
76+
else if (deviceState.deviceModel.IsLeft())
77+
{
78+
leftTrackedHandDevices.Add(deviceState);
79+
}
80+
}
81+
82+
if (rightTrackedHandDevices.Count != 0)
83+
{
84+
if (rightTrackedHandDevices.Count != 1) { rightTrackedHandDevices.Sort(CompareDevice); }
85+
rightIndex = rightTrackedHandDevices[0].deviceIndex;
86+
}
87+
if (leftTrackedHandDevices.Count != 0)
88+
{
89+
if (leftTrackedHandDevices.Count != 1) { leftTrackedHandDevices.Sort(CompareDevice); }
90+
leftIndex = leftTrackedHandDevices[0].deviceIndex;
6591
}
6692

93+
rightTrackedHandDevices.Clear();
94+
leftTrackedHandDevices.Clear();
95+
6796
if (!RoleMap.IsRoleMapped(TrackedHandRole.RightHand))
6897
{
6998
if (rightIndex < deviceCount)

0 commit comments

Comments
 (0)