Tanoda
RiggedHand.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;
10using System.Collections;
11using System.Collections.Generic;
12using System.Linq;
13using Leap;
15using UnityEngine.Serialization;
16
17namespace Leap.Unity {
18
20 public class RiggedHand : HandModel {
21
22 public override ModelType HandModelType { get { return ModelType.Graphics; }}
23 public override bool SupportsEditorPersistence() { return true; }
24
25 [SerializeField]
26 [FormerlySerializedAs("DeformPositionsInFingers")]
27 [OnEditorChange("deformPositionsInFingers")]
28 private bool _deformPositionsInFingers = true;
30 get { return _deformPositionsInFingers; }
31 set {
32 _deformPositionsInFingers = value;
33 updateDeformPositionsInFingers();
34 }
35 }
36
37 [Tooltip("Because bones only exist at their roots in model rigs, the length " +
38 "of the last fingertip bone is lost when placing bones at positions in the " +
39 "tracked hand. " +
40 "This option scales the last bone along its X axis (length axis) to match " +
41 "its bone length to the tracked bone length. This option only has an " +
42 "effect if Deform Positions In Fingers is enabled.")]
43 [DisableIf("_deformPositionsInFingers", isEqualTo: false)]
44 [SerializeField]
45 [OnEditorChange("scaleLastFingerBones")]
46 private bool _scaleLastFingerBones = true;
48 get { return _scaleLastFingerBones; }
49 set {
50 _scaleLastFingerBones = value;
51 updateScaleLastFingerBoneInFingers();
52 }
53 }
54
55 [Tooltip("Hands are typically rigged in 3D packages with the palm transform near the wrist. Uncheck this if your model's palm transform is at the center of the palm similar to Leap API hands.")]
56 [FormerlySerializedAs("ModelPalmAtLeapWrist")]
57 public bool modelPalmAtLeapWrist = true;
58
59 [Tooltip("Set to True if each finger has an extra trasform between palm and base of the finger.")]
60 [FormerlySerializedAs("UseMetaCarpals")]
61 public bool useMetaCarpals;
62
63 #pragma warning disable 0414
64 // TODO: DELETEME these
65 [Header("Values for Stored Start Pose")]
66 [SerializeField]
67 private List<Transform> jointList = new List<Transform>();
68 [SerializeField]
69 private List<Quaternion> localRotations = new List<Quaternion>();
70 [SerializeField]
71 private List<Vector3> localPositions = new List<Vector3>();
72 #pragma warning restore 0414
73
74 [Tooltip("If non-zero, this vector and the modelPalmFacing vector " +
75 "will be used to re-orient the Transform bones in the hand rig, to " +
76 "compensate for bone axis discrepancies between Leap Bones and model " +
77 "bones.")]
78 public Vector3 modelFingerPointing = new Vector3(0, 0, 0);
79 [Tooltip("If non-zero, this vector and the modelFingerPointing vector " +
80 "will be used to re-orient the Transform bones in the hand rig, to " +
81 "compensate for bone axis discrepancies between Leap Bones and model " +
82 "bones.")]
83 public Vector3 modelPalmFacing = new Vector3(0, 0, 0);
84
87 public Quaternion userBoneRotation { get {
88 if (modelFingerPointing == Vector3.zero || modelPalmFacing == Vector3.zero) {
89 return Quaternion.identity;
90 }
91 return Quaternion.Inverse(
92 Quaternion.LookRotation(modelFingerPointing, -modelPalmFacing));
93 }}
94
95 public override void InitHand() {
96 UpdateHand();
97 updateDeformPositionsInFingers();
98 updateScaleLastFingerBoneInFingers();
99 }
100
101 public override void UpdateHand() {
102 if (palm != null) {
104 palm.position = GetWristPosition();
105 }
106 else {
107 palm.position = GetPalmPosition();
108 if (wristJoint) {
109 wristJoint.position = GetWristPosition();
110 }
111 }
112 palm.rotation = getRiggedPalmRotation() * userBoneRotation;
113 }
114
115 if (forearm != null) {
117 }
118
119 for (int i = 0; i < fingers.Length; ++i) {
120 if (fingers[i] != null) {
123 }
124 }
125 }
126
128 [ContextMenu("Setup Rigged Hand")]
129 public void SetupRiggedHand() {
130 Debug.Log("Using transform naming to setup RiggedHand on " + transform.name);
131 modelFingerPointing = new Vector3(0, 0, 0);
132 modelPalmFacing = new Vector3(0, 0, 0);
133 assignRiggedFingersByName();
134 setupRiggedFingers();
135 modelPalmFacing = calculateModelPalmFacing(palm, fingers[2].transform, fingers[1].transform);
136 modelFingerPointing = calculateModelFingerPointing();
137 setFingerPalmFacing();
138 }
139
141 public void AutoRigRiggedHand(Transform palm, Transform finger1, Transform finger2) {
142 Debug.Log("Using Mecanim mapping to setup RiggedHand on " + transform.name);
143 modelFingerPointing = new Vector3(0, 0, 0);
144 modelPalmFacing = new Vector3(0, 0, 0);
145 setupRiggedFingers();
146 modelPalmFacing = calculateModelPalmFacing(palm, finger1, finger2);
147 modelFingerPointing = calculateModelFingerPointing();
148 setFingerPalmFacing();
149 }
150
152 private void assignRiggedFingersByName(){
153 List<string> palmStrings = new List<string> { "palm"};
154 List<string> thumbStrings = new List<string> { "thumb", "tmb" };
155 List<string> indexStrings = new List<string> { "index", "idx"};
156 List<string> middleStrings = new List<string> { "middle", "mid"};
157 List<string> ringStrings = new List<string> { "ring"};
158 List<string> pinkyStrings = new List<string> { "pinky", "pin"};
159 //find palm by name
160 //Transform palm = null;
161 Transform thumb = null;
162 Transform index = null;
163 Transform middle = null;
164 Transform ring = null;
165 Transform pinky = null;
166 Transform[] children = transform.GetComponentsInChildren<Transform>();
167 if (palmStrings.Any(w => transform.name.ToLower().Contains(w))){
168 base.palm = transform;
169 }
170 else{
171 foreach (Transform t in children) {
172 if (palmStrings.Any(w => t.name.ToLower().Contains(w)) == true) {
173 base.palm = t;
174
175 }
176 }
177 }
178 if (!palm) {
179 palm = transform;
180 }
181 if (palm) {
182 foreach (Transform t in children) {
183 RiggedFinger preExistingRiggedFinger;
184 preExistingRiggedFinger = t.GetComponent<RiggedFinger>();
185 string lowercaseName = t.name.ToLower();
186 if (!preExistingRiggedFinger) {
187 if (thumbStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
188 thumb = t;
189 RiggedFinger newRiggedFinger = thumb.gameObject.AddComponent<RiggedFinger>();
190 newRiggedFinger.fingerType = Finger.FingerType.TYPE_THUMB;
191 }
192 if (indexStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
193 index = t;
194 RiggedFinger newRiggedFinger = index.gameObject.AddComponent<RiggedFinger>();
195 newRiggedFinger.fingerType = Finger.FingerType.TYPE_INDEX;
196 }
197 if (middleStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
198 middle = t;
199 RiggedFinger newRiggedFinger = middle.gameObject.AddComponent<RiggedFinger>();
200 newRiggedFinger.fingerType = Finger.FingerType.TYPE_MIDDLE;
201 }
202 if (ringStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
203 ring = t;
204 RiggedFinger newRiggedFinger = ring.gameObject.AddComponent<RiggedFinger>();
205 newRiggedFinger.fingerType = Finger.FingerType.TYPE_RING;
206 }
207 if (pinkyStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
208 pinky = t;
209 RiggedFinger newRiggedFinger = pinky.gameObject.AddComponent<RiggedFinger>();
210 newRiggedFinger.fingerType = Finger.FingerType.TYPE_PINKY;
211 }
212 }
213 }
214 }
215 }
216
218 private void setupRiggedFingers() {
219 RiggedFinger[] fingerModelList = GetComponentsInChildren<RiggedFinger>();
220 for (int i = 0; i < 5; i++) {
221 int fingersIndex = fingerModelList[i].fingerType.indexOf();
222 fingers[fingersIndex] = fingerModelList[i];
223 fingerModelList[i].SetupRiggedFinger(useMetaCarpals);
224 }
225 }
226
228 private void setFingerPalmFacing() {
229 RiggedFinger[] fingerModelList = GetComponentsInChildren<RiggedFinger>();
230 for (int i = 0; i < 5; i++) {
231 int fingersIndex = fingerModelList[i].fingerType.indexOf();
232 fingers[fingersIndex] = fingerModelList[i];
233 fingerModelList[i].modelPalmFacing = modelPalmFacing;
234 }
235 }
236
238 private Vector3 calculateModelPalmFacing(Transform palm, Transform finger1,
239 Transform finger2)
240 {
241 Vector3 a = palm.transform.InverseTransformPoint(palm.position);
242 Vector3 b = palm.transform.InverseTransformPoint(finger1.position);
243 Vector3 c = palm.transform.InverseTransformPoint(finger2.position);
244
245 Vector3 side1 = b - a;
246 Vector3 side2 = c - a;
247 Vector3 perpendicular;
248
249 if (Handedness == Chirality.Left) {
250 perpendicular = Vector3.Cross(side1, side2);
251 }
252 else perpendicular = Vector3.Cross(side2, side1);
253 //flip perpendicular if it is above palm
254 Vector3 calculatedPalmFacing = CalculateZeroedVector(perpendicular);
255 return calculatedPalmFacing;
256 }
257
259 private Vector3 calculateModelFingerPointing() {
260 Vector3 distance = palm.transform.InverseTransformPoint(fingers[2].transform.GetChild(0).transform.position) - palm.transform.InverseTransformPoint(palm.position);
261 Vector3 calculatedFingerPointing = CalculateZeroedVector(distance);
262 return calculatedFingerPointing * -1f;
263 }
264
266 public static Vector3 CalculateZeroedVector(Vector3 vectorToZero) {
267 var zeroed = new Vector3();
268 float max = Mathf.Max(Mathf.Abs(vectorToZero.x), Mathf.Abs(vectorToZero.y), Mathf.Abs(vectorToZero.z));
269 if (Mathf.Abs(vectorToZero.x) == max) {
270 zeroed = (vectorToZero.x < 0) ? new Vector3(1, 0, 0) : new Vector3(-1, 0, 0);
271 }
272 if (Mathf.Abs(vectorToZero.y) == max) {
273 zeroed = (vectorToZero.y < 0) ? new Vector3(0, 1, 0) : new Vector3(0, -1, 0);
274 }
275 if (Mathf.Abs(vectorToZero.z) == max) {
276 zeroed = (vectorToZero.z < 0) ? new Vector3(0, 0, 1) : new Vector3(0, 0, -1);
277 }
278 return zeroed;
279 }
280
281 // /**Stores a snapshot of original joint positions */
282 // [ContextMenu("StoreJointsStartPose")]
283 // public void StoreJointsStartPose() {
284 // foreach (Transform t in palm.parent.GetComponentsInChildren<Transform>()) {
285 // jointList.Add(t);
286 // localRotations.Add(t.localRotation);
287 // localPositions.Add(t.localPosition);
288 // }
289 // }
290
291 // /**Restores original joint positions, particularly after model has been placed in Leap's editor pose */
292 // [ContextMenu("RestoreJointsStartPose")]
293 // public void RestoreJointsStartPose() {
294 // //Debug.Log("RestoreJointsStartPose()");
295 // for (int i = 0; i < jointList.Count; i++) {
296 // Transform jointTrans = jointList[i];
297 // jointTrans.localRotation = localRotations[i];
298 // jointTrans.localPosition = localPositions[i];
299 // }
300 // }
301
302 private void updateDeformPositionsInFingers() {
303 var riggedFingers = GetComponentsInChildren<RiggedFinger>();
304 foreach (var finger in riggedFingers){
305 finger.deformPosition = deformPositionsInFingers;
306 }
307 }
308
309 private void updateScaleLastFingerBoneInFingers() {
310 var riggedFingers = GetComponentsInChildren<RiggedFinger>();
311 foreach (var finger in riggedFingers) {
312 finger.scaleLastFingerBone = scaleLastFingerBones;
313 }
314 }
315
316 // These versions of GetPalmRotation & CalculateRotation return the opposite
317 // vector compared to LeapUnityExtension.CalculateRotation.
318 // This will be deprecated once LeapUnityExtension.CalculateRotation is
319 // flipped in the next release of LeapMotion Core Assets.
320 private Quaternion getRiggedPalmRotation() {
321 if (hand_ != null) {
323 return calculateRotation(trs);
324 }
325 if (palm) {
326 return palm.rotation;
327 }
328 return Quaternion.identity;
329 }
330
331 private Quaternion calculateRotation(LeapTransform trs) {
332 Vector3 up = trs.yBasis.ToVector3();
333 Vector3 forward = trs.zBasis.ToVector3();
334 return Quaternion.LookRotation(forward, up);
335 }
336
337 // [Tooltip("When true, hands will be put into a Leap editor pose near the LeapServiceProvider's transform. When False, the hands will be returned to their Start Pose if it has been saved.")]
338 // [SerializeField]
339 // private bool setEditorLeapPose = true;
340
341 // public bool SetEditorLeapPose {
342 // get { return setEditorLeapPose; }
343 // set {
344 // if (value == false) {
345 // RestoreJointsStartPose();
346 // }
347 // setEditorLeapPose = value;
348 // }
349 // }
350
351 // [Tooltip("When True, hands will be put into a Leap editor pose near the LeapServiceProvider's transform. When False, the hands will be returned to their Start Pose if it has been saved.")]
352 // [SerializeField]
353 // [HideInInspector]
354 // private bool deformPositionsState = false;
355
356 }
357
358}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
The Finger class represents a tracked finger.
Definition: Finger.cs:20
FingerType
Enumerates the names of the fingers.
Definition: Finger.cs:167
LeapTransform Basis
The transform of the hand.
Definition: Hand.cs:204
Finger.FingerType fingerType
Definition: FingerModel.cs:34
abstract void UpdateFinger()
Vector3 GetWristPosition()
Definition: HandModel.cs:165
Vector3 GetPalmPosition()
Definition: HandModel.cs:66
Quaternion GetArmRotation()
Definition: HandModel.cs:179
Transform forearm
Definition: HandModel.cs:53
FingerModel[] fingers
Definition: HandModel.cs:47
Transform wristJoint
Definition: HandModel.cs:55
override Chirality Handedness
Definition: HandModel.cs:29
Manages the position and orientation of the bones in a model rigged for skeletal animation.
Definition: RiggedFinger.cs:22
void SetupRiggedFinger(bool useMetaCarpals)
A skinned and jointed 3D HandModel.
Definition: RiggedHand.cs:20
override void InitHand()
Definition: RiggedHand.cs:95
static Vector3 CalculateZeroedVector(Vector3 vectorToZero)
Definition: RiggedHand.cs:266
void AutoRigRiggedHand(Transform palm, Transform finger1, Transform finger2)
Definition: RiggedHand.cs:141
Quaternion userBoneRotation
Rotation derived from the modelFingerPointing and modelPalmFacing vectors in the RiggedHand inspector...
Definition: RiggedHand.cs:87
override bool SupportsEditorPersistence()
Returns whether or not this hand model supports editor persistence. This is false by default and must...
Definition: RiggedHand.cs:23
override ModelType HandModelType
Definition: RiggedHand.cs:22
Vector3 modelFingerPointing
Definition: RiggedHand.cs:78
override void UpdateHand()
Definition: RiggedHand.cs:101
The LeapTransform class represents a transform in three dimensional space.
Vector zBasis
The z-basis of the transform.
Vector yBasis
The y-basis of the transform.