Tanoda
LeapGraphicGroup.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 System;
10using System.Collections;
11using System.Collections.Generic;
12using UnityEngine;
13using UnityEngine.Assertions;
14#if UNITY_EDITOR
15using UnityEditor;
16#endif
17using Leap.Unity.Space;
18using Leap.Unity.Query;
19
21
22 [Serializable]
23 public partial class LeapGraphicGroup : ISerializationCallbackReceiver, ILeapInternalGraphicGroup {
24
25 #region INSPECTOR FIELDS
26 [SerializeField]
27 private string _groupName = default(string);
28
29 [SerializeField]
30 private RenderingMethodReference _renderingMethod = new RenderingMethodReference();
31
32 [SerializeField]
33 private FeatureList _features = new FeatureList();
34 #endregion
35
36 #region PRIVATE VARIABLES
37 [SerializeField, HideInInspector]
38 private LeapGraphicRenderer _renderer;
39
40 [SerializeField, HideInInspector]
41 private List<LeapGraphic> _graphics = new List<LeapGraphic>();
42
43 [SerializeField, HideInInspector]
44 private List<SupportInfo> _supportInfo = new List<SupportInfo>();
45
46 [SerializeField, HideInInspector]
47 private bool _addRemoveSupported = default(bool);
48
49 private HashSet<LeapGraphic> _toAttach = new HashSet<LeapGraphic>();
50 private HashSet<LeapGraphic> _toDetach = new HashSet<LeapGraphic>();
51 #endregion
52
53 #region PUBLIC RUNTIME API
54
55 public string name {
56 get {
57 return _groupName;
58 }
59 }
60
65 get {
66 return _renderer;
67 }
68 }
69
74 set {
75 _renderer = value;
76 }
77 }
78
85 get {
86 return _renderingMethod.Value;
87 }
88 }
89
95 public IList<LeapGraphicFeatureBase> features {
96 get {
97 Assert.IsNotNull(_features, "The feature list of graphic group was null!");
98 return _features;
99 }
100 }
101
107 public List<LeapGraphic> graphics {
108 get {
109 Assert.IsNotNull(_graphics, "The graphic list of graphic group was null!");
110 return _graphics;
111 }
112 }
113
119 public int toBeAttachedCount {
120 get {
121 return _graphics.Count + _toAttach.Count;
122 }
123 }
124
129 public List<SupportInfo> supportInfo {
130 get {
131 Assert.IsNotNull(_supportInfo, "The support info list of graphic group was null!");
132 Assert.AreEqual(_features.Count, _supportInfo.Count, "The support info list should have the same length as the feature list.");
133 return _supportInfo;
134 }
135 }
136
142 public bool addRemoveSupported {
143 get {
144 return _addRemoveSupported;
145 }
146 }
147
160 public bool TryAddGraphic(LeapGraphic graphic) {
161 Assert.IsNotNull(graphic);
162
163 if (graphic.willbeAttached || (graphic.isAttachedToGroup && !graphic.willbeDetached)) {
164 return false;
165 }
166
167 if (!addRemoveSupportedOrEditTime()) {
168 return false;
169 }
170
171#if UNITY_EDITOR
172 if (!Application.isPlaying) {
173 Undo.RecordObject(graphic, "Added graphic to group");
174 } else
175#endif
176 {
177 if (_toAttach.Contains(graphic)) {
178 return false;
179 }
180 if (_toDetach.Contains(graphic)) {
181 graphic.CancelWillBeDetached();
182 graphic.isRepresentationDirty = true;
183 _toDetach.Remove(graphic);
184 return true;
185 }
186 }
187
188 if (_graphics.Contains(graphic)) {
189 if (graphic.attachedGroup == null) {
190 //detatch and re-add, it forgot it was attached!
191 //This can easily happen at edit time due to prefab shenanigans
192 graphic.OnDetachedFromGroup();
193 _graphics.Remove(graphic);
194 } else {
195 Debug.LogWarning("Could not add graphic because it was already a part of this group.");
196 return false;
197 }
198 }
199
200#if UNITY_EDITOR
201 if (!Application.isPlaying) {
202 int newIndex = _graphics.Count;
203 _graphics.Add(graphic);
204
205 LeapSpaceAnchor anchor = _renderer.space == null ? null : LeapSpaceAnchor.GetAnchor(graphic.transform);
206
209
210 graphic.OnAttachedToGroup(this, anchor);
211
212 if (_renderer.space != null) {
213 _renderer.space.RebuildHierarchy();
214 _renderer.space.RecalculateTransformers();
215 }
216
217 _renderer.editor.ScheduleRebuild();
218 } else
219#endif
220 {
221 if (_toAttach.Contains(graphic)) {
222 return false;
223 }
224
225 graphic.NotifyWillBeAttached(this);
226 _toAttach.Add(graphic);
227 }
228
229 return true;
230 }
231
232 public void RefreshGraphicAnchors() {
233 foreach (var graphic in _graphics) {
234 var anchor = _renderer.space == null ? null : LeapSpaceAnchor.GetAnchor(graphic.transform);
235 graphic.OnUpdateAnchor(anchor);
236 }
237 }
238
250 public bool TryRemoveGraphic(LeapGraphic graphic) {
251 Assert.IsNotNull(graphic);
252
253 if (!addRemoveSupportedOrEditTime()) {
254 return false;
255 }
256
257#if UNITY_EDITOR
258 if (Application.isPlaying)
259#endif
260 {
261 if (_toDetach.Contains(graphic)) {
262 return false;
263 }
264 if (_toAttach.Contains(graphic)) {
265 graphic.CancelWillBeAttached();
266 graphic.isRepresentationDirty = true;
267 _toAttach.Remove(graphic);
268 return true;
269 }
270 }
271
272 int graphicIndex = _graphics.IndexOf(graphic);
273 if (graphicIndex < 0) {
274 return false;
275 }
276
277#if UNITY_EDITOR
278 if (!Application.isPlaying) {
279 Undo.RecordObject(graphic, "Removed graphic from group");
280 Undo.RecordObject(_renderer, "Removed graphic from group");
281
282 graphic.OnDetachedFromGroup();
283 _graphics.RemoveAt(graphicIndex);
284
287
288 if (_renderer.space != null) {
289 _renderer.space.RebuildHierarchy();
290 _renderer.space.RecalculateTransformers();
291 }
292
293 _renderer.editor.ScheduleRebuild();
294 } else
295#endif
296 {
297 if (_toDetach.Contains(graphic)) {
298 return false;
299 }
300
301 graphic.NotifyWillBeDetached(this);
302 _toDetach.Add(graphic);
303 }
304
305 return true;
306 }
307
314 features.Clear();
315 for (int i = 0; i < _features.Count; i++) {
316 var feature = _features[i];
317 if (!(feature is T)) continue;
318 if (_supportInfo[i].support == SupportType.Error) continue;
319
320 features.Add(feature as T);
321 }
322
323 return features.Count != 0;
324 }
325
326 public void UpdateRenderer() {
327#if UNITY_EDITOR
328 if (Application.isPlaying)
329#endif
330 {
331 handleRuntimeAddRemove();
332 }
333
334 _renderingMethod.Value.OnUpdateRenderer();
335
336 foreach (var feature in _features) {
337 feature.isDirty = false;
338 }
339 }
340
341 public void RebuildFeatureData() {
342 using (new ProfilerSample("Rebuild Feature Data")) {
343 foreach (var feature in _features) {
344 feature.ClearDataObjectReferences();
345 }
346
347 for (int i = 0; i < _graphics.Count; i++) {
348 var graphic = _graphics[i];
349#if UNITY_EDITOR
350 EditorUtility.SetDirty(graphic);
351 Undo.RecordObject(graphic, "modified feature data on graphic.");
352#endif
353
354 List<LeapFeatureData> dataList = new List<LeapFeatureData>();
355 foreach (var feature in _features) {
356 var dataObj = graphic.featureData.Query().OfType(feature.GetDataObjectType()).FirstOrDefault();
357 if (dataObj != null) {
358 graphic.featureData.Remove(dataObj);
359 } else {
360 dataObj = feature.CreateFeatureDataForGraphic(graphic);
361 }
362 feature.AddFeatureData(dataObj);
363 dataList.Add(dataObj);
364 }
365
366 graphic.OnAssignFeatureData(dataList);
367 }
368
369 //Could be more efficient
370 foreach (var feature in _features) {
371 feature.AssignFeatureReferences();
372 }
373 }
374 }
375
377 using (new ProfilerSample("Rebuild Support Info")) {
378 var typeToFeatures = new Dictionary<Type, List<LeapGraphicFeatureBase>>();
379 foreach (var feature in _features) {
380 Type featureType = feature.GetType();
381 List<LeapGraphicFeatureBase> list;
382 if (!typeToFeatures.TryGetValue(featureType, out list)) {
383 list = new List<LeapGraphicFeatureBase>();
384 typeToFeatures[featureType] = list;
385 }
386
387 list.Add(feature);
388 }
389
390 var featureToInfo = new Dictionary<LeapGraphicFeatureBase, SupportInfo>();
391
392 foreach (var pair in typeToFeatures) {
393 var featureType = pair.Key;
394 var featureList = pair.Value;
395 var infoList = new List<SupportInfo>().FillEach(featureList.Count, () => SupportInfo.FullSupport());
396
397 var castList = Activator.CreateInstance(typeof(List<>).MakeGenericType(featureType)) as IList;
398 foreach (var feature in featureList) {
399 castList.Add(feature);
400 }
401
402 try {
403 if (_renderingMethod.Value == null) continue;
404
405 var interfaceType = typeof(ISupportsFeature<>).MakeGenericType(featureType);
406 if (!interfaceType.IsAssignableFrom(_renderingMethod.Value.GetType())) {
407 infoList.FillEach(() => SupportInfo.Error("This renderer does not support this feature."));
408 continue;
409 }
410
411 var supportDelegate = interfaceType.GetMethod("GetSupportInfo");
412
413 if (supportDelegate == null) {
414 Debug.LogError("Could not find support delegate.");
415 continue;
416 }
417
418 supportDelegate.Invoke(_renderingMethod.Value, new object[] { castList, infoList });
419 } finally {
420 for (int i = 0; i < featureList.Count; i++) {
421 featureToInfo[featureList[i]] = infoList[i];
422 }
423 }
424 }
425
426 _supportInfo = new List<SupportInfo>();
427 foreach (var feature in _features) {
428 _supportInfo.Add(feature.GetSupportInfo(this).OrWorse(featureToInfo[feature]));
429 }
430 }
431 }
432 #endregion
433
434 #region LIFECYCLE CALLBACKS
435
439 public void OnEnable() {
440 for (int i = 0; i < _features.Count; i++) {
441 _features[i].AssignFeatureReferences();
442 _features[i].ClearDataObjectReferences();
443 _features[i].isDirty = true;
444 foreach (var graphic in _graphics) {
445 _features[i].AddFeatureData(graphic.featureData[i]);
446 }
447 }
448
449 _renderingMethod.Value.OnEnableRenderer();
450 }
451
455 public void OnDisable() {
456 _renderingMethod.Value.OnDisableRenderer();
457 }
458
459 #endregion
460
461 #region PRIVATE IMPLEMENTATION
462
463#if UNITY_EDITOR
464 public LeapGraphicGroup() {
465 editor = new EditorApi(this);
466 }
467#endif
468
469 private void handleRuntimeAddRemove() {
470 if (_toAttach.Count == 0 && _toDetach.Count == 0) {
471 return;
472 }
473
474 using (new ProfilerSample("Handle Runtime Add/Remove")) {
475 List<int> dirtyIndexes = Pool<List<int>>.Spawn();
476
477 try {
478 var attachEnum = _toAttach.GetEnumerator();
479 var detachEnum = _toDetach.GetEnumerator();
480 bool canAttach = attachEnum.MoveNext();
481 bool canDetach = detachEnum.MoveNext();
482
483 //First, we can handle pairs of adds/removes easily by simply placing
484 //the new graphic in the same place the old graphic was.
485 while (canAttach && canDetach) {
486 int toDetatchIndex = _graphics.IndexOf(detachEnum.Current);
487 _graphics[toDetatchIndex] = attachEnum.Current;
488
489 var anchor = _renderer.space == null ? null : LeapSpaceAnchor.GetAnchor(attachEnum.Current.transform);
490
491 detachEnum.Current.OnDetachedFromGroup();
492 attachEnum.Current.OnAttachedToGroup(this, anchor);
493
494 dirtyIndexes.Add(toDetatchIndex);
495
496 canAttach = attachEnum.MoveNext();
497 canDetach = detachEnum.MoveNext();
498 }
499
500 int newGraphicStart = _graphics.Count;
501
502 //Then we append all the new graphics if there are any left. This
503 //only happens if more graphics were added than were remove this
504 //frame.
505 while (canAttach) {
506 _graphics.Add(attachEnum.Current);
507 canAttach = attachEnum.MoveNext();
508 }
509
510 //We remove any graphics that did not have a matching add. This
511 //only happens if more graphics were removed than were added this
512 //frame.
513 while (canDetach) {
514 int toDetachIndex = _graphics.IndexOf(detachEnum.Current);
515 dirtyIndexes.Add(toDetachIndex);
516
517 _graphics[_graphics.Count - 1].isRepresentationDirty = true;
518 _graphics.RemoveAtUnordered(toDetachIndex);
519
520 detachEnum.Current.OnDetachedFromGroup();
521
522 canDetach = detachEnum.MoveNext();
523 }
524
525 //TODO: this is gonna need to be optimized
526 //Make sure to call this before OnAttachedToGroup or else the graphic
527 //will not have the correct feature data when it gets attached!
530
531 //newGraphicStart is either less than _graphics.Count because we added
532 //new graphics, or it is greater than _graphics.Count because we removed
533 //some graphics.
534 for (int i = newGraphicStart; i < _graphics.Count; i++) {
535 var anchor = _renderer.space == null ? null : LeapSpaceAnchor.GetAnchor(attachEnum.Current.transform);
536 _graphics[i].OnAttachedToGroup(this, anchor);
537 }
538
539 attachEnum.Dispose();
540 detachEnum.Dispose();
541 _toAttach.Clear();
542 _toDetach.Clear();
543
544 //Make sure the dirty indexes only point to valid graphics areas.
545 //Could potentially be optimized, but hasnt been a bottleneck.
546 for (int i = dirtyIndexes.Count; i-- != 0;) {
547 if (dirtyIndexes[i] >= _graphics.Count) {
548 dirtyIndexes.RemoveAt(i);
549 }
550 }
551
552 if (renderer.space != null) {
555 }
556
557 foreach (var feature in _features) {
558 feature.isDirty = true;
559 }
560
561 (_renderingMethod.Value as ISupportsAddRemove).OnAddRemoveGraphics(dirtyIndexes);
562 } finally {
563 dirtyIndexes.Clear();
564 Pool<List<int>>.Recycle(dirtyIndexes);
565 }
566 }
567 }
568
569 private bool addRemoveSupportedOrEditTime() {
570#if UNITY_EDITOR
571 if (!Application.isPlaying) {
572 return true;
573 }
574#endif
575
576 return _addRemoveSupported;
577 }
578
579 public void OnBeforeSerialize() { }
580
581 public void OnAfterDeserialize() {
582 if (_renderingMethod.Value == null || renderer == null) {
583 Debug.LogWarning("The rendering group did not find the needed data! If you have a variable of type " +
584 "LeapGraphicGroup make sure to annotate it with a [NonSerialized] attribute, or else " +
585 "Unity will automatically create invalid instances of the class.");
586 } else {
587 ILeapInternalRenderingMethod renderingMethodInternal = _renderingMethod.Value;
588 renderingMethodInternal.group = this;
589 renderingMethodInternal.renderer = renderer;
590 }
591 }
592
593 [Serializable]
594 public class FeatureList : MultiTypedList<LeapGraphicFeatureBase, LeapTextureFeature,
595 LeapSpriteFeature,
596 LeapRuntimeTintFeature,
597 LeapBlendShapeFeature,
598 CustomFloatChannelFeature,
599 CustomVectorChannelFeature,
600 CustomColorChannelFeature,
601 CustomMatrixChannelFeature> { }
602
603 [Serializable]
604 public class RenderingMethodReference : MultiTypedReference<LeapRenderingMethod, LeapBakedRenderer,
605 LeapDynamicRenderer,
606 LeapTextRenderer> { }
607 #endregion
608 }
609}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
bool addRemoveSupported
Returns whether or not add/remove operations are supported at runtime by this group....
bool TryAddGraphic(LeapGraphic graphic)
Tries to add the given graphic to this group. This can safely be called during runtime or edit time....
void OnEnable()
Specifically called during the OnEnable callback during RUNTIME ONLY
List< SupportInfo > supportInfo
Maps 1-to-1 with the feature list, where each element represents the support that feature currently h...
LeapRenderingMethod renderingMethod
Gets the rendering method used for this group. This can only be changed at edit time using either the...
void OnDisable()
Specifically called during the OnDisable callback during RUNTIME ONLY
int toBeAttachedCount
Returns the total number of graphics that will be part of this group after the next update cycle....
List< LeapGraphic > graphics
Returns the list of graphics attached to this group. This getter returns a regular mutable list for s...
bool TryRemoveGraphic(LeapGraphic graphic)
Tries to remove the given graphic from this group. This can safely be called during runtime or edit t...
LeapGraphicRenderer renderer
Gets the renderer this group is attached to.
bool GetSupportedFeatures< T >(List< T > features)
Fills the argument list with all of the currently supported features of type T. Returns true if there...
IList< LeapGraphicFeatureBase > features
Returns the list of features attached to this group. This can only be changed at edit time using eith...
bool isAttachedToGroup
Returns whether or not this graphic is attached to any group. Can still return false at runtime even ...
Definition: LeapGraphic.cs:162
bool isRepresentationDirty
An internal flag that returns true if the visual representation of this graphic needs to be updated....
Definition: LeapGraphic.cs:64
virtual void NotifyWillBeAttached(LeapGraphicGroup toBeAttachedTo)
Called by the system to notify that this graphic will be attached within the next frame....
Definition: LeapGraphic.cs:228
bool willbeDetached
Returns whether or not this graphic will be detached from a group within the next frame....
Definition: LeapGraphic.cs:184
virtual void CancelWillBeDetached()
Called by the system to notify that a previous notification that this graphic would be detached has b...
Definition: LeapGraphic.cs:262
LeapGraphicGroup attachedGroup
Returns the group this graphic is attached to.
Definition: LeapGraphic.cs:140
virtual void CancelWillBeAttached()
Called by the system to notify that a previous notification that this graphic would be attached has b...
Definition: LeapGraphic.cs:240
virtual void OnAttachedToGroup(LeapGraphicGroup group, LeapSpaceAnchor anchor)
Called by the system when this graphic is attached to a group. This method is invoked both at runtime...
Definition: LeapGraphic.cs:272
virtual void OnDetachedFromGroup()
Called by the system when this graphic is detached from a group. This method is invoked both at runti...
Definition: LeapGraphic.cs:303
virtual void NotifyWillBeDetached(LeapGraphicGroup toBeDetachedFrom)
Called by the system to notify that this graphic will be detached within the next frame....
Definition: LeapGraphic.cs:252
bool willbeAttached
Returns whether or not this graphic will be attached to a group within the next frame....
Definition: LeapGraphic.cs:173
LeapSpace space
Returns the leap space that is currently attached to this graphic renderer.
Represents an ordered collection of objects of type BaseType.
Represents a single reference to a value of type BaseType.
static LeapSpaceAnchor GetAnchor(Transform root)
void RecalculateTransformers()
Call to update all transformers in the space. Call this whenever any anchor or parent of an anchor ch...
Definition: LeapSpace.cs:85
void RebuildHierarchy()
Call to traverse the entire hierarchy and rebuild the relationship between anchors....
Definition: LeapSpace.cs:71
The support info class provides a very basic way to notify that something is fully supported,...
Definition: SupportInfo.cs:21
static SupportInfo Error(string message)
Helper getter to return a struct that signifies no support with an error message.
Definition: SupportInfo.cs:42
static SupportInfo FullSupport()
Helper getter to return a struct that signifies full support.
Definition: SupportInfo.cs:28
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....