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.
 
 
 

169 lines
8.0 KiB

// Controller Haptics|Interactions|30030
namespace VRTK
{
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// A collection of static methods for calling haptic functions on a given controller.
/// </summary>
/// <remarks>
/// **Script Usage:**
/// > There is no requirement to add this script to a GameObject as all of the public methods are static and can be called directly e.g. `VRTK_ControllerHaptics.TriggerHapticPulse(ref, 1f)`.
/// </remarks>
public class VRTK_ControllerHaptics : MonoBehaviour
{
protected static VRTK_ControllerHaptics instance;
protected Dictionary<VRTK_ControllerReference, Coroutine> hapticLoopCoroutines = new Dictionary<VRTK_ControllerReference, Coroutine>();
/// <summary>
/// The TriggerHapticPulse/2 method calls a single haptic pulse call on the controller for a single tick.
/// </summary>
/// <param name="controllerReference">The reference to the controller to activate the haptic feedback on.</param>
/// <param name="strength">The intensity of the rumble of the controller motor. `0` to `1`.</param>
public static void TriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength)
{
SetupInstance();
if (instance != null)
{
instance.InternalTriggerHapticPulse(controllerReference, strength);
}
}
/// <summary>
/// The TriggerHapticPulse/4 method calls a haptic pulse for a specified amount of time rather than just a single tick. Each pulse can be separated by providing a `pulseInterval` to pause between each haptic pulse.
/// </summary>
/// <param name="controllerReference">The reference to the controller to activate the haptic feedback on.</param>
/// <param name="strength">The intensity of the rumble of the controller motor. `0` to `1`.</param>
/// <param name="duration">The length of time the rumble should continue for.</param>
/// <param name="pulseInterval">The interval to wait between each haptic pulse.</param>
public static void TriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength, float duration, float pulseInterval)
{
SetupInstance();
if (instance != null)
{
instance.InternalTriggerHapticPulse(controllerReference, strength, duration, pulseInterval);
}
}
/// <summary>
/// The TriggerHapticPulse/2 method calls a haptic pulse based on a given audio clip.
/// </summary>
/// <param name="controllerReference">The reference to the controller to activate the haptic feedback on.</param>
/// <param name="clip">The audio clip to use for the haptic pattern.</param>
public static void TriggerHapticPulse(VRTK_ControllerReference controllerReference, AudioClip clip)
{
SetupInstance();
if (instance != null)
{
instance.InternalTriggerHapticPulse(controllerReference, clip);
}
}
/// <summary>
/// The CancelHapticPulse method cancels the existing running haptic pulse on the given controller index.
/// </summary>
/// <param name="controllerReference">The reference to the controller to cancel the haptic feedback on.</param>
public static void CancelHapticPulse(VRTK_ControllerReference controllerReference)
{
SetupInstance();
if (instance != null)
{
instance.InternalCancelHapticPulse(controllerReference);
}
}
protected virtual void OnDisable()
{
StopAllCoroutines();
hapticLoopCoroutines.Clear();
}
protected static void SetupInstance()
{
if (instance == null && VRTK_SDKManager.ValidInstance())
{
instance = VRTK_SDKManager.instance.gameObject.AddComponent<VRTK_ControllerHaptics>();
}
}
protected virtual void InternalTriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength)
{
InternalCancelHapticPulse(controllerReference);
float hapticPulseStrength = Mathf.Clamp(strength, 0f, 1f);
VRTK_SDK_Bridge.HapticPulse(controllerReference, hapticPulseStrength);
}
protected virtual void InternalTriggerHapticPulse(VRTK_ControllerReference controllerReference, float strength, float duration, float pulseInterval)
{
InternalCancelHapticPulse(controllerReference);
float hapticPulseStrength = Mathf.Clamp(strength, 0f, 1f);
SDK_ControllerHapticModifiers hapticModifiers = VRTK_SDK_Bridge.GetHapticModifiers();
Coroutine hapticLoop = StartCoroutine(SimpleHapticPulseRoutine(controllerReference, duration * hapticModifiers.durationModifier, hapticPulseStrength, pulseInterval * hapticModifiers.intervalModifier));
VRTK_SharedMethods.AddDictionaryValue(hapticLoopCoroutines, controllerReference, hapticLoop);
}
protected virtual void InternalTriggerHapticPulse(VRTK_ControllerReference controllerReference, AudioClip clip)
{
InternalCancelHapticPulse(controllerReference);
if (!VRTK_SDK_Bridge.HapticPulse(controllerReference, clip))
{
//If the SDK Bridge doesn't support audio clips then defer to a local version
Coroutine hapticLoop = StartCoroutine(AudioClipHapticsRoutine(controllerReference, clip));
VRTK_SharedMethods.AddDictionaryValue(hapticLoopCoroutines, controllerReference, hapticLoop);
}
}
protected virtual void InternalCancelHapticPulse(VRTK_ControllerReference controllerReference)
{
Coroutine currentHapticLoopRoutine = VRTK_SharedMethods.GetDictionaryValue(hapticLoopCoroutines, controllerReference);
if (currentHapticLoopRoutine != null)
{
StopCoroutine(currentHapticLoopRoutine);
hapticLoopCoroutines.Remove(controllerReference);
}
}
protected virtual IEnumerator SimpleHapticPulseRoutine(VRTK_ControllerReference controllerReference, float duration, float hapticPulseStrength, float pulseInterval)
{
if (pulseInterval <= 0)
{
yield break;
}
while (duration > 0)
{
VRTK_SDK_Bridge.HapticPulse(controllerReference, hapticPulseStrength);
yield return new WaitForSeconds(pulseInterval);
duration -= pulseInterval;
}
}
protected virtual IEnumerator AudioClipHapticsRoutine(VRTK_ControllerReference controllerReference, AudioClip clip)
{
SDK_ControllerHapticModifiers hapticModifiers = VRTK_SDK_Bridge.GetHapticModifiers();
float hapticScalar = hapticModifiers.maxHapticVibration;
float[] audioData = new float[hapticModifiers.hapticsBufferSize];
int sampleOffset = -hapticModifiers.hapticsBufferSize;
float startTime = Time.time;
float length = clip.length / 1;
float endTime = startTime + length;
float sampleRate = clip.samples;
while (Time.time <= endTime)
{
float lerpVal = (Time.time - startTime) / length;
int sampleIndex = (int)(sampleRate * lerpVal);
if (sampleIndex >= sampleOffset + hapticModifiers.hapticsBufferSize)
{
clip.GetData(audioData, sampleIndex);
sampleOffset = sampleIndex;
}
float currentSample = Mathf.Abs(audioData[sampleIndex - sampleOffset]);
ushort hapticStrength = (ushort)(hapticScalar * currentSample);
VRTK_SDK_Bridge.HapticPulse(controllerReference, hapticStrength);
yield return null;
}
}
}
}