Tanoda
AttachmentHand.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 Leap.Unity.Query;
11using System;
12using System.Collections;
13using System.Collections.Generic;
14#if UNITY_EDITOR
15using UnityEditor;
16#endif
17using UnityEngine;
18
19namespace Leap.Unity.Attachments {
20
26 [AddComponentMenu("")]
27 [ExecuteInEditMode]
28 public class AttachmentHand : MonoBehaviour {
29
39 public Action OnAttachmentPointsModified = () => { };
40
41 #region AttachmentPointBehaviours
42
43 [HideInInspector]
45 [HideInInspector]
47
48 [HideInInspector]
50 [HideInInspector]
52 [HideInInspector]
54
55 [HideInInspector]
57 [HideInInspector]
59 [HideInInspector]
61 [HideInInspector]
63
64 [HideInInspector]
66 [HideInInspector]
68 [HideInInspector]
70 [HideInInspector]
72
73 [HideInInspector]
75 [HideInInspector]
77 [HideInInspector]
79 [HideInInspector]
81
82 [HideInInspector]
84 [HideInInspector]
86 [HideInInspector]
88 [HideInInspector]
90
91 #endregion
92
97
98 private bool _attachmentPointsDirty = false;
99
104 [SerializeField, Disable]
105 private Chirality _chirality;
106
111 public Chirality chirality { get { return _chirality; } set { _chirality = value; } }
112
117 [SerializeField, Disable]
118 private bool _isTracked;
119
124 public bool isTracked { get { return _isTracked; } set { _isTracked = value; } }
125
126 void OnValidate() {
127 initializeAttachmentPointFlagConstants();
128 }
129
130 void Awake() {
131 initializeAttachmentPointFlagConstants();
132 }
133
134 #if !UNITY_EDITOR
135 #pragma warning disable 0414
136 #endif
137 private bool _isBeingDestroyed = false;
138 #if !UNITY_EDITOR
139 #pragma warning restore 0414
140 #endif
141 void OnDestroy() {
142 _isBeingDestroyed = true;
143 }
144
150 AttachmentPointBehaviour behaviour = null;
151
152 switch (singlePoint) {
153 case AttachmentPointFlags.None: break;
154
155 case AttachmentPointFlags.Wrist: behaviour = wrist; break;
156 case AttachmentPointFlags.Palm: behaviour = palm; break;
157
158 case AttachmentPointFlags.ThumbProximalJoint: behaviour = thumbProximalJoint; break;
159 case AttachmentPointFlags.ThumbDistalJoint: behaviour = thumbDistalJoint; break;
160 case AttachmentPointFlags.ThumbTip: behaviour = thumbTip; break;
161
162 case AttachmentPointFlags.IndexKnuckle: behaviour = indexKnuckle; break;
163 case AttachmentPointFlags.IndexMiddleJoint: behaviour = indexMiddleJoint; break;
164 case AttachmentPointFlags.IndexDistalJoint: behaviour = indexDistalJoint; break;
165 case AttachmentPointFlags.IndexTip: behaviour = indexTip; break;
166
167 case AttachmentPointFlags.MiddleKnuckle: behaviour = middleKnuckle; break;
168 case AttachmentPointFlags.MiddleMiddleJoint: behaviour = middleMiddleJoint; break;
169 case AttachmentPointFlags.MiddleDistalJoint: behaviour = middleDistalJoint; break;
170 case AttachmentPointFlags.MiddleTip: behaviour = middleTip; break;
171
172 case AttachmentPointFlags.RingKnuckle: behaviour = ringKnuckle; break;
173 case AttachmentPointFlags.RingMiddleJoint: behaviour = ringMiddleJoint; break;
174 case AttachmentPointFlags.RingDistalJoint: behaviour = ringDistalJoint; break;
175 case AttachmentPointFlags.RingTip: behaviour = ringTip; break;
176
177 case AttachmentPointFlags.PinkyKnuckle: behaviour = pinkyKnuckle; break;
178 case AttachmentPointFlags.PinkyMiddleJoint: behaviour = pinkyMiddleJoint; break;
179 case AttachmentPointFlags.PinkyDistalJoint: behaviour = pinkyDistalJoint; break;
180 case AttachmentPointFlags.PinkyTip: behaviour = pinkyTip; break;
181 }
182
183 return behaviour;
184 }
185
187 if (_attachmentPointFlagConstants == null || _attachmentPointFlagConstants.Length == 0) {
188 initializeAttachmentPointFlagConstants();
189 }
190
191 // First just _check_ whether we'll need to do any destruction or creation
192 bool requiresDestructionOrCreation = false;
193 foreach (var flag in _attachmentPointFlagConstants) {
194 if (flag == AttachmentPointFlags.None) continue;
195
196 if ((!points.Contains(flag) && GetBehaviourForPoint(flag) != null)
197 || (points.Contains(flag) && GetBehaviourForPoint(flag) == null)) {
198 requiresDestructionOrCreation = true;
199 }
200 }
201
202 // Go through the work of flattening and rebuilding if it is necessary.
203 if (requiresDestructionOrCreation) {
204 // Remove parent-child relationships so deleting parent Transforms doesn't annihilate
205 // child Transforms that don't need to be deleted themselves.
206 flattenAttachmentTransformHierarchy();
207
208 foreach (AttachmentPointFlags flag in _attachmentPointFlagConstants) {
209 if (flag == AttachmentPointFlags.None) continue;
210
211 if (points.Contains(flag)) {
212 ensureTransformExists(flag);
213 }
214 else {
215 ensureTransformDoesNotExist(flag);
216 }
217 }
218
219 // Organize transforms, restoring parent-child relationships.
220 organizeAttachmentTransforms();
221 }
222
223 if (_attachmentPointsDirty) {
225 _attachmentPointsDirty = false;
226 }
227 }
228
230 #if UNITY_EDITOR
231 // Only valid if the AttachmentHand itself is also not being destroyed.
232 if (_isBeingDestroyed) return;
233
234 // Refresh this hand's attachment transforms on a slight delay.
235 // Only AttachmentHands can _truly_ remove attachment points!
236 AttachmentHands attachHands = GetComponentInParent<AttachmentHands>();
237 if (attachHands != null) {
238 EditorApplication.delayCall += () => { refreshAttachmentTransforms(attachHands.attachmentPoints); };
239 }
240 #endif
241 }
242
243 #region Internal
244
245 private AttachmentPointFlags[] _attachmentPointFlagConstants;
246 private void initializeAttachmentPointFlagConstants() {
247 Array flagConstants = Enum.GetValues(typeof(AttachmentPointFlags));
248 if (_attachmentPointFlagConstants == null || _attachmentPointFlagConstants.Length == 0) {
249 _attachmentPointFlagConstants = new AttachmentPointFlags[flagConstants.Length];
250 }
251 int i = 0;
252 foreach (int f in flagConstants) {
253 _attachmentPointFlagConstants[i++] = (AttachmentPointFlags)f;
254 }
255 }
256
257 private void setBehaviourForPoint(AttachmentPointFlags singlePoint, AttachmentPointBehaviour behaviour) {
258 switch (singlePoint) {
259 case AttachmentPointFlags.None: break;
260
261 case AttachmentPointFlags.Wrist: wrist = behaviour; break;
262 case AttachmentPointFlags.Palm: palm = behaviour; break;
263
264 case AttachmentPointFlags.ThumbProximalJoint: thumbProximalJoint = behaviour; break;
265 case AttachmentPointFlags.ThumbDistalJoint: thumbDistalJoint = behaviour; break;
266 case AttachmentPointFlags.ThumbTip: thumbTip = behaviour; break;
267
268 case AttachmentPointFlags.IndexKnuckle: indexKnuckle = behaviour; break;
269 case AttachmentPointFlags.IndexMiddleJoint: indexMiddleJoint = behaviour; break;
270 case AttachmentPointFlags.IndexDistalJoint: indexDistalJoint = behaviour; break;
271 case AttachmentPointFlags.IndexTip: indexTip = behaviour; break;
272
273 case AttachmentPointFlags.MiddleKnuckle: middleKnuckle = behaviour; break;
274 case AttachmentPointFlags.MiddleMiddleJoint: middleMiddleJoint = behaviour; break;
275 case AttachmentPointFlags.MiddleDistalJoint: middleDistalJoint = behaviour; break;
276 case AttachmentPointFlags.MiddleTip: middleTip = behaviour; break;
277
278 case AttachmentPointFlags.RingKnuckle: ringKnuckle = behaviour; break;
279 case AttachmentPointFlags.RingMiddleJoint: ringMiddleJoint = behaviour; break;
280 case AttachmentPointFlags.RingDistalJoint: ringDistalJoint = behaviour; break;
281 case AttachmentPointFlags.RingTip: ringTip = behaviour; break;
282
283 case AttachmentPointFlags.PinkyKnuckle: pinkyKnuckle = behaviour; break;
284 case AttachmentPointFlags.PinkyMiddleJoint: pinkyMiddleJoint = behaviour; break;
285 case AttachmentPointFlags.PinkyDistalJoint: pinkyDistalJoint = behaviour; break;
286 case AttachmentPointFlags.PinkyTip: pinkyTip = behaviour; break;
287 }
288
289 #if UNITY_EDITOR
290 EditorUtility.SetDirty(this);
291 #endif
292 }
293
294 private void ensureTransformExists(AttachmentPointFlags singlePoint) {
295 if (!singlePoint.IsSinglePoint()) {
296 Debug.LogError("Tried to ensure transform exists for singlePoint, but it contains more than one set flag.");
297 return;
298 }
299
300 AttachmentPointBehaviour pointBehaviour = GetBehaviourForPoint(singlePoint);
301
302 if (pointBehaviour == null) {
303 // First, see if there's already one in the hierarchy! Might exist due to, e.g. an Undo operation
304 var existingPointBehaviour = this.gameObject.GetComponentsInChildren<AttachmentPointBehaviour>()
305 .Query()
306 .FirstOrDefault(p => p.attachmentPoint == singlePoint);
307
308 // Only make a new object if the transform really doesn't exist.
309 if (existingPointBehaviour == AttachmentPointFlags.None) {
310 GameObject obj = new GameObject(Enum.GetName(typeof(AttachmentPointFlags), singlePoint));
311 #if UNITY_EDITOR
312 Undo.RegisterCreatedObjectUndo(obj, "Created Object");
313 pointBehaviour = Undo.AddComponent<AttachmentPointBehaviour>(obj);
314 #else
315 pointBehaviour = obj.AddComponent<AttachmentPointBehaviour>();
316 #endif
317 }
318 else {
319 pointBehaviour = existingPointBehaviour;
320 }
321
322 #if UNITY_EDITOR
323 Undo.RecordObject(pointBehaviour, "Set Attachment Point");
324 #endif
325 pointBehaviour.attachmentPoint = singlePoint;
326 pointBehaviour.attachmentHand = this;
327 setBehaviourForPoint(singlePoint, pointBehaviour);
328
329 SetTransformParent(pointBehaviour.transform, this.transform);
330
331 _attachmentPointsDirty = true;
332
333 #if UNITY_EDITOR
334 EditorUtility.SetDirty(this);
335 #endif
336 }
337 }
338
339 private static void SetTransformParent(Transform t, Transform parent) {
340 #if UNITY_EDITOR
341 Undo.SetTransformParent(t, parent, "Set Transform Parent");
342 #else
343 t.parent = parent;
344 #endif
345 }
346
347 private void ensureTransformDoesNotExist(AttachmentPointFlags singlePoint) {
348 if (!singlePoint.IsSinglePoint()) {
349 Debug.LogError("Tried to ensure transform exists for singlePoint, but it contains more than one set flag");
350 return;
351 }
352
353 var pointBehaviour = GetBehaviourForPoint(singlePoint);
354 if (pointBehaviour != null) {
355 InternalUtility.Destroy(pointBehaviour.gameObject);
356 setBehaviourForPoint(singlePoint, null);
357
358 pointBehaviour = null;
359
360 _attachmentPointsDirty = true;
361
362 #if UNITY_EDITOR
363 EditorUtility.SetDirty(this);
364 #endif
365 }
366 }
367
368 private void flattenAttachmentTransformHierarchy() {
369 foreach (var point in this.points) {
370 SetTransformParent(point.transform, this.transform);
371 }
372 }
373
374 private void organizeAttachmentTransforms() {
375 int siblingIdx = 0;
376
377 // Wrist
378 if (wrist != null) {
379 wrist.transform.SetSiblingIndex(siblingIdx++);
380 }
381
382 // Palm
383 if (palm != null) {
384 palm.transform.SetSiblingIndex(siblingIdx++);
385 }
386
387 Transform topLevelTransform;
388
389 // Thumb
390 topLevelTransform = tryStackTransformHierarchy(thumbProximalJoint,
392 thumbTip);
393 if (topLevelTransform != null) {
394 topLevelTransform.SetSiblingIndex(siblingIdx++);
395 }
396
397 // Index
398 topLevelTransform = tryStackTransformHierarchy(indexKnuckle,
401 indexTip);
402 if (topLevelTransform != null) {
403 topLevelTransform.SetSiblingIndex(siblingIdx++);
404 }
405
406 // Middle
407 topLevelTransform = tryStackTransformHierarchy(middleKnuckle,
410 middleTip);
411 if (topLevelTransform != null) {
412 topLevelTransform.SetSiblingIndex(siblingIdx++);
413 }
414
415 // Ring
416 topLevelTransform = tryStackTransformHierarchy(ringKnuckle,
419 ringTip);
420 if (topLevelTransform != null) {
421 topLevelTransform.SetSiblingIndex(siblingIdx++);
422 }
423
424 // Pinky
425 topLevelTransform = tryStackTransformHierarchy(pinkyKnuckle,
428 pinkyTip);
429 if (topLevelTransform != null) {
430 topLevelTransform.SetSiblingIndex(siblingIdx++);
431 }
432 }
433
434 private static Transform[] s_hierarchyTransformsBuffer = new Transform[4];
440 private Transform tryStackTransformHierarchy(params Transform[] transforms) {
441 for (int i = 0; i < s_hierarchyTransformsBuffer.Length; i++) {
442 s_hierarchyTransformsBuffer[i] = null;
443 }
444
445 int hierarchyCount = 0;
446
447 foreach (var transform in transforms.Query().Where(t => t != null)) {
448 s_hierarchyTransformsBuffer[hierarchyCount++] = transform;
449 }
450
451 for (int i = hierarchyCount - 1; i > 0; i--) {
452 SetTransformParent(s_hierarchyTransformsBuffer[i], s_hierarchyTransformsBuffer[i - 1]);
453 }
454
455 if (hierarchyCount > 0) {
456 return s_hierarchyTransformsBuffer[0];
457 }
458
459 return null;
460 }
461
462 private static Transform[] s_transformsBuffer = new Transform[4];
463 private Transform tryStackTransformHierarchy(params MonoBehaviour[] monoBehaviours) {
464 for (int i = 0; i < s_transformsBuffer.Length; i++) {
465 s_transformsBuffer[i] = null;
466 }
467
468 int tIdx = 0;
469 foreach (var behaviour in monoBehaviours.Query().Where(b => b != null)) {
470 s_transformsBuffer[tIdx++] = behaviour.transform;
471 }
472
473 return tryStackTransformHierarchy(s_transformsBuffer);
474 }
475
481 private int _curIdx;
482 private AttachmentHand _hand;
483 private int _flagsCount;
484
486
488 if (hand != null && hand._attachmentPointFlagConstants != null) {
489 _curIdx = -1;
490 _hand = hand;
491 _flagsCount = hand._attachmentPointFlagConstants.Length;
492 }
493 else {
494 // Hand doesn't exist (destroyed?) or isn't initialized yet.
495 _curIdx = -1;
496 _hand = null;
497 _flagsCount = 0;
498 }
499 }
500
502 get {
503 if (_hand == null) return null;
504
505 return _hand.GetBehaviourForPoint(GetFlagFromFlagIdx(_curIdx));
506 }
507 }
508
509 public bool MoveNext() {
510 do {
511 _curIdx++;
512 } while (_curIdx < _flagsCount && _hand.GetBehaviourForPoint(GetFlagFromFlagIdx(_curIdx)) == null);
513
514 return _curIdx < _flagsCount;
515 }
516 }
517
518 private static AttachmentPointFlags GetFlagFromFlagIdx(int pointIdx) {
519 return (AttachmentPointFlags)(1 << pointIdx + 1);
520 }
521
522 #endregion
523
524 }
525
526}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
This MonoBehaviour is managed by an AttachmentHands component on a parent MonoBehaviour....
AttachmentPointBehaviour pinkyMiddleJoint
AttachmentPointBehaviour indexMiddleJoint
AttachmentPointBehaviour indexTip
AttachmentPointBehaviour GetBehaviourForPoint(AttachmentPointFlags singlePoint)
Returns the AttachmentPointBehaviour child object of this AttachmentHand given a reference to a singl...
AttachmentPointBehaviour middleKnuckle
bool isTracked
Gets the chirality of this AttachmentHand. This is set automatically by the AttachmentHands parent ob...
AttachmentPointBehaviour middleDistalJoint
AttachmentPointBehaviour thumbDistalJoint
AttachmentPointBehaviour ringMiddleJoint
Action OnAttachmentPointsModified
Called when the AttachmentHand refreshes its AttachmentPointBehaviour transforms. If the user uncheck...
AttachmentPointBehaviour ringTip
AttachmentPointBehaviour indexDistalJoint
AttachmentPointBehaviour thumbProximalJoint
AttachmentPointBehaviour wrist
AttachmentPointBehaviour pinkyDistalJoint
AttachmentPointBehaviour ringKnuckle
AttachmentPointBehaviour pinkyTip
AttachmentPointBehaviour indexKnuckle
AttachmentPointBehaviour pinkyKnuckle
void refreshAttachmentTransforms(AttachmentPointFlags points)
AttachmentPointBehaviour middleTip
void notifyPointBehaviourDeleted(AttachmentPointBehaviour point)
AttachmentPointBehaviour ringDistalJoint
AttachmentPointsEnumerator points
Gets an enumerator that traverses all of the AttachmentPoints beneath this AttachmentHand.
AttachmentPointBehaviour thumbTip
Chirality chirality
Gets the chirality of this AttachmentHand. This is set automatically by the AttachmentHands parent ob...
AttachmentPointBehaviour middleMiddleJoint
Add an GameObject with this script to your scene if you would like to have a Transform hierarchy that...
Simple container class for storing a reference to the attachment point this transform corresponds to ...
AttachmentPointFlags
Flags for attachment points on the hand.
An enumerator that traverses all of the existing AttachmentPointBehaviours beneath an AttachmentHand.
A Query object is a type of immutable ordered collection of elements that can be used to perform usef...
Definition: Query.cs:90