Tanoda
GrabClassifierHeuristics.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
9using UnityEngine;
10
13 public static void UpdateClassifier(GrabClassifier classifier,
14 ClassifierParameters grabParameters,
15 ref Collider[][] collidingCandidates,
16 ref int[] numberOfColliders,
17 bool ignoreTemporal = false) {
18 // Store actual minimum curl in case we override it with the ignoreTemporal flag.
19 float tempMinCurl = grabParameters.MINIMUM_CURL;
20 if (ignoreTemporal) {
21 grabParameters.MINIMUM_CURL = -1f;
22 }
23
24 //For each probe (fingertip)
25 for (int j = 0; j < classifier.probes.Length; j++) {
26 //Calculate how extended the finger is
27 float tempCurl = Vector3.Dot(classifier.probes[j].direction, (j != 0) ? classifier.handDirection : (classifier.handChirality ? 1f : -1f) * classifier.handXBasis);
28 float curlVelocity = tempCurl - classifier.probes[j].prevTempCurl;
29 classifier.probes[j].prevTempCurl = tempCurl;
30
31 //Determine if this probe is intersecting an object
32 bool collidingWithObject = false;
33 for (int i = 0; i < numberOfColliders[j]; i++) {
34 if (collidingCandidates[j][i].attachedRigidbody != null && collidingCandidates[j][i].attachedRigidbody == classifier.body) {
35 collidingWithObject = true;
36 break;
37 }
38 }
39
40 //Nullify above findings if fingers are extended
41 float conditionalMaxCurlVelocity = (classifier.isGrabbed ?
42 grabParameters.GRABBED_MAXIMUM_CURL_VELOCITY :
43 grabParameters.MAXIMUM_CURL_VELOCITY);
44 collidingWithObject = collidingWithObject
45 && (tempCurl < grabParameters.MAXIMUM_CURL)
46 && (tempCurl > grabParameters.MINIMUM_CURL)
47 && (ignoreTemporal
48 || curlVelocity < conditionalMaxCurlVelocity);
49
50 //Probes go inside when they intersect, probes come out when they uncurl
51 if (!classifier.probes[j].isInside) {
52 classifier.probes[j].isInside = collidingWithObject;
53 classifier.probes[j].curl = tempCurl + (j == 0 ? grabParameters.THUMB_STICKINESS : grabParameters.FINGER_STICKINESS);
54 if (ignoreTemporal) {
55 classifier.probes[j].curl = 0f + (j == 0 ? grabParameters.THUMB_STICKINESS : grabParameters.FINGER_STICKINESS);
56 }
57 }
58 else {
59 if (tempCurl > classifier.probes[j].curl) {
60 classifier.probes[j].isInside = collidingWithObject;
61 }
62 }
63 }
64
65 //If thumb and one other finger is "inside" the object, it's a grab!
66 //This is the trick!
67 classifier.isThisControllerGrabbing = (classifier.probes[0].isInside && (classifier.probes[1].isInside ||
68 classifier.probes[2].isInside ||
69 classifier.probes[3].isInside ||
70 classifier.probes[4].isInside));
71 //If grabbing within 10 frames of releasing, discard grab.
72 //Suppresses spurious regrabs and makes throws work better.
73 if (classifier.coolDownProgress <= grabParameters.GRAB_COOLDOWN && !ignoreTemporal) {
74 if (classifier.isThisControllerGrabbing) {
75 classifier.isThisControllerGrabbing = false;
76 }
77 classifier.coolDownProgress += Time.fixedDeltaTime;
78 }
79
80 //Determine if the object is near the hand or if it's too far away
81 if (classifier.isThisControllerGrabbing && !classifier.prevThisControllerGrabbing) {
82 bool nearObject = false;
83 numberOfColliders[5] = Physics.OverlapSphereNonAlloc(classifier.handGrabCenter, grabParameters.MAXIMUM_DISTANCE_FROM_HAND, collidingCandidates[5], grabParameters.LAYER_MASK, grabParameters.GRAB_TRIGGERS);
84 for (int i = 0; i < numberOfColliders[5]; i++) {
85 if (collidingCandidates[5][i].attachedRigidbody != null && collidingCandidates[5][i].attachedRigidbody == classifier.body) {
86 nearObject = true;
87 break;
88 }
89 }
90
91 if (!nearObject) {
92 classifier.isThisControllerGrabbing = false;
93 classifier.probes[0].isInside = false;
94 }
95 }
96
97 // Reset the minimum curl parameter if we modified it due to the ignoreTemporal
98 // flag.
99 if (ignoreTemporal) {
100 grabParameters.MINIMUM_CURL = tempMinCurl;
101 }
102 }
103
104 //Expensive collider query optimization that somehow got undone before
105 public static void UpdateAllProbeColliders(Vector3[] aPositions, Vector3[] bPositions, ref Collider[][] collidingCandidates, ref int[] numberOfColliders, ClassifierParameters grabParameters) {
106 for (int i = 0; i < 5; i++) {
107 numberOfColliders[i] = Physics.OverlapCapsuleNonAlloc(
108 point0: aPositions[i],
109 point1: bPositions[i],
110 radius: i == 0 ? grabParameters.THUMBTIP_RADIUS : grabParameters.FINGERTIP_RADIUS,
111 results: collidingCandidates[i],
112 layerMask: grabParameters.LAYER_MASK,
113 queryTriggerInteraction: grabParameters.GRAB_TRIGGERS);
114 }
115 }
116
117 //Per-Object Per-Hand Classifier
118 public class GrabClassifier {
121 public GrabProbe[] probes = new GrabProbe[5];
122 public Transform transform;
123 public Rigidbody body;
124 public bool isGrabbed;
125 public float coolDownProgress;
126 public Vector3 handGrabCenter;
127 public Vector3 handDirection;
128 public Vector3 handXBasis;
129 public bool handChirality;
130
131 public GrabClassifier(GameObject behaviour) {
132 probes = new GrabProbe[5];
133 for(int i = 0; i<probes.Length; i++) { probes[i].prevTempCurl = 1f; }
134 transform = behaviour.transform;
135 body = behaviour.GetComponent<Rigidbody>();
137 }
138 }
139
140 //Per-Finger Per-Object Probe
141 public struct GrabProbe {
142 public Vector3 direction;
143 public bool isInside;
144 public float curl;
145 public float prevTempCurl;
146 };
147
148 //The parameters that tune how grabbing feels
149 public struct ClassifierParameters {
157 public float GRAB_COOLDOWN;
162 public int LAYER_MASK;
163 public QueryTriggerInteraction GRAB_TRIGGERS;
164
165 public ClassifierParameters(float fingerStickiness = 0f, float thumbStickiness = 0.05f, float maxCurl = 0.65f, float minCurl = -0.1f, float fingerRadius = 0.01f, float thumbRadius = 0.015f, float grabCooldown = 0.2f, float maxCurlVel = 0.0f, float grabbedMaxCurlVel = -0.05f, float maxHandDist = 0.1f, int layerMask = 0, QueryTriggerInteraction queryTriggers = QueryTriggerInteraction.UseGlobal) {
166 FINGER_STICKINESS = fingerStickiness;
167 THUMB_STICKINESS = thumbStickiness;
168 MAXIMUM_CURL = maxCurl;
169 MINIMUM_CURL = minCurl;
170 FINGERTIP_RADIUS = fingerRadius;
171 THUMBTIP_RADIUS = thumbRadius;
172 GRAB_COOLDOWN = grabCooldown;
173 MAXIMUM_CURL_VELOCITY = maxCurlVel;
174 GRABBED_MAXIMUM_CURL_VELOCITY = grabbedMaxCurlVel;
175 MAXIMUM_DISTANCE_FROM_HAND = maxHandDist;
176 LAYER_MASK = layerMask;
177 GRAB_TRIGGERS = queryTriggers;
178 }
179 }
180 }
181}
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)
ClassifierParameters(float fingerStickiness=0f, float thumbStickiness=0.05f, float maxCurl=0.65f, float minCurl=-0.1f, float fingerRadius=0.01f, float thumbRadius=0.015f, float grabCooldown=0.2f, float maxCurlVel=0.0f, float grabbedMaxCurlVel=-0.05f, float maxHandDist=0.1f, int layerMask=0, QueryTriggerInteraction queryTriggers=QueryTriggerInteraction.UseGlobal)
float MAXIMUM_CURL
The minimum and maximum curl values fingers are allowed to "Grab" within
float MAXIMUM_CURL_VELOCITY
The maximum rate that the fingers are extending where grabs are considered.
float GRAB_COOLDOWN
The minimum amount of time between repeated grabs of a single object