From 8263edd59284aba390aca011d25b79efecef4c48 Mon Sep 17 00:00:00 2001 From: pryazha Date: Wed, 2 Jul 2025 08:46:23 -0700 Subject: init --- .../Scripts/HandsOneEuroFilterPostProcessor.cs | 141 +++++++++++ .../HandsOneEuroFilterPostProcessor.cs.meta | 11 + .../Scripts/HideObjectWhenInteractorBlocked.cs | 49 ++++ .../HideObjectWhenInteractorBlocked.cs.meta | 11 + .../LocalPositionOffsetAffordanceReceiver.cs | 73 ++++++ .../LocalPositionOffsetAffordanceReceiver.cs.meta | 11 + .../Scripts/MetaSystemGestureDetector.cs | 263 +++++++++++++++++++++ .../Scripts/MetaSystemGestureDetector.cs.meta | 11 + .../Scripts/OneEuroFilterVector3.cs | 126 ++++++++++ .../Scripts/OneEuroFilterVector3.cs.meta | 11 + .../Scripts/PinchPointFollow.cs | 185 +++++++++++++++ .../Scripts/PinchPointFollow.cs.meta | 11 + .../Scripts/PokeGestureDetector.cs | 200 ++++++++++++++++ .../Scripts/PokeGestureDetector.cs.meta | 11 + .../Scripts/ReleaseThresholdButtonReader.cs | 133 +++++++++++ .../Scripts/ReleaseThresholdButtonReader.cs.meta | 11 + .../Scripts/ValueDerivedButtonReader.cs | 121 ++++++++++ .../Scripts/ValueDerivedButtonReader.cs.meta | 11 + .../Scripts/Vector3ScaleAffordanceReceiver.cs | 33 +++ .../Scripts/Vector3ScaleAffordanceReceiver.cs.meta | 11 + 20 files changed, 1434 insertions(+) create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs.meta create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs create mode 100644 Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs.meta (limited to 'Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts') diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs new file mode 100644 index 0000000..1dd5278 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs @@ -0,0 +1,141 @@ +#if XR_HANDS_1_2_OR_NEWER +using System.Collections.Generic; +using UnityEngine.XR.Hands; +using UnityEngine.XR.Hands.Processing; +#endif + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// A post processor for XR hand tracking data, using the One Euro filter to smooth hand positions. + /// +#if XR_HANDS_1_2_OR_NEWER + public class HandsOneEuroFilterPostProcessor : MonoBehaviour, IXRHandProcessor +#else + public class HandsOneEuroFilterPostProcessor : MonoBehaviour +#endif + { + [SerializeField] + [Tooltip("Smoothing amount at low speeds.")] +#pragma warning disable CS0414 // Field assigned but its value is never used -- Keep to retain serialized value when XR Hands is not installed + float m_FilterMinCutoff = 0.1f; +#pragma warning restore CS0414 + + [SerializeField] + [Tooltip("Filter's responsiveness to speed changes.")] +#pragma warning disable CS0414 // Field assigned but its value is never used -- Keep to retain serialized value when XR Hands is not installed + float m_FilterBeta = 0.2f; +#pragma warning restore CS0414 + +#if XR_HANDS_1_2_OR_NEWER + /// + public int callbackOrder => 0; + + readonly OneEuroFilterVector3 m_LeftHandFilter = new OneEuroFilterVector3(Vector3.zero); + readonly OneEuroFilterVector3 m_RightHandFilter = new OneEuroFilterVector3(Vector3.zero); + + bool m_WasLeftHandTrackedLastFrame; + bool m_WasRightHandTrackedLastFrame; + + XRHandSubsystem m_Subsystem; + static readonly List s_SubsystemsReuse = new List(); +#endif + +#if XR_HANDS_1_2_OR_NEWER + /// + /// See . + /// + void OnDisable() + { + if (m_Subsystem != null) + { + m_Subsystem.UnregisterProcessor(this); + m_Subsystem = null; + } + } + + /// + /// See . + /// + void Update() + { + if (m_Subsystem != null && m_Subsystem.running) + return; + + SubsystemManager.GetSubsystems(s_SubsystemsReuse); + var foundRunningHandSubsystem = false; + for (var i = 0; i < s_SubsystemsReuse.Count; ++i) + { + var handSubsystem = s_SubsystemsReuse[i]; + if (handSubsystem.running) + { + m_Subsystem?.UnregisterProcessor(this); + m_Subsystem = handSubsystem; + foundRunningHandSubsystem = true; + break; + } + } + + if (!foundRunningHandSubsystem) + return; + + m_WasLeftHandTrackedLastFrame = false; + m_WasRightHandTrackedLastFrame = false; + m_Subsystem.RegisterProcessor(this); + } + + /// + public void ProcessJoints(XRHandSubsystem subsystem, XRHandSubsystem.UpdateSuccessFlags successFlags, XRHandSubsystem.UpdateType updateType) + { + var leftHand = subsystem.leftHand; + if (leftHand.isTracked) + { + var leftHandPose = leftHand.rootPose; + if (!m_WasLeftHandTrackedLastFrame) + { + m_LeftHandFilter.Initialize(leftHandPose.position); + } + else + { + var newLeftPosition = m_LeftHandFilter.Filter(leftHandPose.position, Time.deltaTime, m_FilterMinCutoff, m_FilterBeta); + var newLeftPose = new Pose(newLeftPosition, leftHandPose.rotation); + + leftHand.SetRootPose(newLeftPose); + subsystem.SetCorrespondingHand(leftHand); + } + } + + m_WasLeftHandTrackedLastFrame = leftHand.isTracked; + + var rightHand = subsystem.rightHand; + if (rightHand.isTracked) + { + var rightHandPose = rightHand.rootPose; + if (!m_WasRightHandTrackedLastFrame) + { + m_RightHandFilter.Initialize(rightHandPose.position); + } + else + { + var newRightPosition = m_RightHandFilter.Filter(rightHandPose.position, Time.deltaTime, m_FilterMinCutoff, m_FilterBeta); + var newRightPose = new Pose(newRightPosition, rightHandPose.rotation); + + rightHand.SetRootPose(newRightPose); + subsystem.SetCorrespondingHand(rightHand); + } + } + + m_WasRightHandTrackedLastFrame = rightHand.isTracked; + } +#else + /// + /// See . + /// + void Awake() + { + Debug.LogWarning("HandsOneEuroFilterPostProcessor requires XR Hands (com.unity.xr.hands) 1.2.0 or newer. Disabling component.", this); + enabled = false; + } +#endif + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs.meta new file mode 100644 index 0000000..73b2b27 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HandsOneEuroFilterPostProcessor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bc6980b6cb3b4f12b6b75074e4ef59f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs new file mode 100644 index 0000000..912894a --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs @@ -0,0 +1,49 @@ +using UnityEngine.XR.Interaction.Toolkit.Interactors; +using UnityEngine.XR.Interaction.Toolkit.Interactors.Visuals; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Hides the specified GameObject when the associated interactor is blocked by an interaction within its group. + /// + public class HideObjectWhenInteractorBlocked : MonoBehaviour + { + [SerializeField] + [Tooltip("The interactor that this component monitors for blockages.")] + XRBaseInteractor m_Interactor; + + [SerializeField] + [Tooltip("The GameObject to hide when the interactor is blocked.")] + GameObject m_ObjectToHide; + + ICurveInteractionDataProvider m_CurveInteractionDataProvider; + bool m_HasCurveDataProvider; + + /// + /// See . + /// + void OnEnable() + { + if (m_Interactor == null || m_ObjectToHide == null) + enabled = false; + + m_HasCurveDataProvider = false; + if (m_Interactor is ICurveInteractionDataProvider provider) + { + m_CurveInteractionDataProvider = provider; + m_HasCurveDataProvider = true; + } + } + + /// + /// See . + /// + void Update() + { + if (m_HasCurveDataProvider) + m_ObjectToHide.SetActive(m_CurveInteractionDataProvider.isActive); + else + m_ObjectToHide.SetActive(m_Interactor.isActiveAndEnabled && !m_Interactor.IsBlockedByInteractionWithinGroup()); + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs.meta new file mode 100644 index 0000000..7413c99 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/HideObjectWhenInteractorBlocked.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e989a75b2954bdab01ca618a30d5de6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs new file mode 100644 index 0000000..26806fb --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs @@ -0,0 +1,73 @@ +using System; +using Unity.Mathematics; +using UnityEngine.XR.Interaction.Toolkit.AffordanceSystem.Receiver.Primitives; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Affordance receiver applying a Vector3 (Float3) affordance theme to a Transform local position. + /// Broadcasts new affordance value with Unity Event. + /// + [AddComponentMenu("Affordance System/Receiver/Transformation/Local Position Offset Affordance Receiver", 12)] + [Obsolete("The Affordance System namespace and all associated classes have been deprecated. The existing affordance system will be moved, replaced and updated with a new interaction feedback system in a future version of XRI.")] + public class LocalPositionOffsetAffordanceReceiver : Vector3AffordanceReceiver + { + [SerializeField] + [Tooltip("Transform on which to apply a local translation value.")] + Transform m_TransformToTranslate; + + /// + /// Transform on which to apply a local translation value. + /// + public Transform transformToTranslate + { + get => m_TransformToTranslate; + set + { + m_TransformToTranslate = value; + m_HasTransformToTranslate = m_TransformToTranslate != null; + } + } + + bool m_HasTransformToTranslate; + float3 m_InitialOffset = float3.zero; + + /// + protected override void OnEnable() + { + base.OnEnable(); + m_HasTransformToTranslate = m_TransformToTranslate != null; + } + + /// + protected override float3 GetCurrentValueForCapture() + { + if (m_HasTransformToTranslate) + { + m_InitialOffset = m_TransformToTranslate.localPosition; + } + + return float3.zero; + } + + /// + protected override void OnAffordanceValueUpdated(float3 newValue) + { + if (m_HasTransformToTranslate) + { + m_TransformToTranslate.localPosition = m_InitialOffset + newValue; + } + + base.OnAffordanceValueUpdated(newValue); + } + + /// + /// See . + /// + void OnValidate() + { + if (m_TransformToTranslate == null) + m_TransformToTranslate = transform; + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs.meta new file mode 100644 index 0000000..03d9955 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/LocalPositionOffsetAffordanceReceiver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 865d01d2834c9cb4caa8f2c901104c2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs new file mode 100644 index 0000000..a4c142c --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs @@ -0,0 +1,263 @@ +using System; +using Unity.XR.CoreUtils.Bindings.Variables; +using UnityEngine.Events; +using UnityEngine.InputSystem; +using UnityEngine.XR.Interaction.Toolkit.Inputs; +#if XR_HANDS_1_1_OR_NEWER +using UnityEngine.XR.Hands; +#endif + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Behavior that provides events for when the system gesture starts and ends and when the + /// menu palm pinch gesture occurs while hand tracking is in use. + /// + /// + /// See Meta Hand Tracking Aim. + /// + /// + public class MetaSystemGestureDetector : MonoBehaviour + { + /// + /// The state of the system gesture. + /// + /// + public enum SystemGestureState + { + /// + /// The system gesture has fully ended. + /// + Ended, + + /// + /// The system gesture has started or is ongoing. Typically, this means the user is looking at + /// their palm at eye level or has not yet released the palm pinch gesture or turned their hand around. + /// + Started, + } + + [SerializeField] + InputActionProperty m_AimFlagsAction = new InputActionProperty(new InputAction(expectedControlType: "Integer")); + + /// + /// The Input System action to read the Aim Flags. + /// + /// + /// Typically a Value action type with an Integer control type with a binding to either: + /// + /// + /// <MetaAimHand>{LeftHand}/aimFlags + /// + /// + /// <MetaAimHand>{RightHand}/aimFlags + /// + /// + /// + public InputActionProperty aimFlagsAction + { + get => m_AimFlagsAction; + set + { + if (Application.isPlaying) + UnbindAimFlags(); + + m_AimFlagsAction = value; + + if (Application.isPlaying && isActiveAndEnabled) + BindAimFlags(); + } + } + + [SerializeField] + UnityEvent m_SystemGestureStarted; + + /// + /// Calls the methods in its invocation list when the system gesture starts, which typically occurs when + /// the user looks at their palm at eye level. + /// + /// + /// + public UnityEvent systemGestureStarted + { + get => m_SystemGestureStarted; + set => m_SystemGestureStarted = value; + } + + [SerializeField] + UnityEvent m_SystemGestureEnded; + + /// + /// Calls the methods in its invocation list when the system gesture ends. + /// + /// + /// This behavior postpones ending the system gesture until the user has turned their hand around. + /// In other words, it isn't purely based on the + /// being cleared from the aim flags in order to better replicate the native visual feedback in the Meta Home menu. + /// + /// + /// + public UnityEvent systemGestureEnded + { + get => m_SystemGestureEnded; + set => m_SystemGestureEnded = value; + } + + [SerializeField] + UnityEvent m_MenuPressed; + + /// + /// Calls the methods in its invocation list when the menu button is triggered by a palm pinch gesture. + /// + /// + /// This is triggered by the non-dominant hand, which is the one with the menu icon (☰). + /// The universal menu (Oculus icon) on the dominant hand does not trigger this event. + /// + /// + public UnityEvent menuPressed + { + get => m_MenuPressed; + set => m_MenuPressed = value; + } + + /// + /// The state of the system gesture. + /// + /// + /// + /// + public IReadOnlyBindableVariable systemGestureState => m_SystemGestureState; + + readonly BindableEnum m_SystemGestureState = new BindableEnum(checkEquality: false); + +#if XR_HANDS_1_1_OR_NEWER && (ENABLE_VR || UNITY_GAMECORE) + [NonSerialized] // NonSerialized is required to avoid an "Unsupported enum base type" error about the Flags enum being ulong + MetaAimFlags m_AimFlags; +#endif + + bool m_AimFlagsBound; + + /// + /// See . + /// + protected void OnEnable() + { + BindAimFlags(); + +#if XR_HANDS_1_1_OR_NEWER +#if ENABLE_VR || UNITY_GAMECORE + var action = m_AimFlagsAction.action; + if (action != null) + // Force invoking the events upon initialization to simplify making sure the callback's desired results are synced + UpdateAimFlags((MetaAimFlags)action.ReadValue(), true); +#endif +#else + Debug.LogWarning("Script requires XR Hands (com.unity.xr.hands) package to monitor Meta Aim Flags. Install using Window > Package Manager or click Fix on the related issue in Edit > Project Settings > XR Plug-in Management > Project Validation.", this); + SetGestureState(SystemGestureState.Ended, true); +#endif + } + + /// + /// See . + /// + protected void OnDisable() + { + UnbindAimFlags(); + } + + void BindAimFlags() + { + if (m_AimFlagsBound) + return; + + var action = m_AimFlagsAction.action; + if (action == null) + return; + + action.performed += OnAimFlagsActionPerformedOrCanceled; + action.canceled += OnAimFlagsActionPerformedOrCanceled; + m_AimFlagsBound = true; + + m_AimFlagsAction.EnableDirectAction(); + } + + void UnbindAimFlags() + { + if (!m_AimFlagsBound) + return; + + var action = m_AimFlagsAction.action; + if (action == null) + return; + + m_AimFlagsAction.DisableDirectAction(); + + action.performed -= OnAimFlagsActionPerformedOrCanceled; + action.canceled -= OnAimFlagsActionPerformedOrCanceled; + m_AimFlagsBound = false; + } + + void SetGestureState(SystemGestureState state, bool forceInvoke) + { + if (!forceInvoke && m_SystemGestureState.Value == state) + return; + + m_SystemGestureState.Value = state; + switch (state) + { + case SystemGestureState.Ended: + m_SystemGestureEnded?.Invoke(); + break; + case SystemGestureState.Started: + m_SystemGestureStarted?.Invoke(); + break; + } + } + +#if XR_HANDS_1_1_OR_NEWER && (ENABLE_VR || UNITY_GAMECORE) + void UpdateAimFlags(MetaAimFlags value, bool forceInvoke = false) + { + var hadMenuPressed = (m_AimFlags & MetaAimFlags.MenuPressed) != 0; + m_AimFlags = value; + var hasSystemGesture = (m_AimFlags & MetaAimFlags.SystemGesture) != 0; + var hasMenuPressed = (m_AimFlags & MetaAimFlags.MenuPressed) != 0; + var hasValid = (m_AimFlags & MetaAimFlags.Valid) != 0; + var hasIndexPinching = (m_AimFlags & MetaAimFlags.IndexPinching) != 0; + + if (!hadMenuPressed && hasMenuPressed) + { + m_MenuPressed?.Invoke(); + } + + if (hasSystemGesture || hasMenuPressed) + { + SetGestureState(SystemGestureState.Started, forceInvoke); + return; + } + + if (hasValid) + { + SetGestureState(SystemGestureState.Ended, forceInvoke); + return; + } + + // We want to keep the system gesture going when the user is still index pinching + // even though the SystemGesture flag is no longer set. + if (hasIndexPinching && m_SystemGestureState.Value != SystemGestureState.Ended) + { + SetGestureState(SystemGestureState.Started, forceInvoke); + return; + } + + SetGestureState(SystemGestureState.Ended, forceInvoke); + } +#endif + + void OnAimFlagsActionPerformedOrCanceled(InputAction.CallbackContext context) + { +#if XR_HANDS_1_1_OR_NEWER && (ENABLE_VR || UNITY_GAMECORE) + UpdateAimFlags((MetaAimFlags)context.ReadValue()); +#endif + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs.meta new file mode 100644 index 0000000..6a3e37b --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/MetaSystemGestureDetector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a83bc4aa48d0da648b49d0fd56690b25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs new file mode 100644 index 0000000..2e0643c --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs @@ -0,0 +1,126 @@ +using UnityEngine.XR.Interaction.Toolkit.Utilities; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Provides a means to smooth jittery signals. + /// This filter is particularly effective for small and rapid movements, + /// making it useful for applications like motion tracking or gesture recognition. + /// + /// + /// The filtering process relies on two main parameters: minCutoff and beta. + /// + /// + /// minCutoff + /// primarily influences the smoothing at low speeds. + /// + /// + /// beta + /// determines the filter's responsiveness to speed changes. + /// + /// + /// + public class OneEuroFilterVector3 + { + Vector3 m_LastRawValue; + Vector3 m_LastFilteredValue; + readonly float m_MinCutoff; + readonly float m_Beta; + + /// + /// Initializes a new instance of the with specified cutoff and beta values. + /// + /// The initial raw value for the filter. + /// The minimum cutoff value for the filter. Default is 0.1f. + /// The beta value for the filter. Default is 0.02f. + /// + /// Filter parameters: + /// + /// + /// + /// + /// Controls the amount of smoothing at low speeds. A smaller value will introduce + /// more smoothing and potential lag, helping to reduce low-frequency jitter. A larger value + /// may feel more responsive but can let through more jitter. It's advised to start with a + /// value around 0.1 for masking jitter in movements of about 1 cm. + /// + /// + /// + /// + /// + /// Determines the filter's adjustment to speed changes. A smaller value provides consistent + /// smoothing, while a larger one introduces more aggressive adjustments for speed changes, offering + /// responsive filtering at high speeds. A starting value of 0.02 is recommended, but fine-tuning + /// might be necessary based on specific use cases. + /// + /// + /// + /// + /// + public OneEuroFilterVector3(Vector3 initialRawValue, float minCutoff = 0.1f, float beta = 0.02f) + { + m_LastRawValue = initialRawValue; + m_LastFilteredValue = initialRawValue; + m_MinCutoff = minCutoff; + m_Beta = beta; + } + + /// + /// Resets the initial raw value. Useful to recover from tracking loss. + /// + /// Raw value to reset filtering basis to. + public void Initialize(Vector3 initialRawValue) + { + m_LastRawValue = initialRawValue; + m_LastFilteredValue = initialRawValue; + } + + /// + /// Filters the given rawValue using the internal minCutoff and beta parameters. + /// + /// The raw value to be filtered. + /// The time since the last filter update. + /// The filtered value. + public Vector3 Filter(Vector3 rawValue, float deltaTime) + { + return Filter(rawValue, deltaTime, m_MinCutoff, m_Beta); + } + + /// + /// Filters the given rawValue using provided minCutoff and beta parameters. + /// This method computes the speed of change in the signal and dynamically adjusts the amount of smoothing + /// based on the speed and the provided minCutoff and beta values. + /// + /// The raw value to be filtered. + /// The time since the last filter update. + /// The minimum cutoff value for the filter. Influences the amount of smoothing at low speeds. + /// Determines the filter's adjustment to speed changes, influencing its responsiveness. + /// The filtered value. + public Vector3 Filter(Vector3 rawValue, float deltaTime, float minCutoff, float beta) + { + // Calculate speed as a Vector3 + Vector3 speed = (rawValue - m_LastRawValue) / deltaTime; + + // Compute cutoffs for x, y, and z + Vector3 cutoffs = new Vector3(minCutoff, minCutoff, minCutoff); + Vector3 betaValues = new Vector3(beta, beta, beta); + + // Incorporate speed into the cutoffs + Vector3 combinedCutoffs = cutoffs + Vector3.Scale(betaValues, speed); + + // Compute alpha for x, y, and z + BurstMathUtility.FastSafeDivide(Vector3.one, Vector3.one + combinedCutoffs, out Vector3 alpha); + + Vector3 rawFiltered = Vector3.Scale(alpha, rawValue); + Vector3 lastFiltered = Vector3.Scale(Vector3.one - alpha, m_LastFilteredValue); + + // Calculate the final filtered value + Vector3 filteredValue = rawFiltered + lastFiltered; + + m_LastRawValue = rawValue; + m_LastFilteredValue = filteredValue; + + return filteredValue; + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs.meta new file mode 100644 index 0000000..c5cfc42 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/OneEuroFilterVector3.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8cf24614c456bc04c9adb820d19e35c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs new file mode 100644 index 0000000..0e4e4e5 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs @@ -0,0 +1,185 @@ +#if XR_HANDS_1_2_OR_NEWER +using Unity.XR.CoreUtils.Bindings; +using UnityEngine.XR.Hands; +using UnityEngine.XR.Interaction.Toolkit.Utilities.Tweenables.Primitives; +#endif +using UnityEngine.XR.Interaction.Toolkit.Interactors; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// A class that follows the pinch point between the thumb and index finger using XR Hand Tracking. + /// It updates its position to the midpoint between the thumb and index tip while optionally adjusting its rotation + /// to look at a specified target. The rotation towards the target can also be smoothly interpolated over time. + /// + public class PinchPointFollow : MonoBehaviour + { + [Header("Events")] + [SerializeField] + [Tooltip("The XR Hand Tracking Events component that will be used to subscribe to hand tracking events.")] +#if XR_HANDS_1_2_OR_NEWER + XRHandTrackingEvents m_XRHandTrackingEvents; +#else + Object m_XRHandTrackingEvents; +#endif + + [Header("Interactor reference (Pick one)")] + [SerializeField] + [Tooltip("The transform will use the XRRayInteractor endpoint position to calculate the transform rotation.")] + XRRayInteractor m_RayInteractor; + + [SerializeField] + [Tooltip("The transform will use the NearFarInteractor endpoint position to calculate the transform rotation.")] + NearFarInteractor m_NearFarInteractor; + + [Header("Rotation Config")] + [SerializeField] + [Tooltip("The transform to match the rotation of.")] + Transform m_TargetRotation; + + [SerializeField] + [Tooltip("How fast to match rotation (0 means no rotation smoothing.)")] + [Range(0f, 32f)] +#pragma warning disable CS0414 // Field assigned but its value is never used -- Keep to retain serialized value when XR Hands is not installed + float m_RotationSmoothingSpeed = 12f; +#pragma warning restore CS0414 + +#if XR_HANDS_1_2_OR_NEWER + bool m_HasTargetRotationTransform; + IXRRayProvider m_RayProvider; + bool m_HasRayProvider; + OneEuroFilterVector3 m_OneEuroFilterVector3; + +#pragma warning disable CS0618 // Type or member is obsolete + readonly QuaternionTweenableVariable m_QuaternionTweenableVariable = new QuaternionTweenableVariable(); +#pragma warning restore CS0618 // Type or member is obsolete + readonly BindingsGroup m_BindingsGroup = new BindingsGroup(); +#endif + + /// + /// See . + /// + void OnEnable() + { +#if XR_HANDS_1_2_OR_NEWER + if (m_XRHandTrackingEvents != null) + m_XRHandTrackingEvents.jointsUpdated.AddListener(OnJointsUpdated); + + m_OneEuroFilterVector3 = new OneEuroFilterVector3(transform.localPosition); + if (m_RayInteractor != null) + { + m_RayProvider = m_RayInteractor; + m_HasRayProvider = true; + } + if (m_NearFarInteractor != null) + { + m_RayProvider = m_NearFarInteractor; + m_HasRayProvider = true; + } + m_HasTargetRotationTransform = m_TargetRotation != null; + m_BindingsGroup.AddBinding(m_QuaternionTweenableVariable.Subscribe(newValue => transform.rotation = newValue)); +#else + Debug.LogWarning("PinchPointFollow requires XR Hands (com.unity.xr.hands) 1.2.0 or newer. Disabling component.", this); + enabled = false; +#endif + } + + /// + /// See . + /// + void OnDisable() + { +#if XR_HANDS_1_2_OR_NEWER + m_BindingsGroup.Clear(); + if (m_XRHandTrackingEvents != null) + m_XRHandTrackingEvents.jointsUpdated.RemoveListener(OnJointsUpdated); +#endif + } + +#if XR_HANDS_1_2_OR_NEWER + static bool TryGetPinchPosition(XRHandJointsUpdatedEventArgs args, out Vector3 position) + { +#if XR_HANDS_1_5_OR_NEWER + if (args.subsystem != null) + { + var commonHandGestures = args.hand.handedness == Handedness.Left + ? args.subsystem.leftHandCommonGestures + : args.hand.handedness == Handedness.Right + ? args.subsystem.rightHandCommonGestures + : null; + if (commonHandGestures != null && commonHandGestures.TryGetPinchPose(out var pinchPose)) + { + // Protect against platforms returning bad data like (NaN, NaN, NaN) + if (!float.IsNaN(pinchPose.position.x) && + !float.IsNaN(pinchPose.position.y) && + !float.IsNaN(pinchPose.position.z)) + { + position = pinchPose.position; + return true; + } + } + } +#endif + + var thumbTip = args.hand.GetJoint(XRHandJointID.ThumbTip); + if (!thumbTip.TryGetPose(out var thumbTipPose)) + { + position = Vector3.zero; + return false; + } + + var indexTip = args.hand.GetJoint(XRHandJointID.IndexTip); + if (!indexTip.TryGetPose(out var indexTipPose)) + { + position = Vector3.zero; + return false; + } + + position = Vector3.Lerp(thumbTipPose.position, indexTipPose.position, 0.5f); + return true; + } + + void OnJointsUpdated(XRHandJointsUpdatedEventArgs args) + { + if (!TryGetPinchPosition(args, out var targetPos)) + return; + + var filteredTargetPos = m_OneEuroFilterVector3.Filter(targetPos, Time.deltaTime); + + // Hand pose data is in local space relative to the XR Origin. + transform.localPosition = filteredTargetPos; + + if (m_HasTargetRotationTransform && m_HasRayProvider) + { + // Given that the ray endpoint is in world space, we need to use the world space transform of this point to determine the target rotation. + // This allows us to keep orientation consistent when moving the XR Origin for locomotion. + var targetDir = (m_RayProvider.rayEndPoint - transform.position).normalized; + if (targetDir != Vector3.zero) + { + // Use the parent Transform's up vector if available, otherwise use the world up vector. + // The assumption is the parent Transform matches the XR Origin rotation. + // This allows the XR Origin to teleport to angled surfaces or upside down surfaces + // and the visual will still be correct relative to the application's ground. + var upwards = Vector3.up; + var parentTransform = transform.parent; + if (!(parentTransform is null)) + upwards = parentTransform.up; + + var targetRot = Quaternion.LookRotation(targetDir, upwards); + + // If there aren't any major swings in rotation, follow the target rotation. + if (Vector3.Dot(m_TargetRotation.forward, targetDir) > 0.5f) + m_QuaternionTweenableVariable.target = targetRot; + } + else + { + m_QuaternionTweenableVariable.target = m_TargetRotation.rotation; + } + + var tweenTarget = m_RotationSmoothingSpeed > 0f ? m_RotationSmoothingSpeed * Time.deltaTime : 1f; + m_QuaternionTweenableVariable.HandleTween(tweenTarget); + } + } +#endif + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs.meta new file mode 100644 index 0000000..3e39de3 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PinchPointFollow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8693657abb5062a40a80ba3cb86ef181 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs new file mode 100644 index 0000000..85bfa1b --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs @@ -0,0 +1,200 @@ +using System.Collections.Generic; +using UnityEngine.Events; +#if XR_HANDS_1_1_OR_NEWER +using UnityEngine.XR.Hands; +#endif + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Behavior that provides events for when an starts and ends a poke gesture. The gesture is + /// detected if the index finger is extended and the middle, ring, and little fingers are curled in. + /// + public class PokeGestureDetector : MonoBehaviour + { + [SerializeField] + [Tooltip("Which hand to check for the poke gesture.")] +#if XR_HANDS_1_1_OR_NEWER + Handedness m_Handedness; +#else + int m_Handedness; +#endif + + [SerializeField] + [Tooltip("Called when the hand has started a poke gesture.")] + UnityEvent m_PokeGestureStarted; + + [SerializeField] + [Tooltip("Called when the hand has ended a poke gesture.")] + UnityEvent m_PokeGestureEnded; + +#if XR_HANDS_1_1_OR_NEWER + XRHandSubsystem m_Subsystem; + bool m_IsPoking; + + static readonly List s_Subsystems = new List(); +#endif + + /// + /// See . + /// + protected void OnEnable() + { +#if XR_HANDS_1_1_OR_NEWER + SubsystemManager.GetSubsystems(s_Subsystems); + if (s_Subsystems.Count == 0) + return; + + m_Subsystem = s_Subsystems[0]; + m_Subsystem.updatedHands += OnUpdatedHands; +#else + Debug.LogError("Script requires XR Hands (com.unity.xr.hands) package. Install using Window > Package Manager or click Fix on the related issue in Edit > Project Settings > XR Plug-in Management > Project Validation.", this); +#endif + } + + /// + /// See . + /// + protected void OnDisable() + { +#if XR_HANDS_1_1_OR_NEWER + if (m_Subsystem == null) + return; + + m_Subsystem.updatedHands -= OnUpdatedHands; + m_Subsystem = null; +#endif + } + +#if XR_HANDS_1_1_OR_NEWER + void OnUpdatedHands(XRHandSubsystem subsystem, XRHandSubsystem.UpdateSuccessFlags updateSuccessFlags, XRHandSubsystem.UpdateType updateType) + { + var wasPoking = m_IsPoking; + switch (m_Handedness) + { + case Handedness.Left: + if (!HasUpdateSuccessFlag(updateSuccessFlags, XRHandSubsystem.UpdateSuccessFlags.LeftHandJoints)) + return; + + var leftHand = subsystem.leftHand; + m_IsPoking = IsIndexExtended(leftHand) && IsMiddleGrabbing(leftHand) && IsRingGrabbing(leftHand) && + IsLittleGrabbing(leftHand); + break; + case Handedness.Right: + if (!HasUpdateSuccessFlag(updateSuccessFlags, XRHandSubsystem.UpdateSuccessFlags.RightHandJoints)) + return; + + var rightHand = subsystem.rightHand; + m_IsPoking = IsIndexExtended(rightHand) && IsMiddleGrabbing(rightHand) && IsRingGrabbing(rightHand) && + IsLittleGrabbing(rightHand); + break; + } + + if (m_IsPoking && !wasPoking) + StartPokeGesture(); + else if (!m_IsPoking && wasPoking) + EndPokeGesture(); + } + + /// + /// Determines whether one or more bit fields are set in the flags. + /// Non-boxing version of HasFlag for . + /// + /// The flags enum instance. + /// The flag to check if set. + /// Returns if the bit field or bit fields are set, otherwise returns . + static bool HasUpdateSuccessFlag(XRHandSubsystem.UpdateSuccessFlags successFlags, XRHandSubsystem.UpdateSuccessFlags successFlag) + { + return (successFlags & successFlag) == successFlag; + } + + /// + /// Returns true if the given hand's index finger tip is farther from the wrist than the index intermediate joint. + /// + /// Hand to check for the required pose. + /// True if the given hand's index finger tip is farther from the wrist than the index intermediate joint, false otherwise. + static bool IsIndexExtended(XRHand hand) + { + if (!(hand.GetJoint(XRHandJointID.Wrist).TryGetPose(out var wristPose) && + hand.GetJoint(XRHandJointID.IndexTip).TryGetPose(out var tipPose) && + hand.GetJoint(XRHandJointID.IndexIntermediate).TryGetPose(out var intermediatePose))) + { + return false; + } + + var wristToTip = tipPose.position - wristPose.position; + var wristToIntermediate = intermediatePose.position - wristPose.position; + return wristToTip.sqrMagnitude > wristToIntermediate.sqrMagnitude; + } + + /// + /// Returns true if the given hand's middle finger tip is closer to the wrist than the middle proximal joint. + /// + /// Hand to check for the required pose. + /// True if the given hand's middle finger tip is closer to the wrist than the middle proximal joint, false otherwise. + static bool IsMiddleGrabbing(XRHand hand) + { + if (!(hand.GetJoint(XRHandJointID.Wrist).TryGetPose(out var wristPose) && + hand.GetJoint(XRHandJointID.MiddleTip).TryGetPose(out var tipPose) && + hand.GetJoint(XRHandJointID.MiddleProximal).TryGetPose(out var proximalPose))) + { + return false; + } + + var wristToTip = tipPose.position - wristPose.position; + var wristToProximal = proximalPose.position - wristPose.position; + return wristToProximal.sqrMagnitude >= wristToTip.sqrMagnitude; + } + + /// + /// Returns true if the given hand's ring finger tip is closer to the wrist than the ring proximal joint. + /// + /// Hand to check for the required pose. + /// True if the given hand's ring finger tip is closer to the wrist than the ring proximal joint, false otherwise. + static bool IsRingGrabbing(XRHand hand) + { + if (!(hand.GetJoint(XRHandJointID.Wrist).TryGetPose(out var wristPose) && + hand.GetJoint(XRHandJointID.RingTip).TryGetPose(out var tipPose) && + hand.GetJoint(XRHandJointID.RingProximal).TryGetPose(out var proximalPose))) + { + return false; + } + + var wristToTip = tipPose.position - wristPose.position; + var wristToProximal = proximalPose.position - wristPose.position; + return wristToProximal.sqrMagnitude >= wristToTip.sqrMagnitude; + } + + /// + /// Returns true if the given hand's little finger tip is closer to the wrist than the little proximal joint. + /// + /// Hand to check for the required pose. + /// True if the given hand's little finger tip is closer to the wrist than the little proximal joint, false otherwise. + static bool IsLittleGrabbing(XRHand hand) + { + if (!(hand.GetJoint(XRHandJointID.Wrist).TryGetPose(out var wristPose) && + hand.GetJoint(XRHandJointID.LittleTip).TryGetPose(out var tipPose) && + hand.GetJoint(XRHandJointID.LittleProximal).TryGetPose(out var proximalPose))) + { + return false; + } + + var wristToTip = tipPose.position - wristPose.position; + var wristToProximal = proximalPose.position - wristPose.position; + return wristToProximal.sqrMagnitude >= wristToTip.sqrMagnitude; + } + + void StartPokeGesture() + { + m_IsPoking = true; + m_PokeGestureStarted.Invoke(); + } + + void EndPokeGesture() + { + m_IsPoking = false; + m_PokeGestureEnded.Invoke(); + } +#endif + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs.meta new file mode 100644 index 0000000..591d36e --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/PokeGestureDetector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dbac611a2982409ab5f5e604f53bcad0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs new file mode 100644 index 0000000..e286cbf --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs @@ -0,0 +1,133 @@ +using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// An input button reader based on another and holds it true until falling below a lower release threshold. + /// Useful with hand interaction because the bool select value can bounce when the hand is near the tight internal threshold, + /// so using this will keep the pinch true until moving the fingers much further away than the pinch activation threshold. + /// + [DefaultExecutionOrder(XRInteractionUpdateOrder.k_XRInputDeviceButtonReader)] + public class ReleaseThresholdButtonReader : MonoBehaviour, IXRInputButtonReader + { + [SerializeField] + [Tooltip("The source input that this component reads to create a processed button value.")] + XRInputButtonReader m_ValueInput = new XRInputButtonReader("Value"); + + /// + /// The source input that this component reads to create a processed button value. + /// + public XRInputButtonReader valueInput + { + get => m_ValueInput; + set => XRInputReaderUtility.SetInputProperty(ref m_ValueInput, value, this); + } + + [SerializeField] + [Tooltip("The threshold value to use to determine when the button is pressed. Considered pressed equal to or greater than this value.")] + [Range(0f, 1f)] + float m_PressThreshold = 0.8f; + + /// + /// The threshold value to use to determine when the button is pressed. Considered pressed equal to or greater than this value. + /// + /// + /// This reader will also be considered performed if the source input is performed. + /// + public float pressThreshold + { + get => m_PressThreshold; + set => m_PressThreshold = value; + } + + [SerializeField] + [Tooltip("The threshold value to use to determine when the button is released when it was previously pressed. Keeps being pressed until falls back to a value of or below this value.")] + [Range(0f, 1f)] + float m_ReleaseThreshold = 0.25f; + + /// + /// The threshold value to use to determine when the button is released when it was previously pressed. + /// Keeps being pressed until falls back to a value of or below this value. + /// + /// + /// This reader will still be considered performed if the source input is still performed + /// when this threshold is reached. + /// + public float releaseThreshold + { + get => m_ReleaseThreshold; + set => m_ReleaseThreshold = value; + } + + bool m_IsPerformed; + bool m_WasPerformedThisFrame; + bool m_WasCompletedThisFrame; + + /// + /// See . + /// + void OnEnable() + { + m_ValueInput?.EnableDirectActionIfModeUsed(); + } + + /// + /// See . + /// + void OnDisable() + { + m_ValueInput?.DisableDirectActionIfModeUsed(); + } + + /// + /// See . + /// + void Update() + { + // Go true when either the press threshold is reached or the bool is already performed. + // Only drop back to false when the release threshold is reached and the bool is no longer performed. + var prevPerformed = m_IsPerformed; + var pressAmount = m_ValueInput.ReadValue(); + + bool newValue; + if (prevPerformed) + newValue = pressAmount > m_ReleaseThreshold || m_ValueInput.ReadIsPerformed(); + else + newValue = pressAmount >= m_PressThreshold || m_ValueInput.ReadIsPerformed(); + + m_IsPerformed = newValue; + m_WasPerformedThisFrame = !prevPerformed && m_IsPerformed; + m_WasCompletedThisFrame = prevPerformed && !m_IsPerformed; + } + + /// + public bool ReadIsPerformed() + { + return m_IsPerformed; + } + + /// + public bool ReadWasPerformedThisFrame() + { + return m_WasPerformedThisFrame; + } + + /// + public bool ReadWasCompletedThisFrame() + { + return m_WasCompletedThisFrame; + } + + /// + public float ReadValue() + { + return m_ValueInput.ReadValue(); + } + + /// + public bool TryReadValue(out float value) + { + return m_ValueInput.TryReadValue(out value); + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs.meta new file mode 100644 index 0000000..cd19e72 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ReleaseThresholdButtonReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63f61d1c82c9fc6429ebd4791a4d6817 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs new file mode 100644 index 0000000..828ccab --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs @@ -0,0 +1,121 @@ +using UnityEngine.XR.Interaction.Toolkit.Inputs.Readers; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Example class that reads a float value from an and converts it to a bool. + /// Useful with hand interaction because the bool select value can be unreliable when the hand is near the tight internal threshold. + /// + [DefaultExecutionOrder(XRInteractionUpdateOrder.k_XRInputDeviceButtonReader)] + public class ValueDerivedButtonReader : MonoBehaviour, IXRInputButtonReader + { + [SerializeField] + [Tooltip("The input reader used to reference the float value to convert to a bool.")] + XRInputValueReader m_ValueInput = new XRInputValueReader("Value"); + + /// + /// The input reader used to reference the float value to convert to a bool. + /// + public XRInputValueReader valueInput + { + get => m_ValueInput; + set => XRInputReaderUtility.SetInputProperty(ref m_ValueInput, value, this); + } + + [SerializeField] + [Tooltip("The threshold value to use to determine when the button is pressed. Considered pressed equal to or greater than this value.")] + [Range(0f, 1f)] + float m_PressThreshold = 0.8f; + + /// + /// The threshold value to use to determine when the button is pressed. Considered pressed equal to or greater than this value. + /// + public float pressThreshold + { + get => m_PressThreshold; + set => m_PressThreshold = value; + } + + [SerializeField] + [Tooltip("The threshold value to use to determine when the button is released when it was previously pressed. Keeps being pressed until falls back to a value of or below this value.")] + [Range(0f, 1f)] + float m_ReleaseThreshold = 0.25f; + + /// + /// The threshold value to use to determine when the button is released when it was previously pressed. + /// Keeps being pressed until falls back to a value of or below this value. + /// + public float releaseThreshold + { + get => m_ReleaseThreshold; + set => m_ReleaseThreshold = value; + } + + bool m_IsPerformed; + bool m_WasPerformedThisFrame; + bool m_WasCompletedThisFrame; + + /// + /// See . + /// + void OnEnable() + { + m_ValueInput?.EnableDirectActionIfModeUsed(); + } + + /// + /// See . + /// + void OnDisable() + { + m_ValueInput?.DisableDirectActionIfModeUsed(); + } + + /// + /// See . + /// + void Update() + { + var prevPerformed = m_IsPerformed; + var pressAmount = m_ValueInput.ReadValue(); + + var newValue = pressAmount >= m_PressThreshold; + if (!newValue && prevPerformed) + newValue = pressAmount > m_ReleaseThreshold; + + m_IsPerformed = newValue; + m_WasPerformedThisFrame = !prevPerformed && m_IsPerformed; + m_WasCompletedThisFrame = prevPerformed && !m_IsPerformed; + } + + /// + public bool ReadIsPerformed() + { + return m_IsPerformed; + } + + /// + public bool ReadWasPerformedThisFrame() + { + return m_WasPerformedThisFrame; + } + + /// + public bool ReadWasCompletedThisFrame() + { + return m_WasCompletedThisFrame; + } + + /// + public float ReadValue() + { + return m_ValueInput.ReadValue(); + } + + /// + public bool TryReadValue(out float value) + { + return m_ValueInput.TryReadValue(out value); + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs.meta new file mode 100644 index 0000000..1ed61a4 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/ValueDerivedButtonReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf47ae772fb3421292887025bf9b5820 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs new file mode 100644 index 0000000..3845f19 --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs @@ -0,0 +1,33 @@ +using System; +using Unity.Mathematics; +using UnityEngine.XR.Interaction.Toolkit.AffordanceSystem.Receiver.Primitives; + +namespace UnityEngine.XR.Interaction.Toolkit.Samples.Hands +{ + /// + /// Affordance receiver applying a Vector3 (Float3) affordance theme to a Transform local scale. + /// Broadcasts new affordance value with Unity Event. + /// + [Obsolete("The Affordance System namespace and all associated classes have been deprecated. The existing affordance system will be moved, replaced and updated with a new interaction feedback system in a future version of XRI.")] + public class Vector3ScaleAffordanceReceiver : Vector3AffordanceReceiver + { + [SerializeField] + [Tooltip("The transform to apply the scale value to.")] + Transform m_TargetTransform; + + /// + protected override void OnEnable() + { + base.OnEnable(); + if (m_TargetTransform == null) + m_TargetTransform = transform; + } + + /// + protected override void OnAffordanceValueUpdated(float3 newValue) + { + base.OnAffordanceValueUpdated(newValue); + m_TargetTransform.localScale = newValue; + } + } +} diff --git a/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs.meta b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs.meta new file mode 100644 index 0000000..c56261b --- /dev/null +++ b/Assets/Samples/XR Interaction Toolkit/3.1.2/Hands Interaction Demo/Scripts/Vector3ScaleAffordanceReceiver.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27bee223346c63f4bb2b0f85250f58b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: -- cgit v1.2.3-70-g09d2