using UnityEngine; using System.Collections; namespace CurvedUI { /// /// Contains a bunch of events you can subscribe to and bools to ask for current state of the controller. Can also trigger haptic feedback if you ask nicely. /// public class CurvedUIViveController : MonoBehaviour { #if CURVEDUI_STEAMVR_LEGACY #region settings public int axisFidelity = 1; #endregion #region internal variables private uint controllerIndex; private Type controllerType = Type.UNDEFINED; private SteamVR_TrackedObject trackedController; private SteamVR_Controller.Device device; private Vector2 touchpadAxis = Vector2.zero; private Vector2 triggerAxis = Vector2.zero; private bool controllerVisible = true; private ushort hapticPulseStrength; private int hapticPulseCountdown; private ushort maxHapticVibration = 3999; private Vector3 oculusTouchPoitingOriginOffset = new Vector3(0, -0.0258f, -0.033f); private Vector3 oculusTouchPoitingRotationOffset = new Vector3(38, 0, 0); public enum Type { UNDEFINED = 0, VIVE = 1, OCULUS_TOUCH = 2, } #endregion #region EVENTS public event ViveInputEvent TriggerClicked; public event ViveInputEvent TriggerUnclicked; public event ViveInputEvent TriggerAxisChanged; public event ViveInputEvent ApplicationMenuClicked; public event ViveInputEvent ApplicationMenuUnclicked; public event ViveInputEvent GripClicked; public event ViveInputEvent GripUnclicked; public event ViveInputEvent TouchpadClicked; public event ViveInputEvent TouchpadUnclicked; public event ViveInputEvent TouchpadTouched; public event ViveInputEvent TouchpadUntouched; public event ViveInputEvent TouchpadAxisChanged; public event ViveEvent ModelLoaded; public virtual void OnTriggerClicked(ViveInputArgs e) { if (TriggerClicked != null) TriggerClicked(this, e); } public virtual void OnTriggerUnclicked(ViveInputArgs e) { if (TriggerUnclicked != null) TriggerUnclicked(this, e); } public virtual void OnTriggerAxisChanged(ViveInputArgs e) { if (TriggerAxisChanged != null) TriggerAxisChanged(this, e); } public virtual void OnApplicationMenuClicked(ViveInputArgs e) { if (ApplicationMenuClicked != null) ApplicationMenuClicked(this, e); } public virtual void OnApplicationMenuUnclicked(ViveInputArgs e) { if (ApplicationMenuUnclicked != null) ApplicationMenuUnclicked(this, e); } public virtual void OnGripClicked(ViveInputArgs e) { if (GripClicked != null) GripClicked(this, e); } public virtual void OnGripUnclicked(ViveInputArgs e) { if (GripUnclicked != null) GripUnclicked(this, e); } public virtual void OnTouchpadClicked(ViveInputArgs e) { if (TouchpadClicked != null) TouchpadClicked(this, e); } public virtual void OnTouchpadUnclicked(ViveInputArgs e) { if (TouchpadUnclicked != null) TouchpadUnclicked(this, e); } public virtual void OnTouchpadTouched(ViveInputArgs e) { if (TouchpadTouched != null) TouchpadTouched(this, e); } public virtual void OnTouchpadUntouched(ViveInputArgs e) { if (TouchpadUntouched != null) TouchpadUntouched(this, e); } public virtual void OnTouchpadAxisChanged(ViveInputArgs e) { if (TouchpadAxisChanged != null) TouchpadAxisChanged(this, e); } #endregion #region LIFECYCLE void Awake() { trackedController = GetComponent(); SteamVR_Events.RenderModelLoaded.AddListener(SteamVRModelLoaded); } void Update() { controllerIndex = (uint)trackedController.index; //this device is not tracked right now - it has no device index - skip it. if (controllerIndex < 0 || controllerIndex >= Valve.VR.OpenVR.k_unMaxTrackedDeviceCount) return; device = SteamVR_Controller.Input((int)controllerIndex); //get axis inputfrom debice Vector2 currentTriggerAxis = device.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger); Vector2 currentTouchpadAxis = device.GetAxis(); //haptic feedback if (hapticPulseCountdown > 0) { device.TriggerHapticPulse(hapticPulseStrength); hapticPulseCountdown -= 1; } //check for changes in trigger press if (Vector2ShallowEquals(triggerAxis, currentTriggerAxis)) { triggerAxisChanged = false; } else { OnTriggerAxisChanged(SetButtonEvent(ref triggerPressed, true, currentTriggerAxis.x)); triggerAxisChanged = true; } //check for changes in finger pos on touchpad if (Vector2ShallowEquals(touchpadAxis, currentTouchpadAxis)) { touchpadAxisChanged = false; } else { OnTouchpadAxisChanged(SetButtonEvent(ref touchpadTouched, true, 1f)); touchpadAxisChanged = true; } touchpadAxis = new Vector2(currentTouchpadAxis.x, currentTouchpadAxis.y); triggerAxis = new Vector2(currentTriggerAxis.x, currentTriggerAxis.y); //Trigger if (device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger)) { OnTriggerClicked(SetButtonEvent(ref triggerPressed, true, currentTriggerAxis.x)); triggerDown = true; } else { triggerDown = false; } if (device.GetPressUp(SteamVR_Controller.ButtonMask.Trigger)) { OnTriggerUnclicked(SetButtonEvent(ref triggerPressed, false, 0f)); triggerUp = true; } else { triggerUp = false; } //ApplicationMenu if (device.GetPressDown(SteamVR_Controller.ButtonMask.ApplicationMenu)) { OnApplicationMenuClicked(SetButtonEvent(ref applicationMenuPressed, true, 1f)); } else if (device.GetPressUp(SteamVR_Controller.ButtonMask.ApplicationMenu)) { OnApplicationMenuUnclicked(SetButtonEvent(ref applicationMenuPressed, false, 0f)); } //Grip if (device.GetPressDown(SteamVR_Controller.ButtonMask.Grip)) { OnGripClicked(SetButtonEvent(ref gripPressed, true, 1f)); } else if (device.GetPressUp(SteamVR_Controller.ButtonMask.Grip)) { OnGripUnclicked(SetButtonEvent(ref gripPressed, false, 0f)); } //Touchpad Clicked if (device.GetPressDown(SteamVR_Controller.ButtonMask.Touchpad)) { OnTouchpadClicked(SetButtonEvent(ref touchpadPressed, true, 1f)); } else if (device.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad)) { OnTouchpadUnclicked(SetButtonEvent(ref touchpadPressed, false, 0f)); } //Touchpad Touched if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Touchpad)) { OnTouchpadTouched(SetButtonEvent(ref touchpadTouched, true, 1f)); } else if (device.GetTouchUp(SteamVR_Controller.ButtonMask.Touchpad)) { OnTouchpadUntouched(SetButtonEvent(ref touchpadTouched, false, 0f)); } } #endregion #region PRIVATE /// /// Compare two vectors if there are about equal. /// bool Vector2ShallowEquals(Vector2 vectorA, Vector2 vectorB) { return (vectorA.x.ToString("F" + axisFidelity) == vectorB.x.ToString("F" + axisFidelity) && vectorA.y.ToString("F" + axisFidelity) == vectorB.y.ToString("F" + axisFidelity)); } void SteamVRModelLoaded(SteamVR_RenderModel model, bool loaded) { //find if the controller is touch or vive by the type of its trackpad Valve.VR.ETrackedPropertyError pError = new Valve.VR.ETrackedPropertyError(); int axisprop = Valve.VR.OpenVR.System.GetInt32TrackedDeviceProperty((uint)trackedController.index, Valve.VR.ETrackedDeviceProperty.Prop_Axis0Type_Int32, ref pError); controllerType = axisprop == (int)Valve.VR.EVRControllerAxisType.k_eControllerAxis_Joystick ? Type.OCULUS_TOUCH : Type.VIVE; //call evenets if (ModelLoaded != null) ModelLoaded(this); } #endregion #region PUBLIC public void ToggleControllerVisible(bool on) { foreach (MeshRenderer renderer in this.GetComponentsInChildren()) { renderer.enabled = on; } foreach (SkinnedMeshRenderer renderer in this.GetComponentsInChildren()) { renderer.enabled = on; } controllerVisible = on; } /// /// Triggers the haptic pulse. /// /// Duration in frames. /// Strength of the pulse. 100 is barely noticable, 300-500 seems about right for most uses. public void TriggerHapticPulse(int duration, ushort strength) { hapticPulseCountdown = duration; hapticPulseStrength = (strength <= maxHapticVibration ? strength : maxHapticVibration); } #endregion #region SETTERS AND GETTERS /// /// Is this controller a Vive Controller, Oculush Touch, or other? /// public Type ControllerType { get { return controllerType; } } /// /// Returns the pointing direction, based on controller type. Oculus touch controllers point in a slighlty different way than Vive controllers. /// public Vector3 PointingDirection { get { return controllerType == Type.OCULUS_TOUCH ? transform.localToWorldMatrix.MultiplyVector(Quaternion.Euler(oculusTouchPoitingRotationOffset) * Vector3.forward) : transform.forward; } } /// /// Returns the pointing origin, based on controller type. Oculus touch controllers point in a slighlty different way than Vive controllers. /// public Vector3 PointingOrigin { get { return transform.TransformPoint(controllerType == Type.OCULUS_TOUCH ? oculusTouchPoitingOriginOffset : Vector3.zero); } } /// /// Are the render components of the Controller enabled? /// /// true if this instance is controller visible; otherwise, false. public bool IsControllerVisible() { return controllerVisible; } /// /// Has trigger been pressed down this frame? /// public bool IsTriggerDown { get { return triggerDown; } } bool triggerDown = false; /// /// Has trigger been released this frame? /// public bool IsTriggerUp { get { return triggerUp; } } bool triggerUp = false; /// /// Is trigger pressed during this frame? /// public bool IsTriggerPressed { get { return triggerAxis.x > 0.5f; } } bool triggerPressed = false; /// /// Has trigger axis (how hard trigger is pressed) changed this frame? /// public bool IsTriggerAxisChanged { get { return triggerAxisChanged; } } bool triggerAxisChanged = false; /// /// Has user's finger position changed on touchpad this grame? /// public bool IsTouchpadAxisChanged { get { return touchpadAxisChanged; } } bool touchpadAxisChanged = false; /// /// Is Application menu pressed right now? /// public bool IsApplicationMenuPressed { get { return applicationMenuPressed; } } bool applicationMenuPressed = false; /// /// Is touchpad pressed this frame? /// public bool IsTouchpadPressed { get { return touchpadPressed; } } bool touchpadPressed = false; /// /// Is user's finger resting on the touchpad? /// public bool IsTouchpadTouched { get { return touchpadTouched; } } bool touchpadTouched = false; /// /// Is user holding the grip button? /// public bool IsGripPressed { get { return gripPressed; } } bool gripPressed = false; /// /// FingerPosition on touchpad? /// public Vector2 TouchPadAxis { get { return touchpadAxis; } } #endregion ViveInputArgs SetButtonEvent(ref bool buttonBool, bool value, float buttonPressure) { buttonBool = value; ViveInputArgs e; e.controllerIndex = controllerIndex; e.buttonPressure = buttonPressure; e.touchpadAxis = device.GetAxis(); return e; } #endif } public struct ViveInputArgs { public uint controllerIndex; public float buttonPressure; public Vector2 touchpadAxis; } public delegate void ViveInputEvent(object sender, ViveInputArgs e); public delegate void ViveEvent(object sender); }