13using System.Collections;
14using System.Collections.Generic;
37 [DisallowMultipleComponent]
43 [Tooltip(
"The manager responsible for this interaction controller. Interaction "
44 +
"controllers should be children of their interaction manager.")]
47 [Header(
"Interaction Types")]
49 [Tooltip(
"If disabled, this interaction controller will not be used to generate "
50 +
"hover information or primary hover information. Warning: Primary hover "
51 +
"data is required for Interaction Engine user interface components like "
52 +
"InteractionButton and InteractionSlider to function, so this controller "
53 +
"won't able to interact with UI components.")]
55 [OnEditorChange(
"hoverEnabled")]
56 private bool _hoverEnabled =
true;
58 get {
return _hoverEnabled; }
60 _hoverEnabled = value;
68 [Tooltip(
"If disabled, this interaction controller will not collide with interaction "
69 +
"objects and objects will not receive contact callbacks.")]
71 [OnEditorChange(
"contactEnabled")]
72 private bool _contactEnabled =
true;
74 get {
return _contactEnabled; }
76 _contactEnabled = value;
78 if (!_contactEnabled) {
79 disableContactBoneCollision();
84 resetContactBonePose();
91 [Tooltip(
"If disabled, this interaction controller will not be able to grasp "
92 +
"interaction objects.")]
94 [OnEditorChange(
"graspingEnabled")]
95 private bool _graspingEnabled =
true;
97 get {
return _graspingEnabled; }
99 _graspingEnabled = value;
101 if (!_graspingEnabled) {
171 public float scale {
get {
return this.transform.lossyScale.x; } }
223 if (
manager ==
null)
manager = GetComponentInParent<InteractionManager>();
227 if (_contactInitialized) {
230 resetContactBonePose();
239 if (_contactInitialized) {
252 private List<InteractionController> _controllerListBuffer =
new List<InteractionController>();
302 private Func<Collider, IInteractionBehaviour> hoverActivityFilter;
304 Rigidbody rigidbody = collider.attachedRigidbody;
307 bool objectValidForHover = rigidbody !=
null
309 && !intObj.ShouldIgnoreHover(
this)
312 if (objectValidForHover)
return intObj;
317 private Func<int> hoverLayerMaskAccessor;
323 if (_hoverActivityManager ==
null) {
324 if (hoverActivityFilter ==
null) hoverActivityFilter = hoverFilterFunc;
328 hoverActivityFilter);
332 return _hoverActivityManager;
339 private bool _primaryHoverLocked =
false;
345 get {
return _primaryHoverLocked; }
346 set { _primaryHoverLocked = value; }
356 _primaryHoveredObject = intObj;
357 _primaryHoverLocked =
true;
367 private HashSet<IInteractionBehaviour> _hoveredObjects =
new HashSet<IInteractionBehaviour>();
394 private float _primaryHoverDistance =
float.PositiveInfinity;
420 private int _primaryHoverPointIdx = -1;
421 private List<IInteractionBehaviour> _perPointPrimaryHovered =
new List<IInteractionBehaviour>();
422 private List<float> _perPointPrimaryHoverDistance =
new List<float>();
424 private void fixedUpdateHovering() {
425 using (
new ProfilerSample(
"Fixed Update InteractionController Hovering")) {
428 _primaryHoverLocked =
false;
439 using (
new ProfilerSample(
"Find Closest Objects for Primary Hover")) {
445 refreshHoverStateBuffers();
446 refreshPrimaryHoverStateBuffers();
493 private HashSet<IInteractionBehaviour> _hoveredLastFrame =
new HashSet<IInteractionBehaviour>();
499 private void refreshHoverState(HashSet<IInteractionBehaviour> hoverCandidates) {
501 int primaryHoverPointIdxLastFrame = _primaryHoveredLastFrame !=
null ? _primaryHoverPointIdx : -1;
503 _hoveredObjects.Clear();
507 lockedPrimaryHoveredObject = _primaryHoveredObject;
509 _primaryHoveredObject =
null;
510 _primaryHoverDistance =
float.PositiveInfinity;
511 _primaryHoverPointIdx = -1;
512 _perPointPrimaryHovered.Clear();
513 _perPointPrimaryHoverDistance.Clear();
515 _perPointPrimaryHovered.Add(
null);
516 _perPointPrimaryHoverDistance.Add(
float.PositiveInfinity);
523 float maxNewPrimaryHoverDistance =
float.PositiveInfinity;
524 if (_primaryHoveredLastFrame !=
null && primaryHoverPointIdxLastFrame != -1
527 if (_contactBehaviours.ContainsKey(_primaryHoveredLastFrame)) {
529 maxNewPrimaryHoverDistance = 0F;
532 float distanceToLastPrimaryHover = _primaryHoveredLastFrame.
GetHoverDistance(
537 maxNewPrimaryHoverDistance = distanceToLastPrimaryHover
538 * distanceToLastPrimaryHover.Map(0.009F, 0.018F, 0.4F, 0.95F);
543 foreach (IInteractionBehaviour behaviour
in hoverCandidates) {
545 _hoveredObjects.Add(behaviour);
549 if (behaviour.ignorePrimaryHover)
continue;
554 processPrimaryHover(behaviour, maxNewPrimaryHoverDistance);
561 processPrimaryHover(lockedPrimaryHoveredObject,
float.PositiveInfinity);
565 private void processPrimaryHover(IInteractionBehaviour behaviour,
float maxNewPrimaryHoverDistance) {
568 float shortestPointDistance =
float.PositiveInfinity;
574 if (primaryHoverPoint ==
null)
continue;
575 if (!primaryHoverPoint.gameObject.activeInHierarchy)
continue;
583 float behaviourDistance =
GetHoverDistance(primaryHoverPoint.position, behaviour);
584 if (behaviourDistance < shortestPointDistance) {
587 _perPointPrimaryHovered[i] = behaviour;
588 _perPointPrimaryHoverDistance[i] = behaviourDistance;
589 shortestPointDistance = behaviourDistance;
595 _primaryHoveredObject = _perPointPrimaryHovered[i];
596 _primaryHoverDistance = _perPointPrimaryHoverDistance[i];
597 _primaryHoverPointIdx = i;
599 else if (shortestPointDistance < _primaryHoverDistance
600 && (behaviour == _primaryHoveredLastFrame || behaviourDistance < maxNewPrimaryHoverDistance)) {
605 _primaryHoveredObject = _perPointPrimaryHovered[i];
606 _primaryHoverDistance = _perPointPrimaryHoverDistance[i];
607 _primaryHoverPointIdx = i;
620 _controllerListBuffer.Clear();
621 _controllerListBuffer.Add(
this);
623 var tempObjs = Pool<HashSet<IInteractionBehaviour>>.Spawn();
626 tempObjs.Add(intObj);
629 foreach (var intObj
in tempObjs) {
634 _hoveredObjects.Remove(intObj);
635 _hoveredLastFrame.Remove(intObj);
637 intObj.EndHover(_controllerListBuffer);
642 Pool<HashSet<IInteractionBehaviour>>.Recycle(tempObjs);
656 _hoveredObjects.Remove(intObj);
657 _hoveredLastFrame.Remove(intObj);
659 _controllerListBuffer.Clear();
660 _controllerListBuffer.Add(
this);
662 intObj.
EndHover(_controllerListBuffer);
665 #region Hover State Checks
667 private HashSet<IInteractionBehaviour> _hoverEndedBuffer =
new HashSet<IInteractionBehaviour>();
668 private HashSet<IInteractionBehaviour> _hoverBeganBuffer =
new HashSet<IInteractionBehaviour>();
670 private List<IInteractionBehaviour> _hoverRemovalCache =
new List<IInteractionBehaviour>();
671 private void refreshHoverStateBuffers() {
672 _hoverBeganBuffer.Clear();
673 _hoverEndedBuffer.Clear();
676 foreach (var hoverable
in trackedBehaviours) {
677 bool inLastFrame =
false, inCurFrame =
false;
681 if (_hoveredLastFrame.Contains(hoverable)) {
685 if (inCurFrame && !inLastFrame) {
686 _hoverBeganBuffer.Add(hoverable);
687 _hoveredLastFrame.Add(hoverable);
689 if (!inCurFrame && inLastFrame) {
690 _hoverEndedBuffer.Add(hoverable);
691 _hoveredLastFrame.Remove(hoverable);
695 foreach (var hoverable
in _hoveredLastFrame) {
696 if (!trackedBehaviours.Contains(hoverable)) {
697 _hoverEndedBuffer.Add(hoverable);
698 _hoverRemovalCache.Add(hoverable);
701 foreach (var hoverable
in _hoverRemovalCache) {
702 _hoveredLastFrame.Remove(hoverable);
704 _hoverRemovalCache.Clear();
712 bool IInternalInteractionController.CheckHoverEnd(out HashSet<IInteractionBehaviour> hoverEndedObjects) {
715 hoverEndedObjects = _hoverEndedBuffer;
717 return _hoverEndedBuffer.Count > 0;
725 bool IInternalInteractionController.CheckHoverBegin(out HashSet<IInteractionBehaviour> hoverBeganObjects) {
726 hoverBeganObjects = _hoverBeganBuffer;
727 return _hoverBeganBuffer.Count > 0;
735 bool IInternalInteractionController.CheckHoverStay(out HashSet<IInteractionBehaviour>
hoveredObjects) {
755 var formerlyPrimaryHoveredObj = _primaryHoveredObject;
756 _primaryHoveredObject =
null;
757 _primaryHoveredLastFrame =
null;
761 _controllerListBuffer.Clear();
762 _controllerListBuffer.Add(
this);
764 formerlyPrimaryHoveredObj.EndPrimaryHover(_controllerListBuffer);
767 #region Primary Hover State Checks
772 private void refreshPrimaryHoverStateBuffers() {
774 if (_primaryHoveredLastFrame !=
null) _primaryHoverEndedObject = _primaryHoveredLastFrame;
775 else _primaryHoverEndedObject =
null;
779 if (_primaryHoveredLastFrame !=
null) _primaryHoverBeganObject = _primaryHoveredLastFrame;
780 else _primaryHoverBeganObject =
null;
783 _primaryHoverEndedObject =
null;
784 _primaryHoverBeganObject =
null;
793 bool IInternalInteractionController.CheckPrimaryHoverEnd(out IInteractionBehaviour primaryHoverEndedObject) {
794 primaryHoverEndedObject = _primaryHoverEndedObject;
795 bool primaryHoverEnded = primaryHoverEndedObject !=
null;
797 if (primaryHoverEnded && _primaryHoverEndedObject is InteractionBehaviour) {
801 return primaryHoverEnded;
809 bool IInternalInteractionController.CheckPrimaryHoverBegin(out IInteractionBehaviour primaryHoverBeganObject) {
810 primaryHoverBeganObject = _primaryHoverBeganObject;
811 bool primaryHoverBegan = primaryHoverBeganObject !=
null;
813 if (primaryHoverBegan && _primaryHoverBeganObject is InteractionBehaviour) {
817 return primaryHoverBegan;
825 bool IInternalInteractionController.CheckPrimaryHoverStay(out IInteractionBehaviour
primaryHoveredObject) {
833 return primaryHoverStayed;
843 if (behaviour.
space !=
null) {
856 Vector3 localPos = element.
anchor.
space.transform.InverseTransformPoint(worldPoint);
874 #region Contact Bones
878 private float _softContactDislocationDistance = 0.03F;
880 get {
return _softContactDislocationDistance; }
881 set { _softContactDislocationDistance = value; }
884 private static PhysicMaterial s_defaultContactBoneMaterial;
887 if (s_defaultContactBoneMaterial ==
null) {
888 initDefaultContactBoneMaterial();
890 return s_defaultContactBoneMaterial;
898 private static void initDefaultContactBoneMaterial() {
899 if (s_defaultContactBoneMaterial ==
null) {
900 s_defaultContactBoneMaterial =
new PhysicMaterial();
902 s_defaultContactBoneMaterial.hideFlags = HideFlags.HideAndDontSave;
903 s_defaultContactBoneMaterial.bounceCombine = PhysicMaterialCombine.Minimum;
904 s_defaultContactBoneMaterial.bounciness = 0F;
907 private bool _contactInitialized =
false;
913 private Vector3[] _boneTargetPositions;
914 private Quaternion[] _boneTargetRotations;
938 private void finishInitContact() {
943 if (comment ==
null) {
949 contactBone.rigidbody.maxAngularVelocity = 30F;
954 _boneTargetPositions =
new Vector3[
contactBones.Length];
955 _boneTargetRotations =
new Quaternion[
contactBones.Length];
958 private void fixedUpdateContact() {
960 if (!_contactInitialized) {
963 _contactInitialized =
true;
972 if (!
isTracked && _contactBehaviours.Count > 0) {
973 _contactBehaviours.Clear();
976 _softContactCollisions.Clear();
992 using (
new ProfilerSample(
"Update Contact Bone Targets")) {
995 out _boneTargetRotations[i]);
999 using (
new ProfilerSample(
"Update Contact Bones")) {
1000 normalizeBoneMasses();
1002 updateContactBone(i, _boneTargetPositions[i], _boneTargetRotations[i]);
1005 using (
new ProfilerSample(
"Update Soft Contact")) {
1006 fixedUpdateSoftContact();
1008 using (
new ProfilerSample(
"Update Contact Callbacks")) {
1009 fixedUpdateContactState();
1022 out Vector3 targetPosition,
1023 out Quaternion targetRotation);
1025 private void normalizeBoneMasses() {
1030 if (
contactBones[i]._lastObjectTouchedAdjustedMass != tempAdjustedMass) {
1041 private void updateContactBone(
int contactBoneIndex, Vector3 targetPosition, Quaternion targetRotation) {
1042 ContactBone contactBone =
contactBones[contactBoneIndex];
1050 using (
new ProfilerSample(
"updateContactBone: MoveRotation")) {
1051 body.MoveRotation(targetRotation);
1055 float errorDistance = 0f;
1056 float errorFraction = 0f;
1057 using (
new ProfilerSample(
"updateContactBone: errorDistance, errorFraction")) {
1058 Vector3 lastTargetPositionTransformedAhead = contactBone.lastTargetPosition;
1062 errorDistance =
Vector3.Distance(lastTargetPositionTransformedAhead, body.position);
1063 errorFraction = errorDistance / contactBone.width;
1069 using (
new ProfilerSample(
"updateContactBone: speed & massScale")) {
1071 float massScale = Mathf.Clamp(1.0F - (errorFraction * 2.0F), 0.1F, 1.0F)
1072 * Mathf.Clamp(speed * 10F, 1F, 10F);
1073 body.mass = massScale * contactBone._lastObjectTouchedAdjustedMass;
1077 using (
new ProfilerSample(
"updateContactBone: maybe enable Soft Contact")) {
1088 using (
new ProfilerSample(
"updateContactBone: Move to target, with deadzone")) {
1090 Vector3 delta = (targetPosition - body.position);
1091 float deltaMag = delta.magnitude;
1092 if (deltaMag <= deadzone) {
1094 contactBone.lastTargetPosition = body.position;
1097 delta *= (deltaMag - deadzone) / deltaMag;
1098 contactBone.lastTargetPosition = body.position + delta;
1100 Vector3 targetVelocity = delta / Time.fixedDeltaTime;
1101 float targetVelocityMag = targetVelocity.magnitude;
1102 body.velocity = (targetVelocity / targetVelocityMag)
1103 * Mathf.Clamp(targetVelocityMag, 0F, 100F);
1106 body.angularVelocity = PhysicsUtility.ToAngularVelocity(deltaRot, Time.fixedDeltaTime);
1112 #region Soft Contact
1114 private bool _softContactEnabled =
false;
1117 private bool _disableSoftContactEnqueued =
false;
1118 private IEnumerator _delayedDisableSoftContactCoroutine;
1120 private Collider[] _softContactColliderBuffer =
new Collider[32];
1122 private bool _notTrackedLastFrame =
true;
1124 private void fixedUpdateSoftContact() {
1126 _notTrackedLastFrame =
true;
1131 if (_notTrackedLastFrame) {
1135 _notTrackedLastFrame =
false;
1138 if (_softContactEnabled) {
1146 PhysicsUtility.generateSphereContacts(contactBone.rigidbody.position,
1148 contactBone.rigidbody.velocity,
1152 ref _softContactColliderBuffer);
1160 Collider contactBoneCollider = contactBone.collider;
1161 if (contactBoneCollider is SphereCollider) {
1162 var boneSphere = contactBoneCollider as SphereCollider;
1164 int numCollisions = Physics.OverlapSphereNonAlloc(contactBone.transform.TransformPoint(boneSphere.center),
1165 contactBone.transform.lossyScale.x * boneSphere.radius,
1166 _softContactColliderBuffer,
1168 QueryTriggerInteraction.Ignore);
1169 for (
int i = 0; i < numCollisions; i++) {
1173 if (_softContactColliderBuffer[i].attachedRigidbody ==
null)
continue;
1174 IInteractionBehaviour intObj;
1177 if (intObj.ignoreContact)
continue;
1178 if (intObj.isGrasped)
continue;
1181 PhysicsUtility.generateSphereContact(boneSphere, 0, _softContactColliderBuffer[i],
1186 else if (contactBoneCollider is CapsuleCollider) {
1187 var boneCapsule = contactBoneCollider as CapsuleCollider;
1190 boneCapsule.GetCapsulePoints(out point0, out point1);
1192 int numCollisions =
Physics.OverlapCapsuleNonAlloc(point0, point1,
1193 contactBone.transform.lossyScale.x * boneCapsule.radius,
1194 _softContactColliderBuffer,
1196 QueryTriggerInteraction.Ignore);
1197 for (
int i = 0; i < numCollisions; i++) {
1201 if (_softContactColliderBuffer[i].attachedRigidbody ==
null)
continue;
1202 IInteractionBehaviour intObj;
1205 if (intObj.ignoreContact)
continue;
1206 if (intObj.isGrasped)
continue;
1209 PhysicsUtility.generateCapsuleContact(boneCapsule, 0,
1210 _softContactColliderBuffer[i],
1216 var boneBox = contactBoneCollider as BoxCollider;
1218 if (boneBox ==
null) {
1219 Debug.LogError(
"Unsupported collider type in ContactBone. Supported "
1220 +
"types are SphereCollider, CapsuleCollider, and "
1221 +
"BoxCollider.",
this);
1225 int numCollisions =
Physics.OverlapBoxNonAlloc(boneBox.transform.TransformPoint(boneBox.center),
1226 Vector3.Scale(boneBox.size * 0.5F, contactBone.transform.lossyScale),
1227 _softContactColliderBuffer,
1228 boneBox.transform.rotation,
1230 QueryTriggerInteraction.Ignore);
1231 for (
int i = 0; i < numCollisions; i++) {
1235 if (_softContactColliderBuffer[i].attachedRigidbody ==
null)
continue;
1236 IInteractionBehaviour intObj;
1239 if (intObj.ignoreContact)
continue;
1240 if (intObj.isGrasped)
continue;
1243 PhysicsUtility.generateBoxContact(boneBox, 0, _softContactColliderBuffer[i],
1276 if (_softContactCollisions.Count > 0) {
1277 _disableSoftContactEnqueued =
false;
1278 if (_delayedDisableSoftContactCoroutine !=
null) {
1279 manager.StopCoroutine(_delayedDisableSoftContactCoroutine);
1312 _disableSoftContactEnqueued =
false;
1313 if (!_softContactEnabled) {
1316 _softContactEnabled =
true;
1318 if (_delayedDisableSoftContactCoroutine !=
null) {
1319 manager.StopCoroutine(_delayedDisableSoftContactCoroutine);
1326 disableContactBoneCollision();
1335 if (!_disableSoftContactEnqueued) {
1336 _delayedDisableSoftContactCoroutine = DelayedDisableSoftContact();
1337 manager.StartCoroutine(_delayedDisableSoftContactCoroutine);
1338 _disableSoftContactEnqueued =
true;
1343 private IEnumerator DelayedDisableSoftContact() {
1344 yield
return new WaitForSecondsRealtime(0.3f);
1345 if (_disableSoftContactEnqueued) {
1347 _softContactEnabled =
false;
1349 enableContactBoneCollision();
1357 #region Soft Contact Collision Tracking
1371 private Dictionary<BoneIntObjPair, HashSet<Collider>> _softContactCollisions =
new Dictionary<BoneIntObjPair, HashSet<Collider>>();
1373 private struct BoneIntObjPair : IEquatable<BoneIntObjPair> {
1374 public ContactBone bone;
1375 public IInteractionBehaviour intObj;
1377 public override bool Equals(
object obj) {
1378 return obj is BoneIntObjPair &&
this == (BoneIntObjPair)obj;
1380 public bool Equals(BoneIntObjPair other) {
1381 return this == other;
1383 public static bool operator !=(BoneIntObjPair one, BoneIntObjPair other) {
1384 return !(one == other);
1386 public static bool operator ==(BoneIntObjPair one, BoneIntObjPair other) {
1387 return one.bone == other.bone && one.intObj == other.intObj;
1389 public override int GetHashCode() {
1390 return bone.GetHashCode() ^ intObj.GetHashCode();
1396 Collider collider) {
1397 var pair =
new BoneIntObjPair() { bone = bone, intObj = intObj };
1399 if (!_softContactCollisions.ContainsKey(pair)) {
1400 _softContactCollisions[pair] =
new HashSet<Collider>();
1402 _softContactCollisions[pair].Add(collider);
1407 Collider collider) {
1408 var pair =
new BoneIntObjPair() { bone = bone, intObj = intObj };
1410 if (!_softContactCollisions.ContainsKey(pair)) {
1411 Debug.LogError(
"No collision set found for this pair of collisions; Exit method "
1412 +
"was called without a prior, corresponding Enter method!",
this);
1414 _softContactCollisions[pair].Remove(collider);
1416 if (_softContactCollisions[pair].Count == 0) {
1417 _softContactCollisions.Remove(pair);
1425 #region Contact Callbacks
1427 private HashSet<IInteractionBehaviour> _contactBehavioursSet =
new HashSet<IInteractionBehaviour>();
1429 private Dictionary<IInteractionBehaviour, int> _contactBehaviours =
new Dictionary<IInteractionBehaviour, int>();
1430 private HashSet<IInteractionBehaviour> _contactBehavioursLastFrame =
new HashSet<IInteractionBehaviour>();
1431 private List<IInteractionBehaviour> _contactBehaviourRemovalCache =
new List<IInteractionBehaviour>();
1433 private HashSet<IInteractionBehaviour> _contactEndedBuffer =
new HashSet<IInteractionBehaviour>();
1434 private HashSet<IInteractionBehaviour> _contactBeganBuffer =
new HashSet<IInteractionBehaviour>();
1438 if (_contactBehaviours.TryGetValue(interactionObj, out count)) {
1439 _contactBehaviours[interactionObj] = count + 1;
1442 _contactBehaviours[interactionObj] = 1;
1443 _contactBehavioursSet.Add(interactionObj);
1452 if (!_contactBehaviours.TryGetValue(interactionObj, out count)) {
1453 _contactBehaviours[interactionObj] = 1;
1454 _contactBehavioursSet.Add(interactionObj);
1460 if (_contactBehaviours.ContainsKey(interactionObj)) _contactBehaviours.Remove(interactionObj);
1466 if (!_contactBehaviours.ContainsKey(interactionObj))
return;
1468 int count = _contactBehaviours[interactionObj];
1470 _contactBehaviours.Remove(interactionObj);
1471 _contactBehavioursSet.Remove(interactionObj);
1474 _contactBehaviours[interactionObj] = count - 1;
1486 _controllerListBuffer.Clear();
1487 _controllerListBuffer.Add(
this);
1489 var tempObjs = Pool<HashSet<IInteractionBehaviour>>.Spawn();
1492 tempObjs.Add(intObj);
1495 foreach (var intObj
in tempObjs) {
1496 _contactBehavioursSet.Remove(intObj);
1497 _contactBehaviours.Remove(intObj);
1498 _contactBehavioursLastFrame.Remove(intObj);
1500 intObj.EndContact(_controllerListBuffer);
1504 Pool<HashSet<IInteractionBehaviour>>.Recycle(tempObjs);
1518 _contactBehavioursSet.Remove(intObj);
1519 _contactBehaviours.Remove(intObj);
1520 _contactBehavioursLastFrame.Remove(intObj);
1522 _controllerListBuffer.Clear();
1523 _controllerListBuffer.Add(
this);
1532 private void fixedUpdateContactState() {
1533 _contactEndedBuffer.Clear();
1534 _contactBeganBuffer.Clear();
1537 _contactBehaviourRemovalCache.Clear();
1538 foreach (var interactionObj
in _contactBehavioursLastFrame) {
1539 if (!_contactBehaviours.ContainsKey(interactionObj)
1542 _contactEndedBuffer.Add(interactionObj);
1543 _contactBehaviourRemovalCache.Add(interactionObj);
1546 foreach (var interactionObj
in _contactBehaviourRemovalCache) {
1547 _contactBehavioursLastFrame.Remove(interactionObj);
1552 foreach (var intObjCountPair
in _contactBehaviours) {
1553 var interactionObj = intObjCountPair.Key;
1554 if (!_contactBehavioursLastFrame.Contains(interactionObj)) {
1555 _contactBeganBuffer.Add(interactionObj);
1556 _contactBehavioursLastFrame.Add(interactionObj);
1562 private List<IInteractionBehaviour> _removeContactObjsBuffer =
new List<IInteractionBehaviour>();
1568 bool IInternalInteractionController.CheckContactEnd(out HashSet<IInteractionBehaviour> contactEndedObjects) {
1570 _removeContactObjsBuffer.Clear();
1571 foreach (var objTouchCountPair
in _contactBehaviours) {
1572 if (objTouchCountPair.Key.gameObject ==
null
1573 || objTouchCountPair.Key.rigidbody ==
null
1574 || objTouchCountPair.Key.ignoreContact
1576 _removeContactObjsBuffer.Add(objTouchCountPair.Key);
1581 foreach (var intObj
in _removeContactObjsBuffer) {
1582 _contactBehaviours.Remove(intObj);
1583 _contactEndedBuffer.Add(intObj);
1586 contactEndedObjects = _contactEndedBuffer;
1587 return _contactEndedBuffer.Count > 0;
1595 bool IInternalInteractionController.CheckContactBegin(out HashSet<IInteractionBehaviour> contactBeganObjects) {
1596 contactBeganObjects = _contactBeganBuffer;
1597 return _contactBeganBuffer.Count > 0;
1600 private HashSet<IInteractionBehaviour> _contactedObjects =
new HashSet<IInteractionBehaviour>();
1606 bool IInternalInteractionController.CheckContactStay(out HashSet<IInteractionBehaviour> contactedObjects) {
1607 _contactedObjects.Clear();
1608 foreach (var objCountPair
in _contactBehaviours) {
1609 _contactedObjects.Add(objCountPair.Key);
1612 contactedObjects = _contactedObjects;
1613 return contactedObjects.Count > 0;
1618 private void disableContactBoneCollision() {
1620 contactBone.collider.isTrigger =
true;
1624 private void enableContactBoneCollision() {
1626 contactBone.collider.isTrigger =
false;
1630 private void resetContactBonePose() {
1637 contactBone.rigidbody.position =
position;
1638 contactBone.rigidbody.rotation =
rotation;
1639 contactBone.rigidbody.velocity =
Vector3.zero;
1640 contactBone.rigidbody.angularVelocity =
Vector3.zero;
1681 _graspedObject = intObj;
1699 if (_graspedObject ==
null) {
1700 throw new InvalidOperationException(
"Cannot swap grasp if we are not currently grasping.");
1703 if (replacement ==
null) {
1704 throw new ArgumentNullException(
"The replacement object is null!");
1708 throw new InvalidOperationException(
"Cannot swap grasp if the replacement object is already grasped and does not support multi grasp.");
1712 _releasingControllersBuffer.Clear();
1713 _releasingControllersBuffer.Add(
this);
1714 _graspedObject.
EndGrasp(_releasingControllersBuffer);
1718 _graspedObject = replacement;
1720 var tempControllers = Pool<List<InteractionController>>.Spawn();
1723 tempControllers.Add(
this);
1728 tempControllers.Clear();
1729 Pool<List<InteractionController>>.Recycle(tempControllers);
1747 private Func<Collider, IInteractionBehaviour> graspActivityFilter;
1749 Rigidbody body = collider.attachedRigidbody;
1752 bool validForGrasping = body !=
null
1754 && !intObj.ignoreGrasping;
1756 if (validForGrasping)
return intObj;
1762 private Func<int> graspLayerMaskAccessor;
1769 if (_graspActivityManager ==
null) {
1770 if (graspActivityFilter ==
null) graspActivityFilter = graspFilterFunc;
1777 return _graspActivityManager;
1781 private IInteractionBehaviour _graspedObject =
null;
1783 private void fixedUpdateGrasping() {
1784 using (
new ProfilerSample(
"Fixed Update Controller Grasping")) {
1821 private List<InteractionController> _releasingControllersBuffer =
new List<InteractionController>();
1829 if (_graspedObject ==
null) {
1834 _releasingControllersBuffer.Clear();
1835 _releasingControllersBuffer.Add(
this);
1839 var tempGraspedObject = _graspedObject;
1843 _graspedObject =
null;
1847 tempGraspedObject.EndGrasp(_releasingControllersBuffer);
1870 var controllersBuffer = Pool<List<InteractionController>>.Spawn();
1872 foreach (var controller
in controllers) {
1873 if (controller.graspedObject != graspedObj) {
1874 Debug.LogError(
"Argument intObj " + graspedObj.
name +
" is not held by "
1875 +
"controller " + controller.name +
"; skipping release for this "
1880 controllersBuffer.Add(controller);
1886 foreach (var controller
in controllersBuffer) {
1888 controller.OnGraspEnd();
1891 controller._graspedObject =
null;
1895 controller.EnableSoftContact();
1899 graspedObj.
EndGrasp(controllersBuffer);
1902 foreach (var controller
in controllersBuffer) {
1903 controller.onGraspedObjectForciblyReleased(graspedObj);
1907 controllersBuffer.Clear();
1908 Pool<List<InteractionController>>.Recycle(controllersBuffer);
1917 releasedObject = _graspedObject;
1933 if (toRelease ==
null)
return false;
1935 if (_graspedObject == toRelease) {
1944 #region Grasp State Checking
1951 releasedObject =
null;
1953 bool shouldReleaseObject =
false;
1956 if (_graspedObject ==
null) {
1959 else if (_graspedObject.ignoreGrasping) {
1962 releasedObject = _graspedObject;
1963 shouldReleaseObject =
true;
1967 if (!shouldReleaseObject) shouldReleaseObject =
checkShouldRelease(out releasedObject);
1969 if (shouldReleaseObject) {
1971 _graspedObject =
null;
1983 bool IInternalInteractionController.CheckGraspBegin(out IInteractionBehaviour newlyGraspedObject) {
1984 newlyGraspedObject =
null;
1987 if (_graspedObject !=
null) {
1995 if (shouldGraspObject) {
1996 _graspedObject = newlyGraspedObject;
2010 bool IInternalInteractionController.CheckGraspHold(out IInteractionBehaviour
graspedObject) {
2021 bool IInternalInteractionController.CheckSuspensionBegin(out IInteractionBehaviour suspendedObject) {
2022 suspendedObject =
null;
2024 if (_graspedObject !=
null && !
isTracked && !_graspedObject.isSuspended) {
2025 suspendedObject = _graspedObject;
2028 return suspendedObject !=
null;
2036 bool IInternalInteractionController.CheckSuspensionEnd(out IInteractionBehaviour resumedObject) {
2037 resumedObject =
null;
2039 if (_graspedObject !=
null &&
isTracked && _graspedObject.isSuspended) {
2040 resumedObject = _graspedObject;
2043 return resumedObject !=
null;
2052 public static class GizmoColors {
2054 public static Color ContactBone {
get {
return Color.green.WithAlpha(0.5F); } }
2055 public static Color SoftContactBone {
get {
return Color.white.WithAlpha(0.5F); } }
2057 public static Color HoverPoint {
get {
return Color.yellow.WithAlpha(0.5F); } }
2058 public static Color PrimaryHoverPoint {
get {
return Color.Lerp(
Color.red,
Color.yellow, 0.5F).WithAlpha(0.5F); } }
2060 public static Color GraspPoint {
get {
return Color.Lerp(
Color.blue,
Color.cyan, 0.3F).WithAlpha(0.5F); } }
2061 public static Color Graspable {
get {
return Color.cyan.WithAlpha(0.5F); } }
2072 if (!this.isActiveAndEnabled)
return;
2076 drawer.
color = GizmoColors.ContactBone;
2079 drawer.
color = GizmoColors.SoftContactBone;
2093 if (point ==
null)
continue;
2100 drawer.
color = GizmoColors.HoverPoint;
2105 drawer.
color = GizmoColors.PrimaryHoverPoint;
The Hand class reports the physical characteristics of a detected hand.
ActivityManager is a wrapper around PhysX sphere queries for arbitrary Unity objects....
Func< int > activationLayerFunction
This function, if set to a non-null value, overrides the activationLayerMask setting with the result ...
HashSet< T > ActiveObjects
Returns the currently "active" objects – objects that were within the latest sphere query.
InteractionBehaviours are components that enable GameObjects to interact with interaction controllers...
void ClearHoverTracking()
Clears all hover tracking state and fires the hover-end callbacks immediately. If objects are still i...
void ClearPrimaryHoverTracking()
Clears primary hover tracking state for the current primary hovered object.
abstract void onObjectUnregistered(IInteractionBehaviour intObj)
This method is called by the InteractionController when it is notified by the InteractionManager that...
void LockPrimaryHover(InteractionBehaviour intObj)
Sets the argument interaction object to be the current primary hover of this interaction controller a...
const float DEAD_ZONE_FRACTION
abstract bool isBeingMoved
Gets whether the underlying object (Leap hand or a held controller) is currently being moved or being...
abstract List< Transform > _primaryHoverPoints
ActivityManager< IInteractionBehaviour > hoverActivityManager
Action OnGraspEnd
Called when the InteractionController releases an object.
float lastObjectTouchedAdjustedMassMass
virtual void SwapGrasp(IInteractionBehaviour replacement)
Seamlessly swap the currently grasped object for a replacement object. It will behave like the hand r...
abstract Vector3 velocity
Returns the current velocity of this controller.
bool ReleaseGrasp()
Releases the object this hand is holding and returns true if the hand was holding an object,...
ReadonlyHashSet< IInteractionBehaviour > hoveredObjects
Returns a set of all Interaction objects currently hovered by this InteractionController.
abstract ContactBone[] contactBones
void NotifySoftContactCollisionExit(ContactBone bone, IInteractionBehaviour intObj, Collider collider)
float softContactDislocationDistance
bool TryGrasp(IInteractionBehaviour intObj)
Checks if the provided interaction object can be grasped by this interaction controller in its curren...
ReadonlyList< Transform > primaryHoverPoints
Gets the list of Transforms to consider against nearby objects to determine the closest object (prima...
void DisableSoftContact()
bool ReleaseGrasp(out IInteractionBehaviour releasedObject)
As ReleaseGrasp(), but also outputs the released object into releasedObject if the hand successfully ...
IInteractionBehaviour primaryHoveredObject
Gets the InteractionBehaviour that is currently this InteractionController's primary hovered object,...
abstract bool isTracked
Gets whether the underlying object (Leap hand or a held controller) is currently in a tracked state....
abstract void fixedUpdateGraspingState()
Called every fixed frame if grasping is enabled in the Interaction Manager.
abstract bool initContact()
Called to initialize contact colliders. See remarks for implementation requirements.
void ClearContactTrackingForObject(IInteractionBehaviour intObj)
Clears contact state for the specified object and fires its ContactEnd callbacks immediately.
abstract GameObject contactBoneParent
Action< InteractionBehaviour > OnEndPrimaryHoveringObject
Called when this InteractionController stops primarily hovering over an InteractionBehaviour....
Action< InteractionBehaviour > OnBeginPrimaryHoveringObject
Called when this InteractionController begins primarily hovering over an InteractionBehaviour....
abstract bool checkShouldGrasp(out IInteractionBehaviour objectToGrasp)
Returns whether this controller should grasp an object this fixed frame, and if so,...
void NotifyObjectUnregistered(IInteractionBehaviour intObj)
abstract List< Vector3 > graspManipulatorPoints
Gets the points of the controller to add to the calculation to determine how held objects should move...
abstract Quaternion rotation
Returns the current rotation of this controller.
static float GetHoverDistance(Vector3 hoverPoint, IInteractionBehaviour behaviour)
Returns the hover distance from the hoverPoint to the specified object, automatically accounting for ...
ReadonlyHashSet< IInteractionBehaviour > graspCandidates
Gets the set of objects currently considered graspable.
abstract Vector3 hoverPoint
Gets the current position to check against nearby objects for hovering. Position is only used if the ...
Action< InteractionController > OnContactInitialized
Called when contact data is initialized.
void NotifyContactBoneCollisionStay(ContactBone contactBone, IInteractionBehaviour interactionObj)
void NotifySoftContactCollisionEnter(ContactBone bone, IInteractionBehaviour intObj, Collider collider)
Func< IInteractionBehaviour, bool > customHoverActivityFilter
In addition to standard hover validity checks, you can set this filter property to further filter obj...
void NotifyContactBoneCollisionEnter(ContactBone contactBone, IInteractionBehaviour interactionObj)
static Vector3 TransformPoint(Vector3 worldPoint, ISpaceComponent element)
Applies the spatial warping of the provided ISpaceComponent to a world-space point.
Vector3 primaryHoveringPoint
Gets the position of the primary hovering point that is closest to its primary hovered object,...
static void drawHoverPoint(RuntimeGizmoDrawer drawer, Vector3 pos)
abstract ControllerType controllerType
Gets the type of controller this object represents underneath the InteractionController abstraction....
void ClearContactTracking()
Clears contact state for this controller and fires the appropriate ContactEnd callbacks on currently-...
virtual void onGraspedObjectForciblyReleased(IInteractionBehaviour objectToBeReleased)
Optionally override this method to perform logic just before a grasped object is released because it ...
abstract bool checkShouldGraspAtemporal(IInteractionBehaviour intObj)
Checks if the provided interaction object can be grasped by this interaction controller in its curren...
void NotifyContactBoneCollisionExit(ContactBone contactBone, IInteractionBehaviour interactionObj)
void ClearHoverTrackingForObject(IInteractionBehaviour intObj)
Clears the hover tracking state for an object and fires the hover-end callback for that object immedi...
InteractionManager manager
abstract InteractionHand intHand
If this InteractionController's controllerType is ControllerType.Hand, this gets the InteractionHand,...
ReadonlyHashSet< IInteractionBehaviour > contactingObjects
Gets the set of interaction objects that are currently touching this interaction controller.
int primaryHoveringPointIndex
Gets the index in the primaryHoverPoints array of the primary hover point that is currently closest t...
float scale
Contact requires knowledge of the controller's scale. Non-uniformly scaled controllers are NOT suppor...
static PhysicMaterial defaultContactBoneMaterial
static void drawPrimaryHoverPoint(RuntimeGizmoDrawer drawer, Vector3 pos)
static void ReleaseGrasps(IInteractionBehaviour graspedObj, ReadonlyHashSet< InteractionController > controllers)
Helper static method for forcing multiple controllers to release their grasps on a single object simu...
abstract bool checkShouldRelease(out IInteractionBehaviour objectToRelease)
Returns whether this controller should release an object this fixed frame, and if so,...
Action OnGraspBegin
Called when the InteractionController begins grasping an object.
abstract Vector3 GetGraspPoint()
Returns approximately where the controller is grasping the currently grasped InteractionBehaviour....
bool _wasContactInitialized
bool isPrimaryHovering
Gets whether the InteractionController is currently primarily hovering over any interaction object.
virtual void fixedUpdateController()
Called just before the InteractionController proceeds with its usual FixedUpdate.
virtual void onPostDisableSoftContact()
Optionally override this method to perform logic just after soft contact is disabled for this control...
Action< InteractionBehaviour > OnStayPrimaryHoveringObject
Called every (fixed) frame this InteractionController is primarily hovering over an InteractionBehavi...
abstract bool isLeft
Gets whether the underlying object (Leap hand or a held controller) represents or is held by a left h...
virtual void onPreEnableSoftContact()
Optionally override this method to perform logic just before soft contact is enabled for this control...
abstract void getColliderBoneTargetPositionRotation(int contactBoneIndex, out Vector3 targetPosition, out Quaternion targetRotation)
If your controller features no moving colliders relative to itself, simply return the desired positio...
abstract Vector3 position
Returns the current position of this controller.
virtual void OnDrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
By default, this method will draw all of the colliders found in the contactBoneParent hierarchy,...
Action OnGraspStay
Called while the InteractionController is grasping an object.
IInteractionBehaviour graspedObject
Gets the object the controller is currently grasping, or null if there is no such object.
abstract void unwarpColliders(Transform primaryHoverPoint, ISpaceComponent warpedSpaceElement)
Implementing this method is necessary to support curved spaces as rendered by a Leap Graphic Renderer...
bool isRight
Gets whether the underlying object (Leap hand or a held controller) represents or is held by a right ...
bool primaryHoverLocked
When set to true, locks the current primarily hovered object, even if the hand gets closer to a diffe...
bool ReleaseObject(IInteractionBehaviour toRelease)
Attempts to release this hand's object, but only if the argument object is the object currently grasp...
bool isGraspingObject
Gets whether the controller is currently grasping an object.
float primaryHoverDistance
Gets the distance from the closest primary hover point on this controller to its primarily hovered ob...
Hand leapHand
Gets the last tracked state of the Leap hand.
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...
float hoverActivationRadius
List< PhysicsUtility.SoftContact > _softContacts
Stores data for implementing Soft Contact for interaction controllers.
SingleLayer contactBoneLayer
Dictionary< Rigidbody, IInteractionBehaviour > interactionObjectBodies
Maps a Rigidbody to its attached interaction object, if the Rigidbody is part of and interaction obje...
bool hasMovingFrameOfReference
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...
SingleLayer interactionLayer
int GetInteractionLayerMask()
Returns a layer mask containing all layers that might contain interaction objects.
static InteractionManager instance
Often, only one InteractionManager is necessary per Unity scene. This property will contain that Inte...
void DrawColliders(GameObject gameObject, bool useWireframe=true, bool traverseHierarchy=true, bool drawTriggers=false)
void DrawWireSphere(Pose pose, float radius, int numSegments=32)
Color color
Sets or gets the color for the gizmos that will be drawn next.
static List< LeapSpace > allEnabled
IInteractionBehaviour is the interface that defines all Interaction objects, specifying the minimum s...
void EndGrasp(List< InteractionController > endedGrasping)
void EndContact(List< InteractionController > endedContact)
float GetHoverDistance(Vector3 worldPosition)
void EndHover(List< InteractionController > endedHovering)
void BeginGrasp(List< InteractionController > beganGrasping)
bool CheckGraspEnd(out IInteractionBehaviour releasedObject)
void FixedUpdateController()
ControllerType
The Interaction Engine can be controlled by hands tracked by the Leap Motion Controller,...
IgnoreHoverMode
Specified on a per-object basis to allow Interaction objects to ignore hover for the left hand,...
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....
A simple wrapper around HashSet to provide readonly access. Useful when you want to return a HashSet ...
A simple wrapper around List to provide readonly access. Useful when you want to return a list to som...