//========= Copyright 2016-2023, HTC Corporation. All rights reserved. =========== #if VIU_STEAMVR && UNITY_STANDALONE using HTC.UnityPlugin.Utility; using HTC.UnityPlugin.Vive; using System.Text; using UnityEngine; using Valve.VR; using System; using System.Runtime.InteropServices; using System.Collections.Generic; #if UNITY_2017_2_OR_NEWER using UnityEngine.XR; #elif UNITY_5_4_OR_NEWER using XRSettings = UnityEngine.VR.VRSettings; #endif #if VIU_XR_GENERAL_SETTINGS using UnityEngine.XR.Management; #endif #endif namespace HTC.UnityPlugin.VRModuleManagement { public sealed partial class SteamVRModule : VRModule.ModuleBase { public const string OPENVR_XR_LOADER_NAME = "Open VR Loader"; public const string OPENVR_XR_LOADER_CLASS_NAME = "OpenVRLoader"; public const string ACTION_SET_NAME = "htc_viu"; public const string ACTION_SET_PATH = "/actions/" + ACTION_SET_NAME; #if VIU_STEAMVR_2_0_0_OR_NEWER && UNITY_STANDALONE public enum HapticStruct { Haptic } private class HapticStructReslver : EnumToIntResolver { public override int Resolve(HapticStruct e) { return (int)e; } } private static readonly Quaternion s_leftSkeletonWristFixRotation = Quaternion.Euler(0.0f, 0.0f, 90.0f); private static readonly Quaternion s_rightSkeletonWristFixRotation = Quaternion.Euler(0.0f, 0.0f, -90.0f); private static readonly Quaternion s_leftSkeletonFixRotation = Quaternion.Euler(180.0f, 90.0f, 0.0f); private static readonly Quaternion s_rightSkeletonFixRotation = Quaternion.Euler(0.0f, 90.0f, 0.0f); private static bool s_pathInitialized; private static bool s_actionInitialized; public static ActionCollection pressActions { get; private set; } public static ActionCollection touchActions { get; private set; } public static ActionCollection v1Actions { get; private set; } public static ActionCollection v2Actions { get; private set; } public static ActionCollection vibrateActions { get; private set; } public static ulong skeletonActionHandleLeft { get; private set; } public static ulong skeletonActionHandleRight { get; private set; } private static ulong s_actionSetHandle; private static VRBoneTransform_t[] s_tempBoneTransforms = new VRBoneTransform_t[SteamVR_Action_Skeleton.numBones]; private uint m_digitalDataSize; private uint m_analogDataSize; private ETrackingUniverseOrigin m_prevTrackingSpace; private bool m_hasInputFocus = true; private TrackedDevicePose_t[] m_poses; private TrackedDevicePose_t[] m_gamePoses; private OriginDataCache m_originDataCache = new OriginDataCache(); private IndexMap m_indexMap = new IndexMap(); private VRModule.SubmoduleBase.Collection m_submodules = new VRModule.SubmoduleBase.Collection(new ViveHandTrackingSubmodule()); private uint m_openvrRightIndex; private uint m_openvrLeftIndex; private uint m_moduleRightIndex; private uint m_moduleLeftIndex; private static ETrackingUniverseOrigin trackingSpace { get { var compositor = OpenVR.Compositor; if (compositor == null) { return default(ETrackingUniverseOrigin); } return compositor.GetTrackingSpace(); } set { var compositor = OpenVR.Compositor; if (compositor == null) { return; } compositor.SetTrackingSpace(value); } } private static bool inputFocus { get { var system = OpenVR.System; if (system == null) { return false; } return system.IsInputAvailable(); } } public static void InitializePaths() { if (s_pathInitialized) { return; } s_pathInitialized = true; pressActions = new ActionCollection("/in/viu_press_", "boolean"); pressActions.Set(VRModuleRawButton.System, "00", "Press00 (System)"); pressActions.Set(VRModuleRawButton.ApplicationMenu, "01", "Press01 (ApplicationMenu)"); pressActions.Set(VRModuleRawButton.Grip, "02", "Press02 (Grip)"); pressActions.Set(VRModuleRawButton.DPadLeft, "03", "Press03 (DPadLeft)"); pressActions.Set(VRModuleRawButton.DPadUp, "04", "Press04 (DPadUp)"); pressActions.Set(VRModuleRawButton.DPadRight, "05", "Press05 (DPadRight)"); pressActions.Set(VRModuleRawButton.DPadDown, "06", "Press06 (DPadDown)"); pressActions.Set(VRModuleRawButton.A, "07", "Press07 (A)"); pressActions.Set(VRModuleRawButton.Joystick, "08", "Press08 (Thumbstick)"); pressActions.Set(VRModuleRawButton.ProximitySensor, "31", "Press31 (ProximitySensor)"); pressActions.Set(VRModuleRawButton.Touchpad, "32", "Press32 (Touchpad)"); pressActions.Set(VRModuleRawButton.Trigger, "33", "Press33 (Trigger)"); pressActions.Set(VRModuleRawButton.CapSenseGrip, "34", "Press34 (CapSenseGrip)"); pressActions.Set(VRModuleRawButton.Bumper, "35", "Press35 (Bumper)"); touchActions = new ActionCollection("/in/viu_touch_", "boolean"); touchActions.Set(VRModuleRawButton.System, "00", "Touch00 (System)"); touchActions.Set(VRModuleRawButton.ApplicationMenu, "01", "Touch01 (ApplicationMenu)"); touchActions.Set(VRModuleRawButton.Grip, "02", "Touch02 (Grip)"); touchActions.Set(VRModuleRawButton.DPadLeft, "03", "Touch03 (DPadLeft)"); touchActions.Set(VRModuleRawButton.DPadUp, "04", "Touch04 (DPadUp)"); touchActions.Set(VRModuleRawButton.DPadRight, "05", "Touch05 (DPadRight)"); touchActions.Set(VRModuleRawButton.DPadDown, "06", "Touch06 (DPadDown)"); touchActions.Set(VRModuleRawButton.A, "07", "Touch07 (A)"); touchActions.Set(VRModuleRawButton.Joystick, "08", "Touch08 (Thumbstick)"); touchActions.Set(VRModuleRawButton.ProximitySensor, "31", "Touch31 (ProximitySensor)"); touchActions.Set(VRModuleRawButton.Touchpad, "32", "Touch32 (Touchpad)"); touchActions.Set(VRModuleRawButton.Trigger, "33", "Touch33 (Trigger)"); touchActions.Set(VRModuleRawButton.CapSenseGrip, "34", "Touch34 (CapSenseGrip)"); touchActions.Set(VRModuleRawButton.Bumper, "35", "Touch35 (Bumper)"); v1Actions = new ActionCollection("/in/viu_axis_", "vector1"); v1Actions.Set(VRModuleRawAxis.Axis0X, "0x", "Axis0 X (TouchpadX)"); v1Actions.Set(VRModuleRawAxis.Axis0Y, "0y", "Axis0 Y (TouchpadY)"); v1Actions.Set(VRModuleRawAxis.Axis1X, "1x", "Axis1 X (Trigger)"); v1Actions.Set(VRModuleRawAxis.Axis1Y, "1y", "Axis1 Y"); v1Actions.Set(VRModuleRawAxis.Axis2X, "2x", "Axis2 X (CapSenseGrip)"); v1Actions.Set(VRModuleRawAxis.Axis2Y, "2y", "Axis2 Y"); v1Actions.Set(VRModuleRawAxis.Axis3X, "3x", "Axis3 X (IndexCurl)"); v1Actions.Set(VRModuleRawAxis.Axis3Y, "3y", "Axis3 Y (MiddleCurl)"); v1Actions.Set(VRModuleRawAxis.Axis4X, "4x", "Axis4 X (RingCurl)"); v1Actions.Set(VRModuleRawAxis.Axis4Y, "4y", "Axis4 Y (PinkyCurl)"); v2Actions = new ActionCollection("/in/viu_axis_", "vector2"); v2Actions.Set(VRModuleRawAxis.Axis0X, "0xy", "Axis0 X&Y (Touchpad)"); v2Actions.Set(VRModuleRawAxis.Axis1X, "1xy", "Axis1 X&Y"); v2Actions.Set(VRModuleRawAxis.Axis2X, "2xy", "Axis2 X&Y (Thumbstick)"); v2Actions.Set(VRModuleRawAxis.Axis3X, "3xy", "Axis3 X&Y"); v2Actions.Set(VRModuleRawAxis.Axis4X, "4xy", "Axis4 X&Y"); vibrateActions = new ActionCollection("/out/viu_vib_", "vibration"); vibrateActions.Set(HapticStruct.Haptic, "01", "Vibration"); } public static void InitializeHandles() { if (!Application.isPlaying || s_actionInitialized) { return; } s_actionInitialized = true; InitializePaths(); SteamVR.Initialize(); #if VIU_STEAMVR_2_2_0_OR_NEWER SteamVR_ActionSet_Manager.UpdateActionStates(); #elif VIU_STEAMVR_2_1_0_OR_NEWER SteamVR_ActionSet_Manager.UpdateActionSetsState(); #else SteamVR_ActionSet.UpdateActionSetsState(); #endif var vrInput = OpenVR.Input; if (vrInput == null) { Debug.LogError("Fail loading OpenVR.Input"); return; } pressActions.ResolveHandles(vrInput); touchActions.ResolveHandles(vrInput); v1Actions.ResolveHandles(vrInput); v2Actions.ResolveHandles(vrInput); vibrateActions.ResolveHandles(vrInput); s_actionSetHandle = SafeGetActionSetHandle(vrInput, ACTION_SET_PATH); skeletonActionHandleLeft = SafeGetActionHandle(vrInput, ACTION_SET_PATH + "/in/viu_skeleton_left"); skeletonActionHandleRight = SafeGetActionHandle(vrInput, ACTION_SET_PATH + "/in/viu_skeleton_right"); if (skeletonActionHandleLeft == OpenVR.k_ulInvalidActionHandle) { Debug.LogWarning("Skeleton action for left hand is not found: " + ACTION_SET_PATH + "/in/viu_skeleton_left"); } if (skeletonActionHandleRight == OpenVR.k_ulInvalidActionHandle) { Debug.LogWarning("Skeleton action for right hand is not found: " + ACTION_SET_PATH + "/in/viu_skeleton_right"); } } public static ulong GetInputSourceHandleForDevice(uint deviceIndex) { return s_moduleInstance.m_originDataCache.GetPathHandleByTrackedIndex(s_moduleInstance.m_indexMap.Module2TrackedIndex(deviceIndex)); } public override bool ShouldActiveModule() { #if UNITY_2019_3_OR_NEWER && VIU_XR_GENERAL_SETTINGS return VIUSettings.activateSteamVRModule && (UnityXRModuleBase.HasActiveLoader(VRModuleKnownXRLoader.OpenVR) || (XRSettings.enabled && XRSettings.loadedDeviceName.Contains("OpenVR"))); #elif UNITY_5_4_OR_NEWER return VIUSettings.activateSteamVRModule && XRSettings.enabled && XRSettings.loadedDeviceName.Contains("OpenVR"); #else return VIUSettings.activateSteamVRModule && SteamVR.enabled; #endif } public override void OnActivated() { m_digitalDataSize = (uint)Marshal.SizeOf(new InputDigitalActionData_t()); m_analogDataSize = (uint)Marshal.SizeOf(new InputAnalogActionData_t()); m_poses = new TrackedDevicePose_t[OpenVR.k_unMaxTrackedDeviceCount]; m_gamePoses = new TrackedDevicePose_t[0]; InitializeHandles(); #if VIU_STEAMVR_2_1_0_OR_NEWER SteamVR_Input.GetActionSet(ACTION_SET_NAME).Activate(SteamVR_Input_Sources.Any, 0, false); #else var actionSet = SteamVR_Input.GetActionSetFromPath(ACTION_SET_PATH); if (actionSet != null) { actionSet.ActivatePrimary(); } #endif #if VIU_STEAMVR_2_2_0_OR_NEWER SteamVR_Input.onNonVisualActionsUpdated += UpdateDeviceInput; SteamVR_Input.onPosesUpdated += UpdateDeviceConnectionAndPose; #else SteamVR_Input.OnNonVisualActionsUpdated += UpdateDeviceInput; SteamVR_Input.OnPosesUpdated += UpdateDeviceConnectionAndPose; #endif //s_devicePathHandles = new ulong[OpenVR.k_unMaxTrackedDeviceCount]; EnsureDeviceStateLength(8); // preserve previous tracking space m_prevTrackingSpace = trackingSpace; m_hasInputFocus = inputFocus; SteamVR_Events.InputFocus.AddListener(OnInputFocus); s_moduleInstance = this; m_submodules.ActivateAllModules(); } public override void OnDeactivated() { m_submodules.DeactivateAllModules(); SteamVR_Events.InputFocus.RemoveListener(OnInputFocus); #if VIU_STEAMVR_2_2_0_OR_NEWER SteamVR_Input.onNonVisualActionsUpdated -= UpdateDeviceInput; SteamVR_Input.onPosesUpdated -= UpdateDeviceConnectionAndPose; #else SteamVR_Input.OnNonVisualActionsUpdated -= UpdateDeviceInput; SteamVR_Input.OnPosesUpdated -= UpdateDeviceConnectionAndPose; #endif m_originDataCache.ClearCache(); m_indexMap.Clear(); trackingSpace = m_prevTrackingSpace; s_moduleInstance = null; } private void UpdateDeviceInput() { IVRModuleDeviceState prevState; IVRModuleDeviceStateRW currState; var vrInput = OpenVR.Input; if (vrInput == null) { for (uint i = 0, iMax = GetDeviceStateLength(); i < iMax; ++i) { if (TryGetValidDeviceState(i, out prevState, out currState) && currState.isConnected) { currState.buttonPressed = 0ul; currState.buttonTouched = 0ul; currState.ResetAxisValues(); } } } else { m_originDataCache.ClearCache(); foreach (var actionDeviceData in AllActionDevicePath(vrInput, pressActions)) { bool digitValue; var error = TryGetDigitalValue(vrInput, actionDeviceData.actionHandle, actionDeviceData.devicePathHandle, out digitValue); if (error == EVRInputError.None) { actionDeviceData.currState.SetButtonPress(actionDeviceData.inputKey, digitValue); } else { Debug.LogError("[SteamVRv2Module] CVRInput.GetDigitalActionData fail. action=" + pressActions.ActionPaths[actionDeviceData.inputKey] + " error=" + error); } } foreach (var actionDeviceData in AllActionDevicePath(vrInput, touchActions)) { bool value; var error = TryGetDigitalValue(vrInput, actionDeviceData.actionHandle, actionDeviceData.devicePathHandle, out value); if (error == EVRInputError.None) { actionDeviceData.currState.SetButtonTouch(actionDeviceData.inputKey, value); } else { Debug.LogError("[SteamVRv2Module] CVRInput.GetDigitalActionData fail. action=" + touchActions.ActionPaths[actionDeviceData.inputKey] + " error=" + error); } } foreach (var actionDeviceData in AllActionDevicePath(vrInput, v1Actions)) { Vector3 value; var error = TryGetAnalogValue(vrInput, actionDeviceData.actionHandle, actionDeviceData.devicePathHandle, out value); if (error == EVRInputError.None) { actionDeviceData.currState.SetAxisValue(actionDeviceData.inputKey, value.x); } else { Debug.LogError("[SteamVRv2Module] CVRInput.GetAnalogActionData fail. action=" + v1Actions.ActionPaths[actionDeviceData.inputKey] + " error=" + error); } } foreach (var actionDeviceData in AllActionDevicePath(vrInput, v2Actions)) { Vector3 value; var error = TryGetAnalogValue(vrInput, actionDeviceData.actionHandle, actionDeviceData.devicePathHandle, out value); if (error == EVRInputError.None) { actionDeviceData.currState.SetAxisValue(actionDeviceData.inputKey, value.x); actionDeviceData.currState.SetAxisValue(actionDeviceData.inputKey + 1, value.y); } else { Debug.LogError("[SteamVRv2Module] CVRInput.GetAnalogActionData fail. action=" + v2Actions.ActionPaths[actionDeviceData.inputKey] + " error=" + error); } } } m_submodules.UpdateAllModulesActivity(); m_submodules.UpdateModulesDeviceInput(); ProcessDeviceInputChanged(); } private void UpdateDeviceConnectionAndPose(bool obj) { IVRModuleDeviceState prevState; IVRModuleDeviceStateRW currState; FlushDeviceState(); var vrSystem = OpenVR.System; var vrCompositor = OpenVR.Compositor; if (vrSystem == null || vrCompositor == null) { // clear all device states if OpenVR disabled for (uint i = 0, imax = GetDeviceStateLength(); i < imax; ++i) { if (TryGetValidDeviceState(i, out prevState, out currState) && currState.isConnected) { currState.Reset(); } } } else { vrCompositor.GetLastPoses(m_poses, m_gamePoses); for (uint i = 0u, imax = (uint)m_poses.Length; i < imax; ++i) { if (!m_poses[i].bDeviceIsConnected) { uint moduleIndex; if (m_indexMap.UnmapTracked(i, out moduleIndex)) { if (TryGetValidDeviceState(moduleIndex, out prevState, out currState)) { currState.Reset(); } } } else { uint moduleIndex; if (!m_indexMap.TryGetModuleIndex(i, out moduleIndex)) { moduleIndex = FindAndEnsureUnusedNotHMDDeviceState(out prevState, out currState); m_indexMap.Map(i, moduleIndex); } else { EnsureValidDeviceState(moduleIndex, out prevState, out currState); } if (!prevState.isConnected) { currState.isConnected = true; currState.deviceClass = ToVRModuleDeviceClass(vrSystem.GetTrackedDeviceClass(i)); currState.serialNumber = QueryDeviceStringProperty(vrSystem, i, ETrackedDeviceProperty.Prop_SerialNumber_String); currState.modelNumber = QueryDeviceStringProperty(vrSystem, i, ETrackedDeviceProperty.Prop_ModelNumber_String); currState.renderModelName = QueryDeviceStringProperty(vrSystem, i, ETrackedDeviceProperty.Prop_RenderModelName_String); SetupKnownDeviceModel(currState); } // update device status currState.isPoseValid = m_poses[i].bPoseIsValid; currState.isOutOfRange = m_poses[i].eTrackingResult == ETrackingResult.Running_OutOfRange || m_poses[i].eTrackingResult == ETrackingResult.Calibrating_OutOfRange; currState.isCalibrating = m_poses[i].eTrackingResult == ETrackingResult.Calibrating_InProgress || m_poses[i].eTrackingResult == ETrackingResult.Calibrating_OutOfRange; currState.isUninitialized = m_poses[i].eTrackingResult == ETrackingResult.Uninitialized; currState.velocity = new Vector3(m_poses[i].vVelocity.v0, m_poses[i].vVelocity.v1, -m_poses[i].vVelocity.v2); currState.angularVelocity = new Vector3(-m_poses[i].vAngularVelocity.v0, -m_poses[i].vAngularVelocity.v1, m_poses[i].vAngularVelocity.v2); var rigidTransform = new SteamVR_Utils.RigidTransform(m_poses[i].mDeviceToAbsoluteTracking); currState.position = rigidTransform.pos; currState.rotation = rigidTransform.rot; } } } m_submodules.UpdateAllModulesActivity(); m_submodules.UpdateModulesDeviceConnectionAndPoses(); // process hand role bool roleChanged = false; if (vrSystem == null) { m_openvrRightIndex = INVALID_DEVICE_INDEX; m_openvrLeftIndex = INVALID_DEVICE_INDEX; } else { var right = vrSystem.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.RightHand); var left = vrSystem.GetTrackedDeviceIndexForControllerRole(ETrackedControllerRole.LeftHand); uint openvrRight; m_indexMap.TryGetModuleIndex(right, out openvrRight); roleChanged |= ChangeProp.Set(ref m_openvrRightIndex, openvrRight); uint openvrLeft; m_indexMap.TryGetModuleIndex(left, out openvrLeft); roleChanged |= ChangeProp.Set(ref m_openvrLeftIndex, openvrLeft); } var moduleRight = m_openvrRightIndex != INVALID_DEVICE_INDEX ? m_openvrRightIndex : m_submodules.GetFirstRightHandedIndex(); var moduleLeft = m_openvrLeftIndex != INVALID_DEVICE_INDEX ? m_openvrLeftIndex : m_submodules.GetFirstLeftHandedIndex(); roleChanged |= ChangeProp.Set(ref m_moduleRightIndex, moduleRight); roleChanged |= ChangeProp.Set(ref m_moduleLeftIndex, moduleLeft); UpdateHandJoints(m_openvrLeftIndex, skeletonActionHandleLeft, true); UpdateHandJoints(m_openvrRightIndex, skeletonActionHandleRight, false); ProcessConnectedDeviceChanged(); ProcessDevicePoseChanged(); if (roleChanged) { InvokeControllerRoleChangedEvent(); } } public override void Update() { if (SteamVR.active) { SteamVR_Settings.instance.lockPhysicsUpdateRateToRenderFrequency = VRModule.lockPhysicsUpdateRateToRenderFrequency; } } public override void UpdateTrackingSpaceType() { switch (VRModule.trackingSpaceType) { case VRModuleTrackingSpaceType.RoomScale: trackingSpace = ETrackingUniverseOrigin.TrackingUniverseStanding; break; case VRModuleTrackingSpaceType.Stationary: trackingSpace = ETrackingUniverseOrigin.TrackingUniverseSeated; break; } } private void UpdateHandJoints(uint deviceIndex, ulong actionHandle, bool isLeft) { if (deviceIndex == INVALID_DEVICE_INDEX || actionHandle == OpenVR.k_ulInvalidActionHandle) { return; } IVRModuleDeviceState prevState; IVRModuleDeviceStateRW currState; if (TryGetValidDeviceState(deviceIndex, out prevState, out currState)) { RigidPose fixedHandPose = currState.pose; fixedHandPose.rot *= SteamVR_Action_Skeleton.steamVRFixUpRotation; EVRSkeletalMotionRange skeletonType = (EVRSkeletalMotionRange)(isLeft ? VIUSettings.steamVRLeftSkeletonMode : VIUSettings.steamVRRightSkeletonMode); EVRInputError boneError = OpenVR.Input.GetSkeletalBoneData(actionHandle, EVRSkeletalTransformSpace.Model, skeletonType, s_tempBoneTransforms); if (boneError == EVRInputError.None) { AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.Wrist, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.wrist); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.ThumbMetacarpal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.thumbMetacarpal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.ThumbProximal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.thumbProximal); //AssignHandJoint(handPose, isLeft, currState.handJoints, HandJointName.ThumbIntermediate, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.thumbMiddle); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.ThumbDistal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.thumbDistal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.ThumbTip, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.thumbTip); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.IndexMetacarpal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.indexMetacarpal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.IndexProximal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.indexProximal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.IndexIntermediate, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.indexMiddle); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.IndexDistal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.indexDistal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.IndexTip, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.indexTip); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.MiddleMetacarpal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.middleMetacarpal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.MiddleProximal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.middleProximal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.MiddleIntermediate, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.middleMiddle); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.MiddleDistal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.middleDistal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.MiddleTip, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.middleTip); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.RingMetacarpal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.ringMetacarpal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.RingProximal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.ringProximal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.RingIntermediate, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.ringMiddle); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.RingDistal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.ringDistal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.RingTip, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.ringTip); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.PinkyMetacarpal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.pinkyMetacarpal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.PinkyProximal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.pinkyProximal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.PinkyIntermediate, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.pinkyMiddle); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.PinkyDistal, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.pinkyDistal); AssignHandJoint(fixedHandPose, isLeft, currState.handJoints, HandJointName.PinkyTip, s_tempBoneTransforms, SteamVR_Skeleton_JointIndexes.pinkyTip); } } } private void AssignHandJoint(RigidPose handPose, bool isLeft, JointEnumArray joints, HandJointName jointName, VRBoneTransform_t[] boneTransforms, int steamBoneIndex) { VRBoneTransform_t bone = boneTransforms[steamBoneIndex]; Vector3 position = new Vector3(-bone.position.v0, bone.position.v1, bone.position.v2); Quaternion rotation = new Quaternion(bone.orientation.x, -bone.orientation.y, -bone.orientation.z, bone.orientation.w); if (steamBoneIndex == SteamVR_Skeleton_JointIndexes.wrist) { if (isLeft) { rotation *= s_leftSkeletonWristFixRotation; } else { rotation *= s_rightSkeletonWristFixRotation; } } else { if (isLeft) { rotation *= s_leftSkeletonFixRotation; } else { rotation *= s_rightSkeletonFixRotation; } } joints[jointName] = new JointPose(handPose * new RigidPose(position, rotation)); } private void OnInputFocus(bool value) { m_hasInputFocus = value; InvokeInputFocusEvent(value); } public override bool HasInputFocus() { return m_hasInputFocus; } public override uint GetLeftControllerDeviceIndex() { return m_moduleLeftIndex; } public override uint GetRightControllerDeviceIndex() { return m_moduleRightIndex; } private StringBuilder m_sb; private string QueryDeviceStringProperty(CVRSystem system, uint deviceIndex, ETrackedDeviceProperty prop) { var error = default(ETrackedPropertyError); var capacity = (int)system.GetStringTrackedDeviceProperty(deviceIndex, prop, null, 0, ref error); if (capacity <= 1 || capacity > 128) { return string.Empty; } if (m_sb == null) { m_sb = new StringBuilder(capacity); } else { m_sb.EnsureCapacity(capacity); } system.GetStringTrackedDeviceProperty(deviceIndex, prop, m_sb, (uint)m_sb.Capacity, ref error); if (error != ETrackedPropertyError.TrackedProp_Success) { return string.Empty; } var result = m_sb.ToString(); m_sb.Length = 0; return result; } public override void TriggerViveControllerHaptic(uint deviceIndex, ushort durationMicroSec = 500) { TriggerHapticVibration(deviceIndex, 0.01f, 85f, Mathf.InverseLerp(0, 4000, durationMicroSec), 0f); } public override void TriggerHapticVibration(uint deviceIndex, float durationSeconds = 0.01f, float frequency = 85f, float amplitude = 0.125f, float startSecondsFromNow = 0f) { var trackedIndex = m_indexMap.Module2TrackedIndex(deviceIndex); var handle = m_originDataCache.GetPathHandleByTrackedIndex(trackedIndex); if (handle == OpenVR.k_ulInvalidDriverHandle) { return; } var vrInput = OpenVR.Input; if (vrInput != null) { var error = vrInput.TriggerHapticVibrationAction(vibrateActions.ActionHandles[HapticStruct.Haptic], startSecondsFromNow, durationSeconds, frequency, amplitude, handle); if (error != EVRInputError.None) { Debug.LogError("TriggerViveControllerHaptic failed! error=" + error); } } } private static ulong SafeGetActionSetHandle(CVRInput vrInput, string path) { if (string.IsNullOrEmpty(path)) { return 0ul; } var handle = OpenVR.k_ulInvalidActionHandle; var error = vrInput.GetActionSetHandle(path, ref handle); if (error != EVRInputError.None) { Debug.LogError("Load " + path + " action failed! error=" + error); return OpenVR.k_ulInvalidActionHandle; } else { return handle; } } private static ulong SafeGetActionHandle(CVRInput vrInput, string path) { if (string.IsNullOrEmpty(path)) { return 0ul; } var handle = OpenVR.k_ulInvalidActionHandle; var error = vrInput.GetActionHandle(path, ref handle); if (error != EVRInputError.None) { Debug.LogError("Load " + path + " action failed! error=" + error); return OpenVR.k_ulInvalidActionHandle; } else { return handle; } } private EVRInputError TryGetDigitalValue(CVRInput vrInput, ulong actionHandle, ulong devicePathHandle, out bool value) { var data = default(InputDigitalActionData_t); var error = vrInput.GetDigitalActionData(actionHandle, ref data, m_digitalDataSize, devicePathHandle); value = data.bState; return error; } private EVRInputError TryGetAnalogValue(CVRInput vrInput, ulong actionHandle, ulong devicePathHandle, out Vector3 value) { var data = default(InputAnalogActionData_t); var error = vrInput.GetAnalogActionData(actionHandle, ref data, m_analogDataSize, devicePathHandle); value.x = data.x; value.y = data.y; value.z = data.z; return error; } private struct OriginData { public ulong devicePathHandle; public uint deviceIndex; } private class OriginDataCache { private static readonly uint originInfoSize = (uint)Marshal.SizeOf(new InputOriginInfo_t()); private readonly Dictionary cache = new Dictionary(); private readonly ulong[] index2PathHandle = new ulong[OpenVR.k_unMaxTrackedDeviceCount]; public void ClearCache() { Array.Clear(index2PathHandle, 0, (int)OpenVR.k_unMaxTrackedDeviceCount); cache.Clear(); } public ulong GetPathHandleByTrackedIndex(uint trackedIndex) { return trackedIndex < index2PathHandle.Length ? index2PathHandle[trackedIndex] : OpenVR.k_ulInvalidPathHandle; } public EVRInputError TryGetOriginData(CVRInput vrInput, ulong originHandle, out OriginData originData) { if (cache.TryGetValue(originHandle, out originData)) { return EVRInputError.None; } var info = default(InputOriginInfo_t); var error = vrInput.GetOriginTrackedDeviceInfo(originHandle, ref info, originInfoSize); if (error == EVRInputError.None) { cache.Add(originHandle, originData = new OriginData() { devicePathHandle = info.devicePath, deviceIndex = info.trackedDeviceIndex, }); index2PathHandle[originData.deviceIndex] = originData.devicePathHandle; } else { originData = default(OriginData); } return error; } } private ActionCollection.Enumerable AllActionDevicePath(CVRInput vrInput, ActionCollection actionCollection) #if CSHARP_7_OR_LATER where T : Enum #endif { return new ActionCollection.Enumerable(AllActionDevicePathEnumerator(vrInput, actionCollection)); } private static ulong[] outActionOrigins = new ulong[OpenVR.k_unMaxActionOriginCount]; private IEnumerator.EnumData> AllActionDevicePathEnumerator(CVRInput vrInput, ActionCollection actionCollection) #if CSHARP_7_OR_LATER where T : Enum #endif { IVRModuleDeviceState prevState; IVRModuleDeviceStateRW currState; EVRInputError error; var current = default(ActionCollection.EnumData); foreach (var p in actionCollection.ActionHandles.EnumValues) { current.inputKey = p.Key; current.actionHandle = p.Value; if (current.actionHandle == OpenVR.k_ulInvalidActionHandle) { continue; } Array.Clear(outActionOrigins, 0, outActionOrigins.Length); error = vrInput.GetActionOrigins(s_actionSetHandle, current.actionHandle, outActionOrigins); while (error == EVRInputError.BufferTooSmall && outActionOrigins.Length <= 4096) { Array.Resize(ref outActionOrigins, outActionOrigins.Length * 2); Debug.LogWarning("Expanding outActionOrigins size to " + outActionOrigins.Length); error = vrInput.GetActionOrigins(s_actionSetHandle, current.actionHandle, outActionOrigins); } if (error != EVRInputError.None) { Debug.LogError("[SteamVRv2Module] CVRInput.GetActionOrigins fail. input=" + p.Key + " action=" + actionCollection.ActionPaths[p.Key] + " error=" + error); continue; } foreach (var originHandle in outActionOrigins) { if (originHandle == 0ul) { break; } OriginData data; error = m_originDataCache.TryGetOriginData(vrInput, originHandle, out data); if (error != EVRInputError.None) { Debug.LogError("[SteamVRv2Module] CVRInput.GetOriginTrackedDeviceInfo fail. input=" + p.Key + " action=" + actionCollection.ActionPaths[p.Key] + " originHandle=" + originHandle + " error=" + error); continue; } current.devicePathHandle = data.devicePathHandle; uint moduleIndex; if (!m_indexMap.TryGetModuleIndex(data.deviceIndex, out moduleIndex)) { continue; } if (!TryGetValidDeviceState(moduleIndex, out prevState, out currState)) { continue; } current.currState = currState; yield return current; } } } public class ActionCollection #if CSHARP_7_OR_LATER where T : Enum #endif { public struct EnumData { public T inputKey; public ulong actionHandle; public ulong devicePathHandle; public IVRModuleDeviceStateRW currState; } public struct Enumerable { private IEnumerator enumerator; public Enumerable(IEnumerator enumerator) { this.enumerator = enumerator; } public IEnumerator GetEnumerator() { return enumerator; } } private readonly string pathPrefix; private readonly string dataTypeName; private readonly EnumArray actionPaths = new EnumArray(); private readonly EnumArray actionAlias = new EnumArray(); private readonly EnumArray actionHandles = new EnumArray(); public string PathPrefix { get { return pathPrefix; } } public string DataTypeName { get { return dataTypeName; } } public EnumArray.IReadOnly ActionPaths { get { return actionPaths.ReadOnly; } } public EnumArray.IReadOnly ActionAlias { get { return actionAlias.ReadOnly; } } public EnumArray.IReadOnly ActionHandles { get { return actionHandles.ReadOnly; } } public ActionCollection(string pathPrefix, string dataTypeName) { this.pathPrefix = pathPrefix; this.dataTypeName = dataTypeName; } public void Set(T key, string pathName, string alias) { actionPaths[key] = ACTION_SET_PATH + pathPrefix + pathName; actionAlias[key] = alias; } public void ResolveHandles(CVRInput vrInput) { foreach (var key in EnumArrayBase.StaticEnums) { actionHandles[key] = SafeGetActionHandle(vrInput, actionPaths[key]); } } } #endif } }