//========= 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 { /// /// Defines roles for those devices that have buttons /// [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 { #if __VIU_STEAMVR private uint[] m_sortedDevices = new uint[VRModule.MAX_DEVICE_COUNT]; #endif private List m_sortedDeviceList = new List(); // 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 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 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); } } } } } }