RiggedHand.cs
12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/******************************************************************************
* Copyright (C) Leap Motion, Inc. 2011-2017. *
* Leap Motion proprietary and confidential. *
* *
* Use subject to the terms of the Leap Motion SDK Agreement available at *
* https://developer.leapmotion.com/sdk_agreement, or another agreement *
* between Leap Motion and you, your company or other organization. *
******************************************************************************/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Leap;
namespace Leap.Unity {
/** This version of HandModelBase supports a hand respresentation based on a skinned and jointed 3D model asset.*/
public class RiggedHand : HandModel {
public override ModelType HandModelType {
get {
return ModelType.Graphics;
}
}
public override bool SupportsEditorPersistence() {
return SetEditorLeapPose;
}
[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.")]
[SerializeField]
private bool setEditorLeapPose = true;
public bool SetEditorLeapPose {
get { return setEditorLeapPose; }
set {
if (value == false) {
RestoreJointsStartPose();
}
setEditorLeapPose = value;
}
}
[SerializeField]
public bool DeformPositionsInFingers;
[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.")]
[SerializeField]
[HideInInspector]
private bool deformPositionsState = false;
[Tooltip("Hands are typically rigged in 3D packages with the palm transform near the wrist. Uncheck this is your model's palm transform is at the center of the palm similar to Leap's API drives")]
public bool ModelPalmAtLeapWrist = true;
[Tooltip("Set to True if each finger has an extra trasform between palm and base of the finger.")]
public bool UseMetaCarpals;
public Vector3 modelFingerPointing = new Vector3(0, 0, 0);
public Vector3 modelPalmFacing = new Vector3(0, 0, 0);
[Header("Values for Stored Start Pose")]
[SerializeField]
private List<Transform> jointList = new List<Transform>();
[SerializeField]
private List<Quaternion> localRotations = new List<Quaternion>();
[SerializeField]
private List<Vector3> localPositions = new List<Vector3>();
public override void InitHand() {
UpdateHand();
setDeformPositionsInFingers(deformPositionsState);
}
public Quaternion Reorientation() {
return Quaternion.Inverse(Quaternion.LookRotation(modelFingerPointing, -modelPalmFacing));
}
public override void UpdateHand() {
if (palm != null) {
if (ModelPalmAtLeapWrist) {
palm.position = GetWristPosition();
}
else {
palm.position = GetPalmPosition();
if (wristJoint) {
wristJoint.position = GetWristPosition();
}
}
palm.rotation = GetRiggedPalmRotation() * Reorientation();
}
if (forearm != null) {
forearm.rotation = GetArmRotation() * Reorientation();
}
for (int i = 0; i < fingers.Length; ++i) {
if (fingers[i] != null) {
fingers[i].fingerType = (Finger.FingerType)i;
fingers[i].UpdateFinger();
}
}
}
//These versions of GetPalmRotation & CalculateRotation return the opposite vector compared to LeapUnityExtension.CalculateRotation
//This will be deprecated once LeapUnityExtension.CalculateRotation is flipped in the next release of LeapMotion Core Assets
public Quaternion GetRiggedPalmRotation() {
if (hand_ != null) {
LeapTransform trs = hand_.Basis;
return CalculateRotation(trs);
}
if (palm) {
return palm.rotation;
}
return Quaternion.identity;
}
private Quaternion CalculateRotation(LeapTransform trs) {
Vector3 up = trs.yBasis.ToVector3();
Vector3 forward = trs.zBasis.ToVector3();
return Quaternion.LookRotation(forward, up);
}
/**Sets up the rigged hand by finding base of each finger by name */
[ContextMenu("Setup Rigged Hand")]
public void SetupRiggedHand() {
Debug.Log("Using transform naming to setup RiggedHand on " + transform.name);
modelFingerPointing = new Vector3(0, 0, 0);
modelPalmFacing = new Vector3(0, 0, 0);
assignRiggedFingersByName();
SetupRiggedFingers();
modelPalmFacing = calculateModelPalmFacing(palm, fingers[2].transform, fingers[1].transform);
modelFingerPointing = calculateModelFingerPointing();
setFingerPalmFacing();
}
/**Sets up the rigged hand if RiggedFinger scripts have already been assigned using Mecanim values.*/
public void AutoRigRiggedHand(Transform palm, Transform finger1, Transform finger2) {
Debug.Log("Using Mecanim mapping to setup RiggedHand on " + transform.name);
modelFingerPointing = new Vector3(0, 0, 0);
modelPalmFacing = new Vector3(0, 0, 0);
SetupRiggedFingers();
modelPalmFacing = calculateModelPalmFacing(palm, finger1, finger2);
modelFingerPointing = calculateModelFingerPointing();
setFingerPalmFacing();
}
/**Finds palm and finds root of each finger by name and assigns RiggedFinger scripts */
private void assignRiggedFingersByName(){
List<string> palmStrings = new List<string> { "palm"};
List<string> thumbStrings = new List<string> { "thumb", "tmb" };
List<string> indexStrings = new List<string> { "index", "idx"};
List<string> middleStrings = new List<string> { "middle", "mid"};
List<string> ringStrings = new List<string> { "ring"};
List<string> pinkyStrings = new List<string> { "pinky", "pin"};
//find palm by name
//Transform palm = null;
Transform thumb = null;
Transform index = null;
Transform middle = null;
Transform ring = null;
Transform pinky = null;
Transform[] children = transform.GetComponentsInChildren<Transform>();
if (palmStrings.Any(w => transform.name.ToLower().Contains(w))){
base.palm = transform;
}
else{
foreach (Transform t in children) {
if (palmStrings.Any(w => t.name.ToLower().Contains(w)) == true) {
base.palm = t;
}
}
}
if (!palm) {
palm = transform;
}
if (palm) {
foreach (Transform t in children) {
RiggedFinger preExistingRiggedFinger;
preExistingRiggedFinger = t.GetComponent<RiggedFinger>();
string lowercaseName = t.name.ToLower();
if (!preExistingRiggedFinger) {
if (thumbStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
thumb = t;
RiggedFinger newRiggedFinger = thumb.gameObject.AddComponent<RiggedFinger>();
newRiggedFinger.fingerType = Finger.FingerType.TYPE_THUMB;
}
if (indexStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
index = t;
RiggedFinger newRiggedFinger = index.gameObject.AddComponent<RiggedFinger>();
newRiggedFinger.fingerType = Finger.FingerType.TYPE_INDEX;
}
if (middleStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
middle = t;
RiggedFinger newRiggedFinger = middle.gameObject.AddComponent<RiggedFinger>();
newRiggedFinger.fingerType = Finger.FingerType.TYPE_MIDDLE;
}
if (ringStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
ring = t;
RiggedFinger newRiggedFinger = ring.gameObject.AddComponent<RiggedFinger>();
newRiggedFinger.fingerType = Finger.FingerType.TYPE_RING;
}
if (pinkyStrings.Any(w => lowercaseName.Contains(w)) && t.parent == palm) {
pinky = t;
RiggedFinger newRiggedFinger = pinky.gameObject.AddComponent<RiggedFinger>();
newRiggedFinger.fingerType = Finger.FingerType.TYPE_PINKY;
}
}
}
}
}
/**Triggers SetupRiggedFinger() in each RiggedFinger script for this RiggedHand */
private void SetupRiggedFingers() {
RiggedFinger[] fingerModelList = GetComponentsInChildren<RiggedFinger>();
for (int i = 0; i < 5; i++) {
int fingersIndex = fingerModelList[i].fingerType.indexOf();
fingers[fingersIndex] = fingerModelList[i];
fingerModelList[i].SetupRiggedFinger(UseMetaCarpals);
}
}
/**Sets the modelPalmFacing vector in each RiggedFinger to match this RiggedHand */
private void setFingerPalmFacing() {
RiggedFinger[] fingerModelList = GetComponentsInChildren<RiggedFinger>();
for (int i = 0; i < 5; i++) {
int fingersIndex = fingerModelList[i].fingerType.indexOf();
fingers[fingersIndex] = fingerModelList[i];
fingerModelList[i].modelPalmFacing = modelPalmFacing;
}
}
/**Calculates the palm facing direction by finding the vector perpendicular to the palm and two fingers */
private Vector3 calculateModelPalmFacing(Transform palm, Transform finger1, Transform finger2) {
Vector3 a = palm.transform.InverseTransformPoint(palm.position);
Vector3 b = palm.transform.InverseTransformPoint(finger1.position);
Vector3 c = palm.transform.InverseTransformPoint(finger2.position);
Vector3 side1 = b - a;
Vector3 side2 = c - a;
Vector3 perpendicular;
if (Handedness == Chirality.Left) {
perpendicular = Vector3.Cross(side1, side2);
}
else perpendicular = Vector3.Cross(side2, side1);
//flip perpendicular if it is above palm
Vector3 calculatedPalmFacing = CalculateZeroedVector(perpendicular);
return calculatedPalmFacing;
}
/**Find finger direction by finding distance vector from palm to middle finger */
private Vector3 calculateModelFingerPointing() {
Vector3 distance = palm.transform.InverseTransformPoint(fingers[2].transform.GetChild(0).transform.position) - palm.transform.InverseTransformPoint(palm.position);
Vector3 calculatedFingerPointing = CalculateZeroedVector(distance);
return calculatedFingerPointing * -1f;
}
/**Finds nearest cardinal vector to a vector */
public static Vector3 CalculateZeroedVector(Vector3 vectorToZero) {
var zeroed = new Vector3();
float max = Mathf.Max(Mathf.Abs(vectorToZero.x), Mathf.Abs(vectorToZero.y), Mathf.Abs(vectorToZero.z));
if (Mathf.Abs(vectorToZero.x) == max) {
zeroed = (vectorToZero.x < 0) ? new Vector3(1, 0, 0) : new Vector3(-1, 0, 0);
}
if (Mathf.Abs(vectorToZero.y) == max) {
zeroed = (vectorToZero.y < 0) ? new Vector3(0, 1, 0) : new Vector3(0, -1, 0);
}
if (Mathf.Abs(vectorToZero.z) == max) {
zeroed = (vectorToZero.z < 0) ? new Vector3(0, 0, 1) : new Vector3(0, 0, -1);
}
return zeroed;
}
/**Stores a snapshot of original joint positions */
[ContextMenu("StoreJointsStartPose")]
public void StoreJointsStartPose() {
foreach (Transform t in palm.parent.GetComponentsInChildren<Transform>()) {
jointList.Add(t);
localRotations.Add(t.localRotation);
localPositions.Add(t.localPosition);
}
}
/**Restores original joint positions, particularly after model has been placed in Leap's editor pose */
[ContextMenu("RestoreJointsStartPose")]
public void RestoreJointsStartPose() {
Debug.Log("RestoreJointsStartPose()");
for (int i = 0; i < jointList.Count; i++) {
Transform jointTrans = jointList[i];
jointTrans.localRotation = localRotations[i];
jointTrans.localPosition = localPositions[i];
}
}
private void setDeformPositionsInFingers(bool onOff) {
RiggedFinger[] riggedFingers = GetComponentsInChildren<RiggedFinger>();
foreach(RiggedFinger finger in riggedFingers){
finger.deformPosition = onOff;
}
}
public void OnValidate() {
if (DeformPositionsInFingers != deformPositionsState) {
RestoreJointsStartPose();
setDeformPositionsInFingers(DeformPositionsInFingers);
deformPositionsState = DeformPositionsInFingers;
}
if (setEditorLeapPose == false) {
RestoreJointsStartPose();
}
}
}
}