Assignment for RMIT Mixed Reality in 2020
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.
 
 
 

515 lines
20 KiB

namespace VRTK
{
using UnityEngine;
#if VRTK_DEFINE_SDK_WINDOWSMR && UNITY_2017_2_OR_NEWER
using System.Collections;
using UnityEngine.XR.WSA.Input;
using VRTK.WindowsMixedReality.Utilities;
#endif
#if VRTK_DEFINE_WINDOWSMR_CONTROLLER_VISUALIZATION
using VRTK.WindowsMixedReality;
#endif
public class WindowsMR_TrackedObject : MonoBehaviour
{
#if VRTK_DEFINE_SDK_WINDOWSMR && UNITY_2017_2_OR_NEWER
private struct ButtonState
{
//
// Summary:
// ///
// Normalized amount ([0, 1]) representing how much select is pressed.
// ///
public float SelectPressedAmount { get; set; }
//
// Summary:
// ///
// Depending on the InteractionSourceType of the interaction source, this returning
// true could represent a number of equivalent things: main button on a blicker,
// air-tap on a hand, and the trigger on a motion controller.
// ///
public bool SelectPressed { get; set; }
//
// Summary:
// ///
// Whether or not the menu button is pressed.
// ///
public bool MenuPressed { get; set; }
//
// Summary:
// ///
// Whether the controller is grasped.
// ///
public bool Grasped { get; set; }
//
// Summary:
// ///
// Whether or not the touchpad is touched.
// ///
public bool TouchpadTouched { get; set; }
//
// Summary:
// ///
// Whether or not the touchpad is pressed, as if a button.
// ///
public bool TouchpadPressed { get; set; }
//
// Summary:
// ///
// Normalized coordinates for the position of a touchpad interaction.
// ///
public Vector2 TouchpadPosition { get; set; }
//
// Summary:
// ///
// Normalized coordinates for the position of a thumbstick.
// ///
public Vector2 ThumbstickPosition { get; set; }
//
// Summary:
// ///
// Whether or not the thumbstick is pressed.
// ///
public bool ThumbstickPressed { get; set; }
}
[SerializeField]
[Tooltip("Defines the controllers hand.")]
private InteractionSourceHandedness handedness;
public Vector3 AngularVelocity { get { return angularVelocity; } }
public InteractionSourceHandedness Handedness { get { return handedness; } }
public uint Index { get { return index; } }
private uint index = uint.MaxValue;
private ButtonState currentButtonState;
private ButtonState prevButtonState;
private Vector3 angularVelocity;
private float hairTriggerDelta = 0.1f; // amount trigger must be pulled or released to change state
private float hairTriggerLimit;
private bool hairTriggerState;
private bool hairTriggerPrevState;
private bool isDetected;
public float GetPressAmount(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Select:
return currentButtonState.SelectPressedAmount;
}
return 0;
}
public bool GetPress(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Select:
return currentButtonState.SelectPressed;
case InteractionSourcePressType.Grasp:
return currentButtonState.Grasped;
case InteractionSourcePressType.Menu:
return currentButtonState.MenuPressed;
case InteractionSourcePressType.Touchpad:
return currentButtonState.TouchpadPressed;
case InteractionSourcePressType.Thumbstick:
return currentButtonState.ThumbstickPressed;
}
return false;
}
public bool GetPressDown(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Select:
return (prevButtonState.SelectPressed == false && currentButtonState.SelectPressed == true);
case InteractionSourcePressType.Grasp:
return (prevButtonState.Grasped == false && currentButtonState.Grasped == true);
case InteractionSourcePressType.Menu:
return (prevButtonState.MenuPressed == false && currentButtonState.MenuPressed == true);
case InteractionSourcePressType.Touchpad:
return (prevButtonState.TouchpadPressed == false && currentButtonState.TouchpadPressed == true);
case InteractionSourcePressType.Thumbstick:
return (prevButtonState.ThumbstickPressed == false && currentButtonState.ThumbstickPressed == true);
}
return false;
}
public bool GetPressUp(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Select:
return (prevButtonState.SelectPressed == true && currentButtonState.SelectPressed == false);
case InteractionSourcePressType.Grasp:
return (prevButtonState.Grasped == true && currentButtonState.Grasped == false);
case InteractionSourcePressType.Menu:
return (prevButtonState.MenuPressed == true && currentButtonState.MenuPressed == false);
case InteractionSourcePressType.Touchpad:
return (prevButtonState.TouchpadPressed == true && currentButtonState.TouchpadPressed == false);
case InteractionSourcePressType.Thumbstick:
return (prevButtonState.ThumbstickPressed == true && currentButtonState.ThumbstickPressed == false);
}
return false;
}
public bool GetTouch(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Touchpad:
return currentButtonState.TouchpadTouched;
}
return false;
}
public bool GetTouchDown(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Touchpad:
return (prevButtonState.TouchpadTouched == false && currentButtonState.TouchpadTouched == true);
}
return false;
}
public bool GetTouchUp(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Touchpad:
return (prevButtonState.TouchpadTouched == true && currentButtonState.TouchpadTouched == false);
}
return false;
}
public Vector2 GetAxis(InteractionSourcePressType button)
{
switch (button)
{
case InteractionSourcePressType.Select:
return new Vector2(currentButtonState.SelectPressedAmount, 0f);
case InteractionSourcePressType.Touchpad:
return currentButtonState.TouchpadPosition;
case InteractionSourcePressType.Thumbstick:
return currentButtonState.ThumbstickPosition;
}
return Vector2.zero;
}
public bool GetHairTrigger()
{
return hairTriggerState;
}
public bool GetHairTriggerDown()
{
return (hairTriggerState && !hairTriggerPrevState);
}
public bool GetHairTriggerUp()
{
return !hairTriggerState && hairTriggerPrevState;
}
public void StartHaptics(float intensity = 0.5f, float duration = 0.4f)
{
InteractionSourceState[] states = InteractionManager.GetCurrentReading();
foreach (InteractionSourceState state in states)
{
if (state.source.kind == InteractionSourceKind.Controller && state.source.handedness == handedness)
{
state.source.StartHaptics(intensity, duration);
}
}
}
protected virtual void OnEnable()
{
switch (handedness)
{
case InteractionSourceHandedness.Left:
index = 1;
break;
case InteractionSourceHandedness.Right:
index = 2;
break;
case InteractionSourceHandedness.Unknown:
Debug.LogError("Handedness of " + gameObject.name + " is not set.");
break;
}
InitController();
}
protected virtual void OnDisable()
{
InteractionManager.InteractionSourceDetected -= InteractionManager_InteractionSourceDetected;
InteractionManager.InteractionSourceLost -= InteractionManager_InteractionSourceLost;
InteractionManager.InteractionSourceUpdated -= InteractionManager_InteractionSourceUpdated;
InteractionManager.InteractionSourcePressed -= InteractionManager_InteractionSourcePressed;
InteractionManager.InteractionSourceReleased -= InteractionManager_InteractionSourceReleased;
}
protected virtual void Update()
{
if (isDetected)
{
InteractionSourceState[] states = InteractionManager.GetCurrentReading();
foreach (InteractionSourceState state in states)
{
if (state.source.kind == InteractionSourceKind.Controller && state.source.handedness == handedness)
{
// Necessary to update Select Button State in Update Loop since it causes issues with PressDown and PressUp
// Will be changed in a future iteration (probably VRTK 4)
UpdateSelectButton(state);
UpdateTouchpadTouch(state);
}
}
}
}
protected virtual void InitController()
{
InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
InteractionManager.InteractionSourceUpdated += InteractionManager_InteractionSourceUpdated;
InteractionManager.InteractionSourcePressed += InteractionManager_InteractionSourcePressed;
InteractionManager.InteractionSourceReleased += InteractionManager_InteractionSourceReleased;
#if VRTK_DEFINE_WINDOWSMR_CONTROLLER_VISUALIZATION
if (MotionControllerVisualizer.Instance != null)
{
MotionControllerVisualizer.Instance.OnControllerModelLoaded += AttachControllerModel;
}
#endif
}
protected virtual void SetupController(InteractionSource source)
{
index = source.id;
currentButtonState = new ButtonState();
prevButtonState = new ButtonState();
isDetected = true;
}
#if VRTK_DEFINE_WINDOWSMR_CONTROLLER_VISUALIZATION
protected virtual void AttachControllerModel(MotionControllerInfo controllerInfo)
{
if (controllerInfo.Handedness == Handedness)
{
Transform controllerTransform = controllerInfo.ControllerParent.transform;
controllerTransform.SetParent(transform);
controllerTransform.localPosition = Vector3.zero;
controllerTransform.localRotation = Quaternion.identity;
controllerTransform.localScale = Vector3.one;
}
}
#endif
protected virtual void InteractionManager_InteractionSourceDetected(InteractionSourceDetectedEventArgs args)
{
InteractionSourceState state = args.state;
InteractionSource source = state.source;
if (source.kind == InteractionSourceKind.Controller && source.handedness == handedness)
{
SetupController(source);
}
}
protected virtual void InteractionManager_InteractionSourceLost(InteractionSourceLostEventArgs args)
{
InteractionSourceState state = args.state;
InteractionSource source = state.source;
if (source.kind == InteractionSourceKind.Controller && source.handedness == handedness)
{
index = uint.MaxValue;
currentButtonState = new ButtonState();
isDetected = false;
}
}
protected virtual void InteractionManager_InteractionSourceUpdated(InteractionSourceUpdatedEventArgs args)
{
InteractionSourceState state = args.state;
InteractionSource source = state.source;
if (source.kind == InteractionSourceKind.Controller && source.handedness == handedness)
{
if (!isDetected)
{
SetupController(source);
}
UpdateAxis(state);
UpdatePose(state);
}
}
protected virtual void InteractionManager_InteractionSourcePressed(InteractionSourcePressedEventArgs args)
{
InteractionSourceState state = args.state;
InteractionSource source = state.source;
if (source.kind == InteractionSourceKind.Controller && source.handedness == handedness)
{
UpdateButtonState(args.pressType, state);
}
}
protected virtual void InteractionManager_InteractionSourceReleased(InteractionSourceReleasedEventArgs args)
{
InteractionSourceState state = args.state;
InteractionSource source = state.source;
if (source.kind == InteractionSourceKind.Controller && source.handedness == handedness)
{
UpdateButtonState(args.pressType, state);
}
}
protected virtual void UpdatePose(InteractionSourceState state)
{
UpdateAngularVelocity(state.sourcePose);
UpdateControllerPose(state.sourcePose);
}
// Workaround for Select Button
// Issue: Pressed and Released event only recognize Select once and Select is pressed when selectPressedAmount==1,
// so on press Select State is not always true and therefore not 'pressed'.
// Updating SelectPressed in UpdateEvent of WSA.XR causes issues because the event and Unity Update are not synched
// and therefore VRTK's polling of GetPressDown and GetPressUp might already been overwritten.
protected virtual void UpdateSelectButton(InteractionSourceState state)
{
prevButtonState.SelectPressed = currentButtonState.SelectPressed;
currentButtonState.SelectPressed = state.selectPressed;
}
protected virtual void UpdateTouchpadTouch(InteractionSourceState state)
{
if (state.source.supportsTouchpad)
{
prevButtonState.TouchpadTouched = currentButtonState.TouchpadTouched;
currentButtonState.TouchpadTouched = state.touchpadTouched;
}
}
protected virtual void UpdateButtonState(InteractionSourcePressType button, InteractionSourceState state)
{
switch (button)
{
case InteractionSourcePressType.Grasp:
prevButtonState.Grasped = currentButtonState.Grasped;
currentButtonState.Grasped = state.grasped;
break;
case InteractionSourcePressType.Menu:
prevButtonState.MenuPressed = currentButtonState.MenuPressed;
currentButtonState.MenuPressed = state.menuPressed;
break;
case InteractionSourcePressType.Touchpad:
prevButtonState.TouchpadPressed = currentButtonState.TouchpadPressed;
currentButtonState.TouchpadPressed = state.touchpadPressed;
break;
case InteractionSourcePressType.Thumbstick:
prevButtonState.ThumbstickPressed = currentButtonState.ThumbstickPressed;
currentButtonState.ThumbstickPressed = state.thumbstickPressed;
break;
}
StartCoroutine(UpdateButtonStateAfterNextFrame(button));
}
protected virtual IEnumerator UpdateButtonStateAfterNextFrame(InteractionSourcePressType button)
{
yield return new WaitForEndOfFrame();
switch (button)
{
case InteractionSourcePressType.Grasp:
prevButtonState.Grasped = currentButtonState.Grasped;
break;
case InteractionSourcePressType.Menu:
prevButtonState.MenuPressed = currentButtonState.MenuPressed;
break;
case InteractionSourcePressType.Touchpad:
prevButtonState.TouchpadPressed = currentButtonState.TouchpadPressed;
break;
case InteractionSourcePressType.Thumbstick:
prevButtonState.ThumbstickPressed = currentButtonState.ThumbstickPressed;
break;
}
}
protected virtual void UpdateControllerPose(InteractionSourcePose pose)
{
Quaternion newRotation;
if (pose.TryGetRotation(out newRotation, InteractionSourceNode.Grip))
{
transform.localRotation = newRotation;
}
Vector3 newPosition;
if (pose.TryGetPosition(out newPosition, InteractionSourceNode.Grip))
{
transform.localPosition = newPosition;
}
}
protected virtual void UpdateAxis(InteractionSourceState state)
{
InteractionSource source = state.source;
prevButtonState.SelectPressedAmount = currentButtonState.SelectPressedAmount;
currentButtonState.SelectPressedAmount = state.selectPressedAmount;
UpdateHairTrigger();
if (source.supportsTouchpad)
{
currentButtonState.TouchpadPosition = state.touchpadPosition;
}
if (source.supportsThumbstick)
{
currentButtonState.ThumbstickPosition = state.thumbstickPosition;
}
}
protected virtual void UpdateAngularVelocity(InteractionSourcePose pose)
{
Vector3 newAngularVelocity;
if (pose.TryGetAngularVelocity(out newAngularVelocity))
{
angularVelocity = newAngularVelocity;
}
}
protected virtual void UpdateHairTrigger()
{
hairTriggerPrevState = hairTriggerState;
float value = currentButtonState.SelectPressedAmount;
if (hairTriggerState)
{
if (value < hairTriggerLimit - hairTriggerDelta || value <= 0.0f)
hairTriggerState = false;
}
else
{
if (value > hairTriggerLimit + hairTriggerDelta || value >= 1.0f)
hairTriggerState = true;
}
hairTriggerLimit = hairTriggerState ? Mathf.Max(hairTriggerLimit, value) : Mathf.Min(hairTriggerLimit, value);
}
#endif
}
}