935 lines
43 KiB
C#
935 lines
43 KiB
C#
//========= Copyright 2016-2023, HTC Corporation. All rights reserved. ===========
|
|
|
|
using HTC.UnityPlugin.Utility;
|
|
using HTC.UnityPlugin.Vive;
|
|
using HTC.UnityPlugin.Vive.WaveVRExtension;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEngine;
|
|
#if VIU_WAVEVR && UNITY_ANDROID
|
|
using wvr;
|
|
using Object = UnityEngine.Object;
|
|
#endif
|
|
#if VIU_WAVEXR_ESSENCE_CONTROLLER_MODEL || VIU_WAVEXR_ESSENCE_RENDERMODEL
|
|
using Wave.Essence;
|
|
#endif
|
|
|
|
namespace HTC.UnityPlugin.VRModuleManagement
|
|
{
|
|
public partial class VRModule : SingletonBehaviour<VRModule>
|
|
{
|
|
public static readonly bool isWaveVRPluginDetected =
|
|
#if VIU_WAVEVR
|
|
true;
|
|
#else
|
|
false;
|
|
#endif
|
|
|
|
public static readonly bool isWaveVRSupported =
|
|
#if VIU_WAVEVR_SUPPORT
|
|
true;
|
|
#else
|
|
false;
|
|
#endif
|
|
}
|
|
|
|
public sealed class WaveVRModule : VRModule.ModuleBase
|
|
{
|
|
public override int moduleOrder { get { return (int)DefaultModuleOrder.WaveVR; } }
|
|
|
|
public override int moduleIndex { get { return (int)VRModuleSelectEnum.WaveVR; } }
|
|
|
|
[RenderModelHook.CreatorPriorityAttirbute(0)]
|
|
private class XRRenderModelCreator : RenderModelHook.DefaultRenderModelCreator
|
|
{
|
|
private uint m_index = INVALID_DEVICE_INDEX;
|
|
private GameObject m_modelObj;
|
|
|
|
public override bool shouldActive
|
|
{
|
|
get
|
|
{
|
|
#if (VIU_WAVEXR_ESSENCE_CONTROLLER_MODEL || VIU_WAVEXR_ESSENCE_RENDERMODEL) && UNITY_ANDROID
|
|
return VIUSettings.enableWaveXRRenderModel;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public override void UpdateRenderModel()
|
|
{
|
|
if (!ChangeProp.Set(ref m_index, hook.GetModelDeviceIndex())) { return; }
|
|
|
|
if (VRModule.GetDeviceState(m_index).deviceClass == VRModuleDeviceClass.Controller)
|
|
{
|
|
if (m_index == VRModule.GetRightControllerDeviceIndex())
|
|
{
|
|
UpdateDefaultRenderModel(false);
|
|
|
|
if (m_modelObj == null)
|
|
{
|
|
m_modelObj = new GameObject("Model");
|
|
m_modelObj.transform.SetParent(hook.transform, false);
|
|
m_modelObj.SetActive(false);
|
|
#if VIU_WAVEXR_ESSENCE_CONTROLLER_MODEL
|
|
#if VIU_WAVE_XRSDK_3_99_31_OR_NEWER
|
|
m_modelObj.AddComponent<PoseMode>();
|
|
#endif
|
|
GameObject controllerObj = new GameObject("Controller");
|
|
controllerObj.transform.SetParent(m_modelObj.transform, false);
|
|
controllerObj.AddComponent<Wave.Essence.Controller.Model.RenderModel>();
|
|
controllerObj.AddComponent<Wave.Essence.Controller.Model.ButtonEffect>();
|
|
#elif VIU_WAVEXR_ESSENCE_RENDERMODEL
|
|
m_modelObj.AddComponent<Wave.Essence.Controller.RenderModel>();
|
|
m_modelObj.AddComponent<Wave.Essence.Controller.ButtonEffect>();
|
|
#endif
|
|
}
|
|
|
|
m_modelObj.SetActive(true);
|
|
}
|
|
else
|
|
{
|
|
UpdateDefaultRenderModel(false);
|
|
|
|
if (m_modelObj == null)
|
|
{
|
|
m_modelObj = new GameObject("Model");
|
|
m_modelObj.transform.SetParent(hook.transform, false);
|
|
m_modelObj.SetActive(false);
|
|
#if VIU_WAVEXR_ESSENCE_CONTROLLER_MODEL
|
|
#if VIU_WAVE_XRSDK_3_99_31_OR_NEWER
|
|
var pm = m_modelObj.AddComponent<PoseMode>();
|
|
pm.WhichHand = XR_Hand.NonDominant;
|
|
#endif
|
|
GameObject controllerObj = new GameObject("Controller");
|
|
controllerObj.transform.SetParent(m_modelObj.transform, false);
|
|
var rm = controllerObj.AddComponent<Wave.Essence.Controller.Model.RenderModel>();
|
|
rm.WhichHand = XR_Hand.NonDominant;
|
|
var be = controllerObj.AddComponent<Wave.Essence.Controller.Model.ButtonEffect>();
|
|
be.HandType = XR_Hand.NonDominant;
|
|
#elif VIU_WAVEXR_ESSENCE_RENDERMODEL
|
|
var rm = m_modelObj.AddComponent<Wave.Essence.Controller.RenderModel>();
|
|
rm.WhichHand = XR_Hand.NonDominant;
|
|
var be = m_modelObj.AddComponent<Wave.Essence.Controller.ButtonEffect>();
|
|
be.HandType = XR_Hand.NonDominant;
|
|
#endif
|
|
}
|
|
|
|
m_modelObj.SetActive(true);
|
|
}
|
|
}
|
|
else if (VRModule.GetDeviceState(m_index).deviceClass == VRModuleDeviceClass.TrackedHand
|
|
|| VRModule.GetDeviceState(m_index).deviceClass == VRModuleDeviceClass.GenericTracker)
|
|
{
|
|
//VIUWaveVRRenderModel currently doesn't support tracked hand
|
|
// Fallback to default model instead
|
|
UpdateDefaultRenderModel(true);
|
|
|
|
if (m_modelObj != null)
|
|
{
|
|
m_modelObj.SetActive(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateDefaultRenderModel(false);
|
|
// deacitvate object for render model
|
|
if (m_modelObj != null)
|
|
{
|
|
m_modelObj.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void CleanUpRenderModel()
|
|
{
|
|
base.CleanUpRenderModel();
|
|
|
|
if (m_modelObj != null)
|
|
{
|
|
UnityEngine.Object.Destroy(m_modelObj);
|
|
m_modelObj = null;
|
|
m_index = INVALID_DEVICE_INDEX;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if VIU_WAVEVR && UNITY_ANDROID
|
|
private class CameraCreator : VRCameraHook.CameraCreator
|
|
{
|
|
public override bool shouldActive { get { return s_moduleInstance == null ? false : s_moduleInstance.isActivated; } }
|
|
|
|
public override void CreateCamera(VRCameraHook hook)
|
|
{
|
|
if (hook.GetComponent<WaveVR_Render>() == null)
|
|
{
|
|
hook.gameObject.AddComponent<WaveVR_Render>();
|
|
#if VIU_WAVEVR_3_1_3_OR_NEWER && UNITY_EDITOR
|
|
wvr.Interop.WVR_PostInit();
|
|
#endif
|
|
}
|
|
if (hook.GetComponent<VivePoseTracker>() == null)
|
|
{
|
|
hook.gameObject.AddComponent<VivePoseTracker>().viveRole.SetEx(DeviceRole.Hmd);
|
|
}
|
|
if (hook.GetComponent<AudioListener>() != null)
|
|
{
|
|
Object.Destroy(hook.GetComponent<AudioListener>());
|
|
}
|
|
}
|
|
}
|
|
|
|
private class RenderModelCreator : RenderModelHook.RenderModelCreator
|
|
{
|
|
private uint m_index = INVALID_DEVICE_INDEX;
|
|
private GameObject m_model;
|
|
private WVR_DeviceType m_loadedHandType;
|
|
|
|
public override bool shouldActive { get { return s_moduleInstance == null ? false : s_moduleInstance.isActivated; } }
|
|
|
|
public override void UpdateRenderModel()
|
|
{
|
|
if (!ChangeProp.Set(ref m_index, hook.GetModelDeviceIndex())) { return; }
|
|
|
|
var hasValidModel = false;
|
|
var handType = default(WVR_DeviceType);
|
|
if (VRModule.IsValidDeviceIndex(m_index))
|
|
{
|
|
if (m_index == VRModule.GetRightControllerDeviceIndex())
|
|
{
|
|
hasValidModel = true;
|
|
handType = WVR_DeviceType.WVR_DeviceType_Controller_Right;
|
|
}
|
|
else if (m_index == VRModule.GetLeftControllerDeviceIndex())
|
|
{
|
|
hasValidModel = true;
|
|
handType = WVR_DeviceType.WVR_DeviceType_Controller_Left;
|
|
}
|
|
}
|
|
|
|
// NOTE: load renderModel only if it hasn't been loaded or user changes handType
|
|
if (hasValidModel)
|
|
{
|
|
if (m_model != null && m_loadedHandType != handType)
|
|
{
|
|
CleanUpRenderModel();
|
|
}
|
|
|
|
if (m_model == null)
|
|
{
|
|
// Create WaveVR_ControllerLoader silently (to avoid Start and OnEnable)
|
|
var loaderGO = new GameObject("Loader");
|
|
loaderGO.transform.SetParent(hook.transform, false);
|
|
loaderGO.SetActive(false);
|
|
var loader = loaderGO.AddComponent<WaveVR_ControllerLoader>();
|
|
loader.TrackPosition = false;
|
|
loader.TrackRotation = false;
|
|
loader.showIndicator = false;
|
|
// Call onLoadController to create model (chould be Finch/Link/Pico/QIYIVR)
|
|
switch (handType)
|
|
{
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Right:
|
|
#if VIU_WAVEVR_3_0_0_OR_NEWER
|
|
loader.WhichHand = s_moduleInstance.m_deviceHands[RIGHT_INDEX];
|
|
#else
|
|
loader.WhichHand = WaveVR_ControllerLoader.ControllerHand.Controller_Right;
|
|
#endif
|
|
loaderGO.SetActive(true);
|
|
|
|
if (WaveVR.Instance.getDeviceByType(handType).pose.pose.Is6DoFPose && WaveVR_Controller.IsLeftHanded)
|
|
{
|
|
loaderGO.SendMessage("onLoadController", WVR_DeviceType.WVR_DeviceType_Controller_Left);
|
|
}
|
|
else
|
|
{
|
|
loaderGO.SendMessage("onLoadController", WVR_DeviceType.WVR_DeviceType_Controller_Right);
|
|
}
|
|
break;
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Left:
|
|
#if VIU_WAVEVR_3_0_0_OR_NEWER
|
|
loader.WhichHand = s_moduleInstance.m_deviceHands[LEFT_INDEX];
|
|
#elif VIU_WAVEVR_2_1_0_OR_NEWER
|
|
if (Interop.WVR_GetWaveRuntimeVersion() >= 3 && WaveVR_Controller.IsLeftHanded)
|
|
{
|
|
loader.WhichHand = WaveVR_ControllerLoader.ControllerHand.Controller_Right;
|
|
}
|
|
else
|
|
{
|
|
loader.WhichHand = WaveVR_ControllerLoader.ControllerHand.Controller_Left;
|
|
}
|
|
#else
|
|
loader.WhichHand = WaveVR_ControllerLoader.ControllerHand.Controller_Left;
|
|
#endif
|
|
loaderGO.SetActive(true);
|
|
|
|
if (WaveVR.Instance.getDeviceByType(handType).pose.pose.Is6DoFPose && WaveVR_Controller.IsLeftHanded)
|
|
{
|
|
loaderGO.SendMessage("onLoadController", WVR_DeviceType.WVR_DeviceType_Controller_Right);
|
|
}
|
|
else
|
|
{
|
|
loaderGO.SendMessage("onLoadController", WVR_DeviceType.WVR_DeviceType_Controller_Left);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Find transform that only contains controller model (include animator, exclude PoseTracker/Beam/UIPointer)
|
|
// and remove other unnecessary objects
|
|
var ctrllerActions = FindWaveVRControllerActionsObjInChildren();
|
|
if (ctrllerActions != null)
|
|
{
|
|
ctrllerActions.transform.SetParent(hook.transform, false);
|
|
ctrllerActions.transform.SetAsFirstSibling();
|
|
for (int i = hook.transform.childCount - 1; i >= 1; --i)
|
|
{
|
|
Object.Destroy(hook.transform.GetChild(i).gameObject);
|
|
}
|
|
ctrllerActions.gameObject.SetActive(true);
|
|
m_model = ctrllerActions.gameObject;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("FindWaveVRControllerActionsObjInChildren failed");
|
|
for (int i = hook.transform.childCount - 1; i >= 0; --i)
|
|
{
|
|
Object.Destroy(hook.transform.GetChild(i).gameObject);
|
|
}
|
|
}
|
|
|
|
m_loadedHandType = handType;
|
|
}
|
|
|
|
m_model.SetActive(true);
|
|
}
|
|
else
|
|
{
|
|
if (m_model != null)
|
|
{
|
|
m_model.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void CleanUpRenderModel()
|
|
{
|
|
if (m_model != null)
|
|
{
|
|
Object.Destroy(m_model);
|
|
m_model = null;
|
|
}
|
|
}
|
|
|
|
// FIXME: This is for finding Controller model with animator, is reliable?
|
|
private Transform FindWaveVRControllerActionsObjInChildren()
|
|
{
|
|
var nodes = new List<Transform>();
|
|
nodes.Add(hook.transform);
|
|
for (int i = 0; i < nodes.Count; ++i)
|
|
{
|
|
var parent = nodes[i];
|
|
for (int j = 0, jmax = parent.childCount; j < jmax; ++j)
|
|
{
|
|
var child = parent.GetChild(j);
|
|
nodes.Add(child);
|
|
if (child.GetComponent<WaveVR_PoseTrackerManager>() != null) { continue; }
|
|
if (child.GetComponent<WaveVR_Beam>() != null) { continue; }
|
|
if (child.GetComponent<WaveVR_ControllerPointer>() != null) { continue; }
|
|
if (child.GetComponent<WaveVR_ControllerLoader>() != null) { continue; }
|
|
return child;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private const uint DEVICE_COUNT = 3;
|
|
private const uint HEAD_INDEX = 0;
|
|
private const uint RIGHT_INDEX = 1;
|
|
private const uint LEFT_INDEX = 2;
|
|
|
|
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 static WaveVRModule s_moduleInstance;
|
|
private static readonly WVR_DeviceType[] s_index2type;
|
|
private static readonly uint[] s_type2index;
|
|
private static readonly VRModuleDeviceClass[] s_type2class;
|
|
|
|
private IVRModuleDeviceStateRW m_headState;
|
|
private IVRModuleDeviceStateRW m_rightState;
|
|
private IVRModuleDeviceStateRW m_leftState;
|
|
private WaveVR_ControllerLoader.ControllerHand[] m_deviceHands = new WaveVR_ControllerLoader.ControllerHand[DEVICE_COUNT];
|
|
|
|
#region 6Dof Controller Simulation
|
|
|
|
private enum Simulate6DoFControllerMode
|
|
{
|
|
KeyboardWASD,
|
|
KeyboardModifierTrackpad,
|
|
}
|
|
|
|
private static Simulate6DoFControllerMode s_simulationMode = Simulate6DoFControllerMode.KeyboardWASD;
|
|
private static Vector3[] s_simulatedCtrlPosArray;
|
|
|
|
#endregion
|
|
|
|
static WaveVRModule()
|
|
{
|
|
s_index2type = new WVR_DeviceType[DEVICE_COUNT];
|
|
s_index2type[0] = WVR_DeviceType.WVR_DeviceType_HMD;
|
|
s_index2type[1] = WVR_DeviceType.WVR_DeviceType_Controller_Right;
|
|
s_index2type[2] = WVR_DeviceType.WVR_DeviceType_Controller_Left;
|
|
|
|
s_type2index = new uint[EnumUtils.GetMaxValue(typeof(WVR_DeviceType)) + 1];
|
|
for (int i = 0; i < s_type2index.Length; ++i) { s_type2index[i] = INVALID_DEVICE_INDEX; }
|
|
s_type2index[(int)WVR_DeviceType.WVR_DeviceType_HMD] = 0u;
|
|
s_type2index[(int)WVR_DeviceType.WVR_DeviceType_Controller_Right] = 1u;
|
|
s_type2index[(int)WVR_DeviceType.WVR_DeviceType_Controller_Left] = 2u;
|
|
|
|
s_type2class = new VRModuleDeviceClass[s_type2index.Length];
|
|
for (int i = 0; i < s_type2class.Length; ++i) { s_type2class[i] = VRModuleDeviceClass.Invalid; }
|
|
s_type2class[(int)WVR_DeviceType.WVR_DeviceType_HMD] = VRModuleDeviceClass.HMD;
|
|
s_type2class[(int)WVR_DeviceType.WVR_DeviceType_Controller_Right] = VRModuleDeviceClass.Controller;
|
|
s_type2class[(int)WVR_DeviceType.WVR_DeviceType_Controller_Left] = VRModuleDeviceClass.Controller;
|
|
|
|
s_simulatedCtrlPosArray = new Vector3[s_type2index.Length];
|
|
}
|
|
|
|
public override bool ShouldActiveModule()
|
|
{
|
|
#if VIU_WAVEVR_3_1_3_OR_NEWER && UNITY_EDITOR
|
|
return UnityEditor.EditorPrefs.GetBool("WaveVR/DirectPreview/Enable Direct Preview", false);
|
|
#elif !VIU_WAVEVR_2_1_0_OR_NEWER && UNITY_EDITOR
|
|
return false;
|
|
#else
|
|
return VIUSettings.activateWaveVRModule;
|
|
#endif
|
|
}
|
|
|
|
public override void OnActivated()
|
|
{
|
|
if (Object.FindObjectOfType<WaveVR_Init>() == null)
|
|
{
|
|
VRModule.Instance.gameObject.AddComponent<WaveVR_Init>();
|
|
}
|
|
|
|
#if !UNITY_EDITOR && VIU_WAVEVR_3_1_0_OR_NEWER
|
|
if (Object.FindObjectOfType<WaveVR_ButtonList>() == null)
|
|
{
|
|
VRModule.Instance.gameObject.AddComponent<WaveVR_ButtonList>();
|
|
|
|
var buttonList = VRModule.Instance.gameObject.GetComponent<WaveVR_ButtonList>();
|
|
if (buttonList != null)
|
|
{
|
|
buttonList.HmdButtons = new List<WaveVR_ButtonList.EHmdButtons>()
|
|
{
|
|
WaveVR_ButtonList.EHmdButtons.Enter
|
|
};
|
|
buttonList.DominantButtons = new List<WaveVR_ButtonList.EControllerButtons>()
|
|
{
|
|
WaveVR_ButtonList.EControllerButtons.Grip,
|
|
WaveVR_ButtonList.EControllerButtons.Menu,
|
|
WaveVR_ButtonList.EControllerButtons.Touchpad,
|
|
WaveVR_ButtonList.EControllerButtons.Trigger,
|
|
WaveVR_ButtonList.EControllerButtons.A_X,
|
|
WaveVR_ButtonList.EControllerButtons.B_Y
|
|
};
|
|
buttonList.NonDominantButtons = new List<WaveVR_ButtonList.EControllerButtons>()
|
|
{
|
|
WaveVR_ButtonList.EControllerButtons.Grip,
|
|
WaveVR_ButtonList.EControllerButtons.Menu,
|
|
WaveVR_ButtonList.EControllerButtons.Touchpad,
|
|
WaveVR_ButtonList.EControllerButtons.Trigger,
|
|
WaveVR_ButtonList.EControllerButtons.A_X,
|
|
WaveVR_ButtonList.EControllerButtons.B_Y
|
|
};
|
|
}
|
|
}
|
|
#elif !UNITY_EDITOR && VIU_WAVEVR_3_0_0_OR_NEWER
|
|
if (Object.FindObjectOfType<WaveVR_ButtonList>() == null)
|
|
{
|
|
VRModule.Instance.gameObject.AddComponent<WaveVR_ButtonList>();
|
|
|
|
var buttonList = VRModule.Instance.gameObject.GetComponent<WaveVR_ButtonList>();
|
|
if (buttonList != null)
|
|
{
|
|
buttonList.HmdButtons = new List<WaveVR_ButtonList.EButtons>()
|
|
{
|
|
WaveVR_ButtonList.EButtons.HMDEnter
|
|
};
|
|
buttonList.DominantButtons = new List<WaveVR_ButtonList.EButtons>()
|
|
{
|
|
WaveVR_ButtonList.EButtons.Grip,
|
|
WaveVR_ButtonList.EButtons.Menu,
|
|
WaveVR_ButtonList.EButtons.Touchpad,
|
|
WaveVR_ButtonList.EButtons.Trigger
|
|
};
|
|
buttonList.NonDominantButtons = new List<WaveVR_ButtonList.EButtons>()
|
|
{
|
|
WaveVR_ButtonList.EButtons.Grip,
|
|
WaveVR_ButtonList.EButtons.Menu,
|
|
WaveVR_ButtonList.EButtons.Touchpad,
|
|
WaveVR_ButtonList.EButtons.Trigger
|
|
};
|
|
}
|
|
}
|
|
#endif
|
|
|
|
EnsureDeviceStateLength(DEVICE_COUNT);
|
|
|
|
UpdateTrackingSpaceType();
|
|
|
|
s_moduleInstance = this;
|
|
|
|
WaveVR_Utils.Event.Listen(WaveVR_Utils.Event.NEW_POSES, OnNewPoses);
|
|
}
|
|
|
|
public override void OnDeactivated()
|
|
{
|
|
WaveVR_Utils.Event.Remove(WaveVR_Utils.Event.NEW_POSES, OnNewPoses);
|
|
|
|
m_headState = null;
|
|
m_rightState = null;
|
|
m_leftState = null;
|
|
|
|
s_moduleInstance = null;
|
|
}
|
|
|
|
public override void UpdateTrackingSpaceType()
|
|
{
|
|
if (WaveVR_Render.Instance != null)
|
|
{
|
|
// Only effected when origin is OnHead or OnGround
|
|
// This way you can manually set WaveVR_Render origin to other value lik OnTrackingObserver or OnHead_3DoF
|
|
if (VRModule.trackingSpaceType == VRModuleTrackingSpaceType.RoomScale)
|
|
{
|
|
if (WaveVR_Render.Instance.origin == WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnHead)
|
|
{
|
|
WaveVR_Render.Instance.origin = WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnGround;
|
|
}
|
|
}
|
|
else if (VRModule.trackingSpaceType == VRModuleTrackingSpaceType.Stationary)
|
|
{
|
|
if (WaveVR_Render.Instance.origin == WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnGround)
|
|
{
|
|
WaveVR_Render.Instance.origin = WVR_PoseOriginModel.WVR_PoseOriginModel_OriginOnHead;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
var rightDevice = GetWVRControllerDevice(WVR_DeviceType.WVR_DeviceType_Controller_Right);
|
|
UpdateDeviceInput(1, rightDevice);
|
|
var leftDevice = GetWVRControllerDevice(WVR_DeviceType.WVR_DeviceType_Controller_Left);
|
|
UpdateDeviceInput(2, leftDevice);
|
|
|
|
ProcessDeviceInputChanged();
|
|
}
|
|
|
|
private WaveVR_Controller.Device GetWVRControllerDevice(WVR_DeviceType deviceType)
|
|
{
|
|
switch (deviceType)
|
|
{
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Right:
|
|
return WaveVR_Controller.Input(WaveVR_Controller.IsLeftHanded ? WVR_DeviceType.WVR_DeviceType_Controller_Left : WVR_DeviceType.WVR_DeviceType_Controller_Right);
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Left:
|
|
return WaveVR_Controller.Input(WaveVR_Controller.IsLeftHanded ? WVR_DeviceType.WVR_DeviceType_Controller_Right : WVR_DeviceType.WVR_DeviceType_Controller_Left);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private WaveVR.Device GetWVRDevice(WVR_DeviceType deviceType)
|
|
{
|
|
switch (deviceType)
|
|
{
|
|
case WVR_DeviceType.WVR_DeviceType_HMD:
|
|
return WaveVR.Instance.getDeviceByType(deviceType);
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Right:
|
|
return WaveVR.Instance.getDeviceByType(WaveVR_Controller.IsLeftHanded ? WVR_DeviceType.WVR_DeviceType_Controller_Left : WVR_DeviceType.WVR_DeviceType_Controller_Right);
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Left:
|
|
return WaveVR.Instance.getDeviceByType(WaveVR_Controller.IsLeftHanded ? WVR_DeviceType.WVR_DeviceType_Controller_Right : WVR_DeviceType.WVR_DeviceType_Controller_Left);
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private void UpdateDeviceInput(uint deviceIndex, WaveVR_Controller.Device deviceInput)
|
|
{
|
|
#if VIU_WAVEVR_2_1_0_OR_NEWER
|
|
const WVR_InputId digitalTrggerBumpID = WVR_InputId.WVR_InputId_Alias1_Digital_Trigger;
|
|
#else
|
|
const WVR_InputId digitalTrggerBumpID = WVR_InputId.WVR_InputId_Alias1_Bumper;
|
|
#endif
|
|
|
|
IVRModuleDeviceState prevState;
|
|
IVRModuleDeviceStateRW currState;
|
|
|
|
if (!TryGetValidDeviceState(deviceIndex, out prevState, out currState) || !deviceInput.connected) { return; }
|
|
|
|
if (deviceInput != null)
|
|
{
|
|
var systemPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_System);
|
|
var menuPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_Menu);
|
|
var triggerPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_Trigger);
|
|
var digitalTriggerPressed = deviceInput.GetPress(digitalTrggerBumpID);
|
|
var gripPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_Grip);
|
|
var touchpadPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_Touchpad);
|
|
var dpadLeftPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_DPad_Left);
|
|
var dpadUpPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_DPad_Up);
|
|
var dpadRightPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_DPad_Right);
|
|
var dpadDownPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_DPad_Down);
|
|
var buttonAPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_A);
|
|
var buttonBPressed = deviceInput.GetPress(WVR_InputId.WVR_InputId_Alias1_B);
|
|
|
|
currState.SetButtonPress(VRModuleRawButton.System, systemPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.ApplicationMenu, menuPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.Touchpad, touchpadPressed || dpadLeftPressed || dpadUpPressed || dpadRightPressed || dpadDownPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.Trigger, triggerPressed || digitalTriggerPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.Grip, gripPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.DPadLeft, dpadLeftPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.DPadUp, dpadUpPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.DPadRight, dpadRightPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.DPadDown, dpadDownPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.A, buttonAPressed);
|
|
currState.SetButtonPress(VRModuleRawButton.ApplicationMenu, buttonBPressed);
|
|
|
|
var systemTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_System);
|
|
var menuTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_Menu);
|
|
var triggerTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_Trigger);
|
|
var digitalTriggerTouched = deviceInput.GetTouch(digitalTrggerBumpID);
|
|
var gripTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_Grip);
|
|
var touchpadTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_Touchpad);
|
|
var dpadLeftTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_DPad_Left);
|
|
var dpadUpTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_DPad_Up);
|
|
var dpadRightTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_DPad_Right);
|
|
var dpadDownTouched = deviceInput.GetTouch(WVR_InputId.WVR_InputId_Alias1_DPad_Down);
|
|
currState.SetButtonTouch(VRModuleRawButton.System, systemTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.ApplicationMenu, menuTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.Touchpad, touchpadTouched || dpadLeftTouched || dpadUpTouched || dpadRightTouched || dpadDownTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.Trigger, triggerTouched || digitalTriggerTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.Grip, gripTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.DPadLeft, dpadLeftTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.DPadUp, dpadUpTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.DPadRight, dpadRightTouched);
|
|
currState.SetButtonTouch(VRModuleRawButton.DPadDown, dpadDownTouched);
|
|
|
|
var triggerAxis = deviceInput.GetAxis(WVR_InputId.WVR_InputId_Alias1_Trigger);
|
|
var touchAxis = deviceInput.GetAxis(WVR_InputId.WVR_InputId_Alias1_Touchpad);
|
|
var gripAxis = deviceInput.GetAxis(WVR_InputId.WVR_InputId_Alias1_Grip);
|
|
|
|
currState.SetAxisValue(VRModuleRawAxis.Trigger, triggerAxis.x);
|
|
currState.SetAxisValue(VRModuleRawAxis.CapSenseGrip, gripAxis.x);
|
|
currState.SetAxisValue(VRModuleRawAxis.TouchpadX, touchAxis.x);
|
|
currState.SetAxisValue(VRModuleRawAxis.TouchpadY, touchAxis.y);
|
|
}
|
|
else
|
|
{
|
|
currState.buttonPressed = 0u;
|
|
currState.buttonTouched = 0u;
|
|
currState.ResetAxisValues();
|
|
}
|
|
}
|
|
|
|
private IVRModuleDeviceStateRW UpdateDevicePose(uint deviceIndex, WaveVR.Device content)
|
|
{
|
|
IVRModuleDeviceState prevState;
|
|
IVRModuleDeviceStateRW currState;
|
|
EnsureValidDeviceState(deviceIndex, out prevState, out currState);
|
|
|
|
var deviceConnected = content.type == WVR_DeviceType.WVR_DeviceType_HMD ? true : content.connected;
|
|
|
|
if (!deviceConnected)
|
|
{
|
|
if (prevState.isConnected)
|
|
{
|
|
currState.Reset();
|
|
|
|
switch (content.type)
|
|
{
|
|
case WVR_DeviceType.WVR_DeviceType_HMD: m_headState = null; break;
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Right: m_rightState = null; break;
|
|
case WVR_DeviceType.WVR_DeviceType_Controller_Left: m_leftState = null; break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!prevState.isConnected)
|
|
{
|
|
string renderModelName;
|
|
if (!TryGetWVRStringParameter(s_index2type[(int)deviceIndex], "GetRenderModelName", out renderModelName))
|
|
{
|
|
renderModelName = "wvr_unknown_device";
|
|
}
|
|
|
|
currState.isConnected = true;
|
|
currState.deviceClass = s_type2class[(int)content.type];
|
|
currState.serialNumber = content.type.ToString();
|
|
currState.modelNumber = renderModelName;
|
|
currState.renderModelName = renderModelName;
|
|
currState.input2DType = VRModuleInput2DType.TouchpadOnly;
|
|
|
|
SetupKnownDeviceModel(currState);
|
|
}
|
|
|
|
// update pose
|
|
var devicePose = content.pose.pose;
|
|
currState.velocity = new Vector3(devicePose.Velocity.v0, devicePose.Velocity.v1, -devicePose.Velocity.v2);
|
|
currState.angularVelocity = new Vector3(-devicePose.AngularVelocity.v0, -devicePose.AngularVelocity.v1, devicePose.AngularVelocity.v2);
|
|
|
|
var rigidTransform = content.rigidTransform;
|
|
currState.position = rigidTransform.pos;
|
|
currState.rotation = rigidTransform.rot;
|
|
|
|
currState.isPoseValid = devicePose.IsValidPose;
|
|
}
|
|
|
|
return currState;
|
|
}
|
|
|
|
private void OnNewPoses(params object[] args)
|
|
{
|
|
if (WaveVR.Instance == null) { return; }
|
|
|
|
FlushDeviceState();
|
|
|
|
var headDevice = GetWVRDevice(WVR_DeviceType.WVR_DeviceType_HMD);
|
|
m_headState = UpdateDevicePose(0, headDevice);
|
|
var rightDevice = GetWVRDevice(WVR_DeviceType.WVR_DeviceType_Controller_Right);
|
|
m_rightState = UpdateDevicePose(1, rightDevice);
|
|
var leftDevice = GetWVRDevice(WVR_DeviceType.WVR_DeviceType_Controller_Left);
|
|
m_leftState = UpdateDevicePose(2, leftDevice);
|
|
|
|
#if VIU_WAVEVR_3_0_0_OR_NEWER
|
|
if (WaveVR_Controller.IsLeftHanded)
|
|
{
|
|
m_deviceHands[RIGHT_INDEX] = WaveVR_ControllerLoader.ControllerHand.Non_Dominant;
|
|
m_deviceHands[LEFT_INDEX] = WaveVR_ControllerLoader.ControllerHand.Dominant;
|
|
}
|
|
else
|
|
{
|
|
m_deviceHands[RIGHT_INDEX] = WaveVR_ControllerLoader.ControllerHand.Dominant;
|
|
m_deviceHands[LEFT_INDEX] = WaveVR_ControllerLoader.ControllerHand.Non_Dominant;
|
|
}
|
|
#endif
|
|
|
|
if (m_rightState != null && !rightDevice.pose.pose.Is6DoFPose)
|
|
{
|
|
ApplyVirtualArmAndSimulateInput(m_rightState, m_headState, RIGHT_ARM_MULTIPLIER);
|
|
}
|
|
|
|
if (m_leftState != null && !leftDevice.pose.pose.Is6DoFPose)
|
|
{
|
|
ApplyVirtualArmAndSimulateInput(m_leftState, m_headState, LEFT_ARM_MULTIPLIER);
|
|
}
|
|
|
|
ProcessConnectedDeviceChanged();
|
|
ProcessDevicePoseChanged();
|
|
}
|
|
|
|
// FIXME: WVR_IsInputFocusCapturedBySystem currently not implemented yet
|
|
//public override bool HasInputFocus()
|
|
//{
|
|
// return m_hasInputFocus;
|
|
//}
|
|
|
|
public override uint GetRightControllerDeviceIndex() { return RIGHT_INDEX; }
|
|
|
|
public override uint GetLeftControllerDeviceIndex() { return LEFT_INDEX; }
|
|
|
|
private void ApplyVirtualArmAndSimulateInput(IVRModuleDeviceStateRW ctrlState, IVRModuleDeviceStateRW headState, Vector3 handSideMultiplier)
|
|
{
|
|
if (!ctrlState.isConnected) { return; }
|
|
if (!VIUSettings.waveVRAddVirtualArmTo3DoFController && !VIUSettings.simulateWaveVR6DofController) { return; }
|
|
var deviceType = (int)s_index2type[ctrlState.deviceIndex];
|
|
|
|
#if !UNITY_EDITOR
|
|
if (Interop.WVR_GetDegreeOfFreedom((WVR_DeviceType)deviceType) == WVR_NumDoF.WVR_NumDoF_6DoF) { return; }
|
|
#elif VIU_WAVEVR_3_1_3_OR_NEWER && UNITY_EDITOR
|
|
if (!WaveVR.EnableSimulator || WVR_DirectPreview.WVR_GetDegreeOfFreedom_S(0) == (int)WVR_NumDoF.WVR_NumDoF_6DoF) { return; }
|
|
#elif VIU_WAVEVR_3_1_0_OR_NEWER && UNITY_EDITOR
|
|
if (!WaveVR.EnableSimulator || WVR_Simulator.WVR_GetDegreeOfFreedom_S(0) == (int)WVR_NumDoF.WVR_NumDoF_6DoF) { return; }
|
|
#elif VIU_WAVEVR_2_1_0_OR_NEWER && UNITY_EDITOR
|
|
if (!WaveVR.Instance.isSimulatorOn || WaveVR_Utils.WVR_GetDegreeOfFreedom_S() == (int)WVR_NumDoF.WVR_NumDoF_6DoF) { return; }
|
|
#endif
|
|
|
|
if (VIUSettings.simulateWaveVR6DofController)
|
|
{
|
|
if (Input.GetKeyDown(KeyCode.Alpha1)) { s_simulationMode = Simulate6DoFControllerMode.KeyboardWASD; }
|
|
if (Input.GetKeyDown(KeyCode.Alpha2)) { s_simulationMode = Simulate6DoFControllerMode.KeyboardModifierTrackpad; }
|
|
if (Input.GetKeyDown(KeyCode.BackQuote)) { s_simulatedCtrlPosArray[deviceType] = Vector3.zero; }
|
|
|
|
var deltaMove = Time.unscaledDeltaTime;
|
|
var rotY = Quaternion.Euler(0f, ctrlState.rotation.eulerAngles.y, 0f);
|
|
var moveForward = rotY * Vector3.forward;
|
|
var moveRight = rotY * Vector3.right;
|
|
|
|
switch (s_simulationMode)
|
|
{
|
|
case Simulate6DoFControllerMode.KeyboardWASD:
|
|
{
|
|
if (Input.GetKey(KeyCode.D)) { s_simulatedCtrlPosArray[deviceType] += moveRight * deltaMove; }
|
|
if (Input.GetKey(KeyCode.A)) { s_simulatedCtrlPosArray[deviceType] -= moveRight * deltaMove; }
|
|
if (Input.GetKey(KeyCode.E)) { s_simulatedCtrlPosArray[deviceType] += Vector3.up * deltaMove; }
|
|
if (Input.GetKey(KeyCode.Q)) { s_simulatedCtrlPosArray[deviceType] -= Vector3.up * deltaMove; }
|
|
if (Input.GetKey(KeyCode.W)) { s_simulatedCtrlPosArray[deviceType] += moveForward * deltaMove; }
|
|
if (Input.GetKey(KeyCode.S)) { s_simulatedCtrlPosArray[deviceType] -= moveForward * deltaMove; }
|
|
|
|
break;
|
|
}
|
|
|
|
case Simulate6DoFControllerMode.KeyboardModifierTrackpad:
|
|
{
|
|
float speedModifier = 2f;
|
|
float x = ctrlState.GetAxisValue(VRModuleRawAxis.TouchpadX) * speedModifier;
|
|
float y = ctrlState.GetAxisValue(VRModuleRawAxis.TouchpadY) * speedModifier;
|
|
|
|
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
|
|
{
|
|
s_simulatedCtrlPosArray[deviceType] += x * moveRight * deltaMove;
|
|
s_simulatedCtrlPosArray[deviceType] += y * moveForward * deltaMove;
|
|
}
|
|
|
|
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
|
|
{
|
|
s_simulatedCtrlPosArray[deviceType] += x * moveRight * deltaMove;
|
|
s_simulatedCtrlPosArray[deviceType] += y * Vector3.up * deltaMove;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VIUSettings.waveVRAddVirtualArmTo3DoFController)
|
|
{
|
|
var neckPose = new RigidPose(s_simulatedCtrlPosArray[deviceType], Quaternion.identity) * GetNeckPose(headState.pose);
|
|
ctrlState.position = GetControllerPositionWithVirtualArm(neckPose, ctrlState.rotation, handSideMultiplier);
|
|
}
|
|
else
|
|
{
|
|
ctrlState.position += s_simulatedCtrlPosArray[deviceType];
|
|
}
|
|
}
|
|
|
|
private static RigidPose GetNeckPose(RigidPose headPose)
|
|
{
|
|
var headForward = headPose.forward;
|
|
return new RigidPose(headPose.pos + VIUSettings.waveVRVirtualNeckPosition, 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(VIUSettings.waveVRVirtualElbowRestPosition, sideMultiplier) + Vector3.Scale(VIUSettings.waveVRVirtualArmExtensionOffset, sideMultiplier) * extensionRatio,
|
|
Quaternion.Inverse(lerpRotation) * localCtrlXYRot);
|
|
var wristPose = new RigidPose(
|
|
Vector3.Scale(VIUSettings.waveVRVirtualWristRestPosition, sideMultiplier),
|
|
lerpRotation);
|
|
var palmPose = new RigidPose(
|
|
Vector3.Scale(VIUSettings.waveVRVirtualHandRestPosition, sideMultiplier),
|
|
Quaternion.identity);
|
|
|
|
var finalCtrlPose = neckPose * elbowPose * wristPose * palmPose;
|
|
return finalCtrlPose.pos;
|
|
}
|
|
|
|
public override void TriggerViveControllerHaptic(uint deviceIndex, ushort durationMicroSec = 500)
|
|
{
|
|
var deviceInput = WaveVR_Controller.Input(s_index2type[deviceIndex]);
|
|
if (deviceInput != null)
|
|
{
|
|
deviceInput.TriggerHapticPulse(durationMicroSec);
|
|
}
|
|
}
|
|
|
|
private bool TryGetWVRStringParameter(WVR_DeviceType device, string paramName, out string result)
|
|
{
|
|
result = default(string);
|
|
var resultLen = 0u;
|
|
try
|
|
{
|
|
const int resultMaxLen = 128;
|
|
var resultBuffer = resultMaxLen;
|
|
var resultPtr = Marshal.AllocHGlobal(resultBuffer);
|
|
var paramNamePtr = Marshal.StringToHGlobalAnsi(paramName);
|
|
resultLen = Interop.WVR_GetParameters(device, paramNamePtr, resultPtr, resultMaxLen);
|
|
|
|
if (resultLen > 0u)
|
|
{
|
|
result = Marshal.PtrToStringAnsi(resultPtr);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
|
|
return resultLen > 0u;
|
|
}
|
|
|
|
#if VIU_WAVEVR_3_1_0_OR_NEWER
|
|
public override void TriggerHapticVibration(uint deviceIndex, float durationSeconds = 0.01f, float frequency = 85, float amplitude = 0.125f, float startSecondsFromNow = 0)
|
|
{
|
|
var deviceInput = WaveVR_Controller.Input(s_index2type[deviceIndex]);
|
|
var intensity = default(WVR_Intensity);
|
|
if (deviceInput != null)
|
|
{
|
|
if (0 <= amplitude || amplitude <= 0.2)
|
|
{
|
|
intensity = WVR_Intensity.WVR_Intensity_Weak;
|
|
}
|
|
else if (0.2 < amplitude || amplitude <= 0.4)
|
|
{
|
|
intensity = WVR_Intensity.WVR_Intensity_Light;
|
|
}
|
|
else if (0.4 < amplitude || amplitude <= 0.6)
|
|
{
|
|
intensity = WVR_Intensity.WVR_Intensity_Normal;
|
|
}
|
|
else if (0.6 < amplitude || amplitude <= 0.8)
|
|
{
|
|
intensity = WVR_Intensity.WVR_Intensity_Strong;
|
|
}
|
|
else if (0.8 < amplitude || amplitude <= 1)
|
|
{
|
|
intensity = WVR_Intensity.WVR_Intensity_Severe;
|
|
}
|
|
}
|
|
Interop.WVR_TriggerVibration(deviceInput.DeviceType, WVR_InputId.WVR_InputId_Alias1_Touchpad, (uint)(durationSeconds * 1000000), (uint)frequency, intensity);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|