//========= Copyright 2016-2023, HTC Corporation. All rights reserved. =========== using UnityEngine; using System; using System.Collections; using HTC.UnityPlugin.LiteCoroutineSystem; using HTC.UnityPlugin.Utility; using System.Runtime.InteropServices; #if VIU_WAVEVR_HAND_TRACKING using Wave.Native; #endif namespace HTC.UnityPlugin.VRModuleManagement { public class WaveHandTrackingSubmodule : VRModule.SubmoduleBase { #if VIU_WAVEVR_HAND_TRACKING private static readonly string log_prefix = "[" + typeof(WaveHandTrackingSubmodule).Name + "] "; private struct DeviceFeature { private ulong featuresField; public bool supportTracking { get { return (featuresField & (ulong)WVR_SupportedFeature.WVR_SupportedFeature_HandTracking) > 0ul; } } public bool supportGesture { get { return (featuresField & (ulong)WVR_SupportedFeature.WVR_SupportedFeature_HandGesture) > 0ul; } } public void Fetch() { featuresField = Interop.WVR_GetSupportedFeatures(); } } private DeviceFeature deviceFeature; private static TrackingActivator trackingActivator = TrackingActivator.Default; private GestureActivator gestureActivator = GestureActivator.Default; private uint leftDeviceIndex = VRModule.INVALID_DEVICE_INDEX; private uint rightDeviceIndex = VRModule.INVALID_DEVICE_INDEX; private static WVR_HandTrackerType preferredTrackerType = WVR_HandTrackerType.WVR_HandTrackerType_Natural; private static WVR_HandModelType showElectronicHandWithController = VRModuleSettings.showWaveElectronicHandWithController ? WVR_HandModelType.WVR_HandModelType_WithController : WVR_HandModelType.WVR_HandModelType_WithoutController; public override bool ShouldActiveModule() { return VRModuleSettings.activateWaveHandTrackingSubmodule; } protected override void OnActivated() { deviceFeature.Fetch(); } protected override void OnDeactivated() { trackingActivator.SetActive(false); gestureActivator.SetActive(false); } protected override void OnUpdateDeviceConnectionAndPoses() { trackingActivator.SetActive(deviceFeature.supportTracking); gestureActivator.SetActive(VRModuleSettings.enableWaveHandGesture && deviceFeature.supportGesture); if (VRModule.trackingSpaceType == VRModuleTrackingSpaceType.RoomScale) { trackingActivator.TryFetchData(WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnGround); } else { trackingActivator.TryFetchData(WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnHead); } gestureActivator.TryFetchData(); var isFocused = Interop.WVR_IsInputFocusCapturedBySystem(); var isLeftValid = !isFocused && (trackingActivator.isLeftValid || gestureActivator.isLeftValid); var isRightValid = !isFocused && (trackingActivator.isRightValid || gestureActivator.isRightValid); IVRModuleDeviceState prevState; IVRModuleDeviceStateRW currState; // update connection/pose for left hand devices if (isLeftValid) { if (leftDeviceIndex != VRModule.INVALID_DEVICE_INDEX) { EnsureValidDeviceState(leftDeviceIndex, out prevState, out currState); } else { leftDeviceIndex = FindAndEnsureUnusedNotHMDDeviceState(out prevState, out currState); currState.serialNumber = "WaveTrackedHandLeft"; currState.modelNumber = "WaveTrackedHandLeft"; currState.renderModelName = "WaveTrackedHandLeft"; currState.deviceClass = VRModuleDeviceClass.TrackedHand; currState.deviceModel = VRModuleDeviceModel.WaveTrackedHandLeft; currState.input2DType = VRModuleInput2DType.None; } currState.isConnected = true; trackingActivator.UpdateJoints(currState, true); trackingActivator.UpdateDeviceInput(currState, true); gestureActivator.UpdateDeviceInput(currState, true); } else { if (leftDeviceIndex != VRModule.INVALID_DEVICE_INDEX) { EnsureValidDeviceState(leftDeviceIndex, out prevState, out currState); currState.Reset(); leftDeviceIndex = VRModule.INVALID_DEVICE_INDEX; } } if (isRightValid) { if (rightDeviceIndex != VRModule.INVALID_DEVICE_INDEX) { EnsureValidDeviceState(rightDeviceIndex, out prevState, out currState); } else { rightDeviceIndex = FindAndEnsureUnusedNotHMDDeviceState(out prevState, out currState); currState.serialNumber = "WaveTrackedHandRight"; currState.modelNumber = "WaveTrackedHandRight"; currState.renderModelName = "WaveTrackedHandRight"; currState.deviceClass = VRModuleDeviceClass.TrackedHand; currState.deviceModel = VRModuleDeviceModel.WaveTrackedHandRight; currState.input2DType = VRModuleInput2DType.None; } currState.isConnected = true; trackingActivator.UpdateJoints(currState, false); trackingActivator.UpdateDeviceInput(currState, false); gestureActivator.UpdateDeviceInput(currState, false); } else { if (rightDeviceIndex != VRModule.INVALID_DEVICE_INDEX) { EnsureValidDeviceState(rightDeviceIndex, out prevState, out currState); currState.Reset(); rightDeviceIndex = VRModule.INVALID_DEVICE_INDEX; } } } public override uint GetLeftHandedIndex() { return leftDeviceIndex; } public override uint GetRightHandedIndex() { return rightDeviceIndex; } public static bool TryGetLeftPinchRay(out Vector3 origin, out Vector3 direction) { if (!trackingActivator.isLeftValid) { origin = Vector3.zero; direction = Vector3.zero; return false; } var pinch = trackingActivator.getLeftPinchData; Coordinate.GetVectorFromGL(pinch.pinch.origin, out origin); Coordinate.GetVectorFromGL(pinch.pinch.direction, out direction); return pinch.state.type != WVR_HandPoseType.WVR_HandPoseType_Invalid; } public static bool TryGetRightPinchRay(out Vector3 origin, out Vector3 direction) { if (!trackingActivator.isRightValid) { origin = Vector3.zero; direction = Vector3.zero; return false; } var pinch = trackingActivator.getRightPinchData; Coordinate.GetVectorFromGL(pinch.pinch.origin, out origin); Coordinate.GetVectorFromGL(pinch.pinch.direction, out direction); return pinch.state.type != WVR_HandPoseType.WVR_HandPoseType_Invalid; } private enum FeatureActivity { Stopped, StartFailed, Starting, Started, } private class FeatureActivator { private string featureName; private FeatureActivity activity; private bool shouldActive; private LiteCoroutine coroutineHandle; private Func starter; private Func initializer; private Action stopper; public FeatureActivator(string featureName, Func starter, Func initializer, Action stopper) { this.featureName = featureName; this.activity = FeatureActivity.Stopped; this.shouldActive = false; this.coroutineHandle = null; this.starter = starter; this.initializer = initializer; this.stopper = stopper; } public bool isActive { get { return activity == FeatureActivity.Started; } } public void SetActive(bool value) { if (value) { Activate(); } else { Deactivate(); } } public void Activate() { shouldActive = true; if (activity == FeatureActivity.Stopped) { LiteCoroutine.StartCoroutine(ref coroutineHandle, new LiteTask(ActivateCoroutine(), false)); } } public void Deactivate() { shouldActive = false; if (activity == FeatureActivity.Started || activity == FeatureActivity.StartFailed) { activity = FeatureActivity.Stopped; stopper(); Debug.Log(log_prefix + "Stop " + featureName + " done."); } } private IEnumerator ActivateCoroutine() { yield return LiteTask.ToForeground; const long retryInterval = 1000L; var nextRestartTime = default(DateTime); var result = default(WVR_Result); while (true) { if (shouldActive) { switch (activity) { case FeatureActivity.Stopped: activity = FeatureActivity.Starting; break; case FeatureActivity.Starting: break; case FeatureActivity.Started: case FeatureActivity.StartFailed: default: yield break; } } else { switch (activity) { case FeatureActivity.Started: case FeatureActivity.StartFailed: stopper(); Debug.Log(log_prefix + "Stop " + featureName + " done."); activity = FeatureActivity.Stopped; yield break; case FeatureActivity.Starting: case FeatureActivity.Stopped: default: yield break; } } if (DateTime.UtcNow < nextRestartTime) { yield return null; continue; } yield return LiteTask.ToBackground; result = starter(); if (result == WVR_Result.WVR_Success) { result = initializer(); } yield return LiteTask.ToForeground; switch (result) { case WVR_Result.WVR_Error_SystemInvalid: nextRestartTime = DateTime.UtcNow + new TimeSpan(retryInterval * TimeSpan.TicksPerMillisecond); Debug.LogWarning(log_prefix + "Start " + featureName + " fail (system not ready). Retrying in " + retryInterval + " milliseconds..."); yield return null; break; case WVR_Result.WVR_Success: Debug.Log(log_prefix + "Start " + featureName + " success."); activity = FeatureActivity.Started; break; default: Debug.LogError(log_prefix + "Start " + featureName + " error:" + result); activity = FeatureActivity.StartFailed; break; } } } } private class TrackingActivator { private FeatureActivator activator; private static WVR_HandTrackerInfo_t trackerInfo = new WVR_HandTrackerInfo_t(); private static WVR_HandTrackingData_t trackingData; private static WVR_HandPoseData_t pinchData; private static WVR_HandJoint[] s_NaturalHandJoints; private static ulong[] s_NaturalHandJointsFlag; private static WVR_HandJointData_t m_NaturalHandJointDataLeft = new WVR_HandJointData_t(); private static WVR_Pose_t[] s_NaturalHandJointsPoseLeft; private static WVR_HandJointData_t m_NaturalHandJointDataRight = new WVR_HandJointData_t(); private static WVR_Pose_t[] s_NaturalHandJointsPoseRight; private static int[] intJointMappingArray; private static byte[] jointValidFlagArrayBytes; private static uint jointCount; private static EnumArray handJointMapping; static TrackingActivator() { handJointMapping = new EnumArray(); handJointMapping[WVR_HandJoint.WVR_HandJoint_Palm] = HandJointName.Palm; handJointMapping[WVR_HandJoint.WVR_HandJoint_Wrist] = HandJointName.Wrist; handJointMapping[WVR_HandJoint.WVR_HandJoint_Thumb_Joint0] = HandJointName.ThumbMetacarpal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Thumb_Joint1] = HandJointName.ThumbProximal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Thumb_Joint2] = HandJointName.ThumbDistal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Thumb_Tip] = HandJointName.ThumbTip; handJointMapping[WVR_HandJoint.WVR_HandJoint_Index_Joint0] = HandJointName.IndexMetacarpal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Index_Joint1] = HandJointName.IndexProximal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Index_Joint2] = HandJointName.IndexIntermediate; handJointMapping[WVR_HandJoint.WVR_HandJoint_Index_Joint3] = HandJointName.IndexDistal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Index_Tip] = HandJointName.IndexTip; handJointMapping[WVR_HandJoint.WVR_HandJoint_Middle_Joint0] = HandJointName.MiddleMetacarpal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Middle_Joint1] = HandJointName.MiddleProximal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Middle_Joint2] = HandJointName.MiddleIntermediate; handJointMapping[WVR_HandJoint.WVR_HandJoint_Middle_Joint3] = HandJointName.MiddleDistal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Middle_Tip] = HandJointName.MiddleTip; handJointMapping[WVR_HandJoint.WVR_HandJoint_Ring_Joint0] = HandJointName.RingMetacarpal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Ring_Joint1] = HandJointName.RingProximal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Ring_Joint2] = HandJointName.RingIntermediate; handJointMapping[WVR_HandJoint.WVR_HandJoint_Ring_Joint3] = HandJointName.RingDistal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Ring_Tip] = HandJointName.RingTip; handJointMapping[WVR_HandJoint.WVR_HandJoint_Pinky_Joint0] = HandJointName.PinkyMetacarpal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Pinky_Joint1] = HandJointName.PinkyProximal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Pinky_Joint2] = HandJointName.PinkyIntermediate; handJointMapping[WVR_HandJoint.WVR_HandJoint_Pinky_Joint3] = HandJointName.PinkyDistal; handJointMapping[WVR_HandJoint.WVR_HandJoint_Pinky_Tip] = HandJointName.PinkyTip; } public static TrackingActivator Default { get { return new TrackingActivator() { activator = new FeatureActivator("HandTracking", GenerateStarter(), GenerateInitializer(), GenerateStopper()), }; } } private static Func GenerateStarter() { return () => { return Interop.WVR_StartHandTracking(preferredTrackerType); }; } private static Action GenerateStopper() { return () => { Interop.WVR_StopHandTracking(preferredTrackerType); }; } private static Func GenerateInitializer() { return () => { var result = Interop.WVR_GetHandJointCount(preferredTrackerType, ref jointCount); if (result != WVR_Result.WVR_Success) return result; InitializeHandTrackerInfo(ref trackerInfo, ref s_NaturalHandJoints, ref s_NaturalHandJointsFlag, jointCount); InitializeHandTrackerData( ref trackingData, ref m_NaturalHandJointDataLeft, ref m_NaturalHandJointDataRight, ref s_NaturalHandJointsPoseLeft, ref s_NaturalHandJointsPoseRight, jointCount); var trackerInfoResult = Interop.WVR_GetHandTrackerInfo(preferredTrackerType, ref trackerInfo); var hasTrackerInfo = ExtractHandTrackerInfo(trackerInfo, ref s_NaturalHandJoints, ref s_NaturalHandJointsFlag); //if (hasTrackerInfo) //{ // for (int i = 0; i < trackerInfo.jointCount; i++) // { // Debug.Log("GetHandTrackerInfo() " // + "joint count: " + trackerInfo.jointCount // + ", s_NaturalHandJoints[" + i + "] = " + s_NaturalHandJoints[i] // + ", s_NaturalHandJointsFlag[" + i + "] = " + s_NaturalHandJointsFlag[i]); // } //} return default(WVR_Result); }; } public void SetActive(bool value) { activator.SetActive(value); } public bool TryFetchData(WVR_PoseOriginModel originModel) { if (activator.isActive && jointCount > 0) { var result = Interop.WVR_GetHandTrackingData(preferredTrackerType, (preferredTrackerType == WVR_HandTrackerType.WVR_HandTrackerType_Natural) ? WVR_HandModelType.WVR_HandModelType_WithoutController : showElectronicHandWithController, originModel, ref trackingData, ref pinchData); if (result == WVR_Result.WVR_Success) { ExtractHandTrackerData(trackingData, ref s_NaturalHandJointsPoseLeft, ref s_NaturalHandJointsPoseRight); return true; } trackingData.left.isValidPose = false; trackingData.right.isValidPose = false; pinchData.left.state.type = WVR_HandPoseType.WVR_HandPoseType_Invalid; pinchData.right.state.type = WVR_HandPoseType.WVR_HandPoseType_Invalid; Debug.LogError(log_prefix + "WVR_GetHandTrackingData fail. error:" + result); } return false; } public bool isLeftValid { get { return trackingData.left.isValidPose; } } public bool isRightValid { get { return trackingData.right.isValidPose; } } public WVR_HandPoseState_t getLeftPinchData { get { return pinchData.left; } } public WVR_HandPoseState_t getRightPinchData { get { return pinchData.right; } } public void UpdateJoints(IVRModuleDeviceStateRW state, bool isLeft) { var data = isLeft ? trackingData.left : trackingData.right; var pose = isLeft ? s_NaturalHandJointsPoseLeft : s_NaturalHandJointsPoseRight; for (int i = 0; i < trackerInfo.jointCount; i++) { var p = default(RigidPose); Coordinate.GetVectorFromGL(pose[i].position, out p.pos); Coordinate.GetQuaternionFromGL(pose[i].rotation, out p.rot); state.handJoints[handJointMapping[s_NaturalHandJoints[i]]] = new JointPose(p); } state.isPoseValid = data.isValidPose && !Interop.WVR_IsInputFocusCapturedBySystem(); state.pose = state.handJoints[HandJointName.Wrist].pose; } public void UpdateDeviceInput(IVRModuleDeviceStateRW state, bool isLeft) { var pinch = isLeft ? pinchData.left : pinchData.right; var pinched = pinch.pinch.strength >= 0.5f; state.SetButtonPress(VRModuleRawButton.GestureIndexPinch, pinched); state.SetButtonTouch(VRModuleRawButton.GestureIndexPinch, pinched); state.SetAxisValue(VRModuleRawAxis.Trigger, pinch.pinch.strength); var indexCurl = GetFingerCurl(state, HandJointName.IndexTip); var middleCurl = GetFingerCurl(state, HandJointName.MiddleTip); var ringCurl = GetFingerCurl(state, HandJointName.RingTip); var pinkyCurl = GetFingerCurl(state, HandJointName.PinkyTip); var curlAvg = (indexCurl + middleCurl + ringCurl + pinkyCurl) * 0.25f; state.SetAxisValue(VRModuleRawAxis.IndexCurl, indexCurl); state.SetAxisValue(VRModuleRawAxis.MiddleCurl, middleCurl); state.SetAxisValue(VRModuleRawAxis.RingCurl, ringCurl); state.SetAxisValue(VRModuleRawAxis.PinkyCurl, pinkyCurl); state.SetAxisValue(VRModuleRawAxis.CapSenseGrip, curlAvg); state.SetButtonPress(VRModuleRawButton.Grip, curlAvg > 0.75f); state.SetButtonTouch(VRModuleRawButton.Grip, curlAvg > 0.50f); } private float GetFingerCurl(IVRModuleDeviceStateRW state, HandJointName finger) { var palmDir = state.pose.forward; var fingerDir = state.handJoints[finger].pose.forward; var angle = Vector3.SignedAngle(palmDir, fingerDir, state.pose.right); if (angle < -90f) { angle += 360f; } return Mathf.InverseLerp(0f, 180f, angle); } private static void InitializeHandTrackerInfo(ref WVR_HandTrackerInfo_t handTrackerInfo, ref WVR_HandJoint[] jointMappingArray, ref ulong[] jointValidFlagArray, uint count) { handTrackerInfo.jointCount = jointCount; handTrackerInfo.handModelTypeBitMask = 0; /// WVR_HandTrackerInfo_t.jointMappingArray jointMappingArray = new WVR_HandJoint[jointCount]; intJointMappingArray = new int[jointMappingArray.Length]; intJointMappingArray = Array.ConvertAll(jointMappingArray, delegate (WVR_HandJoint value) { return (int)value; }); handTrackerInfo.jointMappingArray = Marshal.AllocHGlobal(sizeof(int) * intJointMappingArray.Length); Marshal.Copy(intJointMappingArray, 0, handTrackerInfo.jointMappingArray, intJointMappingArray.Length); /*unsafe { fixed (WVR_HandJoint* pJointMappingArray = jointMappingArray) { handTrackerInfo.jointMappingArray = pJointMappingArray; } }*/ /// WVR_HandTrackerInfo_t.jointValidFlagArray jointValidFlagArray = new ulong[jointCount]; int jointValidFlagArrayByteLength = Buffer.ByteLength(jointValidFlagArray); jointValidFlagArrayBytes = new byte[jointValidFlagArrayByteLength]; Buffer.BlockCopy(jointValidFlagArray, 0, jointValidFlagArrayBytes, 0, jointValidFlagArrayBytes.Length); handTrackerInfo.jointValidFlagArray = Marshal.AllocHGlobal(sizeof(byte) * jointValidFlagArrayBytes.Length); Marshal.Copy(jointValidFlagArrayBytes, 0, handTrackerInfo.jointValidFlagArray, jointValidFlagArrayBytes.Length); /*unsafe { fixed (ulong* pHandJointsFlag = jointValidFlagArray) { handTrackerInfo.jointValidFlagArray = pHandJointsFlag; } }*/ } private static void InitializeHandTrackerData( ref WVR_HandTrackingData_t handTrackerData, ref WVR_HandJointData_t handJointDataLeft, ref WVR_HandJointData_t handJointDataRight, ref WVR_Pose_t[] handJointsPoseLeft, ref WVR_Pose_t[] handJointsPoseRight, uint count) { handTrackerData.timestamp = 0; InitializeHandJointData(ref handJointDataLeft, ref handJointsPoseLeft, count); handTrackerData.left = handJointDataLeft; InitializeHandJointData(ref handJointDataRight, ref handJointsPoseRight, count); handTrackerData.right = handJointDataRight; } private static void InitializeHandJointData(ref WVR_HandJointData_t handJointData, ref WVR_Pose_t[] jointsPose, uint count) { handJointData.isValidPose = false; handJointData.confidence = 0; handJointData.jointCount = count; WVR_Pose_t wvr_pose_type = default(WVR_Pose_t); handJointData.joints = Marshal.AllocHGlobal(Marshal.SizeOf(wvr_pose_type) * (int)count); jointsPose = new WVR_Pose_t[count]; long offset = 0; if (IntPtr.Size == 4) offset = handJointData.joints.ToInt32(); else offset = handJointData.joints.ToInt64(); for (int i = 0; i < jointsPose.Length; i++) { IntPtr wvr_pose_ptr = new IntPtr(offset); Marshal.StructureToPtr(jointsPose[i], wvr_pose_ptr, false); offset += Marshal.SizeOf(wvr_pose_type); } } private static bool ExtractHandTrackerInfo(WVR_HandTrackerInfo_t handTrackerInfo, ref WVR_HandJoint[] jointMappingArray, ref ulong[] jointValidFlagArray) { if (handTrackerInfo.jointCount == 0) { Debug.Log("ExtractHandTrackerInfo() WVR_GetHandTrackerInfo WVR_HandTrackerInfo_t jointCount SHOULD NOT be 0!!"); return false; } // WVR_HandTrackerInfo_t.jointMappingArray if (jointMappingArray.Length != handTrackerInfo.jointCount) { Debug.Log("ExtractHandTrackerInfo() The WVR_GetHandJointCount count (jointMappingArray) " + jointMappingArray.Length + " differs from WVR_GetHandTrackerInfo WVR_HandTrackerInfo_t jointCount " + handTrackerInfo.jointCount); jointMappingArray = new WVR_HandJoint[handTrackerInfo.jointCount]; intJointMappingArray = new int[jointMappingArray.Length]; } Marshal.Copy(handTrackerInfo.jointMappingArray, intJointMappingArray, 0, intJointMappingArray.Length); jointMappingArray = Array.ConvertAll(intJointMappingArray, delegate (int value) { return (WVR_HandJoint)value; }); /*unsafe { for (int i = 0; i < jointMappingArray.Length; i++) { jointMappingArray[i] = *(handTrackerInfo.jointMappingArray + i); } }*/ // WVR_HandTrackerInfo_t.jointValidFlagArray if (jointValidFlagArray.Length != handTrackerInfo.jointCount) { Debug.Log("ExtractHandTrackerInfo() The WVR_GetHandJointCount count (jointValidFlagArray) " + jointValidFlagArray.Length + " differs from WVR_GetHandTrackerInfo WVR_HandTrackerInfo_t jointCount " + handTrackerInfo.jointCount); jointValidFlagArray = new ulong[handTrackerInfo.jointCount]; int jointValidFlagArrayByteLength = Buffer.ByteLength(jointValidFlagArray); jointValidFlagArrayBytes = new byte[jointValidFlagArrayByteLength]; } Marshal.Copy(handTrackerInfo.jointValidFlagArray, jointValidFlagArrayBytes, 0, jointValidFlagArrayBytes.Length); for (int byteIndex = 0; byteIndex < jointValidFlagArrayBytes.Length; byteIndex = byteIndex + 8) { int i = (byteIndex / 8); jointValidFlagArray[i] = BitConverter.ToUInt64(jointValidFlagArrayBytes, byteIndex); } /*unsafe { for (int i = 0; i < jointValidFlagArray.Length; i++) { jointValidFlagArray[i] = *(handTrackerInfo.jointValidFlagArray + i); } }*/ return true; } private static bool ExtractHandTrackerData(WVR_HandTrackingData_t handTrackerData, ref WVR_Pose_t[] handJointsPoseLeft, ref WVR_Pose_t[] handJointsPoseRight) { if (!ExtractHandJointData(handTrackerData.left, ref handJointsPoseLeft)) return false; if (!ExtractHandJointData(handTrackerData.right, ref handJointsPoseRight)) return false; return true; } private static bool ExtractHandJointData(WVR_HandJointData_t handJointData, ref WVR_Pose_t[] jointsPose) { if (handJointData.jointCount == 0) { Debug.Log("ExtractHandJointData() WVR_GetHandTrackingData WVR_HandJointData_t jointCount SHOULD NOT be 0!!"); return false; } if (jointsPose.Length != handJointData.jointCount) { Debug.Log("ExtractHandJointData() The WVR_GetHandJointCount count " + jointsPose.Length + " differs from WVR_GetHandTrackingData WVR_HandJointData_t jointCount " + handJointData.jointCount); jointsPose = new WVR_Pose_t[handJointData.jointCount]; } WVR_Pose_t wvr_pose_type = default(WVR_Pose_t); int offset = 0; for (int i = 0; i < jointsPose.Length; i++) { if (IntPtr.Size == 4) jointsPose[i] = (WVR_Pose_t)Marshal.PtrToStructure(new IntPtr(handJointData.joints.ToInt32() + offset), typeof(WVR_Pose_t)); else jointsPose[i] = (WVR_Pose_t)Marshal.PtrToStructure(new IntPtr(handJointData.joints.ToInt64() + offset), typeof(WVR_Pose_t)); offset += Marshal.SizeOf(wvr_pose_type); } return true; } } private class GestureActivator { private FeatureActivator activator; public WVR_HandGestureData_t gestureData; private static ulong m_GestureValue = 0; public static GestureActivator Default { get { return new GestureActivator() { activator = new FeatureActivator("HandGesture", GenerateStarter(), GenerateInitializer(), Interop.WVR_StopHandGesture), }; } } private const WVR_HandGestureType WVR_HandGestureType_WVR_HandGestureType_Palm_Pinch = #if VIU_WAVE_XRSDK_4_2_90_OR_NEWER WVR_HandGestureType.WVR_HandGestureType_Palm_Pinch; #else WVR_HandGestureType.WVR_HandGestureType_Inverse; #endif private static Func GenerateStarter() { return () => { m_GestureValue |= 1 << (int)WVR_HandGestureType.WVR_HandGestureType_Fist; m_GestureValue |= 1 << (int)WVR_HandGestureType.WVR_HandGestureType_Five; m_GestureValue |= 1 << (int)WVR_HandGestureType.WVR_HandGestureType_OK; m_GestureValue |= 1 << (int)WVR_HandGestureType.WVR_HandGestureType_ThumbUp; m_GestureValue |= 1 << (int)WVR_HandGestureType.WVR_HandGestureType_IndexUp; m_GestureValue |= 1 << (int)WVR_HandGestureType_WVR_HandGestureType_Palm_Pinch; return Interop.WVR_StartHandGesture(m_GestureValue); }; } private static Func GenerateInitializer() { return () => { return default(WVR_Result); // TODO }; } public void SetActive(bool value) { activator.SetActive(value); } public bool TryFetchData() { if (activator.isActive) { var result = Interop.WVR_GetHandGestureData(ref gestureData); if (result == WVR_Result.WVR_Success) { return true; } gestureData.left = WVR_HandGestureType.WVR_HandGestureType_Invalid; gestureData.right = WVR_HandGestureType.WVR_HandGestureType_Invalid; Debug.LogError(log_prefix + "WVR_GetHandGestureData fail. error:" + result); } return false; } public void UpdateDeviceInput(IVRModuleDeviceStateRW state, bool isLeft) { var gesture = isLeft ? gestureData.left : gestureData.right; state.SetButtonPress(VRModuleRawButton.GestureFist, gesture == WVR_HandGestureType.WVR_HandGestureType_Fist); state.SetButtonTouch(VRModuleRawButton.GestureFist, gesture == WVR_HandGestureType.WVR_HandGestureType_Fist); state.SetButtonPress(VRModuleRawButton.GestureFive, gesture == WVR_HandGestureType.WVR_HandGestureType_Five); state.SetButtonTouch(VRModuleRawButton.GestureFive, gesture == WVR_HandGestureType.WVR_HandGestureType_Five); state.SetButtonPress(VRModuleRawButton.GestureIndexUp, gesture == WVR_HandGestureType.WVR_HandGestureType_IndexUp); state.SetButtonTouch(VRModuleRawButton.GestureIndexUp, gesture == WVR_HandGestureType.WVR_HandGestureType_IndexUp); state.SetButtonPress(VRModuleRawButton.GestureOk, gesture == WVR_HandGestureType.WVR_HandGestureType_OK); state.SetButtonTouch(VRModuleRawButton.GestureOk, gesture == WVR_HandGestureType.WVR_HandGestureType_OK); state.SetButtonPress(VRModuleRawButton.GestureThumbUp, gesture == WVR_HandGestureType.WVR_HandGestureType_ThumbUp); state.SetButtonTouch(VRModuleRawButton.GestureThumbUp, gesture == WVR_HandGestureType.WVR_HandGestureType_ThumbUp); state.SetButtonPress(VRModuleRawButton.System, gesture == WVR_HandGestureType_WVR_HandGestureType_Palm_Pinch); state.SetButtonTouch(VRModuleRawButton.System, gesture == WVR_HandGestureType_WVR_HandGestureType_Palm_Pinch); } public bool isLeftValid { get { return gestureData.left != WVR_HandGestureType.WVR_HandGestureType_Invalid; } } public bool isRightValid { get { return gestureData.right != WVR_HandGestureType.WVR_HandGestureType_Invalid; } } } #endif } }