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);
}