You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
376 lines
14 KiB
376 lines
14 KiB
//========= Copyright 2016-2018, HTC Corporation. All rights reserved. =========== |
|
|
|
using HTC.UnityPlugin.Utility; |
|
using HTC.UnityPlugin.VRModuleManagement; |
|
using System; |
|
using System.Collections.Generic; |
|
using UnityEngine; |
|
|
|
namespace HTC.UnityPlugin.Vive |
|
{ |
|
/// <summary> |
|
/// Defines roles for those devices that have buttons |
|
/// </summary> |
|
[ViveRoleEnum((int)HandRole.Invalid)] |
|
public enum HandRole |
|
{ |
|
Invalid = -1, |
|
RightHand, |
|
LeftHand, |
|
ExternalCamera, |
|
Controller3 = ExternalCamera, |
|
Controller4, |
|
Controller5, |
|
Controller6, |
|
Controller7, |
|
Controller8, |
|
Controller9, |
|
Controller10, |
|
Controller11, |
|
Controller12, |
|
Controller13, |
|
Controller14, |
|
Controller15, |
|
} |
|
|
|
public static class ConvertRoleExtension |
|
{ |
|
[Obsolete("HandRole and DeviceRole are not related now")] |
|
public static DeviceRole ToDeviceRole(this HandRole role) |
|
{ |
|
switch (role) |
|
{ |
|
case HandRole.RightHand: return DeviceRole.RightHand; |
|
case HandRole.LeftHand: return DeviceRole.LeftHand; |
|
case HandRole.Controller3: return DeviceRole.Controller3; |
|
case HandRole.Controller4: return DeviceRole.Controller4; |
|
case HandRole.Controller5: return DeviceRole.Controller5; |
|
case HandRole.Controller6: return DeviceRole.Controller6; |
|
case HandRole.Controller7: return DeviceRole.Controller7; |
|
case HandRole.Controller8: return DeviceRole.Controller8; |
|
case HandRole.Controller9: return DeviceRole.Controller9; |
|
case HandRole.Controller10: return DeviceRole.Controller10; |
|
case HandRole.Controller11: return DeviceRole.Controller11; |
|
case HandRole.Controller12: return DeviceRole.Controller12; |
|
case HandRole.Controller13: return DeviceRole.Controller13; |
|
case HandRole.Controller14: return DeviceRole.Controller14; |
|
case HandRole.Controller15: return DeviceRole.Controller15; |
|
default: return (DeviceRole)((int)DeviceRole.Hmd - 1); // returns invalid value |
|
} |
|
} |
|
} |
|
|
|
public class HandRoleHandler : ViveRole.MapHandler<HandRole> |
|
{ |
|
#if __VIU_STEAMVR |
|
private uint[] m_sortedDevices = new uint[VRModule.MAX_DEVICE_COUNT]; |
|
#endif |
|
private List<uint> m_sortedDeviceList = new List<uint>(); |
|
|
|
// HandRole only tracks controllers |
|
private bool IsController(uint deviceIndex) |
|
{ |
|
return IsController(VRModule.GetCurrentDeviceState(deviceIndex).deviceClass); |
|
} |
|
|
|
private bool IsController(VRModuleDeviceClass deviceClass) |
|
{ |
|
return deviceClass == VRModuleDeviceClass.Controller; |
|
} |
|
|
|
private bool IsTracker(uint deviceIndex) |
|
{ |
|
return IsTracker(VRModule.GetCurrentDeviceState(deviceIndex).deviceClass); |
|
} |
|
|
|
private bool IsTracker(VRModuleDeviceClass deviceClass) |
|
{ |
|
return deviceClass == VRModuleDeviceClass.GenericTracker; |
|
} |
|
|
|
public override void OnAssignedAsCurrentMapHandler() { Refresh(); } |
|
|
|
public override void OnTrackedDeviceRoleChanged() { Refresh(); } |
|
|
|
public override void OnConnectedDeviceChanged(uint deviceIndex, VRModuleDeviceClass deviceClass, string deviceSN, bool connected) |
|
{ |
|
if (!RoleMap.IsDeviceBound(deviceSN) && !IsController(deviceClass) && !IsTracker(deviceClass)) { return; } |
|
|
|
Refresh(); |
|
} |
|
|
|
public override void OnBindingChanged(string deviceSN, bool previousIsBound, HandRole previousRole, bool currentIsBound, HandRole currentRole) |
|
{ |
|
uint deviceIndex; |
|
if (!VRModule.TryGetConnectedDeviceIndex(deviceSN, out deviceIndex)) { return; } |
|
|
|
Refresh(); |
|
} |
|
|
|
public void Refresh() |
|
{ |
|
MappingLeftRightHands(); |
|
MappingOtherControllers(); |
|
} |
|
|
|
private void MappingLeftRightHands() |
|
{ |
|
// assign left/right controllers according to the hint |
|
var rightIndex = VRModule.GetRightControllerDeviceIndex(); |
|
var leftIndex = VRModule.GetLeftControllerDeviceIndex(); |
|
|
|
if (VRModule.GetCurrentDeviceState(rightIndex).isConnected) |
|
{ |
|
MappingRoleIfUnbound(HandRole.RightHand, rightIndex); |
|
rightIndex = RoleMap.GetMappedDeviceByRole(HandRole.RightHand); |
|
} |
|
else if (RoleMap.IsRoleBound(HandRole.RightHand)) |
|
{ |
|
rightIndex = RoleMap.GetMappedDeviceByRole(HandRole.RightHand); |
|
} |
|
else |
|
{ |
|
rightIndex = VRModule.INVALID_DEVICE_INDEX; |
|
} |
|
|
|
if (VRModule.GetCurrentDeviceState(leftIndex).isConnected && leftIndex != rightIndex) |
|
{ |
|
MappingRoleIfUnbound(HandRole.LeftHand, leftIndex); |
|
leftIndex = RoleMap.GetMappedDeviceByRole(HandRole.LeftHand); |
|
} |
|
else if (RoleMap.IsRoleBound(HandRole.LeftHand)) |
|
{ |
|
leftIndex = RoleMap.GetMappedDeviceByRole(HandRole.LeftHand); |
|
} |
|
else |
|
{ |
|
leftIndex = VRModule.INVALID_DEVICE_INDEX; |
|
} |
|
|
|
// if not both left/right controllers are assigned, find and assign them with left/right most controller |
|
if (!VRModule.IsValidDeviceIndex(rightIndex) || !VRModule.IsValidDeviceIndex(leftIndex)) |
|
{ |
|
// find right to left sorted controllers |
|
// FIXME: GetSortedTrackedDeviceIndicesOfClass doesn't return correct devices count right after device connected |
|
#if __VIU_STEAMVR |
|
if (VRModule.activeModule == SupportedVRModule.SteamVR) |
|
{ |
|
var count = 0; |
|
var system = Valve.VR.OpenVR.System; |
|
if (system != null) |
|
{ |
|
count = (int)system.GetSortedTrackedDeviceIndicesOfClass(Valve.VR.ETrackedDeviceClass.Controller, m_sortedDevices, Valve.VR.OpenVR.k_unTrackedDeviceIndex_Hmd); |
|
} |
|
|
|
foreach (var deviceIndex in m_sortedDevices) |
|
{ |
|
if (m_sortedDeviceList.Count >= count) { break; } |
|
if (IsController(deviceIndex) && deviceIndex != rightIndex && deviceIndex != leftIndex && !RoleMap.IsDeviceConnectedAndBound(deviceIndex)) |
|
{ |
|
m_sortedDeviceList.Add(deviceIndex); |
|
} |
|
} |
|
} |
|
else |
|
#endif |
|
{ |
|
for (uint deviceIndex = 1u; deviceIndex < VRModule.MAX_DEVICE_COUNT; ++deviceIndex) |
|
{ |
|
if (IsController(deviceIndex) && deviceIndex != rightIndex && deviceIndex != leftIndex && !RoleMap.IsDeviceConnectedAndBound(deviceIndex)) |
|
{ |
|
m_sortedDeviceList.Add(deviceIndex); |
|
} |
|
} |
|
|
|
if (m_sortedDeviceList.Count > 1) |
|
{ |
|
SortDeviceIndicesByDirection(m_sortedDeviceList, VRModule.GetCurrentDeviceState(VRModule.HMD_DEVICE_INDEX).pose); |
|
} |
|
} |
|
|
|
if (m_sortedDeviceList.Count > 0 && !VRModule.IsValidDeviceIndex(rightIndex)) |
|
{ |
|
rightIndex = m_sortedDeviceList[0]; |
|
m_sortedDeviceList.RemoveAt(0); |
|
// mapping right most controller |
|
MappingRole(HandRole.RightHand, rightIndex); |
|
} |
|
|
|
if (m_sortedDeviceList.Count > 0 && !VRModule.IsValidDeviceIndex(leftIndex)) |
|
{ |
|
leftIndex = m_sortedDeviceList[m_sortedDeviceList.Count - 1]; |
|
// mapping left most controller |
|
MappingRole(HandRole.LeftHand, leftIndex); |
|
} |
|
|
|
m_sortedDeviceList.Clear(); |
|
} |
|
|
|
if (!VRModule.IsValidDeviceIndex(rightIndex)) { UnmappingRole(HandRole.RightHand); } |
|
if (!VRModule.IsValidDeviceIndex(leftIndex)) { UnmappingRole(HandRole.LeftHand); } |
|
} |
|
|
|
private void MappingOtherControllers() |
|
{ |
|
// mapping other controllers in order of device index |
|
var deviceIndex = 0u; |
|
var firstFoundTracker = VRModule.INVALID_DEVICE_INDEX; |
|
var rightIndex = RoleMap.GetMappedDeviceByRole(HandRole.RightHand); |
|
var leftIndex = RoleMap.GetMappedDeviceByRole(HandRole.LeftHand); |
|
|
|
for (var role = RoleInfo.MinValidRole; role <= RoleInfo.MaxValidRole; ++role) |
|
{ |
|
if (!RoleInfo.IsValidRole(role)) { continue; } |
|
if (role == HandRole.RightHand || role == HandRole.LeftHand) { continue; } |
|
if (RoleMap.IsRoleBound(role)) { continue; } |
|
|
|
// find next valid device |
|
if (VRModule.IsValidDeviceIndex(deviceIndex)) |
|
{ |
|
while (!IsController(deviceIndex) || RoleMap.IsDeviceConnectedAndBound(deviceIndex) || deviceIndex == rightIndex || deviceIndex == leftIndex) |
|
{ |
|
if (!VRModule.IsValidDeviceIndex(firstFoundTracker) && IsTracker(deviceIndex)) { firstFoundTracker = deviceIndex; } |
|
if (!VRModule.IsValidDeviceIndex(++deviceIndex)) { break; } |
|
} |
|
} |
|
|
|
if (VRModule.IsValidDeviceIndex(deviceIndex)) |
|
{ |
|
MappingRole(role, deviceIndex++); |
|
} |
|
else |
|
{ |
|
UnmappingRole(role); |
|
} |
|
} |
|
|
|
// if external camera is not mapped, try mapping first found tracker |
|
if (!RoleMap.IsRoleMapped(HandRole.ExternalCamera) && VRModule.IsValidDeviceIndex(firstFoundTracker) && !RoleMap.IsDeviceConnectedAndBound(firstFoundTracker)) |
|
{ |
|
MappingRole(HandRole.ExternalCamera, firstFoundTracker); |
|
} |
|
} |
|
|
|
private static readonly float[] s_deviceDirPoint = new float[VRModule.MAX_DEVICE_COUNT]; |
|
public static void SortDeviceIndicesByDirection(List<uint> deviceList, RigidPose sortingReference) |
|
{ |
|
if (deviceList == null || deviceList.Count == 0) { return; } |
|
|
|
for (int i = 0, imax = deviceList.Count; i < imax; ++i) |
|
{ |
|
var deviceIndex = deviceList[i]; |
|
if (!VRModule.IsValidDeviceIndex(deviceIndex)) { continue; } |
|
|
|
var deviceState = VRModule.GetCurrentDeviceState(deviceIndex); |
|
if (deviceState.isConnected) |
|
{ |
|
var localPos = sortingReference.InverseTransformPoint(deviceState.pose.pos); |
|
s_deviceDirPoint[deviceIndex] = GetDirectionPoint(new Vector2(localPos.x, localPos.z)); |
|
} |
|
else |
|
{ |
|
s_deviceDirPoint[deviceIndex] = -1f; |
|
} |
|
} |
|
|
|
deviceList.Sort(CompareDirection); |
|
} |
|
|
|
[Obsolete] |
|
public static void SortDeviceIndicesByDirection(List<uint> deviceList, PoseTracker.Pose sortingReference) |
|
{ |
|
SortDeviceIndicesByDirection(deviceList, sortingReference); |
|
} |
|
|
|
private static int CompareDirection(uint d1, uint d2) |
|
{ |
|
var d1Point = s_deviceDirPoint[d1]; |
|
var d2Point = s_deviceDirPoint[d2]; |
|
var d1Valid = VRModule.IsValidDeviceIndex(d1) && d1Point >= 0f; |
|
var d2Valid = VRModule.IsValidDeviceIndex(d2) && d2Point >= 0f; |
|
|
|
if (!d1Valid || !d2Valid) |
|
{ |
|
if (d1Valid) { return -1; } |
|
if (d2Valid) { return 1; } |
|
|
|
if (d1 < d2) { return -1; } |
|
if (d1 > d2) { return 1; } |
|
|
|
return 0; |
|
} |
|
|
|
if (d1Point < d2Point) { return -1; } |
|
if (d1Point > d2Point) { return 1; } |
|
|
|
return 0; |
|
} |
|
|
|
// Y+ |
|
// || |
|
// \\ 4 || 3 // |
|
// \\ || // |
|
// 5 \\ ^^ // 2 |
|
// =========[]========= X+ |
|
// 6 // || \\ 1 |
|
// // || \\ |
|
// // 7 || 0 \\ |
|
// || |
|
// less point => right side |
|
public static float GetDirectionPoint(Vector2 pos) |
|
{ |
|
var ax = Mathf.Abs(pos.x); |
|
var ay = Mathf.Abs(pos.y); |
|
if (pos.x > 0f) |
|
{ |
|
if (pos.y < 0f) |
|
{ |
|
if (ax < ay) |
|
{ |
|
return 0f + (ax / ay); |
|
} |
|
else |
|
{ |
|
return 1f + (1f - ay / ax); |
|
} |
|
} |
|
else |
|
{ |
|
if (ax > ay) |
|
{ |
|
return 2f + (ay / ax); |
|
} |
|
else |
|
{ |
|
return 3f + (1f - ax / ay); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if (pos.y > 0f) |
|
{ |
|
if (ax < ay) |
|
{ |
|
return 4f + (ax / ay); |
|
} |
|
else |
|
{ |
|
return 5f + (1f - ay / ax); |
|
} |
|
} |
|
else |
|
{ |
|
if (ax > ay) |
|
{ |
|
return 6f + (ay / ax); |
|
} |
|
else |
|
{ |
|
return 7f + (1 - ax / ay); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |