Tanoda
HandModelManager.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 UnityEngine.Assertions;
11using System.Collections;
12using System.Collections.Generic;
13using UnityEngine.Serialization;
14using System;
16#if UNITY_EDITOR
17using UnityEditor;
18#endif
19
20namespace Leap.Unity {
21
31 public class HandModelManager : MonoBehaviour {
32
33 #region Formerly in LeapHandController
34
35 protected Dictionary<int, HandRepresentation> graphicsHandReps = new Dictionary<int, HandRepresentation>();
36 protected Dictionary<int, HandRepresentation> physicsHandReps = new Dictionary<int, HandRepresentation>();
37
38 protected bool graphicsEnabled = true;
39 protected bool physicsEnabled = true;
40
41 public bool GraphicsEnabled {
42 get {
43 return graphicsEnabled;
44 }
45 set {
46 graphicsEnabled = value;
47 }
48 }
49
50 public bool PhysicsEnabled {
51 get {
52 return physicsEnabled;
53 }
54 set {
55 physicsEnabled = value;
56 }
57 }
58
60 protected virtual void OnUpdateFrame(Frame frame) {
61 if (frame != null && graphicsEnabled) {
63 }
64 }
65
67 protected virtual void OnFixedFrame(Frame frame) {
68 if (frame != null && physicsEnabled) {
70 }
71 }
72
83 protected virtual void UpdateHandRepresentations(Dictionary<int, HandRepresentation> all_hand_reps, ModelType modelType, Frame frame) {
84 for (int i = 0; i < frame.Hands.Count; i++) {
85 var curHand = frame.Hands[i];
87 if (!all_hand_reps.TryGetValue(curHand.Id, out rep)) {
88 rep = MakeHandRepresentation(curHand, modelType);
89 all_hand_reps.Add(curHand.Id, rep);
90 }
91 if (rep != null) {
92 rep.IsMarked = true;
93 rep.UpdateRepresentation(curHand);
94 rep.LastUpdatedTime = (int)frame.Timestamp;
95 }
96 }
97
99 HandRepresentation toBeDeleted = null;
100 for (var it = all_hand_reps.GetEnumerator(); it.MoveNext();) {
101 var r = it.Current;
102 if (r.Value != null) {
103 if (r.Value.IsMarked) {
104 r.Value.IsMarked = false;
105 }
106 else {
108 //Debug.Log("Finishing");
109 toBeDeleted = r.Value;
110 }
111 }
112 }
115 if (toBeDeleted != null) {
116 all_hand_reps.Remove(toBeDeleted.HandID);
117 toBeDeleted.Finish();
118 }
119 }
120
121 #endregion
122
123 #region HandPool Inspector
124
125 [Tooltip("The LeapProvider to use to drive hand representations in the defined "
126 + "model pool groups.")]
127 [SerializeField]
128 [OnEditorChange("leapProvider")]
129 private LeapProvider _leapProvider;
131 get { return _leapProvider; }
132 set {
133 if (_leapProvider != null) {
134 _leapProvider.OnFixedFrame -= OnFixedFrame;
135 _leapProvider.OnUpdateFrame -= OnUpdateFrame;
136 }
137
138 _leapProvider = value;
139
140 if (_leapProvider != null) {
141 _leapProvider.OnFixedFrame += OnFixedFrame;
142 _leapProvider.OnUpdateFrame += OnUpdateFrame;
143 }
144 }
145 }
146
147 [SerializeField]
148 [Tooltip("To add a new set of Hand Models, first add the Left and Right objects as "
149 + "children of this object. Then create a new Model Pool entry referencing "
150 + "the new Hand Models and configure it as desired. "
151 + "Once configured, the Hand Model Manager object pipes Leap tracking data "
152 + "to the Hand Models as hands are tracked, and spawns duplicates as needed "
153 + "if \"Can Duplicate\" is enabled.")]
154 private List<ModelGroup> ModelPool = new List<ModelGroup>();
155 private List<HandRepresentation> activeHandReps = new List<HandRepresentation>();
156
157 private Dictionary<HandModelBase, ModelGroup> modelGroupMapping = new Dictionary<HandModelBase, ModelGroup>();
158 private Dictionary<HandModelBase, HandRepresentation> modelToHandRepMapping = new Dictionary<HandModelBase, HandRepresentation>();
159
160 #endregion
161
162 #region ModelGroup Class
163
171 [System.Serializable]
172 public class ModelGroup {
173 public string GroupName;
174 [HideInInspector]
176
178 [HideInInspector]
179 public bool IsLeftToBeSpawned;
181 [HideInInspector]
183 [NonSerialized]
184 public List<HandModelBase> modelList = new List<HandModelBase>();
185 [NonSerialized]
186 public List<HandModelBase> modelsCheckedOut = new List<HandModelBase>();
187 public bool IsEnabled = true;
188 public bool CanDuplicate;
189
190 /*Looks for suitable HandModelBase is the ModelGroup's modelList, if found, it is added to modelsCheckedOut.
191 * If not, one can be cloned*/
192 public HandModelBase TryGetModel(Chirality chirality, ModelType modelType) {
193 for (int i = 0; i < modelList.Count; i++) {
194 if (modelList[i].HandModelType == modelType && modelList[i].Handedness == chirality) {
195 HandModelBase model = modelList[i];
196 modelList.RemoveAt(i);
197 modelsCheckedOut.Add(model);
198 return model;
199 }
200 }
201 if (CanDuplicate) {
202 for (int i = 0; i < modelsCheckedOut.Count; i++) {
203 if (modelsCheckedOut[i].HandModelType == modelType && modelsCheckedOut[i].Handedness == chirality) {
204 HandModelBase modelToSpawn = modelsCheckedOut[i];
205 HandModelBase spawnedModel = GameObject.Instantiate(modelToSpawn);
206 spawnedModel.transform.parent = _handModelManager.transform;
207 _handModelManager.modelGroupMapping.Add(spawnedModel, this);
208 modelsCheckedOut.Add(spawnedModel);
209 return spawnedModel;
210 }
211 }
212 }
213 return null;
214 }
215 public void ReturnToGroup(HandModelBase model) {
216 modelsCheckedOut.Remove(model);
217 modelList.Add(model);
218 this._handModelManager.modelToHandRepMapping.Remove(model);
219 }
220 }
221
222 #endregion
223
224 #region HandPool Methods
225
226 public void ReturnToPool(HandModelBase model) {
227 ModelGroup modelGroup;
228 bool groupFound = modelGroupMapping.TryGetValue(model, out modelGroup);
229 Assert.IsTrue(groupFound);
230 //First see if there is another active Representation that can use this model
231 for (int i = 0; i < activeHandReps.Count; i++) {
232 HandRepresentation rep = activeHandReps[i];
233 if (rep.RepChirality == model.Handedness && rep.RepType == model.HandModelType) {
234 bool modelFromGroupFound = false;
235 if (rep.handModels != null) {
236 //And that Represention does not contain a model from this model's modelGroup
237 for (int j = 0; j < modelGroup.modelsCheckedOut.Count; j++) {
238 HandModelBase modelToCompare = modelGroup.modelsCheckedOut[j];
239 for (int k = 0; k < rep.handModels.Count; k++) {
240 if (rep.handModels[k] == modelToCompare) {
241 modelFromGroupFound = true;
242 }
243 }
244 }
245 }
246 if (!modelFromGroupFound) {
247 rep.AddModel(model);
248 modelToHandRepMapping[model] = rep;
249 return;
250 }
251 }
252 }
253 //Otherwise return to pool
254 modelGroup.ReturnToGroup(model);
255 }
256
257 // TODO: DELETEME -- unnecessary?
258 // public bool TryGetHand(out HandModelBase handModel, string groupName,
259 // bool isRight)
260 // {
261 // ModelGroup group = ModelPool.Find(g => g.GroupName.Equals(groupName));
262 // if (group == null) { return false; }
263 // else {
264
265 // }
266 // }
267
268 #endregion
269
270 #region Hand Representations
271
278 Chirality handChirality = hand.IsRight ? Chirality.Right : Chirality.Left;
279 HandRepresentation handRep = new HandRepresentation(this, hand, handChirality, modelType);
280 for (int i = 0; i < ModelPool.Count; i++) {
281 ModelGroup group = ModelPool[i];
282 if (group.IsEnabled) {
283 HandModelBase model = group.TryGetModel(handChirality, modelType);
284 if (model != null) {
285 handRep.AddModel(model);
286 if (!modelToHandRepMapping.ContainsKey(model)) {
287 model.group = group;
288 modelToHandRepMapping.Add(model, handRep);
289 }
290 }
291 }
292 }
293 activeHandReps.Add(handRep);
294 return handRep;
295 }
296
297 public void RemoveHandRepresentation(HandRepresentation handRepresentation) {
298 activeHandReps.Remove(handRepresentation);
299 }
300
301 #endregion
302
303 #region Unity Events
304
305 protected virtual void OnEnable() {
306 if (_leapProvider == null) {
307 _leapProvider = Hands.Provider;
308 }
309
310 _leapProvider.OnUpdateFrame -= OnUpdateFrame;
311 _leapProvider.OnUpdateFrame += OnUpdateFrame;
312
313 _leapProvider.OnFixedFrame -= OnFixedFrame;
314 _leapProvider.OnFixedFrame += OnFixedFrame;
315 }
316
317 protected virtual void OnDisable() {
318 _leapProvider.OnUpdateFrame -= OnUpdateFrame;
319 _leapProvider.OnFixedFrame -= OnFixedFrame;
320 }
321
323 void Start() {
324 for(int i=0; i<ModelPool.Count; i++) {
325 InitializeModelGroup(ModelPool[i]);
326 }
327 }
328
329 #endregion
330
331 #region Group Methods
332
333 private void InitializeModelGroup(ModelGroup collectionGroup) {
334 // Prevent the ModelGroup be initialized by multiple times
335 if (modelGroupMapping.ContainsValue(collectionGroup)) {
336 return;
337 }
338
339 collectionGroup._handModelManager = this;
340 HandModelBase leftModel;
341 HandModelBase rightModel;
342 if (collectionGroup.IsLeftToBeSpawned) {
343 HandModelBase modelToSpawn = collectionGroup.LeftModel;
344 GameObject spawnedGO = Instantiate(modelToSpawn.gameObject);
345 leftModel = spawnedGO.GetComponent<HandModelBase>();
346 leftModel.transform.parent = this.transform;
347 } else {
348 leftModel = collectionGroup.LeftModel;
349 }
350 if (leftModel != null) {
351 collectionGroup.modelList.Add(leftModel);
352 modelGroupMapping.Add(leftModel, collectionGroup);
353 }
354
355 if (collectionGroup.IsRightToBeSpawned) {
356 HandModelBase modelToSpawn = collectionGroup.RightModel;
357 GameObject spawnedGO = Instantiate(modelToSpawn.gameObject);
358 rightModel = spawnedGO.GetComponent<HandModelBase>();
359 rightModel.transform.parent = this.transform;
360 } else {
361 rightModel = collectionGroup.RightModel;
362 }
363 if (rightModel != null) {
364 collectionGroup.modelList.Add(rightModel);
365 modelGroupMapping.Add(rightModel, collectionGroup);
366 }
367 }
368
373 public void EnableGroup(string groupName) {
374 StartCoroutine(enableGroup(groupName));
375 }
376 private IEnumerator enableGroup(string groupName) {
377 yield return new WaitForEndOfFrame();
378 ModelGroup group = null;
379 for (int i = 0; i < ModelPool.Count; i++) {
380 if (ModelPool[i].GroupName == groupName) {
381 group = ModelPool[i];
382 for (int hp = 0; hp < activeHandReps.Count; hp++) {
383 HandRepresentation handRep = activeHandReps[hp];
384 HandModelBase model = group.TryGetModel(handRep.RepChirality, handRep.RepType);
385 if (model != null) {
386 handRep.AddModel(model);
387 modelToHandRepMapping.Add(model, handRep);
388 }
389 }
390 group.IsEnabled = true;
391 }
392 }
393 if (group == null) {
394 Debug.LogWarning("A group matching that name does not exisit in the modelPool");
395 }
396 }
397
402 public void DisableGroup(string groupName) {
403 StartCoroutine(disableGroup(groupName));
404 }
405 private IEnumerator disableGroup(string groupName) {
406 yield return new WaitForEndOfFrame();
407 ModelGroup group = null;
408 for (int i = 0; i < ModelPool.Count; i++) {
409 if (ModelPool[i].GroupName == groupName) {
410 group = ModelPool[i];
411 for (int m = 0; m < group.modelsCheckedOut.Count; m++) {
412 HandModelBase model = group.modelsCheckedOut[m];
413 HandRepresentation handRep;
414 if (modelToHandRepMapping.TryGetValue(model, out handRep)) {
415 handRep.RemoveModel(model);
416 group.ReturnToGroup(model);
417 m--;
418 }
419 }
420 Assert.AreEqual(0, group.modelsCheckedOut.Count, group.GroupName + "'s modelsCheckedOut List has not been cleared");
421 group.IsEnabled = false;
422 }
423 }
424 if (group == null) {
425 Debug.LogWarning("A group matching that name does not exisit in the modelPool");
426 }
427 }
428
429 public void ToggleGroup(string groupName) {
430 StartCoroutine(toggleGroup(groupName));
431 }
432 private IEnumerator toggleGroup(string groupName) {
433 yield return new WaitForEndOfFrame();
434 ModelGroup modelGroup = ModelPool.Find(i => i.GroupName == groupName);
435 if (modelGroup != null) {
436 if (modelGroup.IsEnabled == true) {
437 DisableGroup(groupName);
438 modelGroup.IsEnabled = false;
439 } else {
440 EnableGroup(groupName);
441 modelGroup.IsEnabled = true;
442 }
443 } else Debug.LogWarning("A group matching that name does not exisit in the modelPool");
444 }
445
446 public void AddNewGroup(string groupName, HandModelBase leftModel, HandModelBase rightModel) {
447 ModelGroup newGroup = new ModelGroup();
448 newGroup.LeftModel = leftModel;
449 newGroup.RightModel = rightModel;
450 newGroup.GroupName = groupName;
451 newGroup.CanDuplicate = false;
452 newGroup.IsEnabled = true;
453 ModelPool.Add(newGroup);
454 InitializeModelGroup(newGroup);
455 }
456
457 public void RemoveGroup(string groupName) {
458 while (ModelPool.Find(i => i.GroupName == groupName) != null) {
459 ModelGroup modelGroup = ModelPool.Find(i => i.GroupName == groupName);
460 if (modelGroup != null) {
461 ModelPool.Remove(modelGroup);
462 }
463 }
464 }
465
466 public T GetHandModel<T>(int handId) where T : HandModelBase {
467 foreach (ModelGroup group in ModelPool) {
468 foreach (HandModelBase handModel in group.modelsCheckedOut) {
469 if (handModel.GetLeapHand().Id == handId && handModel is T) {
470 return handModel as T;
471 }
472 }
473 }
474 return null;
475 }
476
477 #endregion
478
479 #region Editor-only Methods
480
481 #if UNITY_EDITOR
483 void OnValidate() {
484 if (ModelPool != null) {
485 for (int i = 0; i < ModelPool.Count; i++) {
486 if (ModelPool[i] != null) {
487 if (ModelPool[i].LeftModel) {
488 ModelPool[i].IsLeftToBeSpawned = Utils.IsObjectPartOfPrefabAsset(
489 ModelPool[i].LeftModel);
490 }
491 if (ModelPool[i].RightModel) {
492 ModelPool[i].IsRightToBeSpawned = Utils.IsObjectPartOfPrefabAsset(
493 ModelPool[i].RightModel);
494 }
495 }
496 }
497 }
498 }
499
500 #endif
501
502 #endregion
503
504 }
505}
506
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
The Frame class represents a set of hand and finger tracking data detected in a single frame.
Definition: Frame.cs:24
long Timestamp
The frame capture time in microseconds elapsed since an arbitrary point in time in the past.
Definition: Frame.cs:136
List< Hand > Hands
The list of Hand objects detected in this frame, given in arbitrary order. The list can be empty if n...
Definition: Frame.cs:156
The Hand class reports the physical characteristics of a detected hand.
Definition: Hand.cs:26
int Id
A unique ID assigned to this Hand object, whose value remains the same across consecutive frames whil...
Definition: Hand.cs:152
bool IsRight
Identifies whether this Hand is a right hand.
Definition: Hand.cs:302
abstract ModelType HandModelType
HandModelManager.ModelGroup group
abstract Chirality Handedness
abstract Hand GetLeapHand()
HandModelBase TryGetModel(Chirality chirality, ModelType modelType)
void ReturnToGroup(HandModelBase model)
The HandModelManager manages a pool of HandModelBases and makes HandRepresentations when a it detects...
virtual void OnFixedFrame(Frame frame)
void RemoveHandRepresentation(HandRepresentation handRepresentation)
void DisableGroup(string groupName)
void ToggleGroup(string groupName)
void ReturnToPool(HandModelBase model)
Dictionary< int, HandRepresentation > graphicsHandReps
void EnableGroup(string groupName)
virtual void UpdateHandRepresentations(Dictionary< int, HandRepresentation > all_hand_reps, ModelType modelType, Frame frame)
HandRepresentation MakeHandRepresentation(Hand hand, ModelType modelType)
void RemoveGroup(string groupName)
Dictionary< int, HandRepresentation > physicsHandReps
void AddNewGroup(string groupName, HandModelBase leftModel, HandModelBase rightModel)
virtual void OnUpdateFrame(Frame frame)
void RemoveModel(HandModelBase model)
List< HandModelBase > handModels
void AddModel(HandModelBase model)
Provides Frame object data to the Unity application by firing events as soon as Frame data is availab...
Definition: LeapProvider.cs:21
Action< Frame > OnFixedFrame
Definition: LeapProvider.cs:26
Action< Frame > OnUpdateFrame
Definition: LeapProvider.cs:25