From e71825b07f73cb512a011d1e0aa6bf4e99409dc1 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 11 Jul 2025 15:06:45 -0700 Subject: [PATCH 1/5] Update PinchInputReader.cs --- .../Readers/PinchInputReader.cs | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs index 32e638973..a4412266c 100644 --- a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs +++ b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs @@ -29,7 +29,7 @@ public class PinchInputReader : MonoBehaviour, IXRInputButtonReader /// private struct FallbackState { - public bool hasPinchData; + public bool isInProgress; public bool isPerformed; public bool wasPerformedThisFrame; public bool wasCompletedThisFrame; @@ -167,10 +167,8 @@ public bool ReadIsPerformed() InputActionPhase phase = action.phase; return phase == InputActionPhase.Performed || (phase != InputActionPhase.Disabled && action.WasPerformedThisFrame()); } - else - { - return m_fallbackState.isPerformed; - } + + return m_fallbackState.isPerformed; } /// @@ -180,10 +178,8 @@ public bool ReadWasPerformedThisFrame() { return selectAction.action.WasPerformedThisFrame(); } - else - { - return m_fallbackState.wasPerformedThisFrame; - } + + return m_fallbackState.wasPerformedThisFrame; } /// @@ -193,10 +189,8 @@ public bool ReadWasCompletedThisFrame() { return selectAction.action.WasCompletedThisFrame(); } - else - { - return m_fallbackState.wasCompletedThisFrame; - } + + return m_fallbackState.wasCompletedThisFrame; } /// @@ -206,10 +200,8 @@ public float ReadValue() { return selectActionValue.action.ReadValue(); } - else - { - return m_fallbackState.value; - } + + return m_fallbackState.value; } /// @@ -221,11 +213,9 @@ public bool TryReadValue(out float value) value = action.ReadValue(); return action.IsInProgress(); } - else - { - value = m_fallbackState.value; - return m_fallbackState.hasPinchData; - } + + value = m_fallbackState.value; + return m_fallbackState.isInProgress; } #endregion IXRInputButtonReader @@ -242,6 +232,24 @@ private void UpdatePinchSelection() { using (UpdatePinchSelectionPerfMarker.Auto()) { + // This section accounts for one of "select" and "select value" being bound while the other is polyfilled. + // We can use the data from the bound action to synthesize the other better than the hand joint logic will. + if (!m_isSelectPolyfilled && !m_isTrackingStatePolyfilled) + { + m_fallbackState.isInProgress = ReadIsPerformed(); + m_fallbackState.value = m_fallbackState.isInProgress ? 1 : 0; + return; + } + else if (!m_isSelectValuePolyfilled && !m_isTrackingStatePolyfilled) + { + bool isPinched = ReadValue() >= (m_fallbackState.isPerformed ? 0.9f : 1.0f); + + m_fallbackState.wasPerformedThisFrame = isPinched && !m_fallbackState.isPerformed; + m_fallbackState.wasCompletedThisFrame = !isPinched && m_fallbackState.isPerformed; + m_fallbackState.isPerformed = isPinched; + return; + } + // If we still don't have an aggregator, then don't update selects. if (XRSubsystemHelpers.HandsAggregator == null) { @@ -266,7 +274,7 @@ private void UpdatePinchSelection() m_fallbackState.wasCompletedThisFrame = !isPinched && m_fallbackState.isPerformed; m_fallbackState.isPerformed = isPinched; m_fallbackState.value = pinchAmount; - m_fallbackState.hasPinchData = true; + m_fallbackState.isInProgress = pinchAmount > 0; } else { From a489c798dec4c72a1b02e6e4980880c646bcc0b6 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 11 Jul 2025 16:05:22 -0700 Subject: [PATCH 2/5] Simplify isInProgress --- org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs index a4412266c..4617eed58 100644 --- a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs +++ b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs @@ -29,7 +29,6 @@ public class PinchInputReader : MonoBehaviour, IXRInputButtonReader /// private struct FallbackState { - public bool isInProgress; public bool isPerformed; public bool wasPerformedThisFrame; public bool wasCompletedThisFrame; @@ -215,7 +214,7 @@ public bool TryReadValue(out float value) } value = m_fallbackState.value; - return m_fallbackState.isInProgress; + return value > 0; } #endregion IXRInputButtonReader @@ -236,8 +235,7 @@ private void UpdatePinchSelection() // We can use the data from the bound action to synthesize the other better than the hand joint logic will. if (!m_isSelectPolyfilled && !m_isTrackingStatePolyfilled) { - m_fallbackState.isInProgress = ReadIsPerformed(); - m_fallbackState.value = m_fallbackState.isInProgress ? 1 : 0; + m_fallbackState.value = ReadIsPerformed() ? 1 : 0; return; } else if (!m_isSelectValuePolyfilled && !m_isTrackingStatePolyfilled) @@ -274,7 +272,6 @@ private void UpdatePinchSelection() m_fallbackState.wasCompletedThisFrame = !isPinched && m_fallbackState.isPerformed; m_fallbackState.isPerformed = isPinched; m_fallbackState.value = pinchAmount; - m_fallbackState.isInProgress = pinchAmount > 0; } else { From 8785e67d35ef021b2acc9ee9838ce1466e7c5f68 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Tue, 15 Jul 2025 12:17:53 -0700 Subject: [PATCH 3/5] Update CHANGELOG.md --- org.mixedrealitytoolkit.input/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org.mixedrealitytoolkit.input/CHANGELOG.md b/org.mixedrealitytoolkit.input/CHANGELOG.md index bb0000f72..1e5e77023 100644 --- a/org.mixedrealitytoolkit.input/CHANGELOG.md +++ b/org.mixedrealitytoolkit.input/CHANGELOG.md @@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [4.0.0-pre.2] - 2025-12-05 +### Added + +* Added partial polyfill logic for one of "select" and "select value" being mapped while the other isn't, instead of fully polyfilling based on hand joint data when either is missing. [PR #1041](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1041) + ### Changed * Updated the minimum editor version to 2022.3.6f1 [PR #1003](https://github.com/MixedRealityToolkit/MixedRealityToolkit-Unity/pull/1003) From 6c102c4df792962c10e63432807578057aa1e7e3 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 18 Jul 2025 11:25:33 -0700 Subject: [PATCH 4/5] Only polyfill select, not select value Hand joints are likely better than a 0 or 1 conversion --- .../Readers/PinchInputReader.cs | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs index 4617eed58..094f68ce2 100644 --- a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs +++ b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs @@ -231,53 +231,34 @@ private void UpdatePinchSelection() { using (UpdatePinchSelectionPerfMarker.Auto()) { - // This section accounts for one of "select" and "select value" being bound while the other is polyfilled. - // We can use the data from the bound action to synthesize the other better than the hand joint logic will. - if (!m_isSelectPolyfilled && !m_isTrackingStatePolyfilled) - { - m_fallbackState.value = ReadIsPerformed() ? 1 : 0; - return; - } - else if (!m_isSelectValuePolyfilled && !m_isTrackingStatePolyfilled) - { - bool isPinched = ReadValue() >= (m_fallbackState.isPerformed ? 0.9f : 1.0f); + float pinchAmount; - m_fallbackState.wasPerformedThisFrame = isPinched && !m_fallbackState.isPerformed; - m_fallbackState.wasCompletedThisFrame = !isPinched && m_fallbackState.isPerformed; - m_fallbackState.isPerformed = isPinched; - return; + // This section accounts for "select value" being bound while "select" is polyfilled. + // We can use the data from the bound action to synthesize better than the hand joint logic will. + if (!m_isSelectValuePolyfilled && !m_isTrackingStatePolyfilled) + { + pinchAmount = ReadValue(); } - - // If we still don't have an aggregator, then don't update selects. - if (XRSubsystemHelpers.HandsAggregator == null) + // Workaround for missing select actions on devices without interaction profiles + // for hands, such as Varjo and Quest. Should be removed once we have universal + // hand interaction profile(s) across vendors. + else if (XRSubsystemHelpers.HandsAggregator == null + || !XRSubsystemHelpers.HandsAggregator.TryGetPinchProgress(handNode, out _, out _, out pinchAmount)) { + // If we didn't get pinch data, reset the fallback state. + m_fallbackState = default; return; } - // If we got pinch data, write it into our select interaction state. - if (XRSubsystemHelpers.HandsAggregator.TryGetPinchProgress( - handNode, - out _, - out _, - out float pinchAmount)) - { - // Workaround for missing select actions on devices without interaction profiles - // for hands, such as Varjo and Quest. Should be removed once we have universal - // hand interaction profile(s) across vendors. - - // Debounce the polyfill pinch action value. - bool isPinched = pinchAmount >= (m_fallbackState.isPerformed ? 0.9f : 1.0f); + const float PinchDeactivateThreshold = 0.9f; + const float PinchActivateThreshold = 1.0f; - m_fallbackState.wasPerformedThisFrame = isPinched && !m_fallbackState.isPerformed; - m_fallbackState.wasCompletedThisFrame = !isPinched && m_fallbackState.isPerformed; - m_fallbackState.isPerformed = isPinched; - m_fallbackState.value = pinchAmount; - } - else - { - // If we didn't get pinch data, reset the fallback state. - m_fallbackState = default; - } + // Debounce the polyfill pinch action value. + bool isPinched = pinchAmount >= (m_fallbackState.isPerformed ? PinchDeactivateThreshold : PinchActivateThreshold); + m_fallbackState.wasPerformedThisFrame = isPinched && !m_fallbackState.isPerformed; + m_fallbackState.wasCompletedThisFrame = !isPinched && m_fallbackState.isPerformed; + m_fallbackState.isPerformed = isPinched; + m_fallbackState.value = pinchAmount; } } From f367c11a5ecdf9872af879d5e0caee610f52b713 Mon Sep 17 00:00:00 2001 From: Kurtis Date: Fri, 18 Jul 2025 11:43:47 -0700 Subject: [PATCH 5/5] Update PinchInputReader.cs --- .../Readers/PinchInputReader.cs | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs index 094f68ce2..9a66568aa 100644 --- a/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs +++ b/org.mixedrealitytoolkit.input/Readers/PinchInputReader.cs @@ -231,19 +231,34 @@ private void UpdatePinchSelection() { using (UpdatePinchSelectionPerfMarker.Auto()) { - float pinchAmount; + bool hasPinchData = false; + float pinchAmount = 0; + float pinchProgress = 0; - // This section accounts for "select value" being bound while "select" is polyfilled. - // We can use the data from the bound action to synthesize better than the hand joint logic will. - if (!m_isSelectValuePolyfilled && !m_isTrackingStatePolyfilled) - { - pinchAmount = ReadValue(); - } // Workaround for missing select actions on devices without interaction profiles // for hands, such as Varjo and Quest. Should be removed once we have universal // hand interaction profile(s) across vendors. - else if (XRSubsystemHelpers.HandsAggregator == null - || !XRSubsystemHelpers.HandsAggregator.TryGetPinchProgress(handNode, out _, out _, out pinchAmount)) + if (XRSubsystemHelpers.HandsAggregator != null + && XRSubsystemHelpers.HandsAggregator.TryGetPinchProgress(handNode, out _, out _, out pinchProgress)) + { + hasPinchData |= true; + } + + // This section accounts for one of "select" and "select value" being bound while the other is polyfilled. + // We can use the data from the bound action to synthesize the other better than the hand joint logic will. + if (!m_isSelectPolyfilled && !m_isTrackingStatePolyfilled) + { + // If we successfully read hand joint data, we should use that instead of clamping to 0 or 1 + pinchAmount = pinchProgress > 0 ? pinchProgress : ReadIsPerformed() ? 1 : 0; + hasPinchData |= true; + } + else if (!m_isSelectValuePolyfilled && !m_isTrackingStatePolyfilled) + { + pinchAmount = ReadValue(); + hasPinchData |= true; + } + + if (!hasPinchData) { // If we didn't get pinch data, reset the fallback state. m_fallbackState = default;