diff --git a/Runtime/Interaction/HPUIContinuousInteractable.cs b/Runtime/Interaction/HPUIContinuousInteractable.cs
index f5fd82a..d142680 100644
--- a/Runtime/Interaction/HPUIContinuousInteractable.cs
+++ b/Runtime/Interaction/HPUIContinuousInteractable.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using ubco.ovilab.HPUI.Tracking;
+using ubco.ovilab.HPUI.UI;
using UnityEngine;
using UnityEngine.XR.Hands;
using UnityEngine.XR.Interaction.Toolkit;
@@ -36,6 +37,8 @@ public class HPUIContinuousInteractable: HPUIBaseInteractable
public Material defaultMaterial;
[Tooltip("(Optional) the MeshFilter of the corresponding SkinnedMeshRenderer. If not set, will create a child object with the MeshFilter and SkinnedMeshRenderer.")]
public MeshFilter filter;
+ [Tooltip("(Optional) Will be used to provide feedback during setup.")]
+ [SerializeField] public HPUIContinuousInteractableUI ui;
///
public override Vector2 boundsMax { get => surfaceCollidersManager?.boundsMax ?? Vector2.zero; }
@@ -260,7 +263,8 @@ public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase up
}
if (jointPositionApproximation.TryComputePoseForKeyPoints(keypointsUsed.ToList(),
- out Dictionary keypointPoses))
+ out Dictionary keypointPoses,
+ out float percentageDone))
{
keypointsCache = SetupKeypoints();
@@ -307,6 +311,10 @@ public override void ProcessInteractable(XRInteractionUpdateOrder.UpdatePhase up
jointFollower.enabled = true;
finishedApproximatingJoints = true;
}
+ else
+ {
+ // TODO: display progress
+ }
}
}
}
diff --git a/Runtime/Prefabs.meta b/Runtime/Prefabs.meta
new file mode 100644
index 0000000..a6e676a
--- /dev/null
+++ b/Runtime/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 20fbe3e1dcc15714f8441ad546a01cd8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Prefabs/HPUIContinousUI.prefab b/Runtime/Prefabs/HPUIContinousUI.prefab
new file mode 100644
index 0000000..294ee17
--- /dev/null
+++ b/Runtime/Prefabs/HPUIContinousUI.prefab
@@ -0,0 +1,317 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &2961770035072034218
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8629184124075420591}
+ - component: {fileID: 3135720285395849235}
+ - component: {fileID: 2420295586894504013}
+ m_Layer: 5
+ m_Name: backplate
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8629184124075420591
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2961770035072034218}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8825315508195386409}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0.5}
+ m_AnchorMax: {x: 0.5, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 100, y: 10}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3135720285395849235
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2961770035072034218}
+ m_CullTransparentMesh: 1
+--- !u!114 &2420295586894504013
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2961770035072034218}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 21300000, guid: 2bf62cea73f52f7448758b2229ac7a2c, type: 3}
+ m_Type: 3
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 0
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 1
+--- !u!1 &3373608917536836410
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8770058598125140}
+ - component: {fileID: 5091855221251318508}
+ - component: {fileID: 8593948679709192792}
+ m_Layer: 5
+ m_Name: frontplate
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8770058598125140
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3373608917536836410}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 8825315508195386409}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0.5}
+ m_AnchorMax: {x: 0.5, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 95, y: 8}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5091855221251318508
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3373608917536836410}
+ m_CullTransparentMesh: 1
+--- !u!114 &8593948679709192792
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3373608917536836410}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.15752938, g: 0.5660378, b: 0.18210375, a: 1}
+ m_RaycastTarget: 1
+ m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+ m_Maskable: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 21300000, guid: 2bf62cea73f52f7448758b2229ac7a2c, type: 3}
+ m_Type: 3
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 0
+ m_FillAmount: 0
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+ m_PixelsPerUnitMultiplier: 1
+--- !u!1 &3522842689121506704
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 947454274434934731}
+ - component: {fileID: 2442874235928380245}
+ - component: {fileID: 732220008833224774}
+ - component: {fileID: 7253316176162991818}
+ - component: {fileID: 3157601004788341374}
+ m_Layer: 5
+ m_Name: HPUIContinousUI
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &947454274434934731
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3522842689121506704}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 8825315508195386409}
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
+--- !u!223 &2442874235928380245
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3522842689121506704}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_VertexColorAlwaysGammaSpace: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_UpdateRectTransformForStandalone: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 0
+ m_TargetDisplay: 0
+--- !u!114 &732220008833224774
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3522842689121506704}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+ m_PresetInfoIsWorld: 0
+--- !u!114 &7253316176162991818
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3522842689121506704}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!114 &3157601004788341374
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3522842689121506704}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0f267dc9e93377f4897f95ac98ef2889, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ jointFollowerData:
+ m_UseConstant: 1
+ m_ConstantValue:
+ handedness: 2
+ jointID: 12
+ useSecondJointID: 0
+ secondJointID: 0
+ defaultJointRadius: 0.01
+ offsetAngle: 0
+ offsetAsRatioToRadius: 1
+ longitudinalOffset: 0
+ m_Variable: {fileID: 0}
+ targetTransform: {fileID: 0}
+--- !u!1 &8898444606526160354
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8825315508195386409}
+ m_Layer: 5
+ m_Name: Progress
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8825315508195386409
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8898444606526160354}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children:
+ - {fileID: 8629184124075420591}
+ - {fileID: 8770058598125140}
+ m_Father: {fileID: 947454274434934731}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0.5}
+ m_AnchorMax: {x: 0.5, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 100, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
diff --git a/Runtime/Prefabs/HPUIContinousUI.prefab.meta b/Runtime/Prefabs/HPUIContinousUI.prefab.meta
new file mode 100644
index 0000000..e7a1e76
--- /dev/null
+++ b/Runtime/Prefabs/HPUIContinousUI.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d26f41e57bf50cd4d8d3a53dc066981e
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Tracking/JointPositionApproximation.cs b/Runtime/Tracking/JointPositionApproximation.cs
index 9e49899..b9b1266 100644
--- a/Runtime/Tracking/JointPositionApproximation.cs
+++ b/Runtime/Tracking/JointPositionApproximation.cs
@@ -150,9 +150,10 @@ protected override void ProcessJointData(XRHandSubsystem subsystem)
}
}
- public bool TryComputePoseForKeyPoints(List keypoints, out Dictionary keypointPoses)
+ public bool TryComputePoseForKeyPoints(List keypoints, out Dictionary keypointPoses, out float percentageDone)
{
keypointPoses = null;
+ percentageDone = 0;
// Was just initiated
if (jointsLengthEsitmation.Count == 0)
@@ -162,10 +163,11 @@ public bool TryComputePoseForKeyPoints(List keypoints, out Dictio
// Checking of all joints
// FIXME: Optimization - Avoid computing for all joints if not necessary
- bool jointLengthsStable = jointsLengthEsitmation.All(kvp => kvp.Value.stable);
- bool computeKeypointsStable = computeKeypointJointsData.All(kvp => kvp.Value.stable);
+ float jointLengthsStableRatio = (float)jointsLengthEsitmation.Where(kvp => kvp.Value.stable).Count() / (float)windowSize;
+ float computeKeypointsStableRatio = (float)computeKeypointJointsData.Where(kvp => kvp.Value.stable).Count() / (float)windowSize;
- if (!jointLengthsStable || !computeKeypointsStable || !recievedLastWristPose)
+ percentageDone = (jointLengthsStableRatio + computeKeypointsStableRatio) * 0.5f;
+ if (jointLengthsStableRatio == 1 || computeKeypointsStableRatio == 1 || !recievedLastWristPose)
{
return false;
}
diff --git a/Runtime/UI.meta b/Runtime/UI.meta
new file mode 100644
index 0000000..63648c6
--- /dev/null
+++ b/Runtime/UI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8cf6037dcca42b94eba4558c780577ea
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/UI/HPUIContinuousInteractableUI.cs b/Runtime/UI/HPUIContinuousInteractableUI.cs
new file mode 100644
index 0000000..b65ec07
--- /dev/null
+++ b/Runtime/UI/HPUIContinuousInteractableUI.cs
@@ -0,0 +1,80 @@
+using ubco.ovilab.HPUI.Tracking;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.XR.Hands;
+
+namespace ubco.ovilab.HPUI.UI
+{
+ public class HPUIContinuousInteractableUI : MonoBehaviour
+ {
+ private const string UIPrefab = "Packages/ubc.ok.ovilab.hpui-core/Runtime/Prefabs/HPUIContinousUI.prefab";
+ [SerializeField] private JointFollower jointFollower;
+ [SerializeField] private Transform UIRoot;
+ [SerializeField] private Image progressBarImage;
+ [SerializeField] private Image inProgressImage;
+ [SerializeField] private Text textMessage;
+
+ private bool usingInProgress = false;
+
+ public Handedness Handedness
+ {
+ get => jointFollower?.JointFollowerDatumProperty.Value.handedness ?? Handedness.Invalid;
+ set {
+ if (jointFollower != null)
+ {
+ jointFollower.JointFollowerDatumProperty.Value.handedness = value;
+ }
+ }
+ }
+
+ public string TextMessage {
+ set => textMessage.text = value;
+ }
+
+ ///
+ /// Set the progress bar ratio. Expecting to be a value between 0 and 1.
+ /// This wil also disable the in progress visual.
+ ///
+ public void SetProgress(float progress)
+ {
+ usingInProgress = false;
+ progressBarImage.transform.parent.gameObject.SetActive(true);
+ inProgressImage.transform.parent.gameObject.SetActive(false);
+ progressBarImage.fillAmount = progress;
+ }
+
+ ///
+ /// Show the in progress visual.
+ /// This wil also disable the progress bar visual.
+ ///
+ public void InProgress()
+ {
+ usingInProgress = true;
+ progressBarImage.transform.parent.gameObject.SetActive(false);
+ inProgressImage.transform.parent.gameObject.SetActive(true);
+ }
+
+ ///
+ private void Update()
+ {
+ if (usingInProgress)
+ {
+ // Full rotation every 3 seconds.
+ inProgressImage.fillAmount = Time.time / 3;
+ }
+
+ UIRoot.localPosition = transform.position + Vector3.up * 0.1f;
+ UIRoot.LookAt(Camera.main.transform);
+ }
+
+ public void Show()
+ {
+ gameObject.SetActive(true);
+ }
+
+ public void Hide()
+ {
+ gameObject.SetActive(false);
+ }
+ }
+}
diff --git a/Runtime/UI/HPUIContinuousInteractableUI.cs.meta b/Runtime/UI/HPUIContinuousInteractableUI.cs.meta
new file mode 100644
index 0000000..def883e
--- /dev/null
+++ b/Runtime/UI/HPUIContinuousInteractableUI.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 15ae6f2cc8c7dc648b5ee5f8aa7db9e9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: