787 lines
36 KiB
C#
787 lines
36 KiB
C#
//========= 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<WVR_Result> starter;
|
|
private Func<WVR_Result> initializer;
|
|
private Action stopper;
|
|
|
|
public FeatureActivator(string featureName, Func<WVR_Result> starter, Func<WVR_Result> 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<WVR_HandJoint, HandJointName> handJointMapping;
|
|
static TrackingActivator()
|
|
{
|
|
handJointMapping = new EnumArray<WVR_HandJoint, HandJointName>();
|
|
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<WVR_Result> GenerateStarter()
|
|
{
|
|
return () =>
|
|
{
|
|
return Interop.WVR_StartHandTracking(preferredTrackerType);
|
|
};
|
|
}
|
|
|
|
private static Action GenerateStopper()
|
|
{
|
|
return () =>
|
|
{
|
|
Interop.WVR_StopHandTracking(preferredTrackerType);
|
|
};
|
|
}
|
|
|
|
private static Func<WVR_Result> 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<WVR_Result> 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<WVR_Result> 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
|
|
}
|
|
} |