Tanoda
CapsuleHand.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;
13
14namespace Leap.Unity {
16 public class CapsuleHand : HandModelBase {
17 private const int TOTAL_JOINT_COUNT = 4 * 5;
18 private const float CYLINDER_MESH_RESOLUTION = 0.1f; //in centimeters, meshes within this resolution will be re-used
19 private const int THUMB_BASE_INDEX = (int)Finger.FingerType.TYPE_THUMB * 4;
20 private const int PINKY_BASE_INDEX = (int)Finger.FingerType.TYPE_PINKY * 4;
21
22 private static int _leftColorIndex = 0;
23 private static int _rightColorIndex = 0;
24 private static Color[] _leftColorList = { new Color(0.0f, 0.0f, 1.0f), new Color(0.2f, 0.0f, 0.4f), new Color(0.0f, 0.2f, 0.2f) };
25 private static Color[] _rightColorList = { new Color(1.0f, 0.0f, 0.0f), new Color(1.0f, 1.0f, 0.0f), new Color(1.0f, 0.5f, 0.0f) };
26
27 #pragma warning disable 0649
28 [SerializeField]
29 private Chirality handedness;
30
31 [SerializeField]
32 private bool _showArm = true;
33
34 [SerializeField]
35 private bool _castShadows = true;
36
37 [SerializeField]
38 private Material _material;
39 private Material _backing_material;
40
41 [SerializeField]
42 private Mesh _sphereMesh;
43
44 private Mesh _cylinderMesh;
45
46 [MinValue(3)]
47 [SerializeField]
48 private int _cylinderResolution = 12;
49
50 [MinValue(0)]
51 [SerializeField]
52 private float _jointRadius = 0.008f;
53
54 [MinValue(0)]
55 [SerializeField]
56 private float _cylinderRadius = 0.006f;
57
58 [MinValue(0)]
59 [SerializeField]
60 private float _palmRadius = 0.015f;
61 #pragma warning restore 0649
62
63 private Material _sphereMat;
64 private Hand _hand;
65 private Vector3[] _spherePositions;
66 private Matrix4x4[] _sphereMatrices = new Matrix4x4[32],
67 _cylinderMatrices = new Matrix4x4[32];
68 private int _curSphereIndex = 0, _curCylinderIndex = 0;
69
70 public override ModelType HandModelType {
71 get {
72 return ModelType.Graphics;
73 }
74 }
75
76 public override Chirality Handedness {
77 get {
78 return handedness;
79 }
80 set { }
81 }
82
83 public override bool SupportsEditorPersistence() {
84 return true;
85 }
86
87 public override Hand GetLeapHand() {
88 return _hand;
89 }
90
91 public override void SetLeapHand(Hand hand) {
92 _hand = hand;
93 }
94
95 public override void InitHand() {
96 if (_material != null && (_backing_material == null || !_backing_material.enableInstancing)) {
97 _backing_material = new Material(_material);
98 _backing_material.hideFlags = HideFlags.DontSaveInEditor;
99 if(!Application.isEditor && !_backing_material.enableInstancing) {
100 Debug.LogError("Capsule Hand Material needs Instancing Enabled to render in builds!", this);
101 }
102 _backing_material.enableInstancing = true;
103 _sphereMat = new Material(_backing_material);
104 _sphereMat.hideFlags = HideFlags.DontSaveInEditor;
105 }
106 }
107
108 #if UNITY_EDITOR
109 private void OnValidate() {
110 _meshMap.Clear();
111 if (_material == null || !_material.enableInstancing) {
112 Debug.LogWarning("CapsuleHand's Material must have " +
113 "instancing enabled in order to work in builds! Replacing " +
114 "Material with a Default Material now...", this);
115 _material = (Material)UnityEditor.AssetDatabase.LoadAssetAtPath(
116 System.IO.Path.Combine("Assets", "Plugins", "LeapMotion",
117 "Core", "Materials", "InstancedCapsuleHand.mat"), typeof(Material));
118 }
119 }
120 #endif
121
122 public override void BeginHand() {
123 base.BeginHand();
124
125 if (_hand.IsLeft) {
126 _sphereMat.color = _leftColorList[_leftColorIndex];
127 _leftColorIndex = (_leftColorIndex + 1) % _leftColorList.Length;
128 } else {
129 _sphereMat.color = _rightColorList[_rightColorIndex];
130 _rightColorIndex = (_rightColorIndex + 1) % _rightColorList.Length;
131 }
132 }
133
134 public override void UpdateHand() {
135 _curSphereIndex = 0;
136 _curCylinderIndex = 0;
137
138 if (_spherePositions == null || _spherePositions.Length != TOTAL_JOINT_COUNT) {
139 _spherePositions = new Vector3[TOTAL_JOINT_COUNT];
140 }
141
142 if (_material != null && (_backing_material == null || !_backing_material.enableInstancing)) {
143 _backing_material = new Material(_material);
144 _backing_material.hideFlags = HideFlags.DontSaveInEditor;
145 _backing_material.enableInstancing = true;
146 _sphereMat = new Material(_backing_material);
147 _sphereMat.hideFlags = HideFlags.DontSaveInEditor;
148 }
149
150 //Update all joint spheres in the fingers
151 foreach (var finger in _hand.Fingers) {
152 for (int j = 0; j < 4; j++) {
153 int key = getFingerJointIndex((int)finger.Type, j);
154
155 Vector3 position = finger.Bone((Bone.BoneType)j).NextJoint.ToVector3();
156 _spherePositions[key] = position;
157
158 drawSphere(position);
159 }
160 }
161
162 //Now we just have a few more spheres for the hands
163 //PalmPos, WristPos, and mockThumbJointPos, which is derived and not taken from the frame obj
164
165 Vector3 palmPosition = _hand.PalmPosition.ToVector3();
166 drawSphere(palmPosition, _palmRadius);
167
168 Vector3 thumbBaseToPalm = _spherePositions[THUMB_BASE_INDEX] - _hand.PalmPosition.ToVector3();
169 Vector3 mockThumbJointPos = _hand.PalmPosition.ToVector3() + Vector3.Reflect(thumbBaseToPalm, _hand.Basis.xBasis.ToVector3());
170 drawSphere(mockThumbJointPos);
171
172 //If we want to show the arm, do the calculations and display the meshes
173 if (_showArm) {
174 var arm = _hand.Arm;
175
176 Vector3 right = arm.Basis.xBasis.ToVector3() * arm.Width * 0.7f * 0.5f;
177 Vector3 wrist = arm.WristPosition.ToVector3();
178 Vector3 elbow = arm.ElbowPosition.ToVector3();
179
180 float armLength = Vector3.Distance(wrist, elbow);
181 wrist -= arm.Direction.ToVector3() * armLength * 0.05f;
182
183 Vector3 armFrontRight = wrist + right;
184 Vector3 armFrontLeft = wrist - right;
185 Vector3 armBackRight = elbow + right;
186 Vector3 armBackLeft = elbow - right;
187
188 drawSphere(armFrontRight);
189 drawSphere(armFrontLeft);
190 drawSphere(armBackLeft);
191 drawSphere(armBackRight);
192
193 drawCylinder(armFrontLeft, armFrontRight);
194 drawCylinder(armBackLeft, armBackRight);
195 drawCylinder(armFrontLeft, armBackLeft);
196 drawCylinder(armFrontRight, armBackRight);
197 }
198
199 //Draw cylinders between finger joints
200 for (int i = 0; i < 5; i++) {
201 for (int j = 0; j < 3; j++) {
202 int keyA = getFingerJointIndex(i, j);
203 int keyB = getFingerJointIndex(i, j + 1);
204
205 Vector3 posA = _spherePositions[keyA];
206 Vector3 posB = _spherePositions[keyB];
207
208 drawCylinder(posA, posB);
209 }
210 }
211
212 //Draw cylinders between finger knuckles
213 for (int i = 0; i < 4; i++) {
214 int keyA = getFingerJointIndex(i, 0);
215 int keyB = getFingerJointIndex(i + 1, 0);
216
217 Vector3 posA = _spherePositions[keyA];
218 Vector3 posB = _spherePositions[keyB];
219
220 drawCylinder(posA, posB);
221 }
222
223 //Draw the rest of the hand
224 drawCylinder(mockThumbJointPos, THUMB_BASE_INDEX);
225 drawCylinder(mockThumbJointPos, PINKY_BASE_INDEX);
226
227 // Draw Spheres
228 Graphics.DrawMeshInstanced(_sphereMesh, 0, _sphereMat, _sphereMatrices, _curSphereIndex, null,
229 _castShadows?UnityEngine.Rendering.ShadowCastingMode.On: UnityEngine.Rendering.ShadowCastingMode.Off, true, gameObject.layer);
230
231 // Draw Cylinders
232 if(_cylinderMesh == null) { _cylinderMesh = getCylinderMesh(1f); }
233 Graphics.DrawMeshInstanced(_cylinderMesh, 0, _backing_material, _cylinderMatrices, _curCylinderIndex, null,
234 _castShadows ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.Off, true, gameObject.layer);
235 }
236
237 private void drawSphere(Vector3 position) {
238 drawSphere(position, _jointRadius);
239 }
240
241 private void drawSphere(Vector3 position, float radius) {
242 if (isNaN(position)) { return; }
243
244 //multiply radius by 2 because the default unity sphere has a radius of 0.5 meters at scale 1.
245 _sphereMatrices[_curSphereIndex++] = Matrix4x4.TRS(position,
246 Quaternion.identity, Vector3.one * radius * 2.0f * transform.lossyScale.x);
247 }
248
249 private void drawCylinder(Vector3 a, Vector3 b) {
250 if (isNaN(a) || isNaN(b)) { return; }
251
252 float length = (a - b).magnitude;
253
254 if ((a - b).magnitude > 0.001f) {
255 _cylinderMatrices[_curCylinderIndex++] = Matrix4x4.TRS(a,
256 Quaternion.LookRotation(b - a), new Vector3(transform.lossyScale.x, transform.lossyScale.x, length));
257 }
258 }
259
260 private bool isNaN(Vector3 v) {
261 return float.IsNaN(v.x) || float.IsNaN(v.y) || float.IsNaN(v.z);
262 }
263
264 private void drawCylinder(int a, int b) {
265 drawCylinder(_spherePositions[a], _spherePositions[b]);
266 }
267
268 private void drawCylinder(Vector3 a, int b) {
269 drawCylinder(a, _spherePositions[b]);
270 }
271
272 private int getFingerJointIndex(int fingerIndex, int jointIndex) {
273 return fingerIndex * 4 + jointIndex;
274 }
275
276 private Dictionary<int, Mesh> _meshMap = new Dictionary<int, Mesh>();
277 private Mesh getCylinderMesh(float length) {
278 int lengthKey = Mathf.RoundToInt(length * 100 / CYLINDER_MESH_RESOLUTION);
279
280 Mesh mesh;
281 if (_meshMap.TryGetValue(lengthKey, out mesh)) {
282 return mesh;
283 }
284
285 mesh = new Mesh();
286 mesh.name = "GeneratedCylinder";
287 mesh.hideFlags = HideFlags.DontSave;
288
289 List<Vector3> verts = new List<Vector3>();
290 List<Color> colors = new List<Color>();
291 List<int> tris = new List<int>();
292
293 Vector3 p0 = Vector3.zero;
294 Vector3 p1 = Vector3.forward * length;
295 for (int i = 0; i < _cylinderResolution; i++) {
296 float angle = (Mathf.PI * 2.0f * i) / _cylinderResolution;
297 float dx = _cylinderRadius * Mathf.Cos(angle);
298 float dy = _cylinderRadius * Mathf.Sin(angle);
299
300 Vector3 spoke = new Vector3(dx, dy, 0);
301
302 verts.Add(p0 + spoke);
303 verts.Add(p1 + spoke);
304
305 colors.Add(Color.white);
306 colors.Add(Color.white);
307
308 int triStart = verts.Count;
309 int triCap = _cylinderResolution * 2;
310
311 tris.Add((triStart + 0) % triCap);
312 tris.Add((triStart + 2) % triCap);
313 tris.Add((triStart + 1) % triCap);
314
315 tris.Add((triStart + 2) % triCap);
316 tris.Add((triStart + 3) % triCap);
317 tris.Add((triStart + 1) % triCap);
318 }
319
320 mesh.SetVertices(verts);
321 mesh.SetIndices(tris.ToArray(), MeshTopology.Triangles, 0);
322 mesh.RecalculateBounds();
323 mesh.RecalculateNormals();
324 mesh.UploadMeshData(true);
325
326 _meshMap[lengthKey] = mesh;
327
328 return mesh;
329 }
330 }
331}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
The Bone class represents a tracked bone.
Definition: Bone.cs:26
BoneType
Enumerates the type of bones.
Definition: Bone.cs:168
The Finger class represents a tracked finger.
Definition: Finger.cs:20
FingerType
Enumerates the names of the fingers.
Definition: Finger.cs:167
The Hand class reports the physical characteristics of a detected hand.
Definition: Hand.cs:26
LeapTransform Basis
The transform of the hand.
Definition: Hand.cs:204
bool IsLeft
Identifies whether this Hand is a left hand.
Definition: Hand.cs:296
Arm Arm
The arm to which this hand is attached.
Definition: Hand.cs:311
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
override void InitHand()
Definition: CapsuleHand.cs:95
override Chirality Handedness
Definition: CapsuleHand.cs:76
override void SetLeapHand(Hand hand)
Definition: CapsuleHand.cs:91
override void UpdateHand()
Definition: CapsuleHand.cs:134
override Hand GetLeapHand()
Definition: CapsuleHand.cs:87
override void BeginHand()
Definition: CapsuleHand.cs:122
override ModelType HandModelType
Definition: CapsuleHand.cs:70
override bool SupportsEditorPersistence()
Returns whether or not this hand model supports editor persistence. This is false by default and must...
Definition: CapsuleHand.cs:83
Vector xBasis
The x-basis of the transform.