clean project

This commit is contained in:
Helar Jaadla
2022-03-07 17:52:41 +02:00
parent a174b45bd2
commit cbeb10ec35
5100 changed files with 837159 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection
{
/// <summary>
/// Test if hand joint is inside generic collider and updates its active state
/// based on that test. We could trigger-based testing, but if the hand disappears
/// during one frame, we will not get a trigger exit event (which means we require
/// manual testing in Update anyway to accomodate that edge case).
/// </summary>
public class ColliderContainsHandJointActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
private IHand Hand;
[SerializeField]
private Collider[] _entryColliders;
[SerializeField]
private Collider[] _exitColliders;
[SerializeField]
private HandJointId _jointToTest = HandJointId.HandWristRoot;
public bool Active { get; private set; }
private bool _active = false;
protected virtual void Awake()
{
Hand = _hand as IHand;
Active = false;
}
protected virtual void Start()
{
Assert.IsNotNull(Hand);
Assert.IsTrue(_entryColliders != null && _entryColliders.Length > 0);
Assert.IsTrue(_exitColliders != null && _exitColliders.Length > 0);
}
protected virtual void Update()
{
if (Hand.GetJointPose(_jointToTest, out Pose jointPose))
{
Active = JointPassesTests(jointPose);
}
else
{
Active = false;
}
}
private bool JointPassesTests(Pose jointPose)
{
bool passesCollisionTest;
if (_active)
{
passesCollisionTest = IsPointWithinColliders(jointPose.position,
_exitColliders);
}
else
{
passesCollisionTest = IsPointWithinColliders(jointPose.position,
_entryColliders);
}
_active = passesCollisionTest;
return passesCollisionTest;
}
private bool IsPointWithinColliders(Vector3 point, Collider[] colliders)
{
foreach (var collider in colliders)
{
if (!Collisions.IsPointWithinCollider(point, collider))
{
return false;
}
}
return true;
}
#region Inject
public void InjectAllColliderContainsHandJointActiveState(IHand hand, Collider[] entryColliders,
Collider[] exitColliders, HandJointId jointToTest)
{
InjectHand(hand);
InjectEntryColliders(entryColliders);
InjectExitColliders(exitColliders);
InjectJointToTest(jointToTest);
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectEntryColliders(Collider[] entryColliders)
{
_entryColliders = entryColliders;
}
public void InjectExitColliders(Collider[] exitColliders)
{
_exitColliders = exitColliders;
}
public void InjectJointToTest(HandJointId jointToTest)
{
_jointToTest = jointToTest;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: af496e475135e134aa6a3ad7e0109882
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7098ce5b7bdb8734980e21c523861a71
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class ActiveStateDebugVisual : MonoBehaviour
{
[SerializeField, Interface(typeof(IActiveState))]
private MonoBehaviour _activeState;
private IActiveState ActiveState { get; set; }
[SerializeField]
private Renderer _target;
[SerializeField]
private Color _normalColor = Color.red;
[SerializeField]
private Color _activeColor = Color.green;
private Material _material;
private bool _lastActiveValue = false;
protected virtual void Awake()
{
ActiveState = _activeState as IActiveState;
Assert.IsNotNull(ActiveState);
Assert.IsNotNull(_target);
_material = _target.material;
SetMaterialColor(_lastActiveValue ? _activeColor : _normalColor);
}
private void OnDestroy()
{
Destroy(_material);
}
protected virtual void Update()
{
bool isActive = ActiveState.Active;
if (_lastActiveValue != isActive)
{
SetMaterialColor(isActive ? _activeColor : _normalColor);
_lastActiveValue = isActive;
}
}
private void SetMaterialColor(Color activeColor)
{
_material.color = activeColor;
_target.enabled = _material.color.a > 0.0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38d73b2d0c0ee504587e0237ec474ca4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,137 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class FingerFeatureSkeletalDebugVisual : MonoBehaviour
{
[SerializeField]
private LineRenderer _lineRenderer;
[SerializeField]
private Color _normalColor = Color.red;
[SerializeField]
private Color _activeColor = Color.green;
[SerializeField]
private float _lineWidth = 0.005f;
private IHand _hand;
private FingerFeatureStateProvider _featureState;
private bool _lastFeatureActiveValue = false;
private IReadOnlyList<HandJointId> _jointsCovered = null;
private HandFinger _finger;
private ShapeRecognizer.FingerFeatureConfig _fingerFeatureConfig;
private bool _initializedPositions;
private bool _initialized;
protected virtual void Awake()
{
Assert.IsNotNull(_lineRenderer);
UpdateFeatureActiveValueAndVisual(false);
}
private void UpdateFeatureActiveValueAndVisual(bool newValue)
{
var colorToUse = newValue ? _activeColor : _normalColor;
_lineRenderer.startColor = colorToUse;
_lineRenderer.endColor = colorToUse;
_lastFeatureActiveValue = newValue;
}
public void Initialize(
IHand hand,
HandFinger finger,
ShapeRecognizer.FingerFeatureConfig fingerFeatureConfig)
{
_hand = hand;
_initialized = true;
bool foundAspect = hand.GetHandAspect(out _featureState);
Assert.IsTrue(foundAspect);
var featureValueProvider = _featureState.GetValueProvider(finger);
_jointsCovered = featureValueProvider.GetJointsAffected(
finger,
fingerFeatureConfig.Feature);
_finger = finger;
_fingerFeatureConfig = fingerFeatureConfig;
_initializedPositions = false;
}
protected virtual void Update()
{
if (!_initialized || !_hand.IsTrackedDataValid)
{
ToggleLineRendererEnableState(false);
return;
}
ToggleLineRendererEnableState(true);
UpdateDebugSkeletonLineRendererJoints();
UpdateFeatureActiveValue();
}
private void ToggleLineRendererEnableState(bool enableState)
{
if (_lineRenderer.enabled == enableState)
{
return;
}
_lineRenderer.enabled = enableState;
}
private void UpdateDebugSkeletonLineRendererJoints()
{
if (!_initializedPositions)
{
_lineRenderer.positionCount = _jointsCovered.Count;
_initializedPositions = true;
}
if (Mathf.Abs(_lineRenderer.startWidth - _lineWidth) > Mathf.Epsilon)
{
_lineRenderer.startWidth = _lineWidth;
_lineRenderer.endWidth = _lineWidth;
}
int numJoints = _jointsCovered.Count;
for (int i = 0; i < numJoints; i++)
{
if (_hand.GetJointPose(_jointsCovered[i], out Pose jointPose))
{
_lineRenderer.SetPosition(i, jointPose.position);
}
}
}
private void UpdateFeatureActiveValue()
{
bool isActive = _featureState.IsStateActive(_finger, _fingerFeatureConfig.Feature,
_fingerFeatureConfig.Mode, _fingerFeatureConfig.State);
if (isActive != _lastFeatureActiveValue)
{
UpdateFeatureActiveValueAndVisual(isActive);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: df12566059c64f34a8985d7383779198
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class HandShapeSkeletalDebugVisual : MonoBehaviour
{
[SerializeField]
private ShapeRecognizerActiveState _shapeRecognizerActiveState;
[SerializeField]
private GameObject _fingerFeatureDebugVisualPrefab;
protected virtual void Awake()
{
Assert.IsNotNull(_shapeRecognizerActiveState);
Assert.IsNotNull(_fingerFeatureDebugVisualPrefab);
}
protected virtual void Start()
{
var statesByFinger = AllFeatureStates()
.GroupBy(s => s.Item1)
.Select(group => new
{
HandFinger = group.Key,
FingerFeatures = group.SelectMany(item => item.Item2)
});
foreach (var g in statesByFinger)
{
foreach (var feature in g.FingerFeatures)
{
var boneDebugObject = Instantiate(_fingerFeatureDebugVisualPrefab);
var skeletalComp = boneDebugObject.GetComponent<FingerFeatureSkeletalDebugVisual>();
skeletalComp.Initialize(_shapeRecognizerActiveState.Hand, g.HandFinger, feature);
var debugVisTransform = boneDebugObject.transform;
debugVisTransform.parent = this.transform;
debugVisTransform.localScale = Vector3.one;
debugVisTransform.localRotation = Quaternion.identity;
debugVisTransform.localPosition = Vector3.zero;
}
}
}
private IEnumerable<ValueTuple<HandFinger, IReadOnlyList<ShapeRecognizer.FingerFeatureConfig>>> AllFeatureStates()
{
foreach (ShapeRecognizer shapeRecognizer in _shapeRecognizerActiveState.Shapes)
{
foreach (var handFingerConfigs in shapeRecognizer.GetFingerFeatureConfigs())
{
yield return handFingerConfigs;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 86fda5ce347f35d44a5c6faee65c6679
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,110 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using TMPro;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class TransformFeatureDebugVisual : MonoBehaviour
{
[SerializeField]
private Renderer _target;
[SerializeField]
private Color _normalColor = Color.red;
[SerializeField]
private Color _activeColor = Color.green;
[SerializeField]
private TextMeshPro _targetText;
private TransformFeatureStateProvider _transformFeatureStateProvider;
private TransformRecognizerActiveState _transformRecognizerActiveState;
private Material _material;
private bool _lastActiveValue;
private TransformFeatureConfig _targetConfig;
private bool _initialized;
private Handedness _handedness;
protected virtual void Awake()
{
_material = _target.material;
Assert.IsNotNull(_material);
Assert.IsNotNull(_targetText);
_material.color = _lastActiveValue ? _activeColor : _normalColor;
}
private void OnDestroy()
{
Destroy(_material);
}
public void Initialize(
Handedness handedness,
TransformFeatureConfig targetConfig,
TransformFeatureStateProvider transformFeatureStateProvider,
TransformRecognizerActiveState transformActiveState)
{
_handedness = handedness;
_initialized = true;
_transformFeatureStateProvider = transformFeatureStateProvider;
_transformRecognizerActiveState = transformActiveState;
_targetConfig = targetConfig;
}
protected virtual void Update()
{
if (!_initialized)
{
return;
}
bool isActive = false;
TransformFeature feature = _targetConfig.Feature;
if (_transformFeatureStateProvider.GetCurrentState(
_transformRecognizerActiveState.TransformConfig,
feature,
out string currentState))
{
float? featureVal = _transformFeatureStateProvider.GetFeatureValue(
_transformRecognizerActiveState.TransformConfig, feature);
isActive = _transformFeatureStateProvider.IsStateActive(
_transformRecognizerActiveState.TransformConfig,
feature,
_targetConfig.Mode,
_targetConfig.State);
string featureValStr = featureVal.HasValue ? featureVal.Value.ToString("F2") : "--";
_targetText.text = $"{feature}\n" +
$"{currentState} ({featureValStr})";
}
else
{
_targetText.text = $"{feature}\n";
}
if (isActive != _lastActiveValue)
{
_material.color = isActive ? _activeColor : _normalColor;
_lastActiveValue = isActive;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9390f6f5772c6d942a736437d338b667
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,61 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class TransformFeatureVectorDebugParentVisual : MonoBehaviour
{
[SerializeField]
private TransformRecognizerActiveState _transformRecognizerActiveState;
[SerializeField]
private GameObject _vectorVisualPrefab;
public void GetTransformFeatureVectorAndWristPos(TransformFeature feature,
bool isHandVector, ref Vector3? featureVec, ref Vector3? wristPos)
{
_transformRecognizerActiveState.GetFeatureVectorAndWristPos(feature, isHandVector,
ref featureVec, ref wristPos);
}
protected virtual void Awake()
{
Assert.IsNotNull(_transformRecognizerActiveState);
Assert.IsNotNull(_vectorVisualPrefab);
}
protected virtual void Start()
{
var featureConfigs = _transformRecognizerActiveState.FeatureConfigs;
foreach (var featureConfig in featureConfigs)
{
var feature = featureConfig.Feature;
CreateVectorDebugView(feature, false);
CreateVectorDebugView(feature, true);
}
}
private void CreateVectorDebugView(TransformFeature feature, bool trackingHandVector)
{
var featureDebugVis = Instantiate(_vectorVisualPrefab, this.transform);
var debugVisComp = featureDebugVis.GetComponent<TransformFeatureVectorDebugVisual>();
debugVisComp.Initialize(feature, trackingHandVector, this, trackingHandVector ?
Color.blue : Color.black);
var debugVisTransform = debugVisComp.transform;
debugVisTransform.localRotation = Quaternion.identity;
debugVisTransform.localPosition = Vector3.zero;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8cd27ccbb9a942e47a1b2bfd53f957e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class TransformFeatureVectorDebugVisual : MonoBehaviour
{
public IHand Hand { get; private set; }
[SerializeField]
private LineRenderer _lineRenderer;
[SerializeField]
private float _lineWidth = 0.005f;
[SerializeField]
private float _lineScale = 0.1f;
private bool _isInitialized = false;
private TransformFeature _feature;
private TransformFeatureVectorDebugParentVisual _parent;
private bool _trackingHandVector = false;
protected virtual void Awake()
{
Assert.IsNotNull(_lineRenderer);
_lineRenderer.enabled = false;
}
public void Initialize(TransformFeature feature,
bool trackingHandVector,
TransformFeatureVectorDebugParentVisual parent,
Color lineColor)
{
_isInitialized = true;
_lineRenderer.enabled = true;
_lineRenderer.positionCount = 2;
_lineRenderer.startColor = lineColor;
_lineRenderer.endColor = lineColor;
_feature = feature;
_trackingHandVector = trackingHandVector;
_parent = parent;
}
protected virtual void Update()
{
if (!_isInitialized)
{
return;
}
Vector3? featureVec = null;
Vector3? wristPos = null;
_parent.GetTransformFeatureVectorAndWristPos(_feature,
_trackingHandVector, ref featureVec, ref wristPos);
if (featureVec == null || wristPos == null)
{
if (_lineRenderer.enabled)
{
_lineRenderer.enabled = false;
}
return;
}
if (!_lineRenderer.enabled)
{
_lineRenderer.enabled = true;
}
if (Mathf.Abs(_lineRenderer.startWidth - _lineWidth) > Mathf.Epsilon)
{
_lineRenderer.startWidth = _lineWidth;
_lineRenderer.endWidth = _lineWidth;
}
_lineRenderer.SetPosition(0, wristPos.Value);
_lineRenderer.SetPosition(1, wristPos.Value + _lineScale*featureVec.Value);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5b3b9e0179019c46a27bcc4ed7916ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,135 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection.Debug
{
public class TransformRecognizerDebugVisual : MonoBehaviour
{
[SerializeField]
private Hand _hand;
[SerializeField]
private TransformRecognizerActiveState[] _transformRecognizerActiveStates;
[SerializeField]
private Renderer _target;
[SerializeField]
private Color _normalColor = Color.red;
[SerializeField]
private Color _activeColor = Color.green;
[SerializeField]
private GameObject _transformFeatureDebugVisualPrefab;
[SerializeField]
private Transform _debugVisualParent;
[SerializeField]
private Vector3 _featureSpacingVec = new Vector3(1.0f, 0.0f, 0.0f);
[SerializeField]
private Vector3 _featureDebugLocalScale = new Vector3(0.3f, 0.3f, 0.3f);
[SerializeField]
private TextMeshPro _targetText;
private Material _material;
private bool _lastActiveValue = false;
protected virtual void Awake()
{
Assert.IsNotNull(_hand);
Assert.IsTrue(_transformRecognizerActiveStates != null &&
_transformRecognizerActiveStates.Length > 0);
Assert.IsNotNull(_target);
Assert.IsNotNull(_transformFeatureDebugVisualPrefab);
Assert.IsNotNull(_targetText);
_material = _target.material;
_material.color = _lastActiveValue ? _activeColor : _normalColor;
if (_debugVisualParent == null)
{
_debugVisualParent = transform;
}
}
protected virtual void Start()
{
Vector3 totalDisp = Vector3.zero;
string shapeNames = "";
foreach (var activeState in _transformRecognizerActiveStates)
{
bool foundAspect = activeState.Hand.GetHandAspect(out TransformFeatureStateProvider stateProvider);
Assert.IsTrue(foundAspect);
var featureConfigs = activeState.FeatureConfigs;
foreach (var featureConfig in featureConfigs)
{
var featureDebugVis = Instantiate(_transformFeatureDebugVisualPrefab, _debugVisualParent);
var debugVisComp = featureDebugVis.GetComponent<TransformFeatureDebugVisual>();
debugVisComp.Initialize(activeState.Hand.Handedness, featureConfig, stateProvider,
activeState);
var debugVisTransform = debugVisComp.transform;
debugVisTransform.localScale = _featureDebugLocalScale;
debugVisTransform.localRotation = Quaternion.identity;
debugVisTransform.localPosition = totalDisp;
totalDisp += _featureSpacingVec;
if (!String.IsNullOrEmpty(shapeNames)) { shapeNames += "\n "; }
shapeNames += $"{featureConfig.Mode} {featureConfig.State} ({activeState.Hand.Handedness})";
}
}
_targetText.text = $"{shapeNames}";
}
private void OnDestroy()
{
Destroy(_material);
}
private bool AllActive()
{
foreach (var activeState in _transformRecognizerActiveStates)
{
if (!activeState.Active)
{
return false;
}
}
return true;
}
protected virtual void Update()
{
bool isActive = AllActive();
if (_lastActiveValue != isActive)
{
_material.color = isActive ? _activeColor : _normalColor;
_lastActiveValue = isActive;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aca13d9f89c0c904a8d62bde280f5b52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,151 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction.PoseDetection
{
using FingerFeatureConfig = ShapeRecognizer.FingerFeatureConfig;
public class FeatureConfigBuilder
{
public class BuildCondition<TBuildState>
{
private readonly BuildStateDelegate _buildStateFn;
public delegate TBuildState BuildStateDelegate(FeatureStateActiveMode mode);
public BuildCondition(BuildStateDelegate buildStateFn)
{
_buildStateFn = buildStateFn;
}
public TBuildState Is => _buildStateFn(FeatureStateActiveMode.Is);
public TBuildState IsNot => _buildStateFn(FeatureStateActiveMode.IsNot);
}
}
public class FingerFeatureConfigBuilder : FeatureConfigBuilder
{
public static BuildCondition<OpenCloseStateBuilder> Curl { get; } =
new BuildCondition<OpenCloseStateBuilder>(mode => new OpenCloseStateBuilder(mode, FingerFeature.Curl));
public static BuildCondition<OpenCloseStateBuilder> Flexion { get; } =
new BuildCondition<OpenCloseStateBuilder>(mode => new OpenCloseStateBuilder(mode, FingerFeature.Flexion));
public static BuildCondition<AbductionStateBuilder> Abduction { get; } =
new BuildCondition<AbductionStateBuilder>(mode => new AbductionStateBuilder(mode));
public static BuildCondition<OppositionStateBuilder> Opposition { get; } =
new BuildCondition<OppositionStateBuilder>(mode => new OppositionStateBuilder(mode));
public class OpenCloseStateBuilder
{
private readonly FeatureStateActiveMode _mode;
private readonly FingerFeature _fingerFeature;
private readonly FeatureStateDescription[] _states;
public OpenCloseStateBuilder(FeatureStateActiveMode featureStateActiveMode,
FingerFeature fingerFeature)
{
_mode = featureStateActiveMode;
_fingerFeature = fingerFeature;
_states = FingerFeatureProperties.FeatureDescriptions[_fingerFeature].FeatureStates;
}
public FingerFeatureConfig Open =>
new FingerFeatureConfig { Feature = _fingerFeature, Mode = _mode, State = _states[0].Id };
public FingerFeatureConfig Neutral =>
new FingerFeatureConfig { Feature = _fingerFeature, Mode = _mode, State = _states[1].Id };
public FingerFeatureConfig Closed =>
new FingerFeatureConfig { Feature = _fingerFeature, Mode = _mode, State = _states[2].Id };
}
public class AbductionStateBuilder
{
private readonly FeatureStateActiveMode _mode;
public AbductionStateBuilder(FeatureStateActiveMode mode)
{
_mode = mode;
}
public FingerFeatureConfig None =>
new FingerFeatureConfig { Feature = FingerFeature.Abduction, Mode = _mode, State = FingerFeatureProperties.AbductionFeatureStates[0].Id };
public FingerFeatureConfig Closed =>
new FingerFeatureConfig { Feature = FingerFeature.Abduction, Mode = _mode, State = FingerFeatureProperties.AbductionFeatureStates[1].Id };
public FingerFeatureConfig Open =>
new FingerFeatureConfig { Feature = FingerFeature.Abduction, Mode = _mode, State = FingerFeatureProperties.AbductionFeatureStates[2].Id };
}
public class OppositionStateBuilder
{
private readonly FeatureStateActiveMode _mode;
public OppositionStateBuilder(FeatureStateActiveMode mode)
{
_mode = mode;
}
public FingerFeatureConfig Touching =>
new FingerFeatureConfig { Feature = FingerFeature.Opposition, Mode = _mode, State = FingerFeatureProperties.OppositionFeatureStates[0].Id };
public FingerFeatureConfig Near =>
new FingerFeatureConfig { Feature = FingerFeature.Opposition, Mode = _mode, State = FingerFeatureProperties.OppositionFeatureStates[1].Id };
public FingerFeatureConfig None =>
new FingerFeatureConfig { Feature = FingerFeature.Opposition, Mode = _mode, State = FingerFeatureProperties.OppositionFeatureStates[2].Id };
}
}
public class TransformFeatureConfigBuilder : FeatureConfigBuilder
{
public static BuildCondition<TrueFalseStateBuilder> WristUp { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.WristUp));
public static BuildCondition<TrueFalseStateBuilder> WristDown { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.WristDown));
public static BuildCondition<TrueFalseStateBuilder> PalmDown { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.PalmDown));
public static BuildCondition<TrueFalseStateBuilder> PalmUp { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.PalmUp));
public static BuildCondition<TrueFalseStateBuilder> PalmTowardsFace { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.PalmTowardsFace));
public static BuildCondition<TrueFalseStateBuilder> PalmAwayFromFace { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.PalmAwayFromFace));
public static BuildCondition<TrueFalseStateBuilder> FingersUp { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.FingersUp));
public static BuildCondition<TrueFalseStateBuilder> FingersDown { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.FingersDown));
public static BuildCondition<TrueFalseStateBuilder> PinchClear { get; } =
new BuildCondition<TrueFalseStateBuilder>(mode => new TrueFalseStateBuilder(mode, TransformFeature.PinchClear));
public class TrueFalseStateBuilder
{
private readonly FeatureStateActiveMode _mode;
private readonly TransformFeature _transformFeature;
private readonly FeatureStateDescription[] _states;
public TrueFalseStateBuilder(FeatureStateActiveMode featureStateActiveMode,
TransformFeature transformFeature)
{
_mode = featureStateActiveMode;
_transformFeature = transformFeature;
_states = TransformFeatureProperties.FeatureDescriptions[_transformFeature].FeatureStates;
}
public TransformFeatureConfig Open =>
new TransformFeatureConfig { Feature = _transformFeature, Mode = _mode, State = _states[0].Id };
public TransformFeatureConfig Closed =>
new TransformFeatureConfig { Feature = _transformFeature, Mode = _mode, State = _states[1].Id };
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1bb5c328f4204486ad98f3f962154e07
timeCreated: 1633634969

View File

@@ -0,0 +1,52 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction.PoseDetection
{
public class FeatureStateDescription
{
public FeatureStateDescription(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; }
public string Name { get; }
}
public class FeatureDescription
{
public FeatureDescription(string shortDescription, string description,
float minValueHint, float maxValueHint,
FeatureStateDescription[] featureStates)
{
ShortDescription = shortDescription;
Description = description;
MinValueHint = minValueHint;
MaxValueHint = maxValueHint;
FeatureStates = featureStates;
}
public string ShortDescription { get; }
public string Description { get; }
public float MinValueHint { get; }
public float MaxValueHint { get; }
/// <summary>
/// A hint to the editor on which feature states to provide by default, and in which order
/// they should appear.
/// The underlying system will accept other ranges; this is just for the UI.
/// </summary>
public FeatureStateDescription[] FeatureStates { get; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 53118e1a377b4b6685ca776f1d23ca3b
timeCreated: 1633632912

View File

@@ -0,0 +1,305 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection
{
public enum FeatureStateActiveMode {
Is,
IsNot,
}
[Serializable]
public abstract class FeatureConfigBase<TFeature>
{
[SerializeField]
private FeatureStateActiveMode _mode;
[SerializeField]
private TFeature _feature;
[SerializeField]
private string _state;
public FeatureStateActiveMode Mode
{
get => _mode;
set { _mode = value; }
}
public TFeature Feature
{
get => _feature;
set { _feature = value; }
}
public string State
{
get => _state;
set { _state = value; }
}
}
/// <summary>
/// A helper class that keeps track of the current state of features, quantized into
/// corresponding FeatureStates.
/// </summary>
/// <typeparam name="TFeature">
/// An enum containing all features that can be tracked.
/// </typeparam>
/// <typeparam name="TFeatureState">
/// An enum of all the possible states of each member of the <see cref="TFeature"/> param.
/// The name of each member of this enum must be prefixed with one of the values of TFeature.
/// </typeparam>
public class FeatureStateProvider<TFeature, TFeatureState>
where TFeature : unmanaged, Enum
where TFeatureState : IEquatable<TFeatureState>
{
/// <summary>
/// This should be updated with current value of the input data frameId. It is used to
/// determine if values need to be recalculated.
/// </summary>
public int LastUpdatedFrameId { get; set; }
private struct FeatureStateSnapshot
{
public bool HasCurrentState;
public TFeatureState State;
public TFeatureState DesiredState;
public int LastUpdatedFrameId;
public double DesiredStateEntryTime;
}
// A map of Map of (int)Feature => current state
private FeatureStateSnapshot[] _featureToCurrentState;
// A map of Map of (int)Feature => threshold configuration
private IFeatureStateThresholds<TFeature, TFeatureState>[] _featureToThresholds;
private readonly Func<TFeature, float?> _valueReader;
private readonly Func<TFeature, int> _featureToInt;
private readonly Func<float> _timeProvider;
#region Lookup Helpers
private int EnumToInt(TFeature value) => _featureToInt(value);
private static readonly TFeature[] FeatureEnumValues = (TFeature[])Enum.GetValues(typeof(TFeature));
private IFeatureThresholds<TFeature,TFeatureState> _featureThresholds;
#endregion
public FeatureStateProvider(Func<TFeature, float?> valueReader,
Func<TFeature, int> featureToInt,
Func<float> timeProvider)
{
_valueReader = valueReader;
_featureToInt = featureToInt;
_timeProvider = timeProvider;
}
public void InitializeThresholds(IFeatureThresholds<TFeature, TFeatureState> featureThresholds)
{
_featureThresholds = featureThresholds;
_featureToThresholds = ValidateFeatureThresholds(featureThresholds.FeatureStateThresholds);
InitializeStates();
}
public IFeatureStateThresholds<TFeature, TFeatureState>[] ValidateFeatureThresholds(
IReadOnlyList<IFeatureStateThresholds<TFeature, TFeatureState>> featureStateThresholdsList)
{
var featureToFeatureStateThresholds =
new IFeatureStateThresholds<TFeature, TFeatureState>[Enum.GetNames(typeof(TFeature)).Length];
foreach (var featureStateThresholds in featureStateThresholdsList)
{
var featureIdx = EnumToInt(featureStateThresholds.Feature);
featureToFeatureStateThresholds[featureIdx] = featureStateThresholds;
// Just check that the thresholds are set correctly.
for (var index = 0; index < featureStateThresholds.Thresholds.Count; index++)
{
var featureStateThreshold = featureStateThresholds.Thresholds[index];
if (featureStateThreshold.ToFirstWhenBelow >
featureStateThreshold.ToSecondWhenAbove)
{
Assert.IsTrue(false,
$"Feature {featureStateThresholds.Feature} threshold at index {index}: ToFirstWhenBelow should be less than ToSecondWhenAbove.");
}
}
}
for (int i = 0; i < featureToFeatureStateThresholds.Length; i++)
{
if (featureToFeatureStateThresholds[i] == null)
{
Assert.IsNotNull(featureToFeatureStateThresholds[i],
$"StateThresholds does not contain an entry for feature with value {i}");
}
}
return featureToFeatureStateThresholds;
}
private void InitializeStates()
{
// Set up current state
_featureToCurrentState = new FeatureStateSnapshot[FeatureEnumValues.Length];
foreach (TFeature feature in FeatureEnumValues)
{
int featureIdx = EnumToInt(feature);
// Set default state.
ref var currentState = ref _featureToCurrentState[featureIdx];
currentState.State = default;
currentState.DesiredState = default;
currentState.DesiredStateEntryTime = 0;
}
}
private ref IFeatureStateThresholds<TFeature, TFeatureState> GetFeatureThresholds(TFeature feature)
{
Assert.IsNotNull(_featureToThresholds, "Must call InitializeThresholds() before querying state");
return ref _featureToThresholds[EnumToInt(feature)];
}
public TFeatureState GetCurrentFeatureState(TFeature feature)
{
Assert.IsNotNull(_featureToThresholds, "Must call InitializeThresholds() before querying state");
ref var currentState = ref _featureToCurrentState[EnumToInt(feature)];
if (currentState.LastUpdatedFrameId == LastUpdatedFrameId)
{
return currentState.State;
}
// Reads the raw value
float? value = _valueReader(feature);
if (!value.HasValue)
{
return currentState.State;
}
// Hand data changed since this was last queried.
currentState.LastUpdatedFrameId = LastUpdatedFrameId;
// Determine which state we should transition to based on the thresholds, and previous state.
var featureStateThresholds = GetFeatureThresholds(feature).Thresholds;
TFeatureState desiredState;
if (!currentState.HasCurrentState)
{
desiredState = ReadDesiredState(value.Value, featureStateThresholds);
}
else
{
desiredState = ReadDesiredState(value.Value, featureStateThresholds,
currentState.State);
}
// If this is the same as the current state, do nothing.
if (desiredState.Equals(currentState.State))
{
return currentState.State;
}
// If the desired state is different from the previous frame, reset the timer
var currentTime = _timeProvider();
if (!desiredState.Equals(currentState.DesiredState))
{
currentState.DesiredStateEntryTime = currentTime;
currentState.DesiredState = desiredState;
}
// If the time in the desired state has exceeded the threshold, update the actual
// state.
if (currentState.DesiredStateEntryTime + _featureThresholds.MinTimeInState <= currentTime)
{
currentState.HasCurrentState = true;
currentState.State = desiredState;
}
return currentState.State;
}
private TFeatureState ReadDesiredState(float value,
IReadOnlyList<IFeatureStateThreshold<TFeatureState>> featureStateThresholds,
TFeatureState previousState)
{
// Run it through the threshold calculation.
var currentFeatureState = previousState;
for (int i = 0; i < featureStateThresholds.Count; ++i)
{
var featureStateThreshold = featureStateThresholds[i];
if (currentFeatureState.Equals(featureStateThreshold.FirstState) &&
value > featureStateThreshold.ToSecondWhenAbove)
{
// In the first state and exceeded the threshold to enter the second state.
return featureStateThreshold.SecondState;
}
if (currentFeatureState.Equals(featureStateThreshold.SecondState) &&
value < featureStateThreshold.ToFirstWhenBelow)
{
// In the second state and exceeded the threshold to enter the first state.
return featureStateThreshold.FirstState;
}
}
return previousState;
}
private TFeatureState ReadDesiredState(float value,
IReadOnlyList<IFeatureStateThreshold<TFeatureState>> featureStateThresholds)
{
// Run it through the threshold calculation.
TFeatureState currentFeatureState = default;
for (int i = 0; i < featureStateThresholds.Count; ++i)
{
var featureStateThreshold = featureStateThresholds[i];
if (value <= featureStateThreshold.ToSecondWhenAbove)
{
currentFeatureState = featureStateThreshold.FirstState;
break;
}
currentFeatureState = featureStateThreshold.SecondState;
}
return currentFeatureState;
}
public void ReadTouchedFeatureStates()
{
Assert.IsNotNull(_featureToThresholds, "Must call InitializeThresholds() before querying state");
for (var featureIdx = 0;
featureIdx < _featureToCurrentState.Length;
featureIdx++)
{
ref FeatureStateSnapshot stateSnapshot =
ref _featureToCurrentState[featureIdx];
if (stateSnapshot.LastUpdatedFrameId == 0)
{
// This state has never been queried via IsStateActive, so don't
// bother updating it.
continue;
}
// Force evaluation with this new frame Id.
GetCurrentFeatureState(FeatureEnumValues[featureIdx]);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b2b0882e924a4f52b3cebb599b3fa0d1
timeCreated: 1628024315

View File

@@ -0,0 +1,84 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
namespace Oculus.Interaction.PoseDetection
{
public static class FingerFeatureProperties
{
public static readonly FeatureStateDescription[] CurlFeatureStates =
{
new FeatureStateDescription("0", "Open"),
new FeatureStateDescription("1", "Neutral"),
new FeatureStateDescription("2", "Closed"),
};
public static readonly FeatureStateDescription[] FlexionFeatureStates =
{
new FeatureStateDescription("3", "Open"),
new FeatureStateDescription("4", "Neutral"),
new FeatureStateDescription("5", "Closed"),
};
public static readonly FeatureStateDescription[] AbductionFeatureStates =
{
new FeatureStateDescription("6", "None"),
new FeatureStateDescription("7", "Closed"),
new FeatureStateDescription("8", "Open"),
};
public static readonly FeatureStateDescription[] OppositionFeatureStates =
{
new FeatureStateDescription("9", "Touching"),
new FeatureStateDescription("10", "Near"),
new FeatureStateDescription("11", "None"),
};
public static IReadOnlyDictionary<FingerFeature, FeatureDescription> FeatureDescriptions
{
get;
} =
new Dictionary<FingerFeature, FeatureDescription>
{
[FingerFeature.Curl] = new FeatureDescription(
"Convex angle (in degrees) representing the top 2 joints of the fingers. Angle increases as finger curl becomes closed.",
"Calculated from the average of the convex angles formed by the 2 bones connected to Joint 2, and 2 bones connected to Joint 3.\n" +
"Values above 180 Positive show a curled state, while values below 180 represent hyper-extension.",
180,
260,
CurlFeatureStates),
[FingerFeature.Flexion] = new FeatureDescription(
"Convex angle (in degrees) of joint 1 of the finger. Angle increases as finger flexion becomes closed.",
"Calculated from the angle between the bones connected to finger Joint 1 around the Z axis of the joint.\n" +
"For fingers, joint 1 is commonly known as the 'Knuckle'; but for the thumb it is alongside the wrist.\n" +
"Values above 180 Positive show a curled state, while values below 180 represent hyper-extension." +
"upwards from the palm.",
180,
260,
FlexionFeatureStates),
[FingerFeature.Abduction] = new FeatureDescription(
"Angle (in degrees) between the given finger, and the next finger towards the pinkie.",
"Zero value implies that the two fingers are parallel.\n" +
"Positive angles indicate that the fingertips are spread apart.\n" +
"Small negative angles are possible, and indicate that the finger is pressed up against the next finger.",
8,
90,
AbductionFeatureStates),
[FingerFeature.Opposition] = new FeatureDescription(
"Distance between the tip of the given finger and the tip of the thumb.\n" +
"Calculated tracking space, with a 1.0 hand scale.",
"Positive values indicate that the fingertips are spread apart.\n" +
"Negative values are not possible.",
0,
0.2f,
OppositionFeatureStates)
};
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ef4951587e241c79251e9b88839be0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,288 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.PoseDetection
{
internal class FingerFeatureStateDictionary
{
struct HandFingerState
{
public FeatureStateProvider<FingerFeature, string> StateProvider;
}
private readonly HandFingerState[] _fingerState = new HandFingerState[Constants.NUM_FINGERS];
public void InitializeFinger(HandFinger finger,
FeatureStateProvider<FingerFeature, string> stateProvider)
{
_fingerState[(int)finger] = new HandFingerState
{
StateProvider = stateProvider
};
}
public FeatureStateProvider<FingerFeature, string> GetStateProvider(HandFinger finger)
{
return _fingerState[(int)finger].StateProvider;
}
}
public interface IFingerFeatureStateProvider
{
bool GetCurrentState(HandFinger finger, FingerFeature fingerFeature, out string currentState);
bool IsStateActive(HandFinger finger, FingerFeature feature, FeatureStateActiveMode mode, string stateId);
}
/// <summary>
/// Interprets finger feature values using <see cref="FingerShapes"/> and uses
/// the given <see cref="FingerFeatureStateThresholds"/> to quantize these values into states.
/// To avoid rapid fluctuations at the edges of two states, this class uses the calculated
/// feature state from the previous frame and the given state thresholds to apply a buffer
/// between state transition edges.
/// </summary>
public class FingerFeatureStateProvider : MonoBehaviour, IFingerFeatureStateProvider
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
public IHand Hand { get; private set; }
[Serializable]
public struct FingerStateThresholds
{
public HandFinger Finger;
public FingerFeatureStateThresholds StateThresholds;
}
[SerializeField]
private List<FingerStateThresholds> _fingerStateThresholds;
[Header("Advanced Settings")]
[SerializeField]
[Tooltip("If true, disables proactive evaluation of any FingerFeature that has been " +
"queried at least once. This will force lazy-evaluation of state within calls " +
"to IsStateActive, which means you must do so each frame to avoid missing " +
"transitions between states.")]
private bool _disableProactiveEvaluation;
protected bool _started = false;
private FingerFeatureStateDictionary _state;
Func<float> _timeProvider;
public static FingerShapes DefaultFingerShapes { get; } = new FingerShapes();
private FingerShapes _fingerShapes = DefaultFingerShapes;
private ReadOnlyHandJointPoses _handJointPoses;
#region Unity Lifecycle Methods
protected virtual void Awake()
{
Hand = _hand as IHand;
_state = new FingerFeatureStateDictionary();
_handJointPoses = ReadOnlyHandJointPoses.Empty;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Hand);
if (_timeProvider == null)
{
_timeProvider = () => Time.time;
}
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
Hand.HandUpdated += HandDataAvailable;
ReadStateThresholds();
}
}
protected virtual void OnDisable()
{
if (_started)
{
Hand.HandUpdated -= HandDataAvailable;
_handJointPoses = ReadOnlyHandJointPoses.Empty;
}
}
#endregion
private void ReadStateThresholds()
{
Assert.IsNotNull(_fingerStateThresholds);
Assert.IsNotNull(_timeProvider);
Assert.AreEqual(Constants.NUM_FINGERS, _fingerStateThresholds.Count);
HandFingerFlags seenFingers = HandFingerFlags.None;
foreach (FingerStateThresholds fingerStateThresholds in _fingerStateThresholds)
{
seenFingers |= HandFingerUtils.ToFlags(fingerStateThresholds.Finger);
HandFinger finger = fingerStateThresholds.Finger;
var featureStateProvider = _state.GetStateProvider(finger);
if (featureStateProvider == null)
{
featureStateProvider =
new FeatureStateProvider<FingerFeature, string>(
feature => GetFeatureValue(finger, feature),
feature => (int)feature,
_timeProvider);
_state.InitializeFinger(fingerStateThresholds.Finger,
featureStateProvider);
}
featureStateProvider.InitializeThresholds(fingerStateThresholds.StateThresholds);
}
Assert.AreEqual(seenFingers, HandFingerFlags.All);
}
private void HandDataAvailable()
{
int frameId = Hand.CurrentDataVersion;
if (!Hand.GetJointPosesFromWrist(out _handJointPoses))
{
return;
}
// Update the frameId of all state providers to mark data as dirty. If
// proactiveEvaluation is enabled, also read the state of any feature that has been
// touched, which will force it to evaluate.
if (!_disableProactiveEvaluation)
{
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; ++fingerIdx)
{
var featureStateProvider = _state.GetStateProvider((HandFinger)fingerIdx);
featureStateProvider.LastUpdatedFrameId = frameId;
featureStateProvider.ReadTouchedFeatureStates();
}
}
else
{
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; ++fingerIdx)
{
_state.GetStateProvider((HandFinger)fingerIdx).LastUpdatedFrameId =
frameId;
}
}
}
public bool GetCurrentState(HandFinger finger, FingerFeature fingerFeature, out string currentState)
{
if (!IsDataValid())
{
currentState = default;
return false;
}
else
{
currentState = GetCurrentFingerFeatureState(finger, fingerFeature);
return currentState != default;
}
}
private string GetCurrentFingerFeatureState(HandFinger finger, FingerFeature fingerFeature)
{
return _state.GetStateProvider(finger).GetCurrentFeatureState(fingerFeature);
}
/// <summary>
/// Returns the current value of the feature. If the finger joints are not populated with
/// valid data (for instance, due to a disconnected hand), the method will return NaN.
/// </summary>
public float? GetFeatureValue(HandFinger finger, FingerFeature fingerFeature)
{
if (!IsDataValid())
{
return null;
}
return _fingerShapes.GetValue(finger, fingerFeature, Hand);
}
private bool IsDataValid()
{
return _handJointPoses.Count > 0;
}
public FingerShapes GetValueProvider(HandFinger finger)
{
return _fingerShapes;
}
public bool IsStateActive(HandFinger finger, FingerFeature feature, FeatureStateActiveMode mode, string stateId)
{
var currentState = GetCurrentFingerFeatureState(finger, feature);
switch (mode)
{
case FeatureStateActiveMode.Is:
return currentState == stateId;
case FeatureStateActiveMode.IsNot:
return currentState != stateId;
default:
return false;
}
}
#region Inject
public void InjectAllFingerFeatureStateProvider(IHand hand, List<FingerStateThresholds> fingerStateThresholds, FingerShapes fingerShapes,
bool disableProactiveEvaluation)
{
InjectHand(hand);
InjectFingerStateThresholds(fingerStateThresholds);
InjectFingerShapes(fingerShapes);
InjectDisableProactiveEvaluation(disableProactiveEvaluation);
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectFingerStateThresholds(List<FingerStateThresholds> fingerStateThresholds)
{
_fingerStateThresholds = fingerStateThresholds;
}
public void InjectFingerShapes(FingerShapes fingerShapes)
{
_fingerShapes = fingerShapes;
}
public void InjectDisableProactiveEvaluation(bool disableProactiveEvaluation)
{
_disableProactiveEvaluation = disableProactiveEvaluation;
}
public void InjectOptionalTimeProvider(Func<float> timeProvider)
{
_timeProvider = timeProvider;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b942c16a6d6a4edaad7c18c7d5762cdf
timeCreated: 1627755300

View File

@@ -0,0 +1,100 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.PoseDetection
{
[Serializable]
public class FingerFeatureStateThreshold : IFeatureStateThreshold<string>
{
public FingerFeatureStateThreshold() { }
public FingerFeatureStateThreshold(float thresholdMidpoint,
float thresholdWidth,
string firstState,
string secondState)
{
_thresholdMidpoint = thresholdMidpoint;
_thresholdWidth = thresholdWidth;
_firstState = firstState;
_secondState = secondState;
}
[SerializeField]
private float _thresholdMidpoint;
[SerializeField]
private float _thresholdWidth;
[SerializeField]
private string _firstState;
[SerializeField]
private string _secondState;
public float ThresholdMidpoint => _thresholdMidpoint;
public float ThresholdWidth => _thresholdWidth;
public float ToFirstWhenBelow => _thresholdMidpoint - _thresholdWidth * 0.5f;
public float ToSecondWhenAbove => _thresholdMidpoint + _thresholdWidth * 0.5f;
public string FirstState => _firstState;
public string SecondState => _secondState;
}
[Serializable]
public class FingerFeatureThresholds : IFeatureStateThresholds<FingerFeature, string>
{
public FingerFeatureThresholds() { }
public FingerFeatureThresholds(FingerFeature feature,
IEnumerable<FingerFeatureStateThreshold> thresholds)
{
_feature = feature;
_thresholds = new List<FingerFeatureStateThreshold>(thresholds);
}
[SerializeField]
private FingerFeature _feature;
[SerializeField]
private List<FingerFeatureStateThreshold> _thresholds;
public FingerFeature Feature => _feature;
public IReadOnlyList<IFeatureStateThreshold<string>> Thresholds => _thresholds;
}
/// <summary>
/// A configuration class that contains a list of threshold boundaries
/// </summary>
[CreateAssetMenu(menuName = "Oculus/Interaction/SDK/Pose Detection/Finger Thresholds")]
public class FingerFeatureStateThresholds : ScriptableObject,
IFeatureThresholds<FingerFeature, string>
{
[SerializeField]
private List<FingerFeatureThresholds> _featureThresholds;
[SerializeField]
[Tooltip("Length of time that the finger must be in the new state before the feature " +
"state provider will use the new value.")]
private double _minTimeInState;
public void Construct(List<FingerFeatureThresholds> featureThresholds,
double minTimeInState)
{
_featureThresholds = featureThresholds;
_minTimeInState = minTimeInState;
}
public IReadOnlyList<IFeatureStateThresholds<FingerFeature, string>>
FeatureStateThresholds => _featureThresholds;
public double MinTimeInState => _minTimeInState;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c5af6ab9d1bd4a47970ad151090281f3
timeCreated: 1627757407

View File

@@ -0,0 +1,234 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.PoseDetection
{
public enum FingerFeature
{
Curl,
Flexion,
Abduction,
Opposition
}
public class FingerShapes
{
#region Joints Visualization Mappings
private static readonly HandJointId[][] CURL_LINE_JOINTS =
{
new [] {HandJointId.HandThumb2, HandJointId.HandThumb3, HandJointId.HandThumbTip},
new [] {HandJointId.HandIndex2, HandJointId.HandIndex3, HandJointId.HandIndexTip},
new [] {HandJointId.HandMiddle2, HandJointId.HandMiddle3, HandJointId.HandMiddleTip},
new [] {HandJointId.HandRing2, HandJointId.HandRing3, HandJointId.HandRingTip},
new [] {HandJointId.HandPinky2, HandJointId.HandPinky3, HandJointId.HandPinkyTip}
};
private static readonly HandJointId[][] FLEXION_LINE_JOINTS =
{
new [] {HandJointId.HandThumb1, HandJointId.HandThumb2, HandJointId.HandThumb3},
new [] {HandJointId.HandIndex1, HandJointId.HandIndex2, HandJointId.HandIndex3},
new [] {HandJointId.HandMiddle1, HandJointId.HandMiddle2, HandJointId.HandMiddle3},
new [] {HandJointId.HandRing1, HandJointId.HandRing2, HandJointId.HandRing3},
new [] {HandJointId.HandPinky1, HandJointId.HandPinky2, HandJointId.HandPinky3}
};
private static readonly HandJointId[][] ABDUCTION_LINE_JOINTS =
{
new [] {HandJointId.HandThumbTip, HandJointId.HandThumb1, HandJointId.HandIndex1, HandJointId.HandIndexTip},
new [] {HandJointId.HandIndexTip, HandJointId.HandIndex1, HandJointId.HandMiddle1, HandJointId.HandMiddleTip},
new [] {HandJointId.HandMiddleTip, HandJointId.HandMiddle1, HandJointId.HandRing1, HandJointId.HandRingTip},
new [] {HandJointId.HandRingTip, HandJointId.HandRing1, HandJointId.HandPinky1, HandJointId.HandPinkyTip},
Array.Empty<HandJointId>()
};
private static readonly HandJointId[][] OPPOSITION_LINE_JOINTS =
{
Array.Empty<HandJointId>(),
new [] {HandJointId.HandThumbTip, HandJointId.HandIndexTip},
new [] {HandJointId.HandThumbTip, HandJointId.HandMiddleTip},
new [] {HandJointId.HandThumbTip, HandJointId.HandRingTip},
new [] {HandJointId.HandThumbTip, HandJointId.HandPinkyTip},
};
#endregion
#region Joint Calculation Mappings
private static readonly HandJointId[][] CURL_ANGLE_JOINTS =
{
new[]
{
HandJointId.HandThumb1, HandJointId.HandThumb2, HandJointId.HandThumb3,
HandJointId.HandThumbTip
},
new[]
{
HandJointId.HandIndex1, HandJointId.HandIndex2, HandJointId.HandIndex3,
HandJointId.HandIndexTip
},
new[]
{
HandJointId.HandMiddle1, HandJointId.HandMiddle2, HandJointId.HandMiddle3,
HandJointId.HandMiddleTip
},
new[]
{
HandJointId.HandRing1, HandJointId.HandRing2, HandJointId.HandRing3,
HandJointId.HandRingTip
},
new[]
{
HandJointId.HandPinky1, HandJointId.HandPinky2, HandJointId.HandPinky3,
HandJointId.HandPinkyTip
}
};
private static readonly HandJointId[][] FLEXION_ANGLE_JOINTS =
{
new[]
{
HandJointId.HandWristRoot, HandJointId.HandThumb1, HandJointId.HandThumb2
},
new[]
{
HandJointId.HandWristRoot, HandJointId.HandIndex1, HandJointId.HandIndex2
},
new[]
{
HandJointId.HandWristRoot, HandJointId.HandMiddle1, HandJointId.HandMiddle2
},
new[]
{
HandJointId.HandWristRoot, HandJointId.HandRing1, HandJointId.HandRing2
},
new[]
{
HandJointId.HandWristRoot, HandJointId.HandPinky1, HandJointId.HandPinky2
}
};
#endregion
public virtual float GetValue(HandFinger finger, FingerFeature feature, IHand hand)
{
switch (feature)
{
case FingerFeature.Curl:
return GetCurlValue(finger, hand);
case FingerFeature.Flexion:
return GetFlexionValue(finger, hand);
case FingerFeature.Abduction:
return GetAbductionValue(finger, hand);
case FingerFeature.Opposition:
return GetOppositionValue(finger, hand);
default:
return 0.0f;
}
}
protected float ComputeAngleSum(HandJointId[] joints, IHand hand)
{
if (!hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
{
return 0.0f;
}
float angleSum = 0;
for (int i = 0; i < joints.Length - 2; i++)
{
ref readonly Pose midpointPose = ref poses[joints[i + 1]];
Vector3 pos0 = poses[joints[i]].position;
Vector3 pos1 = midpointPose.position;
Vector3 pos2 = poses[joints[i + 2]].position;
Vector3 bone1 = pos0 - pos1;
Vector3 bone2 = pos2 - pos1;
float angle = Vector3.SignedAngle(bone1, bone2, midpointPose.forward * -1f);
if (angle < 0f) angle += 360f;
angleSum += angle;
}
return angleSum;
}
public float GetCurlValue(HandFinger finger, IHand hand)
{
HandJointId[] handJointIds = CURL_ANGLE_JOINTS[(int)finger];
return ComputeAngleSum(handJointIds, hand) / (handJointIds.Length - 2);
}
public float GetFlexionValue(HandFinger finger, IHand hand)
{
return ComputeAngleSum(FLEXION_ANGLE_JOINTS[(int)finger], hand);
}
public float GetAbductionValue(HandFinger finger, IHand hand)
{
if (finger == HandFinger.Pinky
|| !hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
{
return 0.0f;
}
HandFinger nextFinger = finger + 1;
Vector3 fingerProximal = poses[HandJointUtils.GetHandFingerProximal(finger)].position;
Vector3 proximalMidpoint = Vector3.Lerp(
fingerProximal,
poses[HandJointUtils.GetHandFingerProximal(nextFinger)].position,
0.5f);
Vector3 normal1;
if (finger == HandFinger.Thumb)
{
normal1 = poses[HandJointUtils.GetHandFingerTip(finger)].position -
fingerProximal;
}
else
{
normal1 = poses[HandJointUtils.GetHandFingerTip(finger)].position -
proximalMidpoint;
}
Vector3 normal2 = poses[HandJointUtils.GetHandFingerTip(nextFinger)].position -
proximalMidpoint;
Vector3 axis = Vector3.Cross(normal1, normal2);
return Vector3.SignedAngle(normal1, normal2, axis);
}
public float GetOppositionValue(HandFinger finger, IHand hand)
{
if (finger == HandFinger.Thumb
|| !hand.GetJointPosesFromWrist(out ReadOnlyHandJointPoses poses))
{
return 0.0f;
}
Vector3 pos1 = poses[HandJointUtils.GetHandFingerTip(finger)].position;
Vector3 pos2 = poses[HandJointId.HandThumbTip].position;
return Vector3.Magnitude(pos1 - pos2);
}
public virtual IReadOnlyList<HandJointId> GetJointsAffected(HandFinger finger, FingerFeature feature)
{
switch (feature)
{
case FingerFeature.Curl:
return CURL_LINE_JOINTS[(int)finger];
case FingerFeature.Flexion:
return FLEXION_LINE_JOINTS[(int)finger];
case FingerFeature.Abduction:
return ABDUCTION_LINE_JOINTS[(int)finger];
case FingerFeature.Opposition:
return OPPOSITION_LINE_JOINTS[(int)finger];
default:
return null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b35099b781e8a9a449c26d6df610d496
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,122 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection
{
public class HmdOffset : MonoBehaviour
{
[SerializeField, Interface(typeof(IHmd))]
private MonoBehaviour _hmd;
private IHmd Hmd;
[SerializeField]
private Vector3 _offsetTranslation = Vector3.zero;
[SerializeField]
private Vector3 _offsetRotation = Vector3.zero;
[SerializeField]
private bool _disablePitchFromSource = false;
[SerializeField]
private bool _disableYawFromSource = false;
[SerializeField]
private bool _disableRollFromSource = false;
protected virtual void Awake()
{
Hmd = _hmd as IHmd;
}
protected virtual void Start()
{
Assert.IsNotNull(Hmd);
}
protected virtual void Update()
{
if (!Hmd.GetRootPose(out Pose centerEyePose))
{
return;
}
var centerEyePosition = centerEyePose.position;
var centerEyeRotation = centerEyePose.rotation;
var eulerAngles = centerEyeRotation.eulerAngles;
var pitch = Quaternion.Euler(new Vector3(eulerAngles.x, 0.0f, 0.0f));
var yaw = Quaternion.Euler(new Vector3(0.0f, eulerAngles.y, 0.0f));
var roll = Quaternion.Euler(new Vector3(0.0f, 0.0f, eulerAngles.z));
var finalSourceRotation = Quaternion.identity;
if (!_disableYawFromSource)
{
finalSourceRotation *= yaw;
}
if (!_disablePitchFromSource)
{
finalSourceRotation *= pitch;
}
if (!_disableRollFromSource)
{
finalSourceRotation *= roll;
}
var totalRotation = finalSourceRotation * Quaternion.Euler(_offsetRotation);
transform.position = centerEyePosition +
totalRotation * _offsetTranslation;
transform.rotation = totalRotation;
}
#region Inject
public void InjectAllHmdOffset(IHmd hmd)
{
InjectHmd(hmd);
}
public void InjectHmd(IHmd hmd)
{
_hmd = hmd as MonoBehaviour;
Hmd = hmd;
}
public void InjectOptionalOffsetTranslation(Vector3 val)
{
_offsetTranslation = val;
}
public void InjectOptionalOffsetRotation(Vector3 val)
{
_offsetRotation = val;
}
public void InjectOptionalDisablePitchFromSource(bool val)
{
_disablePitchFromSource = val;
}
public void InjectOptionalDisableYawFromSource(bool val)
{
_disableYawFromSource = val;
}
public void InjectOptionalDisableRollFromSource(bool val)
{
_disableRollFromSource = val;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8310d4698426ea24e923c8a77fe0f3a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,22 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
namespace Oculus.Interaction.PoseDetection
{
public interface IFeatureStateThreshold<TFeatureState>
{
float ToFirstWhenBelow {get;}
float ToSecondWhenAbove {get;}
TFeatureState FirstState {get;}
TFeatureState SecondState {get;}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6152072e57cc4c7c87b467b1237dc8e0
timeCreated: 1628024382

View File

@@ -0,0 +1,22 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
namespace Oculus.Interaction.PoseDetection
{
public interface IFeatureStateThresholds<TFeature, TFeatureState>
{
TFeature Feature {get;}
IReadOnlyList<IFeatureStateThreshold<TFeatureState>> Thresholds {get;}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a32c5568211a4c8086cbf588996b654b
timeCreated: 1628024369

View File

@@ -0,0 +1,27 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
namespace Oculus.Interaction.PoseDetection
{
public interface IFeatureThresholds<TFeature, TFeatureState>
{
IReadOnlyList<IFeatureStateThresholds<TFeature, TFeatureState>>
FeatureStateThresholds
{
get;
}
double MinTimeInState { get; }
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b736c28bc3ad4a639641c5c8f0fbd88d
timeCreated: 1628622856

View File

@@ -0,0 +1,179 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.PoseDetection
{
/// <summary>
/// This component tracks the distance between two hand joints and reports
/// <see cref="IActiveState.Active"/> when distance is under a provided threshold.
/// </summary>
public class JointDistanceActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _handA;
private IHand HandA;
[SerializeField]
private HandJointId _jointIdA;
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _handB;
private IHand HandB;
[SerializeField]
private HandJointId _jointIdB;
[SerializeField]
private float _distance = 0.05f;
[SerializeField]
private float _thresholdWidth = 0.02f;
[SerializeField]
private float _minTimeInState = 0.05f;
public bool Active
{
get
{
if (!isActiveAndEnabled)
{
return false;
}
UpdateActiveState();
return _activeState;
}
}
private bool _activeState = false;
private bool _internalState = false;
private float _lastStateChangeTime = 0f;
private int _lastStateUpdateFrame = 0;
protected virtual void Awake()
{
HandA = _handA as IHand;
HandB = _handB as IHand;
}
protected virtual void Start()
{
Assert.IsNotNull(HandA);
Assert.IsNotNull(HandB);
}
protected virtual void Update()
{
UpdateActiveState();
}
private void UpdateActiveState()
{
if (Time.frameCount <= _lastStateUpdateFrame)
{
return;
}
_lastStateUpdateFrame = Time.frameCount;
bool newState = JointDistanceWithinThreshold();
if (newState != _internalState)
{
_internalState = newState;
_lastStateChangeTime = Time.unscaledTime;
}
if (Time.unscaledTime - _lastStateChangeTime >= _minTimeInState)
{
_activeState = _internalState;
}
}
private bool JointDistanceWithinThreshold()
{
if (HandA.GetJointPose(_jointIdA, out Pose poseA) &&
HandB.GetJointPose(_jointIdB, out Pose poseB))
{
float threshold = _internalState ?
_distance + _thresholdWidth * 0.5f :
_distance - _thresholdWidth * 0.5f;
return Vector3.Distance(poseA.position, poseB.position) <= threshold;
}
else
{
return false;
}
}
#if UNITY_EDITOR
protected virtual void OnValidate()
{
_distance = Mathf.Max(_distance, 0f);
_minTimeInState = Mathf.Max(_minTimeInState, 0f);
_thresholdWidth = Mathf.Max(_thresholdWidth, 0f);
}
#endif
#region Inject
public void InjectAllJointDistanceActiveState(IHand handA, HandJointId jointIdA, IHand handB, HandJointId jointIdB)
{
InjectHandA(handA);
InjectJointIdA(jointIdA);
InjectHandB(handB);
InjectJointIdB(jointIdB);
}
public void InjectHandA(IHand handA)
{
_handA = handA as MonoBehaviour;
HandA = handA;
}
public void InjectJointIdA(HandJointId jointIdA)
{
_jointIdA = jointIdA;
}
public void InjectHandB(IHand handB)
{
_handB = handB as MonoBehaviour;
HandB = handB;
}
public void InjectJointIdB(HandJointId jointIdB)
{
_jointIdB = jointIdB;
}
public void InjectOptionalDistance(float val)
{
_distance = val;
}
public void InjectOptionalThresholdWidth(float val)
{
_thresholdWidth = val;
}
public void InjectOptionalMinTimeInState(float val)
{
_minTimeInState = val;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 06fe84cad95e40a428c3ca4346ad3afd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,270 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection
{
/// <summary>
/// Chains together a number of IActiveStates into a sequence.
/// The Sequence._stepsToActivate field contains an optional list of IActiveState's which must be 'activated' in
/// order.
/// The sequence can progress from Step N to N + 1 when: MinActiveTime <= "Time step N active for" <= MaxStepTime, and:
/// Step N just became inactive OR
/// Step N is the last step OR
/// Step N+1 is active
///
/// Note that once the sequence has moved on to the next step, the previous step does not need to remain active.
/// Each step has three fields:
/// ActiveState: The IActiveState that is used to determine if the conditions of this step are fulfilled
/// MinActiveTime: How long (in seconds) the IActiveState of this step must be contiguously active before moving
/// on to the next step. If the ActiveState drops out of being active for even a single frame
/// the countdown is reset.
/// MaxStepTime: If the elapsed time that the sequence is spent waiting for this step to reach its MinActiveTime
/// exceeds this value then the whole sequence is reset back to the beginning.
///
/// Once all steps are complete the Sequence.Active becomes true. It will remain true as long as RemainActiveWhile
/// is true. If _remainActiveCooldown > 0, Sequence.Active will remain active even after RemainActiveWhile becomes
/// false until the cooldown timer is met. The timer is reset if RemainActiveWhile becomes true again.
/// </summary>
public class Sequence : MonoBehaviour
{
[Serializable]
public class ActivationStep
{
[SerializeField, Interface(typeof(IActiveState))]
private MonoBehaviour _activeState;
public IActiveState ActiveState { get; private set; }
[SerializeField]
[Tooltip("This step must be consistently active for this amount of time before continuing to the next step.")]
private float _minActiveTime;
public float MinActiveTime => _minActiveTime;
[SerializeField]
[Tooltip(
"Maximum time that can be spent waiting for this step to complete, before the whole sequence is abandoned. This value must be greater than minActiveTime, or zero. This value is ignored if zero, and for the first step in the list.")]
private float _maxStepTime;
public float MaxStepTime => _maxStepTime;
public ActivationStep()
{
}
public ActivationStep(IActiveState activeState, float minActiveTime, float maxStepTime)
{
ActiveState = activeState;
_minActiveTime = minActiveTime;
_maxStepTime = maxStepTime;
}
public void Start()
{
if (ActiveState == null)
{
ActiveState = _activeState as IActiveState;
}
Assert.IsNotNull(ActiveState);
}
}
[SerializeField, Optional]
private ActivationStep[] _stepsToActivate;
[SerializeField, Optional, Interface(typeof(IActiveState))]
private MonoBehaviour _remainActiveWhile;
[SerializeField, Optional]
private float _remainActiveCooldown;
private IActiveState RemainActiveWhile { get; set; }
/// <summary>
/// Returns the index of the step in <see cref="_stepsToActivate"/> whose conditions are
/// waiting to be activated.
/// If <see cref="Active"/> is true, this value will be set to the
/// size of <see cref="_stepsToActivate"/>.
/// If <see cref="_stepsToActivate"/> has no steps, this property will be 0.
/// </summary>
public int CurrentActivationStep { get; private set; }
private float _currentStepActivatedTime;
private float _stepFailedTime;
private bool _currentStepWasActive;
Func<float> _timeProvider;
private float _cooldownExceededTime;
private bool _wasRemainActive;
#region Unity Lifecycle
protected virtual void Awake()
{
RemainActiveWhile = _remainActiveWhile as IActiveState;
ResetState();
}
protected virtual void Start()
{
if (_timeProvider == null)
{
_timeProvider = () => Time.time;
}
if (_stepsToActivate == null)
{
_stepsToActivate = Array.Empty<ActivationStep>();
}
foreach (var step in _stepsToActivate)
{
step.Start();
}
}
protected virtual void Update()
{
var time = _timeProvider();
if (Active)
{
// Test for active, if RemainActiveWhile is set.
bool shouldBeActive = RemainActiveWhile != null && RemainActiveWhile.Active;
if (!shouldBeActive)
{
if (_wasRemainActive)
{
_cooldownExceededTime = time + _remainActiveCooldown;
}
if (_cooldownExceededTime <= time)
{
Active = false;
}
}
_wasRemainActive = shouldBeActive;
// No longer active; start activation condition at the beginning
if (!Active)
{
ResetState();
}
return;
}
if (CurrentActivationStep < _stepsToActivate.Length)
{
var currentStep = _stepsToActivate[CurrentActivationStep];
if (time > _stepFailedTime && CurrentActivationStep > 0 && currentStep.MaxStepTime > 0.0f)
{
// Failed to activate before max time limit reached. Start from the beginning.
ResetState();
}
bool currentStepIsActive = currentStep.ActiveState.Active;
if (currentStepIsActive)
{
if (!_currentStepWasActive)
{
// Step wasn't active, but now it is! Start the timer until next step can
// be entered.
_currentStepActivatedTime = time + currentStep.MinActiveTime;
}
}
if (time >= _currentStepActivatedTime && _currentStepWasActive)
{
// Time constraint met. Go to next step if either:
// - this step just became inactive OR
// - this is the last step OR
// - the next step is active
var nextStepIndex = CurrentActivationStep + 1;
bool thisStepCondition = !currentStepIsActive;
bool nextStepCondition = (nextStepIndex == _stepsToActivate.Length) ||
_stepsToActivate[nextStepIndex].ActiveState.Active;
if (thisStepCondition || nextStepCondition)
{
EnterNextStep(time);
}
}
_currentStepWasActive = currentStepIsActive;
}
else if (RemainActiveWhile != null)
{
Active = RemainActiveWhile.Active;
}
}
private void EnterNextStep(float time)
{
var currentStep = _stepsToActivate[CurrentActivationStep];
CurrentActivationStep++;
_currentStepWasActive = false;
_stepFailedTime = time + currentStep.MaxStepTime;
if (CurrentActivationStep != _stepsToActivate.Length)
{
return;
}
// This was the last step. Activate.
Active = true;
// In case there is no RemainActiveWhile condition, start the cooldown
// timer
_cooldownExceededTime = time + _remainActiveCooldown;
}
private void ResetState()
{
CurrentActivationStep = 0;
_currentStepWasActive = false;
_currentStepActivatedTime = 0.0f;
}
#endregion
public bool Active { get; private set; }
#region Inject
public void InjectOptionalStepsToActivate(ActivationStep[] stepsToActivate)
{
_stepsToActivate = stepsToActivate;
}
public void InjectOptionalRemainActiveWhile(IActiveState activeState)
{
_remainActiveWhile = activeState as MonoBehaviour;
RemainActiveWhile = activeState;
}
public void InjectOptionalTimeProvider(Func<float> timeProvider)
{
_timeProvider = timeProvider;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e7ab4178b1f98e40bc6baf2176709df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using UnityEngine;
using UnityEngine.Assertions;
namespace Oculus.Interaction.PoseDetection
{
public class SequenceActiveState : MonoBehaviour, IActiveState
{
[SerializeField]
private Sequence _sequence;
[SerializeField]
private bool _activateIfStepsStarted;
[SerializeField]
private bool _activateIfStepsComplete = true;
protected virtual void Start()
{
Assert.IsNotNull(_sequence);
}
public bool Active
{
get
{
return (_activateIfStepsStarted && _sequence.CurrentActivationStep > 0 && !_sequence.Active) ||
(_activateIfStepsComplete && _sequence.Active);
}
}
#region Inject
public void InjectAllSequenceActiveState(Sequence sequence,
bool activateIfStepsStarted, bool activateIfStepsComplete)
{
InjectSequence(sequence);
InjectActivateIfStepsStarted(activateIfStepsStarted);
InjectActivateIfStepsComplete(activateIfStepsComplete);
}
public void InjectSequence(Sequence sequence)
{
_sequence = sequence;
}
public void InjectActivateIfStepsStarted(bool activateIfStepsStarted)
{
_activateIfStepsStarted = activateIfStepsStarted;
}
public void InjectActivateIfStepsComplete(bool activateIfStepsComplete)
{
_activateIfStepsComplete = activateIfStepsComplete;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d2a8d5cf844b463aabaed6d6db3da8c0
timeCreated: 1634670079

View File

@@ -0,0 +1,154 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.PoseDetection
{
[CreateAssetMenu(menuName = "Oculus/Interaction/SDK/Pose Detection/Shape")]
public class ShapeRecognizer : ScriptableObject
{
[Serializable]
public class FingerFeatureConfigList
{
[SerializeField]
private List<FingerFeatureConfig> _value;
public IReadOnlyList<FingerFeatureConfig> Value => _value;
public FingerFeatureConfigList() { }
public FingerFeatureConfigList(List<FingerFeatureConfig> value)
{
_value = value;
}
}
[Serializable]
public class FingerFeatureConfig : FeatureConfigBase<FingerFeature>
{
}
[SerializeField]
private string _shapeName;
[SerializeField]
private FingerFeatureConfigList _thumbFeatureConfigs = new FingerFeatureConfigList();
[SerializeField]
private FingerFeatureConfigList _indexFeatureConfigs = new FingerFeatureConfigList();
[SerializeField]
private FingerFeatureConfigList _middleFeatureConfigs = new FingerFeatureConfigList();
[SerializeField]
private FingerFeatureConfigList _ringFeatureConfigs = new FingerFeatureConfigList();
[SerializeField]
private FingerFeatureConfigList _pinkyFeatureConfigs = new FingerFeatureConfigList();
public IReadOnlyList<FingerFeatureConfig> ThumbFeatureConfigs => _thumbFeatureConfigs.Value;
public IReadOnlyList<FingerFeatureConfig> IndexFeatureConfigs => _indexFeatureConfigs.Value;
public IReadOnlyList<FingerFeatureConfig> MiddleFeatureConfigs => _middleFeatureConfigs.Value;
public IReadOnlyList<FingerFeatureConfig> RingFeatureConfigs => _ringFeatureConfigs.Value;
public IReadOnlyList<FingerFeatureConfig> PinkyFeatureConfigs => _pinkyFeatureConfigs.Value;
public string ShapeName => _shapeName;
public IReadOnlyList<FingerFeatureConfig> GetFingerFeatureConfigs(HandFinger finger)
{
switch (finger)
{
case HandFinger.Thumb:
return ThumbFeatureConfigs;
case HandFinger.Index:
return IndexFeatureConfigs;
case HandFinger.Middle:
return MiddleFeatureConfigs;
case HandFinger.Ring:
return RingFeatureConfigs;
case HandFinger.Pinky:
return PinkyFeatureConfigs;
default:
throw new ArgumentException("must be a HandFinger enum value",
nameof(finger));
}
}
public IEnumerable<ValueTuple<HandFinger, IReadOnlyList<FingerFeatureConfig>>>
GetFingerFeatureConfigs()
{
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; ++fingerIdx)
{
HandFinger finger = (HandFinger)fingerIdx;
var configs = GetFingerFeatureConfigs(finger);
if (configs.Count == 0)
{
continue;
}
yield return new ValueTuple<HandFinger, IReadOnlyList<FingerFeatureConfig>>(finger,
configs);
}
}
#region Inject
public void InjectAllShapeRecognizer(IDictionary<HandFinger, FingerFeatureConfig[]> fingerFeatureConfigs)
{
FingerFeatureConfigList ReadFeatureConfigs(HandFinger finger)
{
if (!fingerFeatureConfigs.TryGetValue(finger, out FingerFeatureConfig[] configs))
{
configs = Array.Empty<FingerFeatureConfig>();
}
return new FingerFeatureConfigList(new List<FingerFeatureConfig>(configs));
}
_thumbFeatureConfigs = ReadFeatureConfigs(HandFinger.Thumb);
_indexFeatureConfigs = ReadFeatureConfigs(HandFinger.Index);
_middleFeatureConfigs = ReadFeatureConfigs(HandFinger.Middle);
_ringFeatureConfigs = ReadFeatureConfigs(HandFinger.Ring);
_pinkyFeatureConfigs = ReadFeatureConfigs(HandFinger.Pinky);
}
public void InjectThumbFeatureConfigs(FingerFeatureConfig[] configs)
{
_thumbFeatureConfigs = new FingerFeatureConfigList(new List<FingerFeatureConfig>(configs));
}
public void InjectIndexFeatureConfigs(FingerFeatureConfig[] configs)
{
_indexFeatureConfigs = new FingerFeatureConfigList(new List<FingerFeatureConfig>(configs));
}
public void InjectMiddleFeatureConfigs(FingerFeatureConfig[] configs)
{
_middleFeatureConfigs = new FingerFeatureConfigList(new List<FingerFeatureConfig>(configs));
}
public void InjectRingFeatureConfigs(FingerFeatureConfig[] configs)
{
_ringFeatureConfigs = new FingerFeatureConfigList(new List<FingerFeatureConfig>(configs));
}
public void InjectPinkyFeatureConfigs(FingerFeatureConfig[] configs)
{
_pinkyFeatureConfigs = new FingerFeatureConfigList(new List<FingerFeatureConfig>(configs));
}
public void InjectShapeName(string shapeName)
{
_shapeName = shapeName;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4cfe1df7ed391a24fbe2a2d275e81b06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,146 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.PoseDetection
{
public class ShapeRecognizerActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
public IHand Hand { get; private set; }
[SerializeField]
private ShapeRecognizer[] _shapes;
public IReadOnlyList<ShapeRecognizer> Shapes => _shapes;
private IFingerFeatureStateProvider FingerFeatureStateProvider { get; set; }
public Handedness Handedness => Hand.Handedness;
struct FingerFeatureStateUsage
{
public HandFinger handFinger;
public ShapeRecognizer.FingerFeatureConfig config;
}
private List<FingerFeatureStateUsage> _allFingerStates = new List<FingerFeatureStateUsage>();
protected virtual void Awake()
{
Hand = _hand as IHand;
}
protected virtual void Start()
{
Assert.IsNotNull(Hand);
Assert.IsNotNull(_shapes);
for (var index = 0; index < _shapes.Length; index++)
{
var sr = _shapes[index];
if (sr == null)
{
Assert.IsNotNull(sr, "_shapes[" + index + "] != null");
}
}
bool foundAspect = Hand.GetHandAspect(out IFingerFeatureStateProvider state);
Assert.IsTrue(foundAspect);
FingerFeatureStateProvider = state;
_allFingerStates = FlattenUsedFeatures();
// Warm up the proactive evaluation
InitStateProvider();
}
private void InitStateProvider()
{
foreach (FingerFeatureStateUsage state in _allFingerStates)
{
FingerFeatureStateProvider.GetCurrentState(state.handFinger, state.config.Feature, out _);
}
}
private List<FingerFeatureStateUsage> FlattenUsedFeatures()
{
var fingerFeatureStateUsages = new List<FingerFeatureStateUsage>();
foreach (var sr in _shapes)
{
int configCount = 0;
for (var fingerIdx = 0; fingerIdx < Constants.NUM_FINGERS; ++fingerIdx)
{
var handFinger = (HandFinger)fingerIdx;
foreach (var config in sr.GetFingerFeatureConfigs(handFinger))
{
++configCount;
fingerFeatureStateUsages.Add(new FingerFeatureStateUsage()
{
handFinger = handFinger, config = config
});
}
}
// If this assertion is hit, open the ScriptableObject in the Unity Inspector
// and ensure that it has at least one valid condition.
Assert.IsTrue(configCount > 0, $"Shape {sr.ShapeName} has no valid conditions.");
}
return fingerFeatureStateUsages;
}
public bool Active
{
get
{
if (!isActiveAndEnabled || _allFingerStates.Count == 0)
{
return false;
}
foreach (FingerFeatureStateUsage stateUsage in _allFingerStates)
{
if (!FingerFeatureStateProvider.IsStateActive(stateUsage.handFinger,
stateUsage.config.Feature, stateUsage.config.Mode, stateUsage.config.State))
{
return false;
}
}
return true;
}
}
#region Inject
public void InjectAllShapeRecognizerActiveState(IHand hand, ShapeRecognizer[] shapes)
{
InjectHand(hand);
InjectShapes(shapes);
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectShapes(ShapeRecognizer[] shapes)
{
_shapes = shapes;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08f7cfb1f9629da4494ac0840f3a3cfd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System.Collections.Generic;
namespace Oculus.Interaction.PoseDetection
{
public static class TransformFeatureProperties
{
public static IReadOnlyDictionary<TransformFeature, FeatureDescription> FeatureDescriptions
{
get;
} = CreateFeatureDescriptions();
private static IReadOnlyDictionary<TransformFeature, FeatureDescription> CreateFeatureDescriptions()
{
int startIndex = 0;
return new Dictionary<TransformFeature, FeatureDescription>
{
[TransformFeature.WristUp] = CreateDesc(ref startIndex),
[TransformFeature.WristDown] = CreateDesc(ref startIndex),
[TransformFeature.PalmDown] = CreateDesc(ref startIndex),
[TransformFeature.PalmUp] = CreateDesc(ref startIndex),
[TransformFeature.PalmTowardsFace] = CreateDesc(ref startIndex),
[TransformFeature.PalmAwayFromFace] = CreateDesc(ref startIndex),
[TransformFeature.FingersUp] = CreateDesc(ref startIndex),
[TransformFeature.FingersDown] = CreateDesc(ref startIndex),
[TransformFeature.PinchClear] = CreateDesc(ref startIndex),
};
}
private static FeatureDescription CreateDesc(ref int startIndex)
{
var desc = new FeatureDescription("", "", 0, 180,
new[]
{
new FeatureStateDescription((startIndex).ToString(), "True"),
// to support legacy data (which had a 3rd intermediary step), need to skip 1.
new FeatureStateDescription((startIndex + 2).ToString(), "False")
});
startIndex += 3;
return desc;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8d8fbcf2f8f5475fa39c11050b34cdab
timeCreated: 1631578210

View File

@@ -0,0 +1,348 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.PoseDetection
{
public enum UpVectorType
{
Head,
Tracking,
World
}
[Serializable]
public class TransformConfig
{
public TransformConfig()
{
PositionOffset = Vector3.zero;
RotationOffset = Vector3.zero;
UpVectorType = UpVectorType.Head;
FeatureThresholds = null;
InstanceId = 0;
}
// Position offset relative to the reference transform.
public Vector3 PositionOffset;
// Rotational offset relative to the reference transform.
public Vector3 RotationOffset;
public UpVectorType UpVectorType;
public TransformFeatureStateThresholds FeatureThresholds;
// set via component that uses this class
public int InstanceId { get; set; }
}
public class TransformJointData
{
public bool IsValid;
public Handedness Handedness;
public Pose CenterEyePose, WristPose;
public Vector3 TrackingSystemUp;
}
internal class TransformFeatureStateCollection
{
public class TransformStateInfo
{
public TransformStateInfo(TransformConfig transformConfig,
FeatureStateProvider<TransformFeature, string> stateProvider)
{
Config = transformConfig;
StateProvider = stateProvider;
}
public TransformConfig Config;
public FeatureStateProvider<TransformFeature, string> StateProvider;
}
private Dictionary<int, TransformStateInfo> _idToTransformStateInfo =
new Dictionary<int, TransformStateInfo>();
public void RegisterConfig(TransformConfig transformConfig, TransformJointData jointData,
Func<float> timeProvider)
{
bool containsKeyAlready = _idToTransformStateInfo.ContainsKey(transformConfig.InstanceId);
Assert.IsFalse(containsKeyAlready,
"Trying to register multiple configs with the same id into " +
"TransformFeatureStateCollection.");
var featureStateProvider = new FeatureStateProvider<TransformFeature, string>
// note that jointData and transformConfig are reference types (classes), because they can change
// during run time
((feature) => TransformFeatureValueProvider.GetValue(feature, jointData, transformConfig),
feature => (int)feature,
timeProvider);
TransformStateInfo newTransfState = new TransformStateInfo(transformConfig, featureStateProvider);
featureStateProvider.InitializeThresholds(transformConfig.FeatureThresholds);
_idToTransformStateInfo.Add(transformConfig.InstanceId, newTransfState);
}
public void UnRegisterConfig(TransformConfig transformConfig)
{
_idToTransformStateInfo.Remove(transformConfig.InstanceId);
}
public FeatureStateProvider<TransformFeature, string> GetStateProvider(
TransformConfig transformConfig)
{
return _idToTransformStateInfo[transformConfig.InstanceId].StateProvider;
}
public void SetConfig(int configId, TransformConfig config)
{
_idToTransformStateInfo[configId].Config = config;
}
public TransformConfig GetConfig(int configId)
{
return _idToTransformStateInfo[configId].Config;
}
public void UpdateFeatureStates(int lastUpdatedFrameId,
bool disableProactiveEvaluation)
{
foreach(var transformStateInfo in _idToTransformStateInfo.Values)
{
var featureStateProvider = transformStateInfo.StateProvider;
if (!disableProactiveEvaluation)
{
featureStateProvider.LastUpdatedFrameId = lastUpdatedFrameId;
featureStateProvider.ReadTouchedFeatureStates();
}
else
{
featureStateProvider.LastUpdatedFrameId = lastUpdatedFrameId;
}
}
}
}
/// <summary>
/// Interprets transform feature values from a <see cref="TransformFeatureValueProvider"/>
/// and uses the given <see cref="TransformFeatureStateThresholds"/> to quantize
/// these values into states. To avoid rapid fluctuations at the edges
/// of two states, this classes uses the calculated feature states from the previous
/// frame and the given state thresholds to apply a buffer between
/// state transition edges.
/// </summary>
public class TransformFeatureStateProvider : MonoBehaviour
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
public IHand Hand { get; private set; }
[SerializeField, Interface(typeof(ITrackingToWorldTransformer))]
private MonoBehaviour _trackingToWorldTransformer;
public ITrackingToWorldTransformer TrackingToWorldTransformer { get; private set; }
[Header("Advanced Settings")]
[SerializeField]
[Tooltip("If true, disables proactive evaluation of any TransformFeature that has been " +
"queried at least once. This will force lazy-evaluation of state within calls " +
"to IsStateActive, which means you must do so each frame to avoid missing " +
"transitions between states.")]
private bool _disableProactiveEvaluation;
private TransformJointData _jointData = new TransformJointData();
private TransformFeatureStateCollection _transformFeatureStateCollection;
Func<float> _timeProvider;
protected bool _started = false;
protected virtual void Awake()
{
Hand = _hand as IHand;
_transformFeatureStateCollection = new TransformFeatureStateCollection();
if (_timeProvider == null)
{
_timeProvider = () => Time.time;
}
}
public void RegisterNewConfig(TransformConfig transformConfig)
{
_transformFeatureStateCollection.RegisterConfig(transformConfig, _jointData, _timeProvider);
}
public void UnRegisterConfig(TransformConfig transformConfig)
{
_transformFeatureStateCollection.UnRegisterConfig(transformConfig);
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Hand);
TrackingToWorldTransformer = _trackingToWorldTransformer as ITrackingToWorldTransformer;
Assert.IsNotNull(TrackingToWorldTransformer);
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
Hand.HandUpdated += HandDataAvailable;
}
}
protected virtual void OnDisable()
{
if (_started)
{
Hand.HandUpdated -= HandDataAvailable;
}
}
private void HandDataAvailable()
{
UpdateJointData();
UpdateStateForHand();
}
private void UpdateJointData()
{
_jointData.IsValid = Hand.GetRootPose(out _jointData.WristPose) &&
Hand.GetCenterEyePose(out _jointData.CenterEyePose);
if (!_jointData.IsValid)
{
return;
}
_jointData.Handedness = Hand.Handedness;
_jointData.TrackingSystemUp = TrackingToWorldTransformer.Transform.up;
}
private void UpdateStateForHand()
{
// Update the frameId of all state providers to mark data as dirty. If
// proactiveEvaluation is enabled, also read the state of any feature that has been
// touched, which will force it to evaluate.
_transformFeatureStateCollection.UpdateFeatureStates(
Hand.CurrentDataVersion,
_disableProactiveEvaluation);
}
public bool IsHandDataValid()
{
return _jointData.IsValid;
}
public bool IsStateActive(TransformConfig config, TransformFeature feature, FeatureStateActiveMode mode, string stateId)
{
var currentState = GetCurrentFeatureState(config, feature);
switch (mode)
{
case FeatureStateActiveMode.Is:
return currentState == stateId;
case FeatureStateActiveMode.IsNot:
return currentState != stateId;
default:
return false;
}
}
private string GetCurrentFeatureState(TransformConfig config,
TransformFeature feature)
{
return _transformFeatureStateCollection.GetStateProvider(config).
GetCurrentFeatureState(feature);
}
public bool GetCurrentState(TransformConfig config, TransformFeature transformFeature,
out string currentState)
{
if (!IsHandDataValid())
{
currentState = default;
return false;
}
currentState = GetCurrentFeatureState(config, transformFeature);
return currentState != default;
}
/// <summary>
/// Returns the current value of the feature. If the hand joints are not populated with
/// valid data (for instance, due to a disconnected hand), the method will return null;
/// </summary>
public float? GetFeatureValue(TransformConfig config,
TransformFeature transformFeature)
{
if (!IsHandDataValid())
{
return null;
}
return TransformFeatureValueProvider.GetValue(transformFeature,
_jointData, config);
}
public void GetFeatureVectorAndWristPos(TransformConfig config,
TransformFeature transformFeature, bool isHandVector, ref Vector3? featureVec,
ref Vector3? wristPos)
{
featureVec = null;
wristPos = null;
if (!IsHandDataValid())
{
return;
}
featureVec = isHandVector ?
TransformFeatureValueProvider.GetHandVectorForFeature(transformFeature,
_jointData, in config) :
TransformFeatureValueProvider.GetTargetVectorForFeature(transformFeature,
_jointData, in config);
wristPos = _jointData.WristPose.position;
}
#region Inject
public void InjectAllTransformFeatureStateProvider(IHand hand, bool disableProactiveEvaluation)
{
Hand = hand;
_disableProactiveEvaluation = disableProactiveEvaluation;
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectDisableProactiveEvaluation(bool disabled)
{
_disableProactiveEvaluation = disabled;
}
public void InjectOptionalTimeProvider(Func<float> timeProvider)
{
_timeProvider = timeProvider;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bffe606b408599b4fad696ddc889a943
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,115 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Oculus.Interaction.PoseDetection
{
[Serializable]
public class TransformFeatureStateThreshold : IFeatureStateThreshold<string>
{
public TransformFeatureStateThreshold()
{
}
public TransformFeatureStateThreshold(
float thresholdMidpoint,
float thresholdWidth,
string firstState,
string secondState)
{
_thresholdMidpoint = thresholdMidpoint;
_thresholdWidth = thresholdWidth;
_firstState = firstState;
_secondState = secondState;
}
[SerializeField]
private float _thresholdMidpoint;
[SerializeField]
private float _thresholdWidth;
[SerializeField]
private string _firstState;
[SerializeField]
private string _secondState;
public float ToFirstWhenBelow => _thresholdMidpoint - _thresholdWidth * 0.5f;
public float ToSecondWhenAbove => _thresholdMidpoint + _thresholdWidth * 0.5f;
public string FirstState => _firstState;
public string SecondState => _secondState;
}
[Serializable]
public class TransformFeatureThresholds : IFeatureStateThresholds<TransformFeature,
string>
{
public TransformFeatureThresholds() { }
public TransformFeatureThresholds(TransformFeature featureTransform,
IEnumerable<TransformFeatureStateThreshold> thresholds)
{
_feature = featureTransform;
_thresholds = new List<TransformFeatureStateThreshold>(thresholds);
}
[SerializeField]
private TransformFeature _feature;
[SerializeField]
private List<TransformFeatureStateThreshold> _thresholds;
[SerializeField]
[Tooltip("Length of time that the transform must be in the new state before the feature " +
"state provider will use the new value.")]
private double _minTimeInState;
public TransformFeature Feature => _feature;
public IReadOnlyList<IFeatureStateThreshold<string>>
Thresholds => _thresholds;
public double MinTimeInState => _minTimeInState;
}
[CreateAssetMenu(menuName = "Oculus/Interaction/SDK/Pose Detection/Transform Thresholds")]
public class TransformFeatureStateThresholds : ScriptableObject,
IFeatureThresholds<TransformFeature, string>
{
[SerializeField]
private List<TransformFeatureThresholds> _featureThresholds;
[SerializeField]
[Tooltip("Length of time that the transform must be in the new state before the feature " +
"state provider will use the new value.")]
private double _minTimeInState;
public void Construct(List<TransformFeatureThresholds> featureThresholds,
double minTimeInState)
{
_featureThresholds = featureThresholds;
_minTimeInState = minTimeInState;
}
public IReadOnlyList<IFeatureStateThresholds<TransformFeature, string>>
FeatureStateThresholds => _featureThresholds;
public double MinTimeInState => _minTimeInState;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac666f23650b94b4ea891467ab3677e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,305 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using UnityEngine;
namespace Oculus.Interaction.PoseDetection
{
public enum TransformFeature
{
WristUp,
WristDown,
PalmDown,
PalmUp,
PalmTowardsFace,
PalmAwayFromFace,
FingersUp,
FingersDown,
PinchClear
};
public class TransformFeatureValueProvider
{
public struct TransformProperties
{
public TransformProperties(Pose centerEyePos,
Pose wristPose,
Handedness handedness,
Vector3 trackingSystemUp)
{
CenterEyePose = centerEyePos;
WristPose = wristPose;
Handedness = handedness;
TrackingSystemUp = trackingSystemUp;
}
public readonly Pose CenterEyePose;
public readonly Pose WristPose;
public readonly Handedness Handedness;
public readonly Vector3 TrackingSystemUp;
}
public static float GetValue(TransformFeature transformFeature, TransformJointData transformJointData,
TransformConfig transformConfig)
{
TransformProperties transformProps =
new TransformProperties(transformJointData.CenterEyePose, transformJointData.WristPose,
transformJointData.Handedness, transformJointData.TrackingSystemUp);
switch (transformFeature)
{
case TransformFeature.WristDown:
return GetWristDownValue(in transformProps, in transformConfig);
case TransformFeature.WristUp:
return GetWristUpValue(in transformProps, in transformConfig);
case TransformFeature.PalmDown:
return GetPalmDownValue(in transformProps, in transformConfig);
case TransformFeature.PalmUp:
return GetPalmUpValue(in transformProps, in transformConfig);
case TransformFeature.PalmTowardsFace:
return GetPalmTowardsFaceValue(in transformProps, in transformConfig);
case TransformFeature.PalmAwayFromFace:
return GetPalmAwayFromFaceValue(in transformProps, in transformConfig);
case TransformFeature.FingersUp:
return GetFingersUpValue(in transformProps, in transformConfig);
case TransformFeature.FingersDown:
return GetFingersDownValue(in transformProps, in transformConfig);
case TransformFeature.PinchClear:
default:
return GetPinchClearValue(in transformProps, in transformConfig);
}
}
public static Vector3 GetHandVectorForFeature(TransformFeature transformFeature,
in TransformJointData transformJointData,
in TransformConfig transformConfig)
{
TransformProperties transformProps =
new TransformProperties(transformJointData.CenterEyePose, transformJointData.WristPose,
transformJointData.Handedness, transformJointData.TrackingSystemUp);
return GetHandVectorForFeature(transformFeature, in transformProps, in transformConfig);
}
private static Vector3 GetHandVectorForFeature(TransformFeature transformFeature,
in TransformProperties transformProps,
in TransformConfig transformConfig)
{
Vector3 handVector = Vector3.zero;
switch (transformFeature)
{
case TransformFeature.WristDown:
case TransformFeature.WristUp:
handVector = transformProps.Handedness == Handedness.Left ?
transformProps.WristPose.forward :
-1.0f * transformProps.WristPose.forward;
break;
case TransformFeature.PalmDown:
case TransformFeature.PalmUp:
case TransformFeature.PalmTowardsFace:
case TransformFeature.PalmAwayFromFace:
handVector = transformProps.Handedness == Handedness.Left ?
transformProps.WristPose.up : -1.0f * transformProps.WristPose.up;
break;
case TransformFeature.FingersUp:
case TransformFeature.FingersDown:
handVector = transformProps.Handedness == Handedness.Left ?
transformProps.WristPose.right : -1.0f * transformProps.WristPose.right;
break;
case TransformFeature.PinchClear:
default:
handVector = transformProps.Handedness == Handedness.Left ?
transformProps.WristPose.forward : -1.0f * transformProps.WristPose.forward;
break;
}
return handVector;
}
public static Vector3 GetTargetVectorForFeature(TransformFeature transformFeature,
in TransformJointData transformJointData,
in TransformConfig transformConfig)
{
TransformProperties transformProps =
new TransformProperties(transformJointData.CenterEyePose, transformJointData.WristPose,
transformJointData.Handedness, transformJointData.TrackingSystemUp);
return GetTargetVectorForFeature(transformFeature, in transformProps, in transformConfig);
}
private static Vector3 GetTargetVectorForFeature(TransformFeature transformFeature,
in TransformProperties transformProps,
in TransformConfig transformConfig)
{
Vector3 targetVector = Vector3.zero;
switch (transformFeature)
{
case TransformFeature.WristDown:
case TransformFeature.PalmDown:
case TransformFeature.FingersDown:
targetVector = OffsetVectorWithRotation(
GetVerticalVector(transformProps.CenterEyePose,
transformProps.TrackingSystemUp, false,
in transformConfig),
in transformConfig);
break;
case TransformFeature.WristUp:
case TransformFeature.PalmUp:
case TransformFeature.FingersUp:
targetVector = OffsetVectorWithRotation(
GetVerticalVector(transformProps.CenterEyePose,
transformProps.TrackingSystemUp, true,
in transformConfig),
in transformConfig);
break;
case TransformFeature.PalmTowardsFace:
targetVector = OffsetVectorWithRotation(
-1.0f * transformProps.CenterEyePose.forward,
in transformConfig);
break;
case TransformFeature.PalmAwayFromFace:
targetVector = OffsetVectorWithRotation(
transformProps.CenterEyePose.forward,
in transformConfig);
break;
case TransformFeature.PinchClear:
targetVector = OffsetVectorWithRotation(
-1.0f * transformProps.CenterEyePose.forward,
in transformConfig);
break;
default:
break;
}
return targetVector;
}
private static float GetWristDownValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.WristDown,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.WristDown,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetWristUpValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.WristUp,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.WristUp,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetPalmDownValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.PalmDown,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.PalmDown,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetPalmUpValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.PalmUp,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.PalmUp,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetPalmTowardsFaceValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.PalmTowardsFace,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.PalmTowardsFace,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetPalmAwayFromFaceValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.PalmAwayFromFace,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.PalmAwayFromFace,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetFingersUpValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.FingersUp,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.FingersUp,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetFingersDownValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.FingersDown,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.FingersDown,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static float GetPinchClearValue(in TransformProperties transformProps,
in TransformConfig transformConfig)
{
var handVector = GetHandVectorForFeature(TransformFeature.PinchClear,
in transformProps,
in transformConfig);
var targetVector = GetTargetVectorForFeature(TransformFeature.PinchClear,
in transformProps, in transformConfig);
return Vector3.Angle(handVector, targetVector);
}
private static Vector3 GetVerticalVector(in Pose centerEyePose,
in Vector3 trackingSystemUp,
bool isUp,
in TransformConfig transformConfig)
{
switch (transformConfig.UpVectorType)
{
case UpVectorType.Head:
return isUp ? centerEyePose.up : -1.0f * centerEyePose.up;
case UpVectorType.Tracking:
return isUp ? trackingSystemUp : -1.0f * trackingSystemUp;
case UpVectorType.World:
default:
return isUp ? Vector3.up : Vector3.down;
}
}
private static Vector3 OffsetVectorWithRotation(in Vector3 originalVector,
in TransformConfig transformConfig)
{
Quaternion rotationAmount = Quaternion.Euler(transformConfig.RotationOffset);
return rotationAmount * originalVector;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 849e24afed5709e4a82857a19ce08501
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,164 @@
/************************************************************************************
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at
https://developer.oculus.com/licenses/oculussdk/
Unless required by applicable law or agreed to in writing, the Utilities SDK distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied. See the License for the specific language governing
permissions and limitations under the License.
************************************************************************************/
using Oculus.Interaction.Input;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Serialization;
namespace Oculus.Interaction.PoseDetection
{
[Serializable]
public class TransformFeatureConfigList
{
[SerializeField]
private List<TransformFeatureConfig> _values;
public List<TransformFeatureConfig> Values => _values;
}
[Serializable]
public class TransformFeatureConfig : FeatureConfigBase<TransformFeature>
{
}
public class TransformRecognizerActiveState : MonoBehaviour, IActiveState
{
[SerializeField, Interface(typeof(IHand))]
private MonoBehaviour _hand;
public IHand Hand { get; private set; }
[SerializeField]
private TransformFeatureConfigList _transformFeatureConfigs;
[SerializeField]
[Tooltip("State provider uses this to determine the state of features during real time, so" +
" edit at runtime at your own risk.")]
private TransformConfig _transformConfig;
public IReadOnlyList<TransformFeatureConfig> FeatureConfigs => _transformFeatureConfigs.Values;
public TransformConfig TransformConfig => _transformConfig;
private TransformFeatureStateProvider FeatureStateProvider { get; set; }
protected bool _started = false;
protected virtual void Awake()
{
Hand = _hand as IHand;
}
protected virtual void Start()
{
this.BeginStart(ref _started);
Assert.IsNotNull(Hand);
bool foundAspect = Hand.GetHandAspect(out TransformFeatureStateProvider aspect);
Assert.IsTrue(foundAspect);
FeatureStateProvider = aspect;
Assert.IsNotNull(_transformFeatureConfigs);
Assert.IsNotNull(_transformConfig);
_transformConfig.InstanceId = GetInstanceID();
this.EndStart(ref _started);
}
protected virtual void OnEnable()
{
if (_started)
{
FeatureStateProvider.RegisterNewConfig(_transformConfig);
// Warm up the proactive evaluation
InitStateProvider();
}
}
protected virtual void OnDisable()
{
if (_started)
{
FeatureStateProvider.UnRegisterConfig(_transformConfig);
}
}
private void InitStateProvider()
{
foreach(var featureConfig in FeatureConfigs)
{
FeatureStateProvider.GetCurrentState(_transformConfig, featureConfig.Feature, out _);
}
}
public void GetFeatureVectorAndWristPos(TransformFeature feature, bool isHandVector,
ref Vector3? featureVec, ref Vector3? wristPos)
{
FeatureStateProvider.GetFeatureVectorAndWristPos(
TransformConfig, feature, isHandVector, ref featureVec, ref wristPos);
}
public bool Active
{
get
{
if (!isActiveAndEnabled)
{
return false;
}
foreach(var featureConfig in FeatureConfigs)
{
if (!FeatureStateProvider.IsStateActive(
_transformConfig,
featureConfig.Feature,
featureConfig.Mode,
featureConfig.State))
{
return false;
}
}
return true;
}
}
#region Inject
public void InjectAllTransformRecognizerActiveState(IHand hand, TransformFeatureConfigList transformFeatureList,
TransformConfig transformConfig)
{
InjectHand(hand);
InjectTransformFeatureList(transformFeatureList);
InjectTransformConfig(transformConfig);
}
public void InjectHand(IHand hand)
{
_hand = hand as MonoBehaviour;
Hand = hand;
}
public void InjectTransformFeatureList(TransformFeatureConfigList transformFeatureList)
{
_transformFeatureConfigs = transformFeatureList;
}
public void InjectTransformConfig(TransformConfig transformConfig)
{
_transformConfig = transformConfig;
}
#endregion
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c6f9440d09721c849864899e8986e219
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: