You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
15 KiB
274 lines
15 KiB
using HTC.UnityPlugin.Utility; |
|
using HTC.UnityPlugin.Vive; |
|
using UnityEngine; |
|
#if VIU_WAVEVR && UNITY_ANDROID |
|
using wvr; |
|
#endif |
|
|
|
namespace HTC.UnityPlugin.VRModuleManagement |
|
{ |
|
public sealed class WaveVRModule : VRModule.ModuleBase |
|
{ |
|
#if VIU_WAVEVR && UNITY_ANDROID |
|
private const uint HMD_INDEX = 0; |
|
private const uint RIGHT_INDEX = 1; |
|
private const uint LEFT_INDEX = 2; |
|
private const uint DEVICE_COUNT = 3; |
|
private const uint INPUT_TYPE = (uint)(WVR_InputType.WVR_InputType_Button | WVR_InputType.WVR_InputType_Touch | WVR_InputType.WVR_InputType_Analog); |
|
|
|
public static readonly Vector3 DEFAULT_NECK_POSITION = new Vector3(0.0f, -0.15f, 0.0f); |
|
public static readonly Vector3 DEFAULT_ELBOW_REST_POSITION = new Vector3(0.195f, -0.5f, 0.005f); |
|
public static readonly Vector3 DEFAULT_WRIST_REST_POSITION = new Vector3(0.0f, 0.0f, 0.25f); |
|
public static readonly Vector3 DEFAULT_CONTROLLER_REST_POSITION = new Vector3(0.0f, 0.0f, 0.05f); |
|
public static readonly Vector3 DEFAULT_ARM_EXTENSION_OFFSET = new Vector3(-0.13f, 0.14f, 0.08f); |
|
public static readonly Vector3 RIGHT_ARM_MULTIPLIER = new Vector3(1f, 1f, 1f); |
|
public static readonly Vector3 LEFT_ARM_MULTIPLIER = new Vector3(1f, 1f, 1f); |
|
public const float DEFAULT_ELBOW_BEND_RATIO = 0.6f; |
|
public const float MIN_EXTENSION_ANGLE = 7.0f; |
|
public const float MAX_EXTENSION_ANGLE = 60.0f; |
|
public const float EXTENSION_WEIGHT = 0.4f; |
|
|
|
private Vector3 handedMultiplier; |
|
|
|
private WVR_DeviceType[] m_deviceTypes = new WVR_DeviceType[] |
|
{ |
|
WVR_DeviceType.WVR_DeviceType_HMD, |
|
WVR_DeviceType.WVR_DeviceType_Controller_Right, |
|
WVR_DeviceType.WVR_DeviceType_Controller_Left, |
|
}; |
|
private WVR_DevicePosePair_t[] m_poses = new WVR_DevicePosePair_t[DEVICE_COUNT]; // HMD, R, L controllers. |
|
private WVR_AnalogState_t[] m_analogStates = new WVR_AnalogState_t[2]; |
|
|
|
public override bool ShouldActiveModule() |
|
{ |
|
return !Application.isEditor && VIUSettings.activateWaveVRModule; |
|
} |
|
|
|
public override void OnActivated() |
|
{ |
|
var instance = Object.FindObjectOfType<WaveVR_Init>(); |
|
if (instance == null) |
|
{ |
|
VRModule.Instance.gameObject.AddComponent<WaveVR_Init>(); |
|
} |
|
} |
|
|
|
public override void OnDeactivated() { } |
|
|
|
public override uint GetRightControllerDeviceIndex() { return RIGHT_INDEX; } |
|
|
|
public override uint GetLeftControllerDeviceIndex() { return LEFT_INDEX; } |
|
|
|
public override void UpdateDeviceState(IVRModuleDeviceState[] prevState, IVRModuleDeviceStateRW[] currState) |
|
{ |
|
if (WaveVR.Instance == null) { return; } |
|
|
|
WVR_PoseOriginModel poseOrigin; |
|
switch (VRModule.trackingSpaceType) |
|
{ |
|
case VRModuleTrackingSpaceType.RoomScale: |
|
{ poseOrigin = WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnGround; break; } |
|
case VRModuleTrackingSpaceType.Stationary: |
|
default: |
|
{ poseOrigin = WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnHead; break; } |
|
} |
|
|
|
IVRModuleDeviceStateRW headState = null; |
|
IVRModuleDeviceStateRW rightState = null; |
|
IVRModuleDeviceStateRW leftState = null; |
|
|
|
Interop.WVR_GetSyncPose(poseOrigin, m_poses, DEVICE_COUNT); |
|
|
|
for (int i = 0; i < DEVICE_COUNT; ++i) |
|
{ |
|
var deviceType = m_poses[i].type; |
|
var devicePose = m_poses[i].pose; |
|
|
|
if (deviceType == m_deviceTypes[i]) |
|
{ |
|
currState[i].isConnected = true; |
|
if (deviceType == WVR_DeviceType.WVR_DeviceType_HMD) |
|
{ |
|
currState[i].deviceClass = VRModuleDeviceClass.HMD; |
|
currState[i].deviceModel = VRModuleDeviceModel.ViveFocusHMD; |
|
} |
|
else |
|
{ |
|
currState[i].deviceClass = VRModuleDeviceClass.Controller; |
|
currState[i].deviceModel = VRModuleDeviceModel.ViveFocusFinch; |
|
} |
|
|
|
var rigidTransform = new WaveVR_Utils.RigidTransform(devicePose.PoseMatrix); |
|
|
|
currState[i].isPoseValid = devicePose.IsValidPose; |
|
currState[i].isOutOfRange = false; |
|
currState[i].isCalibrating = false; |
|
currState[i].isUninitialized = false; |
|
|
|
currState[i].velocity = new Vector3(devicePose.Velocity.v0, devicePose.Velocity.v1, -devicePose.Velocity.v2); |
|
currState[i].angularVelocity = new Vector3(-devicePose.AngularVelocity.v0, -devicePose.AngularVelocity.v1, devicePose.AngularVelocity.v2); |
|
|
|
currState[i].position = rigidTransform.pos; |
|
currState[i].rotation = rigidTransform.rot; |
|
|
|
switch (deviceType) |
|
{ |
|
case WVR_DeviceType.WVR_DeviceType_HMD: |
|
headState = currState[i]; |
|
break; |
|
case WVR_DeviceType.WVR_DeviceType_Controller_Right: |
|
rightState = currState[i]; |
|
break; |
|
case WVR_DeviceType.WVR_DeviceType_Controller_Left: |
|
leftState = currState[i]; |
|
break; |
|
} |
|
|
|
uint buttons = 0; |
|
uint touches = 0; |
|
|
|
// FIXME: What does WVR_GetInputTypeCount means? |
|
//var analogCount = Interop.WVR_GetInputTypeCount(deviceType, WVR_InputType.WVR_InputType_Analog); |
|
//if (m_analogStates == null || m_analogStates.Length < analogCount) { m_analogStates = new WVR_AnalogState_t[analogCount]; } |
|
|
|
#if VIU_WAVEVR_2_0_32_OR_NEWER |
|
if (Interop.WVR_GetInputDeviceState(deviceType, INPUT_TYPE, ref buttons, ref touches, m_analogStates, (uint)m_analogStates.Length)) |
|
#else |
|
if (Interop.WVR_GetInputDeviceState(deviceType, INPUT_TYPE, ref buttons, ref touches, m_analogStates, m_analogStates.Length)) |
|
#endif |
|
{ |
|
const uint dpadMask = |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_Touchpad)) | |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_DPad_Left)) | |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_DPad_Up)) | |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_DPad_Right)) | |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_DPad_Down)); |
|
|
|
const uint triggerBumperMask = |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_Trigger)) | |
|
(1 << (int)(WVR_InputId.WVR_InputId_Alias1_Bumper)); |
|
|
|
currState[i].SetButtonPress(VRModuleRawButton.System, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_System)) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.ApplicationMenu, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_Menu)) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.Touchpad, (buttons & dpadMask) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.Trigger, (buttons & triggerBumperMask) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.Grip, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_Grip)) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.DPadLeft, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Left)) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.DPadUp, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Up)) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.DPadRight, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Right)) != 0u); |
|
currState[i].SetButtonPress(VRModuleRawButton.DPadDown, (buttons & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Down)) != 0u); |
|
|
|
currState[i].SetButtonTouch(VRModuleRawButton.System, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_System)) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.ApplicationMenu, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_Menu)) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.Touchpad, (touches & dpadMask) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.Trigger, (touches & triggerBumperMask) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.Grip, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_Grip)) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.DPadLeft, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Left)) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.DPadUp, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Up)) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.DPadRight, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Right)) != 0u); |
|
currState[i].SetButtonTouch(VRModuleRawButton.DPadDown, (touches & (1 << (int)WVR_InputId.WVR_InputId_Alias1_DPad_Down)) != 0u); |
|
|
|
for (int j = 0, jmax = m_analogStates.Length; j < jmax; ++j) |
|
{ |
|
switch (m_analogStates[j].id) |
|
{ |
|
case WVR_InputId.WVR_InputId_Alias1_Trigger: |
|
if (m_analogStates[j].type == WVR_AnalogType.WVR_AnalogType_Trigger) |
|
{ |
|
currState[i].SetAxisValue(VRModuleRawAxis.Trigger, m_analogStates[j].axis.x); |
|
} |
|
break; |
|
case WVR_InputId.WVR_InputId_Alias1_Touchpad: |
|
if (m_analogStates[j].type == WVR_AnalogType.WVR_AnalogType_TouchPad && currState[i].GetButtonTouch(VRModuleRawButton.Touchpad)) |
|
{ |
|
currState[i].SetAxisValue(VRModuleRawAxis.TouchpadX, m_analogStates[j].axis.x); |
|
currState[i].SetAxisValue(VRModuleRawAxis.TouchpadY, m_analogStates[j].axis.y); |
|
} |
|
else |
|
{ |
|
currState[i].SetAxisValue(VRModuleRawAxis.TouchpadX, 0f); |
|
currState[i].SetAxisValue(VRModuleRawAxis.TouchpadY, 0f); |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
currState[i].buttonPressed = 0u; |
|
currState[i].buttonTouched = 0u; |
|
for (int j = 0, jmax = currState[i].axisValue.Length; j < jmax; ++j) { currState[i].axisValue[j] = 0f; } |
|
} |
|
} |
|
else |
|
{ |
|
if (prevState[i].isConnected) |
|
{ |
|
currState[i].Reset(); |
|
} |
|
} |
|
} |
|
|
|
// add right arm |
|
if (rightState != null && rightState.isConnected && rightState.position == Vector3.zero) |
|
{ |
|
rightState.position = GetControllerPositionWithVirtualArm(GetNeckPose(headState.pose), rightState.rotation, RIGHT_ARM_MULTIPLIER); |
|
} |
|
|
|
// add left arm |
|
if (leftState != null && leftState.isConnected && leftState.position == Vector3.zero) |
|
{ |
|
leftState.position = GetControllerPositionWithVirtualArm(GetNeckPose(headState.pose), leftState.rotation, LEFT_ARM_MULTIPLIER); |
|
} |
|
} |
|
|
|
private static RigidPose GetNeckPose(RigidPose headPose) |
|
{ |
|
var headForward = headPose.forward; |
|
return new RigidPose(headPose.pos + DEFAULT_NECK_POSITION, Quaternion.FromToRotation(Vector3.forward, new Vector3(headForward.x, 0f, headForward.z))); |
|
} |
|
|
|
private static float GetExtensionRatio(Vector3 v) |
|
{ |
|
var xAngle = 90f - Vector3.Angle(v, Vector3.up); |
|
return Mathf.Clamp01(Mathf.InverseLerp(MIN_EXTENSION_ANGLE, MAX_EXTENSION_ANGLE, xAngle)); |
|
} |
|
|
|
private static Quaternion GetLerpRotation(Quaternion xyRotation, float extensionRatio) |
|
{ |
|
float totalAngle = Quaternion.Angle(xyRotation, Quaternion.identity); |
|
float lerpSuppresion = 1.0f - Mathf.Pow(totalAngle / 180.0f, 6.0f); |
|
float inverseElbowBendRatio = 1.0f - DEFAULT_ELBOW_BEND_RATIO; |
|
float lerpValue = inverseElbowBendRatio + DEFAULT_ELBOW_BEND_RATIO * extensionRatio * EXTENSION_WEIGHT; |
|
lerpValue *= lerpSuppresion; |
|
return Quaternion.Lerp(Quaternion.identity, xyRotation, lerpValue); |
|
} |
|
|
|
private static Vector3 GetControllerPositionWithVirtualArm(RigidPose neckPose, Quaternion ctrlRot, Vector3 sideMultiplier) |
|
{ |
|
var localCtrlForward = (Quaternion.Inverse(neckPose.rot) * ctrlRot) * Vector3.forward; |
|
var localCtrlXYRot = Quaternion.FromToRotation(Vector3.forward, localCtrlForward); |
|
var extensionRatio = GetExtensionRatio(localCtrlForward); |
|
var lerpRotation = GetLerpRotation(localCtrlXYRot, extensionRatio); |
|
|
|
var elbowPose = new RigidPose( |
|
Vector3.Scale(DEFAULT_ELBOW_REST_POSITION, sideMultiplier) + Vector3.Scale(DEFAULT_ARM_EXTENSION_OFFSET, sideMultiplier) * extensionRatio, |
|
Quaternion.Inverse(lerpRotation) * localCtrlXYRot); |
|
var wristPose = new RigidPose( |
|
Vector3.Scale(DEFAULT_WRIST_REST_POSITION, sideMultiplier), |
|
lerpRotation); |
|
var palmPose = new RigidPose( |
|
Vector3.Scale(DEFAULT_CONTROLLER_REST_POSITION, sideMultiplier), |
|
Quaternion.identity); |
|
|
|
var finalCtrlPose = neckPose * elbowPose * wristPose * palmPose; |
|
return finalCtrlPose.pos; |
|
} |
|
|
|
public override void TriggerViveControllerHaptic(uint deviceIndex, ushort durationMicroSec = 500) |
|
{ |
|
Interop.WVR_TriggerVibrator(m_deviceTypes[deviceIndex], WVR_InputId.WVR_InputId_Alias1_Touchpad, durationMicroSec); |
|
} |
|
#endif |
|
} |
|
} |