Tanoda
HeuristicGrabClassifier.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
10using System;
11using System.Collections.Generic;
12#if UNITY_EDITOR
13using UnityEditor;
14#endif
15using UnityEngine;
16
18
20
22
23 private Dictionary<IInteractionBehaviour, GrabClassifierHeuristics.GrabClassifier> _classifiers
25 private GrabClassifierHeuristics.ClassifierParameters _defaultGrabParams, _scaledGrabParams;
26 private Collider[][] _collidingCandidates = new Collider[6][];
27 private int[] _numberOfColliders = new int[6];
28 private Vector3[] _fingerTipPositions = new Vector3[5];
29 private Vector3[] _fingerKnucklePositions = new Vector3[5];
30
32 float fingerStickiness = 0F,
33 float thumbStickiness = 0.04F,
34 float maxCurl = 0.65F,
35 float minCurl = -0.1F,
36 float fingerRadius = 0.012F,
37 float thumbRadius = 0.017F,
38 float grabCooldown = 0.2F,
39 float maxCurlVel = 0.0F,
40 float grabbedMaxCurlVel = -0.025F,
41 float maxGrabDistance = 0.05F,
42 int layerMask = 0,
43 QueryTriggerInteraction queryTriggers = QueryTriggerInteraction.UseGlobal) {
44
45 interactionHand = intHand;
46 _defaultGrabParams = new GrabClassifierHeuristics.ClassifierParameters(
47 fingerStickiness, thumbStickiness, maxCurl, minCurl, fingerRadius,
48 thumbRadius, grabCooldown, maxCurlVel, grabbedMaxCurlVel, maxGrabDistance,
49 layerMask == 0 ? interactionHand.manager.GetInteractionLayerMask() : layerMask,
50 queryTriggers);
51 _scaledGrabParams = new GrabClassifierHeuristics.ClassifierParameters(
52 fingerStickiness, thumbStickiness, maxCurl, minCurl, fingerRadius,
53 thumbRadius, grabCooldown, maxCurlVel, grabbedMaxCurlVel, maxGrabDistance,
54 layerMask == 0 ? interactionHand.manager.GetInteractionLayerMask() : layerMask,
55 queryTriggers);
56
57 for (int i = 0; i < _collidingCandidates.Length; i++) {
58 _collidingCandidates[i] = new Collider[5];
59 }
60 }
61
62 public void FixedUpdateClassifierHandState(Transform headTransform = null) {
63 using (new ProfilerSample("Update Classifier Hand State")) {
64 var hand = interactionHand.leapHand;
65
67 // Ensure that all scale dependent variables are properly set.
68 _scaledGrabParams.FINGERTIP_RADIUS = _defaultGrabParams.FINGERTIP_RADIUS
70 _scaledGrabParams.THUMBTIP_RADIUS = _defaultGrabParams.THUMBTIP_RADIUS
72 _scaledGrabParams.MAXIMUM_DISTANCE_FROM_HAND = _defaultGrabParams.MAXIMUM_DISTANCE_FROM_HAND
74
75 // Ensure layer mask is up-to-date.
76 _scaledGrabParams.LAYER_MASK = interactionHand.manager.GetInteractionLayerMask();
77
78
79 /*if (headTransform != null) {
80 //SERIOUS HACKS: Have the collider for the grab classifier stretch along the camera's projective axis
81 //This will make it easier for users to grab far away objects (while being uncertain of their depth)
82 for (int i = 0; i < hand.Fingers.Count; i++) {
83 Vector3 fingerTipPosition = hand.Fingers[i].TipPosition.ToVector3();
84 Vector3 stretchDirection = (fingerTipPosition - headTransform.position) * 0.1f;
85 _fingerTipPositions[i] = fingerTipPosition + stretchDirection;
86 _fingerKnucklePositions[i] = fingerTipPosition - stretchDirection;
87 }
88 } else {*/
89 for (int i = 0; i < hand.Fingers.Count; i++) {
90 _fingerTipPositions[i] = hand.Fingers[i].TipPosition.ToVector3();
91 _fingerKnucklePositions[i] = hand.Fingers[i].Bone(Bone.BoneType.TYPE_METACARPAL).NextJoint.ToVector3();
92 }
93 // }
94
95 GrabClassifierHeuristics.UpdateAllProbeColliders(_fingerTipPositions, _fingerKnucklePositions, ref _collidingCandidates, ref _numberOfColliders, _scaledGrabParams);
96 }
97 }
98 }
99
100 public bool FixedUpdateClassifierGrasp(out IInteractionBehaviour graspedObject) {
101 using (new ProfilerSample("Update Grab Classifier - Grasp", interactionHand.manager)) {
102 graspedObject = null;
104 // Cannot grasp another object with an untracked hand or while the hand is
105 // already grasping an object or if the hand is not tracked.
106 return false;
107 }
108
109 foreach (var interactionObj in interactionHand.graspCandidates) {
110 if (updateBehaviour(interactionObj, interactionHand.leapHand, graspMode: GraspUpdateMode.BeginGrasp)) {
111 graspedObject = interactionObj;
112 return true;
113 }
114 }
115
116 return false;
117 }
118 }
119
120 public bool FixedUpdateClassifierRelease(out IInteractionBehaviour releasedObject) {
121 using (new ProfilerSample("Update Grab Classifier - Release", interactionHand.manager)) {
122 releasedObject = null;
124 // Can't release an object if the hand is already not grasping one.
125 return false;
126 }
127
128 if (updateBehaviour(interactionHand.graspedObject, interactionHand.leapHand, graspMode: GraspUpdateMode.ReleaseGrasp)) {
129 releasedObject = interactionHand.graspedObject;
130 return true;
131 }
132
133 return false;
134 }
135 }
136
137 private enum GraspUpdateMode {
138 BeginGrasp,
139 ReleaseGrasp
140 }
141
146 private bool updateBehaviour(IInteractionBehaviour behaviour, Hand hand, GraspUpdateMode graspMode, bool ignoreTemporal = false) {
147 using (new ProfilerSample("Update Individual Grab Classifier", behaviour.gameObject)) {
148 // Ensure a classifier exists for this Interaction Behaviour.
150 if (!_classifiers.TryGetValue(behaviour, out classifier)) {
151 classifier = new GrabClassifierHeuristics.GrabClassifier(behaviour.gameObject);
152 _classifiers.Add(behaviour, classifier);
153 }
154
155 // Do the actual grab classification logic.
156 FillClassifier(behaviour, hand, ref classifier);
157 GrabClassifierHeuristics.UpdateClassifier(classifier, _scaledGrabParams,
158 ref _collidingCandidates,
159 ref _numberOfColliders,
160 ignoreTemporal);
161
162 // Determine whether there was a state change.
163 bool didStateChange = false;
164 if (!classifier.prevThisControllerGrabbing && classifier.isThisControllerGrabbing
165 && graspMode == GraspUpdateMode.BeginGrasp) {
166 didStateChange = true;
167
168 classifier.prevThisControllerGrabbing = classifier.isThisControllerGrabbing;
169 }
170 else if (classifier.prevThisControllerGrabbing && !classifier.isThisControllerGrabbing
171 && interactionHand.graspedObject == behaviour && graspMode == GraspUpdateMode.ReleaseGrasp) {
172 didStateChange = true;
173
174 classifier.coolDownProgress = 0f;
175 classifier.prevThisControllerGrabbing = classifier.isThisControllerGrabbing;
176 }
177
178 return didStateChange;
179 }
180 }
181
183 _classifiers.Remove(behaviour);
184 }
185
188 if (_classifiers.TryGetValue(behaviour, out classifier)) {
189 classifier.prevThisControllerGrabbing = false;
190 classifier.isThisControllerGrabbing = false;
191 classifier.coolDownProgress = 0F;
192 for (int i = 0; i < classifier.probes.Length; i++) {
193 classifier.probes[i].isInside = false;
194 }
195 }
196 }
197
198 public void GetGraspingFingertipPositions(IInteractionBehaviour behaviour, Vector3[] fingertipPositionsBuffer, out int numGraspingFingertips) {
200 if (_classifiers.TryGetValue(behaviour, out classifier)) {
201 int writeIdx = 0;
202 for (int probeIdx = 0; probeIdx < classifier.probes.Length; probeIdx++) {
203 if (classifier.probes[probeIdx].isInside) {
204 fingertipPositionsBuffer[writeIdx++] = _fingerTipPositions[probeIdx];
205 }
206 }
207 numGraspingFingertips = writeIdx;
208 }
209 else {
210 numGraspingFingertips = 0;
211 }
212 }
213
214 public bool TryGrasp(IInteractionBehaviour intObj, Hand hand) {
215 Debug.Log("HEY THIS SHOULDN'T BE BEING CALLED");
217
218 return updateBehaviour(intObj, hand, GraspUpdateMode.BeginGrasp,
219 ignoreTemporal: true);
220 }
221
223 if (original == null) {
224 throw new ArgumentNullException("original");
225 }
226
227 if (replacement == null) {
228 throw new ArgumentNullException("replacement");
229 }
230
232 if (!_classifiers.TryGetValue(original, out classifier)) {
233 throw new InvalidOperationException("Cannot swap from something that is not currently grasped!");
234 }
235
236 classifier.body = replacement.rigidbody;
237 classifier.transform = replacement.transform;
238
239 _classifiers.Remove(original);
240 _classifiers[replacement] = classifier;
241 }
242
243 protected void FillClassifier(IInteractionBehaviour behaviour, Hand hand, ref GrabClassifierHeuristics.GrabClassifier classifier) {
244 classifier.handChirality = hand.IsLeft;
245 classifier.handDirection = hand.Direction.ToVector3();
246 classifier.handXBasis = hand.Basis.xBasis.ToVector3();
247 float simScale = interactionHand.manager.SimulationScale;
248 classifier.handGrabCenter = (hand.PalmPosition
249 + (hand.Direction * 0.05f * simScale)
250 + (hand.PalmNormal * 0.01f * simScale)).ToVector3();
251 for (int i = 0; i < hand.Fingers.Count; i++) {
252 classifier.probes[i].direction = hand.Fingers[i].Direction.ToVector3();
253 }
254 classifier.isGrabbed = behaviour.isGrasped;
255 }
256
257 }
258
259}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
The Bone class represents a tracked bone.
Definition: Bone.cs:26
BoneType
Enumerates the type of bones.
Definition: Bone.cs:168
The Hand class reports the physical characteristics of a detected hand.
Definition: Hand.cs:26
Vector Direction
The direction from the palm position toward the fingers.
Definition: Hand.cs:196
LeapTransform Basis
The transform of the hand.
Definition: Hand.cs:204
Vector PalmNormal
The normal vector to the palm. If your hand is flat, this vector will point downward,...
Definition: Hand.cs:184
bool IsLeft
Identifies whether this Hand is a left hand.
Definition: Hand.cs:296
Vector PalmPosition
The center position of the palm.
Definition: Hand.cs:165
List< Finger > Fingers
The list of Finger objects detected in this frame that are attached to this hand, given in order from...
Definition: Hand.cs:159
static void UpdateAllProbeColliders(Vector3[] aPositions, Vector3[] bPositions, ref Collider[][] collidingCandidates, ref int[] numberOfColliders, ClassifierParameters grabParameters)
static void UpdateClassifier(GrabClassifier classifier, ClassifierParameters grabParameters, ref Collider[][] collidingCandidates, ref int[] numberOfColliders, bool ignoreTemporal=false)
ReadonlyHashSet< IInteractionBehaviour > graspCandidates
Gets the set of objects currently considered graspable.
IInteractionBehaviour graspedObject
Gets the object the controller is currently grasping, or null if there is no such object.
bool isGraspingObject
Gets whether the controller is currently grasping an object.
override bool isTracked
Gets whether the underlying Leap hand is currently tracked.
Hand leapHand
Gets the last tracked state of the Leap hand.
float SimulationScale
A scale that can be used to appropriately transform distances that otherwise expect one Unity unit to...
int GetInteractionLayerMask()
Returns a layer mask containing all layers that might contain interaction objects.
bool TryGrasp(IInteractionBehaviour intObj, Hand hand)
bool FixedUpdateClassifierRelease(out IInteractionBehaviour releasedObject)
void FixedUpdateClassifierHandState(Transform headTransform=null)
void UnregisterInteractionBehaviour(IInteractionBehaviour behaviour)
void SwapClassifierState(IInteractionBehaviour original, IInteractionBehaviour replacement)
void GetGraspingFingertipPositions(IInteractionBehaviour behaviour, Vector3[] fingertipPositionsBuffer, out int numGraspingFingertips)
void FillClassifier(IInteractionBehaviour behaviour, Hand hand, ref GrabClassifierHeuristics.GrabClassifier classifier)
HeuristicGrabClassifier(InteractionHand intHand, float fingerStickiness=0F, float thumbStickiness=0.04F, float maxCurl=0.65F, float minCurl=-0.1F, float fingerRadius=0.012F, float thumbRadius=0.017F, float grabCooldown=0.2F, float maxCurlVel=0.0F, float grabbedMaxCurlVel=-0.025F, float maxGrabDistance=0.05F, int layerMask=0, QueryTriggerInteraction queryTriggers=QueryTriggerInteraction.UseGlobal)
void NotifyGraspForciblyReleased(IInteractionBehaviour behaviour)
bool FixedUpdateClassifierGrasp(out IInteractionBehaviour graspedObject)
IInteractionBehaviour is the interface that defines all Interaction objects, specifying the minimum s...
Vector xBasis
The x-basis of the transform.
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....