Tanoda
InteractionManager.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
12using System;
13using System.Collections.Generic;
14using UnityEngine;
15
16#if UNITY_EDITOR
17using UnityEditor;
18#endif
19
20namespace Leap.Unity.Interaction {
21
22 [DisallowMultipleComponent]
23 [ExecuteInEditMode]
24 public class InteractionManager : MonoBehaviour, IInternalInteractionManager,
26
27 #region Inspector
28
29 // Header "Interaction Controllers" via InteractionManagerEditor.cs.
30 [SerializeField]
31 private InteractionControllerSet _interactionControllers = new InteractionControllerSet();
36 get { return _interactionControllers; }
37 }
38
39 [Header("Interaction Settings")]
40
41 [SerializeField]
42 [Tooltip("Beyond this radius, an interaction object will not receive hover or primary "
43 + "hover callbacks from an interaction controller. (Smaller values are "
44 + "cheaper.) This value is automatically scaled under the hood by the "
45 + "Interaction Manager's lossyScale.x, so it's recommended to keep your "
46 + "Interaction Manager with unit scale underneath your 'Player' Transform if "
47 + "you expect your player's hands or controllers to ever have non-unit scale.")]
48 public float hoverActivationRadius = 0.2F;
49
50 [Tooltip("Beyond this radius, an interaction object will not be considered for "
51 + "contact or grasping logic. The radius should be small as an optimization "
52 + "but certainly not smaller than an interaction controller and not too tight "
53 + "around the controller to allow good behavior when it is moving quickly "
54 + "through space. This value is automatically scaled under the hood by the "
55 + "Interaction Manager's lossyScale.x, so it's recommended to keep your "
56 + "Interaction Manager with unit scale underneath your 'Player' Transform if "
57 + "you expect your player's hands or controllers to ever have non-unit scale.")]
58 public float touchActivationRadius = 0.075F;
59
61 PreservePosePerController,
62 ReinitializeOnAnyRelease
63 }
64 [Tooltip("If set to PreservePosePerController, when holding a multi-grasp-enabled object and releasing with a single hand or controller, the object's held pose will adjust to reflect only the remaining holding controllers.\n\nIf set to PreservePosePerController, when any hand or controller releases an object, the remaining controllers will reinitialize their holding pose to match the last-held state of the object, allowing the user to rotate and translate the object more loosely.\n\nPreservePosePerController is the default setting.")]
66
67 [Header("Layer Settings")]
68 [Tooltip("Whether or not to create the layers used for interaction when the scene "
69 + "runs. Interactions require an interaction layer (for objects), a grasped "
70 + "object layer, and a contact bone layer (for interaction controller 'bone' "
71 + "colliders). Keep this checked to have these layers created for you, but be "
72 + "aware that the generated layers will have blank names due to Unity "
73 + "limitations.")]
74 [SerializeField]
76 protected bool _autoGenerateLayers = true;
80 public bool autoGenerateLayers { get { return _autoGenerateLayers; } }
81
82 [Tooltip("When automatically generating layers, the Interaction layer (for "
83 + "interactable objects) will use the same physics collision flags as the "
84 + "layer specified here.")]
85 [SerializeField]
87 public SingleLayer templateLayer { get { return _templateLayer; } }
88
89 [Tooltip("The layer for interactable objects (i.e. InteractionBehaviours). Usually "
90 + "this would have the same collision flags as the Default layer, but it "
91 + "should be its own layer so interaction controllers don't have to check "
92 + "collision against all physics objects in the scene.")]
93 [SerializeField]
96
97 [Tooltip("The layer objects are moved to when they become grasped, or if they are "
98 + "otherwise ignoring controller contact. This layer should not collide with "
99 + "the contact bone layer, but should collide with everything else that the "
100 + "interaction layer collides with.")]
101 [SerializeField]
104
105 [Tooltip("The layer containing the collider 'bones' of the interaction controller. "
106 + "This layer should collide with anything you'd like to be able to touch, "
107 + "but it should not collide with the grasped object layer.")]
108 [SerializeField]
111
112 [Header("Debug Settings")]
113 [SerializeField]
114 [Tooltip("Rendering runtime gizmos requires having a Runtime Gizmo Manager somewhere "
115 + "in the scene.")]
116 private bool _drawControllerRuntimeGizmos = false;
117
118 #endregion
119
120 #region Events
121
122 public Action OnGraphicalUpdate = () => { };
123 public Action OnPrePhysicalUpdate = () => { };
124 public Action OnPostPhysicalUpdate = () => { };
125
126 #endregion
127
128 #region Scale Support
129
130 private float _scale = 1F;
131
136 public float WorldHoverActivationRadius { get { return hoverActivationRadius * _scale; } }
137
143 public float WorldTouchActivationRadius { get { return touchActivationRadius * _scale; } }
144
149 public float SimulationScale { get { return _scale; } }
150
151 #endregion
152
153 #region Object Tracking
154
155 private HashSet<IInteractionBehaviour> _interactionObjects = new HashSet<IInteractionBehaviour>();
161 get { return _interactionObjects; }
162 }
163
164 private Dictionary<Rigidbody, IInteractionBehaviour> _interactionObjectBodies;
169 public Dictionary<Rigidbody, IInteractionBehaviour> interactionObjectBodies {
170 get {
171 if (_interactionObjectBodies == null) {
172 _interactionObjectBodies = new Dictionary<Rigidbody, IInteractionBehaviour>();
173 }
174 return _interactionObjectBodies;
175 }
176 }
177
178 private Dictionary<Rigidbody, ContactBone> _contactBoneBodies;
183 public Dictionary<Rigidbody, ContactBone> contactBoneBodies {
184 get {
185 if (_contactBoneBodies == null) {
186 _contactBoneBodies = new Dictionary<Rigidbody, ContactBone>();
187 }
188 return _contactBoneBodies;
189 }
190 }
191
192 #endregion
193
194 #region Singleton Pattern (Optional)
195
196 private static InteractionManager s_instance;
214 get {
215 if (s_instance == null) { s_instance = FindObjectOfType<InteractionManager>(); }
216 return s_instance;
217 }
218 set { s_instance = value; }
219 }
220
221 #endregion
222
223 #region Unity Events
224
225 void OnValidate() {
226 if (!Application.isPlaying && _autoGenerateLayers) {
228 }
229
230 refreshInteractionControllers();
231 }
232
233 void Awake() {
234 refreshInteractionControllers();
235
236 if (!Application.isPlaying) return;
237
238 if (s_instance == null) s_instance = this;
239
242 setupAutomaticCollisionLayers();
243 }
244
245 _prevPosition = this.transform.position;
246 _prevRotation = this.transform.rotation;
247
248 #if UNITY_EDITOR
249 if (_drawControllerRuntimeGizmos == true) {
250 if (FindObjectOfType<RuntimeGizmoManager>() == null) {
251 Debug.LogWarning("'_drawControllerRuntimeGizmos' is enabled, but there is no "
252 + "RuntimeGizmoManager in your scene. Please add one if you'd "
253 + "like to render gizmos in the editor and in your headset.");
254 }
255 }
256 #endif
257 }
258
259 void OnDisable() {
260 #if UNITY_EDITOR
261 if (!Application.isPlaying) return;
262 #endif
263
264 foreach (var intController in _interactionControllers) {
265 // Disables the colliders in the interaction controller;
266 // soft contact won't be applied if the controller is not updating.
267 intController.EnableSoftContact();
268
269 if (intController.isGraspingObject) {
270 intController.ReleaseGrasp();
271 }
272 }
273 }
274
275 void Update() {
276 #if UNITY_EDITOR
277 refreshInteractionControllers();
278 #endif
279 }
280
281 void FixedUpdate() {
283
284 // Physics should only be synced once at the beginning of the physics simulation.
285 // (Will be re-set to its original value at the end of the update.)
286 #if UNITY_2017_2_OR_NEWER
287 var preUpdateAutoSyncTransforms = Physics.autoSyncTransforms;
288 Physics.autoSyncTransforms = false;
289 #endif
290 try {
291
292 refreshInteractionControllers();
293
294 #if UNITY_EDITOR
295 if (!Application.isPlaying) return;
296 #endif
297
298 using (new ProfilerSample("Interaction Manager FixedUpdate", this.gameObject)) {
299 // Ensure scale information is up-to-date.
300 _scale = this.transform.lossyScale.x;
301
302 // Update each interaction controller (Leap hands or supported VR controllers).
303 fixedUpdateInteractionControllers();
304
305 // Perform each interaction object's FixedUpdateObject.
306 using (new ProfilerSample("FixedUpdateObject per-InteractionBehaviour")) {
307 foreach (var interactionObj in _interactionObjects) {
308 interactionObj.FixedUpdateObject();
309 }
310 }
311
312 // Apply soft contacts from all controllers in a unified solve.
313 // (This will clear softContacts and originalVelocities as well.)
314 using (new ProfilerSample("Apply Soft Contacts")) {
315 if (_drawControllerRuntimeGizmos) {
316 _softContactsToDraw = new List<PhysicsUtility.SoftContact>(_softContacts);
317 }
318 if (_softContacts.Count > 0) {
319 PhysicsUtility.applySoftContacts(_softContacts, _softContactOriginalVelocities);
320 }
321 }
322 }
323
325
326 updateMovingFrameOfReferenceSupport();
327
328 if (autoGenerateLayers) {
329 autoUpdateContactBoneLayerCollision();
330 }
331
332 }
333 finally {
334 #if UNITY_2017_2_OR_NEWER
335 // Restore the autoSyncTransforms setting to whatever the user had it as before
336 // the Manager FixedUpdate.
337 Physics.autoSyncTransforms = preUpdateAutoSyncTransforms;
338 #endif
339 }
340 }
341
342 void LateUpdate() {
344 }
345
346#endregion
347
348 #region Controller Interaction State & Callbacks Update
349
350 private HashSet<InteractionController> _activeControllersBuffer = new HashSet<InteractionController>();
351 private HashSet<InteractionController> _hoverControllersBuffer = new HashSet<InteractionController>();
352 private HashSet<InteractionController> _contactControllersBuffer = new HashSet<InteractionController>();
353 private HashSet<InteractionController> _graspingControllersBuffer = new HashSet<InteractionController>();
354
355 private void fixedUpdateInteractionControllers() {
356
357 _hoverControllersBuffer.Clear();
358 _contactControllersBuffer.Clear();
359 _graspingControllersBuffer.Clear();
360 _activeControllersBuffer.Clear();
361 foreach (var controller in interactionControllers) {
362 if (!controller.isActiveAndEnabled) continue;
363
364 _activeControllersBuffer.Add(controller);
365
366 if (controller.hoverEnabled) _hoverControllersBuffer.Add(controller);
367 if (controller.contactEnabled) _contactControllersBuffer.Add(controller);
368 if (controller.graspingEnabled) _graspingControllersBuffer.Add(controller);
369 }
370
371 using (new ProfilerSample("Fixed Update Controllers (General Update)")) {
372 // Perform general controller update, for controller collider and point
373 // representations.
374 foreach (var controller in _activeControllersBuffer) {
375 if (!controller.isActiveAndEnabled) continue;
376 (controller as IInternalInteractionController).FixedUpdateController();
377 }
378 }
379
380 using (new ProfilerSample("Fixed Update Controllers (Interaction State and Callbacks)")) {
381
382 /*
383 * Interactions are checked here in a very specific manner so that interaction
384 * callbacks always occur in a strict order and interaction object state is
385 * always updated directly before the relevant callbacks occur.
386 *
387 * Interaction callbacks will only occur outside this order if a script
388 * manually forces interaction state-changes; for example, calling
389 * interactionController.ReleaseGrasp() will immediately call
390 * interactionObject.OnPerControllerGraspEnd() on the formerly grasped object.
391 *
392 * Callback order:
393 * - Suspension (when a grasped object's grasping controller loses tracking)
394 * - Just-Ended Interactions (Grasps, then Contacts, then Hovers)
395 * - Just-Begun Interactions (Hovers, then Contacts, then Grasps)
396 * - Sustained Interactions (Hovers, then Contacts, then Grasps)
397 */
398
399 // Suspension //
400
401 // Check controllers beginning object suspension.
402 foreach (var controller in _graspingControllersBuffer) {
403 IInteractionBehaviour suspendedObj;
404 if ((controller as IInternalInteractionController).CheckSuspensionBegin(out suspendedObj)) {
405 suspendedObj.BeginSuspension(controller);
406 }
407 }
408
409 // Check controllers ending object suspension.
410 foreach (var controller in _graspingControllersBuffer) {
411 IInteractionBehaviour resumedObj;
412 if ((controller as IInternalInteractionController).CheckSuspensionEnd(out resumedObj)) {
413 resumedObj.EndSuspension(controller);
414 }
415 }
416
417 // Ending Interactions //
418
419 checkEndingGrasps(_graspingControllersBuffer);
420 checkEndingContacts(_contactControllersBuffer);
421 checkEndingPrimaryHovers(_hoverControllersBuffer);
422 checkEndingHovers(_hoverControllersBuffer);
423
424 // Beginning Interactions //
425
426 checkBeginningHovers(_hoverControllersBuffer);
427 checkBeginningPrimaryHovers(_hoverControllersBuffer);
428 checkBeginningContacts(_contactControllersBuffer);
429 checkBeginningGrasps(_graspingControllersBuffer);
430
431 // Sustained Interactions //
432
433 checkSustainingHovers(_hoverControllersBuffer);
434 checkSustainingPrimaryHovers(_hoverControllersBuffer);
435 checkSustainingContacts(_contactControllersBuffer);
436 checkSustainingGrasps(_graspingControllersBuffer);
437
438 }
439 }
440
441 #region State-Check Remapping Functions
442
443 private void checkEndingGrasps(ReadonlyHashSet<InteractionController> interactionControllers) {
444 remapInteractionObjectStateChecks(
445 controllers: interactionControllers,
446 stateCheckFunc: (InteractionController maybeReleasingController, out IInteractionBehaviour maybeReleasedObject) => {
447 return (maybeReleasingController as IInternalInteractionController).CheckGraspEnd(out maybeReleasedObject);
448 },
449 actionPerInteractionObject: (releasedObject, releasingIntControllers) => {
450 releasedObject.EndGrasp(releasingIntControllers);
451 });
452 }
453
454 private void checkEndingContacts(ReadonlyHashSet<InteractionController> interactionControllers) {
455 remapMultiInteractionObjectStateChecks(
456 controllers: interactionControllers,
457 multiObjectStateCheckFunc: (InteractionController maybeEndedContactingController, out HashSet<IInteractionBehaviour> endContactedObjects) => {
458 return (maybeEndedContactingController as IInternalInteractionController).CheckContactEnd(out endContactedObjects);
459 },
460 actionPerInteractionObject: (endContactedObject, endContactedIntControllers) => {
461 endContactedObject.EndContact(endContactedIntControllers);
462 });
463 }
464
465 private void checkEndingPrimaryHovers(ReadonlyHashSet<InteractionController> interactionControllers) {
466 remapInteractionObjectStateChecks(
467 controllers: interactionControllers,
468 stateCheckFunc: (InteractionController maybeEndedPrimaryHoveringController, out IInteractionBehaviour endPrimaryHoveredObject) => {
469 return (maybeEndedPrimaryHoveringController as IInternalInteractionController).CheckPrimaryHoverEnd(out endPrimaryHoveredObject);
470 },
471 actionPerInteractionObject: (endPrimaryHoveredObject, noLongerPrimaryHoveringControllers) => {
472 endPrimaryHoveredObject.EndPrimaryHover(noLongerPrimaryHoveringControllers);
473 });
474 }
475
476 private void checkEndingHovers(ReadonlyHashSet<InteractionController> interactionControllers) {
477 remapMultiInteractionObjectStateChecks(
478 controllers: interactionControllers,
479 multiObjectStateCheckFunc: (InteractionController maybeEndedHoveringController, out HashSet<IInteractionBehaviour> endHoveredObjects) => {
480 return (maybeEndedHoveringController as IInternalInteractionController).CheckHoverEnd(out endHoveredObjects);
481 },
482 actionPerInteractionObject: (endHoveredObject, endHoveringIntControllers) => {
483 endHoveredObject.EndHover(endHoveringIntControllers);
484 });
485 }
486
487 private void checkBeginningHovers(ReadonlyHashSet<InteractionController> interactionControllers) {
488 remapMultiInteractionObjectStateChecks(
489 controllers: interactionControllers,
490 multiObjectStateCheckFunc: (InteractionController maybeBeganHoveringController, out HashSet<IInteractionBehaviour> beganHoveredObjects) => {
491 return (maybeBeganHoveringController as IInternalInteractionController).CheckHoverBegin(out beganHoveredObjects);
492 },
493 actionPerInteractionObject: (beganHoveredObject, beganHoveringIntControllers) => {
494 beganHoveredObject.BeginHover(beganHoveringIntControllers);
495 });
496 }
497
498 private void checkBeginningPrimaryHovers(ReadonlyHashSet<InteractionController> interactionControllers) {
499 remapInteractionObjectStateChecks(
500 controllers: interactionControllers,
501 stateCheckFunc: (InteractionController maybeBeganPrimaryHoveringController, out IInteractionBehaviour primaryHoveredObject) => {
502 return (maybeBeganPrimaryHoveringController as IInternalInteractionController).CheckPrimaryHoverBegin(out primaryHoveredObject);
503 },
504 actionPerInteractionObject: (newlyPrimaryHoveredObject, beganPrimaryHoveringControllers) => {
505 newlyPrimaryHoveredObject.BeginPrimaryHover(beganPrimaryHoveringControllers);
506 });
507 }
508
509 private void checkBeginningContacts(ReadonlyHashSet<InteractionController> interactionControllers) {
510 remapMultiInteractionObjectStateChecks(
511 controllers: interactionControllers,
512 multiObjectStateCheckFunc: (InteractionController maybeBeganContactingController, out HashSet<IInteractionBehaviour> beganContactedObjects) => {
513 return (maybeBeganContactingController as IInternalInteractionController).CheckContactBegin(out beganContactedObjects);
514 },
515 actionPerInteractionObject: (beganContactedObject, beganContactingIntControllers) => {
516 beganContactedObject.BeginContact(beganContactingIntControllers);
517 });
518 }
519
520 private void checkBeginningGrasps(ReadonlyHashSet<InteractionController> interactionControllers) {
521 remapInteractionObjectStateChecks(
522 controllers: interactionControllers,
523 stateCheckFunc: (InteractionController maybeBeganGraspingController, out IInteractionBehaviour graspedObject) => {
524 return (maybeBeganGraspingController as IInternalInteractionController).CheckGraspBegin(out graspedObject);
525 },
526 actionPerInteractionObject: (newlyGraspedObject, beganGraspingIntControllers) => {
527 newlyGraspedObject.BeginGrasp(beganGraspingIntControllers);
528 });
529 }
530
531 private void checkSustainingHovers(ReadonlyHashSet<InteractionController> interactionControllers) {
532 remapMultiInteractionObjectStateChecks(
533 controllers: interactionControllers,
534 multiObjectStateCheckFunc: (InteractionController maybeSustainedHoveringController, out HashSet<IInteractionBehaviour> hoveredObjects) => {
535 return (maybeSustainedHoveringController as IInternalInteractionController).CheckHoverStay(out hoveredObjects);
536 },
537 actionPerInteractionObject: (hoveredObject, hoveringIntControllers) => {
538 hoveredObject.StayHovered(hoveringIntControllers);
539 });
540 }
541
542 private void checkSustainingPrimaryHovers(ReadonlyHashSet<InteractionController> interactionControllers) {
543 remapInteractionObjectStateChecks(
544 controllers: interactionControllers,
545 stateCheckFunc: (InteractionController maybeSustainedPrimaryHoveringController, out IInteractionBehaviour primaryHoveredObject) => {
546 return (maybeSustainedPrimaryHoveringController as IInternalInteractionController).CheckPrimaryHoverStay(out primaryHoveredObject);
547 },
548 actionPerInteractionObject: (primaryHoveredObject, primaryHoveringControllers) => {
549 primaryHoveredObject.StayPrimaryHovered(primaryHoveringControllers);
550 });
551 }
552
553 private void checkSustainingContacts(ReadonlyHashSet<InteractionController> interactionControllers) {
554 remapMultiInteractionObjectStateChecks(
555 controllers: interactionControllers,
556 multiObjectStateCheckFunc: (InteractionController maybeSustainedContactingController, out HashSet<IInteractionBehaviour> contactedObjects) => {
557 return (maybeSustainedContactingController as IInternalInteractionController).CheckContactStay(out contactedObjects);
558 },
559 actionPerInteractionObject: (contactedObject, contactingIntControllers) => {
560 contactedObject.StayContacted(contactingIntControllers);
561 });
562 }
563
564 private void checkSustainingGrasps(ReadonlyHashSet<InteractionController> interactionControllers) {
565 remapInteractionObjectStateChecks(
566 controllers: interactionControllers,
567 stateCheckFunc: (InteractionController maybeSustainedGraspingController, out IInteractionBehaviour graspedObject) => {
568 return (maybeSustainedGraspingController as IInternalInteractionController).CheckGraspHold(out graspedObject);
569 },
570 actionPerInteractionObject: (contactedObject, contactingIntControllers) => {
571 contactedObject.StayGrasped(contactingIntControllers);
572 });
573 }
574
575 private delegate bool StateChangeCheckFunc(InteractionController controller, out IInteractionBehaviour obj);
576 private delegate bool MultiStateChangeCheckFunc(InteractionController controller, out HashSet<IInteractionBehaviour> objs);
577
578 [ThreadStatic]
579 private static Dictionary<IInteractionBehaviour, List<InteractionController>> s_objControllersMap = new Dictionary<IInteractionBehaviour, List<InteractionController>>();
580
584 private void remapInteractionObjectStateChecks(ReadonlyHashSet<InteractionController> controllers,
585 StateChangeCheckFunc stateCheckFunc,
586 Action<IInteractionBehaviour, List<InteractionController>> actionPerInteractionObject) {
587
588 // Ensure the object->controllers buffer is non-null (ThreadStatic quirk) and clean.
589 if (s_objControllersMap == null) s_objControllersMap = new Dictionary<IInteractionBehaviour, List<InteractionController>>();
590 s_objControllersMap.Clear();
591
592 // In a nutshell, this remaps methods per-controller that output an interaction object if the controller changed that object's state
593 // to methods per-object with all of the controllers for which the check produced a state-change.
594 foreach (var controller in controllers) {
595 IInteractionBehaviour objectWhoseStateChanged;
596 if (stateCheckFunc(controller, out objectWhoseStateChanged)) {
597 if (!s_objControllersMap.ContainsKey(objectWhoseStateChanged)) {
598 s_objControllersMap[objectWhoseStateChanged] = Pool<List<InteractionController>>.Spawn();
599 }
600 s_objControllersMap[objectWhoseStateChanged].Add(controller);
601 }
602 }
603 // Finally, iterate through each (object, controllers) pair and call the action for each pair
604 foreach (var objControllesPair in s_objControllersMap) {
605 actionPerInteractionObject(objControllesPair.Key, objControllesPair.Value);
606
607 // Clear each controllers list and return it to the list pool.
608 objControllesPair.Value.Clear();
609 Pool<List<InteractionController>>.Recycle(objControllesPair.Value);
610 }
611 }
612
616 private void remapMultiInteractionObjectStateChecks(ReadonlyHashSet<InteractionController> controllers,
617 MultiStateChangeCheckFunc multiObjectStateCheckFunc,
618 Action<IInteractionBehaviour, List<InteractionController>> actionPerInteractionObject) {
619 // Ensure object<->controllers buffer is non-null (ThreadStatic quirk) and clean.
620 if (s_objControllersMap == null) s_objControllersMap = new Dictionary<IInteractionBehaviour, List<InteractionController>>();
621 s_objControllersMap.Clear();
622
623 // In a nutshell, this remaps methods per-controller that output multiple interaction objects if the controller changed those objects' states
624 // to methods per-object with all of the controllers for which the check produced a state-change.
625 foreach (var controller in controllers) {
626 HashSet<IInteractionBehaviour> stateChangedObjects;
627 if (multiObjectStateCheckFunc(controller, out stateChangedObjects)) {
628 foreach (var stateChangedObject in stateChangedObjects) {
629 if (!s_objControllersMap.ContainsKey(stateChangedObject)) {
630 s_objControllersMap[stateChangedObject] = Pool<List<InteractionController>>.Spawn();
631 }
632 s_objControllersMap[stateChangedObject].Add(controller);
633 }
634 }
635 }
636 // Finally, iterate through each (object, controllers) pair and call the action for each pair
637 foreach (var objControllersPair in s_objControllersMap) {
638 actionPerInteractionObject(objControllersPair.Key, objControllersPair.Value);
639
640 // Clear each controllers list and return it to the list pool.
641 objControllersPair.Value.Clear();
642 Pool<List<InteractionController>>.Recycle(objControllersPair.Value);
643 }
644 }
645
646 #endregion
647
648 #region State Notifications
649
650 // TODO: Delete this whole sction
651
652 //private HashSet<InteractionController> controllerSetBuffer = new HashSet<InteractionController>();
653
654 //void IInternalInteractionManager.NotifyControllerDisabled(InteractionController controller) {
655 // controllerSetBuffer.Clear();
656 // controllerSetBuffer.Add(controller);
657
658 // checkEndingGrasps(controllerSetBuffer);
659 // checkEndingContacts(controllerSetBuffer);
660 // checkEndingPrimaryHovers(controllerSetBuffer);
661 // checkEndingHovers(controllerSetBuffer);
662 //}
663
664 //void IInternalInteractionManager.NotifyHoverDisabled(InteractionController controller) {
665 // controllerSetBuffer.Clear();
666 // controllerSetBuffer.Add(controller);
667
668 // checkEndingPrimaryHovers(controllerSetBuffer);
669 // checkEndingHovers(controllerSetBuffer);
670 //}
671
672 //void IInternalInteractionManager.NotifyContactDisabled(InteractionController controller) {
673 // controllerSetBuffer.Clear();
674 // controllerSetBuffer.Add(controller);
675
676 // checkEndingContacts(controllerSetBuffer);
677 //}
678
679 //void IInternalInteractionManager.NotifyObjectHoverIgnored(IInteractionBehaviour intObj) {
680 // controllerSetBuffer.Clear();
681
682 // foreach (var controller in interactionControllers) {
683 // if (controller.hoveredObjects.Contains(intObj)) {
684 // (controller as IInternalInteractionController).ClearHoverTrackingForObject(intObj);
685
686 // controllerSetBuffer.Add(controller);
687 // }
688 // }
689
690 // checkEndingHovers(controllerSetBuffer);
691 //}
692
693 //void IInternalInteractionManager.NotifyObjectPrimaryHoverIgnored(IInteractionBehaviour intObj) {
694 // controllerSetBuffer.Clear();
695
696 // foreach (var controller in interactionControllers) {
697 // if (controller.primaryHoveredObject == intObj) {
698 // (controller as IInternalInteractionController).ClearPrimaryHoverTrackingForObject(intObj);
699
700 // controllerSetBuffer.Add(controller);
701 // }
702 // }
703
704 // checkEndingPrimaryHovers(controllerSetBuffer);
705 //}
706
707 //void IInternalInteractionManager.NotifyObjectContactIgnored(IInteractionBehaviour intObj) {
708 // controllerSetBuffer.Clear();
709
710 // foreach (var controller in interactionControllers) {
711 // if (controller.contactingObjects.Contains(intObj)) {
712 // (controller as IInternalInteractionController).ClearContactTrackingForObject(intObj);
713
714 // controllerSetBuffer.Add(controller);
715 // }
716 // }
717
718 // checkEndingContacts(controllerSetBuffer);
719 //}
720
721 #endregion
722
723 #endregion
724
725 #region Object Registration
726
728 _interactionObjects.Add(interactionObj);
729 interactionObjectBodies[interactionObj.rigidbody] = interactionObj;
730 }
731
738 bool wasRemovalSuccessful = _interactionObjects.Remove(interactionObj);
739 if (wasRemovalSuccessful) {
740 foreach (var intController in _interactionControllers) {
741 intController.ReleaseObject(interactionObj);
742
743 intController.NotifyObjectUnregistered(interactionObj);
744 }
745 interactionObjectBodies.Remove(interactionObj.rigidbody);
746 }
747 return wasRemovalSuccessful;
748 }
749
750 public bool IsBehaviourRegistered(IInteractionBehaviour interactionObj) {
751 return _interactionObjects.Contains(interactionObj);
752 }
753
754 #endregion
755
756 #region Moving Frame of Reference Support
757
759 get {
760 return (this.transform.position - _prevPosition).magnitude > 0.0001F
761 || Quaternion.Angle(transform.rotation * Quaternion.Inverse(_prevRotation),
762 Quaternion.identity) > 0.01F;
763 }
764 }
765
766 // Support for a moving frame of reference.
767 private Vector3 _prevPosition = Vector3.zero;
768 private Quaternion _prevRotation = Quaternion.identity;
769
770 private void updateMovingFrameOfReferenceSupport() {
771 _prevPosition = this.transform.position;
772 _prevRotation = this.transform.rotation;
773 }
774
781 public void TransformAheadByFixedUpdate(Vector3 position, Quaternion rotation, out Vector3 newPosition, out Quaternion newRotation) {
782 Vector3 worldDisplacement = this.transform.position - _prevPosition;
783 Quaternion worldRotation = this.transform.rotation * Quaternion.Inverse(_prevRotation);
784 newPosition = ((worldRotation * (position - this.transform.position + worldDisplacement))) + this.transform.position;
785 newRotation = worldRotation * rotation;
786 }
787
794 public void TransformAheadByFixedUpdate(Vector3 position, out Vector3 newPosition) {
795 Vector3 worldDisplacement = this.transform.position - _prevPosition;
796 Quaternion worldRotation = this.transform.rotation * Quaternion.Inverse(_prevRotation);
797 newPosition = ((worldRotation * (position - this.transform.position + worldDisplacement))) + this.transform.position;
798 }
799
800 #endregion
801
802 #region Soft Contact Support
803
807 [NonSerialized]
808 public List<PhysicsUtility.SoftContact> _softContacts
809 = new List<PhysicsUtility.SoftContact>(80);
810
814 [NonSerialized]
815 public Dictionary<Rigidbody, PhysicsUtility.Velocities> _softContactOriginalVelocities
816 = new Dictionary<Rigidbody, PhysicsUtility.Velocities>(5);
817
821 private List<PhysicsUtility.SoftContact> _softContactsToDraw
822 = new List<PhysicsUtility.SoftContact>();
823
824 #endregion
825
826 #region Interaction Controllers
827
828 private void refreshInteractionControllers() {
829 _interactionControllers.Clear();
830
831 var tempControllers = Pool<List<InteractionController>>.Spawn();
832 try {
833 this.transform.GetComponentsInChildren<InteractionController>(false, tempControllers);
834 foreach (var controller in tempControllers) {
835 _interactionControllers.Add(controller);
836 }
837 }
838 finally {
839 tempControllers.Clear();
840 Pool<List<InteractionController>>.Recycle(tempControllers);
841 }
842 }
843
844 #endregion
845
846 #region Layers
847
848 #region Automatic Layers
849
850 protected void generateAutomaticLayers() {
854 for (int i = 8; i < 32; i++) {
855 string layerName = LayerMask.LayerToName(i);
856 if (string.IsNullOrEmpty(layerName)) {
857 if (_interactionLayer == -1) {
859 }
860 else if (_interactionNoContactLayer == -1) {
862 }
863 else if (_contactBoneLayer == -1) {
865 break;
866 }
867 }
868 }
869
871 if (Application.isPlaying) {
872 enabled = false;
873 }
874 Debug.LogError("InteractionManager Could not find enough free layers for "
875 + "auto-setup; manual setup is required.", this.gameObject);
876 _autoGenerateLayers = false;
877 return;
878 }
879 }
880
881 private void setupAutomaticCollisionLayers() {
882 for (int i = 0; i < 32; i++) {
883 // Copy ignore settings from template layer
884 bool shouldIgnore = Physics.GetIgnoreLayerCollision(_templateLayer, i);
885 Physics.IgnoreLayerCollision(_interactionLayer, i, shouldIgnore);
886 Physics.IgnoreLayerCollision(_interactionNoContactLayer, i, shouldIgnore);
887
888 // Contact bones, generally, shouldn't collide with anything except interaction
889 // layers.
890 Physics.IgnoreLayerCollision(_contactBoneLayer, i, true);
891 }
892
893 // Enable interactions between the contact bones and the interaction layer.
894 Physics.IgnoreLayerCollision(_contactBoneLayer, _interactionLayer, false);
895
896 // Disable interactions between the contact bones and the no-contact layer.
897 Physics.IgnoreLayerCollision(_contactBoneLayer, _interactionNoContactLayer, true);
898 }
899
900 #endregion
901
902 #region Interaction Object Layer Tracking
903
904 private Dictionary<SingleLayer, HashSet<IInteractionBehaviour>> _intObjInteractionLayers = new Dictionary<SingleLayer, HashSet<IInteractionBehaviour>>();
905 private Dictionary<SingleLayer, HashSet<IInteractionBehaviour>> _intObjNoContactLayers = new Dictionary<SingleLayer, HashSet<IInteractionBehaviour>>();
906
907 private int _interactionLayerMask = 0;
912 return _interactionLayerMask;
913 }
914
915 private void refreshInteractionLayerMask() {
916 _interactionLayerMask = 0;
917
918 // Accumulate single-layer layer masks into the combined interaction layer mask.
919 foreach (var layerObjSetPair in _intObjInteractionLayers) {
920 // Skip any layers that may no longer have interaction objects.
921 if (layerObjSetPair.Value.Count == 0) continue;
922
923 _interactionLayerMask = layerObjSetPair.Key.layerMask | _interactionLayerMask;
924 }
925 foreach (var layerObjSetPair in _intObjNoContactLayers) {
926 // Skip any layers that may no longer have interaction objects.
927 if (layerObjSetPair.Value.Count == 0) continue;
928
929 _interactionLayerMask = layerObjSetPair.Key.layerMask | _interactionLayerMask;
930 }
931 }
932
933 private bool[] _contactBoneIgnoreCollisionLayers = new bool[32];
940 private void autoUpdateContactBoneLayerCollision() {
941 // Make sure we ignore all layers by default!
942 for (int i = 0; i < 32; i++) {
943 _contactBoneIgnoreCollisionLayers[i] = true;
944 }
945
946 // Ignore everything except those layers that we know are at least one
947 // interaction object's interaction layer.
948 foreach (var layerObjSetPair in _intObjInteractionLayers) {
949 bool ignoreLayerCollision;
950
951 if (layerObjSetPair.Value.Count == 0) {
952 ignoreLayerCollision = true;
953 }
954 else {
955 ignoreLayerCollision = false;
956 }
957
958 if (layerObjSetPair.Key.layerIndex < _contactBoneIgnoreCollisionLayers.Length) {
959 _contactBoneIgnoreCollisionLayers[layerObjSetPair.Key.layerIndex] = ignoreLayerCollision;
960 }
961 }
962
963 for (int i = 0; i < 32; i++) {
964 Physics.IgnoreLayerCollision(contactBoneLayer.layerIndex, i,
965 _contactBoneIgnoreCollisionLayers[i]);
966 }
967 }
968
969 void IInternalInteractionManager.RefreshLayersNow() {
970 refreshInteractionLayerMask();
971 }
972
973 void IInternalInteractionManager.NotifyIntObjAddedInteractionLayer(IInteractionBehaviour intObj, int layer, bool refreshImmediately) {
974 if (!_intObjInteractionLayers.ContainsKey(layer)) {
975 _intObjInteractionLayers[layer] = new HashSet<IInteractionBehaviour>();
976 }
977
978 _intObjInteractionLayers[layer].Add(intObj);
979
980 if (refreshImmediately) {
981 refreshInteractionLayerMask();
982 }
983 }
984
985 void IInternalInteractionManager.NotifyIntObjRemovedInteractionLayer(IInteractionBehaviour intObj, int layer, bool refreshImmediately) {
986 _intObjInteractionLayers[layer].Remove(intObj);
987
988 if (refreshImmediately) {
989 refreshInteractionLayerMask();
990 }
991 }
992
993 void IInternalInteractionManager.NotifyIntObjAddedNoContactLayer(IInteractionBehaviour intObj, int layer, bool refreshImmediately) {
994 if (!_intObjNoContactLayers.ContainsKey(layer)) {
995 _intObjNoContactLayers[layer] = new HashSet<IInteractionBehaviour>();
996 }
997
998 _intObjNoContactLayers[layer].Add(intObj);
999
1000 if (refreshImmediately) {
1001 refreshInteractionLayerMask();
1002 }
1003 }
1004
1005 void IInternalInteractionManager.NotifyIntObjRemovedNoContactLayer(IInteractionBehaviour intObj, int layer, bool refreshImmediately) {
1006 _intObjNoContactLayers[layer].Remove(intObj);
1007
1008 if (refreshImmediately) {
1009 refreshInteractionLayerMask();
1010 }
1011 }
1012
1013 #endregion
1014
1015 #endregion
1016
1017 #region Runtime Gizmos
1018
1020 if (_drawControllerRuntimeGizmos) {
1021 foreach (var controller in _interactionControllers) {
1022 if (controller != null) {
1023 controller.OnDrawRuntimeGizmos(drawer);
1024 }
1025 }
1026
1027 foreach (PhysicsUtility.SoftContact contact in _softContactsToDraw) {
1028 drawer.DrawSphere(contact.position, 0.01f);
1029 drawer.DrawLine(contact.position, contact.position + (contact.normal * 0.02f));
1030 }
1031 }
1032 }
1033
1034 #endregion
1035
1036 }
1037
1038}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
Dictionary< Rigidbody, ContactBone > contactBoneBodies
Maps a Rigidbody to its attached ContactBone, if the Rigidbody is part of an interaction controller.
Dictionary< Rigidbody, PhysicsUtility.Velocities > _softContactOriginalVelocities
Stores data for implementing Soft Contact for interaction controllers.
float SimulationScale
A scale that can be used to appropriately transform distances that otherwise expect one Unity unit to...
float WorldHoverActivationRadius
Interaction objects further than this distance from a given controller's hover point will not be cons...
List< PhysicsUtility.SoftContact > _softContacts
Stores data for implementing Soft Contact for interaction controllers.
bool UnregisterInteractionBehaviour(IInteractionBehaviour interactionObj)
Returns true if the Interaction Behaviour was registered with this manager; otherwise returns false....
ReadonlyHashSet< InteractionController > interactionControllers
Gets the list of interaction controllers managed by this InteractionManager.
Dictionary< Rigidbody, IInteractionBehaviour > interactionObjectBodies
Maps a Rigidbody to its attached interaction object, if the Rigidbody is part of and interaction obje...
void TransformAheadByFixedUpdate(Vector3 position, out Vector3 newPosition)
Transforms a position ahead by one FixedUpdate based on the prior motion (position AND rotation) of t...
bool autoGenerateLayers
Gets whether auto-generate layers was enabled for this Interaction Manager.
bool IsBehaviourRegistered(IInteractionBehaviour interactionObj)
void TransformAheadByFixedUpdate(Vector3 position, Quaternion rotation, out Vector3 newPosition, out Quaternion newRotation)
Transforms a position and rotation ahead by one FixedUpdate based on the prior motion of the Interact...
int GetInteractionLayerMask()
Returns a layer mask containing all layers that might contain interaction objects.
void RegisterInteractionBehaviour(IInteractionBehaviour interactionObj)
ReadonlyHashSet< IInteractionBehaviour > interactionObjects
Gets a set of all interaction objects currently registered with this Interaction Manager.
static InteractionManager instance
Often, only one InteractionManager is necessary per Unity scene. This property will contain that Inte...
void OnDrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
float WorldTouchActivationRadius
Interaction objects further than this distance from a given controller's hover point will not be cons...
void DrawLine(Vector3 a, Vector3 b)
Draws a gizmo line that connects the two positions.
void DrawSphere(Vector3 center, float radius)
Draws a filled gizmo sphere at the given position with the given radius.
IInteractionBehaviour is the interface that defines all Interaction objects, specifying the minimum s...
Have your MonoBehaviour implement this interface to be able to draw runtime gizmos....
A simple wrapper around HashSet to provide readonly access. Useful when you want to return a HashSet ...
An object you can use to represent a single Unity layer as a dropdown in the inspector....
Definition: SingleLayer.cs:20