15using System.Collections;
16using System.Collections.Generic;
24 [DisallowMultipleComponent]
32 [Header(
"Hand Configuration")]
34 [Tooltip(
"Should the data for the underlying Leap hand come from the player's left "
35 +
"hand or their right hand? Alternatively, you can set this mode to Custom "
36 +
"to specify accessor functions manually via script (recommended for advanced "
41 get {
return _handDataMode; }
44 _handDataMode = value;
65 get {
return _leapProvider; }
67 if (_leapProvider !=
null && Application.isPlaying) {
71 _leapProvider = value;
73 if (_leapProvider !=
null && Application.isPlaying) {
86 get {
return _handAccessorFunc; }
87 set { _handAccessorFunc = value; }
100 private Hand _unwarpedHandData =
new Hand();
126 Debug.LogError(
"handDataMode is set to Custom, but no provider is set! "
127 +
"Please add a custom script that will configure the correct "
128 +
"LeapProvider for this InteractionHand before its Start() is "
129 +
"called, or set the handDataMode to a value other than Custom.",
134 Debug.LogError(
"handDataMode is set to Custom, but no handAccessorFunc has "
135 +
"been set! Please add a custom script that will configure the "
136 +
"hand accessor function that will convert Leap frames into "
137 +
"Leap hand data for this InteractionHand before its Start() "
138 +
"is called, or set the handDataMode to a value other than "
148 Debug.LogError(
"No LeapServiceProvider was found in your scene! Please "
149 +
"make sure you have a LeapServiceProvider if you intend to "
150 +
"use Leap hands in your scene.",
this);
158 .FirstOrDefault(hand => hand.IsLeft);
162 .FirstOrDefault(hand => hand.IsRight);
171 Transform palmTransform =
new GameObject(
"Palm Transform").transform;
172 palmTransform.parent = this.transform;
173 _backingHoverPointTransform = palmTransform;
177 for (
int i = 0; i < 5; i++) {
178 Transform fingertipTransform =
new GameObject(
"Fingertip Transform").transform;
179 fingertipTransform.parent = this.transform;
180 _backingFingertipTransforms.Add(fingertipTransform);
181 _fingertipTransforms.Add(
null);
185 private void OnDestroy() {
186 if (_leapProvider !=
null) {
191 private void onProviderFixedFrame(
Leap.
Frame frame) {
195 _handData.CopyFrom(_hand);
196 _unwarpedHandData.CopyFrom(_handData);
198 refreshPointDataFromHand();
199 _lastCustomHandWasLeft = _unwarpedHandData.
IsLeft;
206 #region General InteractionController Implementation
211 public override bool isTracked {
get {
return _hand !=
null; } }
229 private bool _lastCustomHandWasLeft =
false;
241 return _lastCustomHandWasLeft;
257 get {
return _handData.
Rotation.ToQuaternion(); }
290 Vector3 transformAheadPosition;
291 Quaternion transformAheadRotation;
293 _unwarpedHandData.
Rotation.ToQuaternion(),
294 out transformAheadPosition,
295 out transformAheadRotation);
296 _unwarpedHandData.SetTransform(transformAheadPosition, transformAheadRotation);
302 #region Hovering Controller Implementation
304 private Transform _backingHoverPointTransform =
null;
307 if (_backingHoverPointTransform ==
null) {
311 return _backingHoverPointTransform.position;
316 private List<Transform> _backingFingertipTransforms =
new List<Transform>();
317 private List<Transform> _fingertipTransforms =
new List<Transform>();
320 return _fingertipTransforms;
324 private void refreshPointDataFromHand() {
326 refreshPrimaryHoverPoints();
327 refreshGraspManipulatorPoints();
330 private void refreshHoverPoint() {
335 private void refreshPrimaryHoverPoints() {
338 _fingertipTransforms[i] = _backingFingertipTransforms[i];
341 _fingertipTransforms[i].position = finger.
TipPosition.ToVector3();
342 _fingertipTransforms[i].rotation = finger.
bones[3].
Rotation.ToQuaternion();
345 _fingertipTransforms[i] =
null;
353 Vector3 unwarpedPosition;
354 Quaternion unwarpedRotation;
355 warpedSpaceElement.
anchor.
transformer.WorldSpaceUnwarp(primaryHoverPoint.position,
356 primaryHoverPoint.rotation,
357 out unwarpedPosition,
358 out unwarpedRotation);
363 _unwarpedHandData.Transform(-primaryHoverPoint.position, Quaternion.identity);
364 _unwarpedHandData.Transform(unwarpedPosition, unwarpedRotation
365 * Quaternion.Inverse(primaryHoverPoint.rotation));
368 refreshPointDataFromHand();
373 #region Contact Controller Implementation
375 private const int NUM_FINGERS = 5;
376 private const int BONES_PER_FINGER = 3;
380 get {
return _contactBones; }
383 private GameObject _contactBoneParent;
385 get {
return _contactBoneParent; }
388 private delegate
void BoneMapFunc(
Leap.
Hand hand, out Vector3 targetPosition,
389 out Quaternion targetRotation);
390 private BoneMapFunc[] _handContactBoneMapFunctions;
393 out Vector3 targetPosition,
394 out Quaternion targetRotation) {
395 using (
new ProfilerSample(
"InteractionHand: getColliderBoneTargetPositionRotation")) {
396 _handContactBoneMapFunctions[contactBoneIndex](_unwarpedHandData,
405 initContactBoneContainer();
412 resetContactBoneJoints();
419 #region Contact Bone Management
421 private void initContactBoneContainer() {
422 string name = (_unwarpedHandData.
IsLeft ?
"Left" :
"Right") +
" Interaction Hand Contact Bones";
423 _contactBoneParent =
new GameObject(name);
426 private void initContactBones() {
427 _contactBones =
new ContactBone[NUM_FINGERS * BONES_PER_FINGER + 1];
428 _handContactBoneMapFunctions =
new BoneMapFunc[NUM_FINGERS * BONES_PER_FINGER + 1];
431 for (
int fingerIndex = 0; fingerIndex < NUM_FINGERS; fingerIndex++) {
432 for (
int jointIndex = 0; jointIndex < BONES_PER_FINGER; jointIndex++) {
433 GameObject contactBoneObj =
new GameObject(
"Contact Fingerbone", typeof(CapsuleCollider), typeof(Rigidbody), typeof(ContactBone));
438 int boneArrayIndex = fingerIndex * BONES_PER_FINGER + jointIndex;
439 contactBoneObj.transform.position = bone.
Center.ToVector3();
440 contactBoneObj.transform.rotation = bone.
Rotation.ToQuaternion();
444 int fingerIndexCopy = fingerIndex;
445 int jointIndexCopy = jointIndex;
446 _handContactBoneMapFunctions[boneArrayIndex] = (
Leap.
Hand hand,
447 out Vector3 targetPosition,
448 out Quaternion targetRotation) => {
450 targetPosition = theBone.
Center.ToVector3();
451 targetRotation = theBone.
Rotation.ToQuaternion();
454 CapsuleCollider capsule = contactBoneObj.GetComponent<CapsuleCollider>();
455 capsule.direction = 2;
456 capsule.radius = bone.
Width * 0.5f;
460 ContactBone contactBone = initContactBone(bone, contactBoneObj, boneArrayIndex, capsule);
462 contactBone.lastTargetPosition = bone.
Center.ToVector3();
469 GameObject contactBoneObj =
new GameObject(
"Contact Palm Bone", typeof(BoxCollider), typeof(Rigidbody), typeof(ContactBone));
471 Bone bone = _unwarpedHandData.
Fingers[(int)Finger.FingerType.TYPE_MIDDLE].Bone(Bone.BoneType.TYPE_METACARPAL);
472 int boneArrayIndex = NUM_FINGERS * BONES_PER_FINGER;
473 contactBoneObj.transform.position = _unwarpedHandData.
PalmPosition.ToVector3();
474 contactBoneObj.transform.rotation = _unwarpedHandData.
Rotation.ToQuaternion();
477 _handContactBoneMapFunctions[boneArrayIndex] = (
Leap.
Hand hand,
480 targetPosition = hand.PalmPosition.ToVector3();
481 targetRotation = hand.Rotation.ToQuaternion();
484 BoxCollider box = contactBoneObj.GetComponent<BoxCollider>();
485 box.center =
new Vector3(_unwarpedHandData.
IsLeft ? -0.005f : 0.005f, bone.Width * -0.3f, -0.01f);
486 box.size =
new Vector3(bone.Length, bone.Width, bone.Length);
489 initContactBone(
null, contactBoneObj, boneArrayIndex, box);
493 addContactBoneJoints();
496 private ContactBone initContactBone(
Leap.
Bone bone, GameObject contactBoneObj,
int boneArrayIndex, Collider boneCollider) {
497 contactBoneObj.layer = _contactBoneParent.gameObject.layer;
498 contactBoneObj.transform.localScale =
Vector3.one;
500 ContactBone contactBone = contactBoneObj.GetComponent<ContactBone>();
501 contactBone.collider = boneCollider;
502 contactBone.interactionController =
this;
503 contactBone.interactionHand =
this;
504 _contactBones[boneArrayIndex] = contactBone;
506 Transform capsuleTransform = contactBoneObj.transform;
507 capsuleTransform.SetParent(_contactBoneParent.transform,
false);
509 Rigidbody body = contactBoneObj.GetComponent<Rigidbody>();
510 body.freezeRotation =
true;
512 body.useGravity =
false;
513 body.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
516 body.position = bone !=
null ? bone.Center.ToVector3()
518 body.rotation = bone !=
null ? bone.Rotation.ToQuaternion()
519 : _unwarpedHandData.
Rotation.ToQuaternion();
520 contactBone.lastTargetPosition = bone !=
null ? bone.Center.ToVector3()
526 private void addContactBoneJoints() {
527 for (
int fingerIndex = 0; fingerIndex < NUM_FINGERS; fingerIndex++) {
528 for (
int jointIndex = 0; jointIndex < BONES_PER_FINGER; jointIndex++) {
529 Bone bone = _unwarpedHandData.
Fingers[fingerIndex].Bone((Bone.BoneType)(jointIndex) + 1);
530 int boneArrayIndex = fingerIndex * BONES_PER_FINGER + jointIndex;
532 FixedJoint joint = _contactBones[boneArrayIndex].gameObject.AddComponent<FixedJoint>();
533 joint.autoConfigureConnectedAnchor =
false;
534 if (jointIndex != 0) {
535 Bone prevBone = _unwarpedHandData.
Fingers[fingerIndex].Bone((Bone.BoneType)(jointIndex));
536 joint.connectedBody = _contactBones[boneArrayIndex - 1].
rigidbody;
537 joint.anchor =
Vector3.back * bone.Length / 2f;
538 joint.connectedAnchor =
Vector3.forward * prevBone.Length / 2f;
539 _contactBones[boneArrayIndex].
joint = joint;
542 joint.connectedBody = _contactBones[NUM_FINGERS * BONES_PER_FINGER].
rigidbody;
543 joint.anchor =
Vector3.back * bone.Length / 2f;
544 joint.connectedAnchor = _contactBones[NUM_FINGERS * BONES_PER_FINGER].transform.InverseTransformPoint(bone.PrevJoint.ToVector3());
552 private void resetContactBoneJoints() {
555 if (_contactBones ==
null)
return;
558 if (_contactBones[NUM_FINGERS * BONES_PER_FINGER] ==
null)
return;
560 _contactBones[NUM_FINGERS * BONES_PER_FINGER].transform.position = _unwarpedHandData.
PalmPosition.ToVector3();
561 _contactBones[NUM_FINGERS * BONES_PER_FINGER].transform.rotation = _unwarpedHandData.
Rotation.ToQuaternion();
562 _contactBones[NUM_FINGERS * BONES_PER_FINGER].
rigidbody.velocity =
Vector3.zero;
563 _contactBones[NUM_FINGERS * BONES_PER_FINGER].
rigidbody.angularVelocity =
Vector3.zero;
565 for (
int fingerIndex = 0; fingerIndex < NUM_FINGERS; fingerIndex++) {
566 for (
int jointIndex = 0; jointIndex < BONES_PER_FINGER; jointIndex++) {
567 Bone bone = _unwarpedHandData.
Fingers[fingerIndex].Bone((Bone.BoneType)(jointIndex) + 1);
568 int boneArrayIndex = fingerIndex * BONES_PER_FINGER + jointIndex;
570 _contactBones[boneArrayIndex].transform.position = bone.Center.ToVector3();
571 _contactBones[boneArrayIndex].transform.rotation = bone.Rotation.ToQuaternion();
572 _contactBones[boneArrayIndex].
rigidbody.position = bone.Center.ToVector3();
573 _contactBones[boneArrayIndex].
rigidbody.rotation = bone.Rotation.ToQuaternion();
577 if (jointIndex != 0 && _contactBones[boneArrayIndex].joint !=
null) {
578 Bone prevBone = _unwarpedHandData.
Fingers[fingerIndex].Bone((Bone.BoneType)(jointIndex));
579 _contactBones[boneArrayIndex].
joint.connectedBody = _contactBones[boneArrayIndex - 1].
rigidbody;
580 _contactBones[boneArrayIndex].
joint.anchor =
Vector3.back * bone.Length / 2f;
581 _contactBones[boneArrayIndex].
joint.connectedAnchor =
Vector3.forward * prevBone.Length / 2f;
583 else if (_contactBones[boneArrayIndex].metacarpalJoint !=
null) {
584 _contactBones[boneArrayIndex].
metacarpalJoint.connectedBody = _contactBones[NUM_FINGERS * BONES_PER_FINGER].
rigidbody;
586 _contactBones[boneArrayIndex].
metacarpalJoint.connectedAnchor = _contactBones[NUM_FINGERS * BONES_PER_FINGER].transform
587 .InverseTransformPoint(bone.PrevJoint.ToVector3());
599 if (Application.isPlaying && _contactBones.Length == NUM_FINGERS * BONES_PER_FINGER + 1) {
601 inHand.SetTransform(_contactBones[NUM_FINGERS * BONES_PER_FINGER].rigidbody.position, _contactBones[NUM_FINGERS * BONES_PER_FINGER].
rigidbody.rotation);
603 for (
int fingerIndex = 0; fingerIndex < NUM_FINGERS; fingerIndex++) {
604 for (
int jointIndex = 0; jointIndex < BONES_PER_FINGER; jointIndex++) {
606 int boneArrayIndex = fingerIndex * BONES_PER_FINGER + jointIndex;
608 bone.
Center += displacement;
611 bone.
Rotation = _contactBones[boneArrayIndex].
rigidbody.rotation.ToLeapQuaternion();
625 #region Grasp Controller Implementation
627 private List<Vector3> _graspManipulatorPoints =
new List<Vector3>();
630 return _graspManipulatorPoints;
634 private void refreshGraspManipulatorPoints() {
636 for (
int i = 0; i < NUM_FINGERS; i++) {
637 for (
int boneIdx = 0; boneIdx < 2; boneIdx++) {
641 Vector3 point =
leapHand.
Fingers[i].bones[boneIdx].NextJoint.ToVector3();
643 if (_graspManipulatorPoints.Count - 1 < bufferIndex) {
644 _graspManipulatorPoints.Add(point);
647 _graspManipulatorPoints[bufferIndex] = point;
661 return _grabClassifier;
665 private Vector3[] _fingertipPositionsBuffer =
new Vector3[5];
675 Debug.LogError(
"Tried to get grasp point of InteractionHand, but it is not "
676 +
"currently grasping an object.",
this);
680 int numGraspingFingertips = 0;
682 if (numGraspingFingertips > 0) {
683 Vector3 sum = Vector3.zero;
684 for (
int i = 0; i < numGraspingFingertips; i++) {
685 sum += _fingertipPositionsBuffer[i];
687 return sum / numGraspingFingertips;
702 var tempControllers = Pool<List<InteractionController>>.Spawn();
704 tempControllers.Add(
this);
709 tempControllers.Clear();
710 Pool<List<InteractionController>>.Recycle(tempControllers);
720 base.SwapGrasp(replacement);
751 if (Application.isPlaying) {
752 base.OnDrawRuntimeGizmos(drawer);
756 if (provider ==
null) {
757 provider = Hands.Provider;
760 if (_testHand ==
null && provider !=
null) {
761 _testHand = provider.MakeTestHand(this.
isLeft);
765 _unwarpedHandData = _testHand;
769 for (
int i = 0; i < NUM_FINGERS; i++) {
Vector ElbowPosition
The position of the elbow. If not in view, the elbow position is estimated based on typical human ana...
The Bone class represents a tracked bone.
LeapQuaternion Rotation
The orientation of this Bone as a Quaternion.
Vector Direction
The normalized direction of the bone from base to tip.
float Width
The average width of the flesh around the bone.
BoneType
Enumerates the type of bones.
Vector Center
The midpoint of the bone.
Vector PrevJoint
The base of the bone, closest to the wrist. In anatomical terms, this is the proximal end of the bone...
Bone()
Constructs a default invalid Bone object.
float Length
The estimated length of the bone.
Vector NextJoint
The end of the bone, closest to the finger tip. In anatomical terms, this is the distal end of the bo...
The Finger class represents a tracked finger.
Vector TipPosition
The tip position of this Finger.
The Frame class represents a set of hand and finger tracking data detected in a single frame.
The Hand class reports the physical characteristics of a detected hand.
Vector PalmVelocity
The rate of change of the palm position.
bool IsLeft
Identifies whether this Hand is a left hand.
Arm Arm
The arm to which this hand is attached.
Vector PalmPosition
The center position of the palm.
List< Finger > Fingers
The list of Finger objects detected in this frame that are attached to this hand, given in order from...
LeapQuaternion Rotation
The rotation of the hand as a quaternion.
static void drawHoverPoint(RuntimeGizmoDrawer drawer, Vector3 pos)
InteractionManager manager
static PhysicMaterial defaultContactBoneMaterial
static void drawPrimaryHoverPoint(RuntimeGizmoDrawer drawer, Vector3 pos)
virtual void OnDrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
By default, this method will draw all of the colliders found in the contactBoneParent hierarchy,...
IInteractionBehaviour graspedObject
Gets the object the controller is currently grasping, or null if there is no such object.
bool isGraspingObject
Gets whether the controller is currently grasping an object.
override bool initContact()
Called to initialize contact colliders. See remarks for implementation requirements.
Func< Leap.Frame, Leap.Hand > handAccessorFunc
If the hand data mode for this InteractionHand is set to Custom, you must manually specify how this I...
bool[] enabledPrimaryHoverFingertips
Set slots to true to consider the corresponding finger's fingertip for primary hover checks....
override InteractionHand intHand
Returns this InteractionHand object. This property will be null if the InteractionControllerBase is n...
override void SwapGrasp(IInteractionBehaviour replacement)
Seamlessly swap the currently grasped object for a replacement object. It will behave like the hand r...
override Vector3 hoverPoint
override void onGraspedObjectForciblyReleased(IInteractionBehaviour objectToBeReleased)
Optionally override this method to perform logic just before a grasped object is released because it ...
override void unwarpColliders(Transform primaryHoverPoint, ISpaceComponent warpedSpaceElement)
Implementing this method is necessary to support curved spaces as rendered by a Leap Graphic Renderer...
override bool checkShouldGraspAtemporal(IInteractionBehaviour intObj)
Attempts to manually initiate a grasp on the argument interaction object. A grasp will only begin if ...
override List< Vector3 > graspManipulatorPoints
void FillBones(Hand inHand)
A utility function that sets a Hand object's bones based on this InteractionHand. Can be used to disp...
override GameObject contactBoneParent
override bool isTracked
Gets whether the underlying Leap hand is currently tracked.
override bool isLeft
Gets whether the underlying tracked Leap hand is a left hand.
Hand leapHand
Gets the last tracked state of the Leap hand.
override Vector3 position
Gets the last-tracked position of the underlying Leap hand.
override void onPreEnableSoftContact()
Optionally override this method to perform logic just before soft contact is enabled for this control...
override List< Transform > _primaryHoverPoints
override 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...
override void fixedUpdateGraspingState()
Called every fixed frame if grasping is enabled in the Interaction Manager.
override ContactBone[] contactBones
override Vector3 velocity
Gets the velocity of the underlying tracked Leap hand.
override bool checkShouldRelease(out IInteractionBehaviour objectToRelease)
Returns whether this controller should release an object this fixed frame, and if so,...
override void onPostDisableSoftContact()
Optionally override this method to perform logic just after soft contact is disabled for this control...
override bool isBeingMoved
Gets whether the underlying Leap hand is currently being moved in worldspace.
override Vector3 GetGraspPoint()
Returns approximately where the controller is grasping the currently-grasped InteractionBehaviour....
override void fixedUpdateController()
Called just before the InteractionController proceeds with its usual FixedUpdate.
override bool checkShouldGrasp(out IInteractionBehaviour objectToGrasp)
Returns whether this controller should grasp an object this fixed frame, and if so,...
HeuristicGrabClassifier grabClassifier
Handles logic determining whether a hand has grabbed or released an interaction object.
override Quaternion rotation
Gets the last-tracked rotation of the underlying Leap hand.
override ControllerType controllerType
Gets the controller type of this InteractionControllerBase. InteractionHands are Interaction Engine c...
LeapProvider leapProvider
If the hand data mode for this InteractionHand is set to Custom, you must also manually specify the p...
override void onObjectUnregistered(IInteractionBehaviour intObj)
This method is called by the InteractionController when it is notified by the InteractionManager that...
HandDataMode handDataMode
SingleLayer contactBoneLayer
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...
bool TryGrasp(IInteractionBehaviour intObj, Hand hand)
bool FixedUpdateClassifierRelease(out IInteractionBehaviour releasedObject)
void FixedUpdateClassifierHandState(Transform headTransform=null)
void UnregisterInteractionBehaviour(IInteractionBehaviour behaviour)
void SwapClassifierState(IInteractionBehaviour original, IInteractionBehaviour replacement)
void GetGraspingFingertipPositions(IInteractionBehaviour behaviour, Vector3[] fingertipPositionsBuffer, out int numGraspingFingertips)
void NotifyGraspForciblyReleased(IInteractionBehaviour behaviour)
bool FixedUpdateClassifierGrasp(out IInteractionBehaviour graspedObject)
Provides Frame object data to the Unity application by firing events as soon as Frame data is availab...
Action< Frame > OnFixedFrame
IInteractionBehaviour is the interface that defines all Interaction objects, specifying the minimum s...
void BeginGrasp(List< InteractionController > beganGrasping)
ControllerType
The Interaction Engine can be controlled by hands tracked by the Leap Motion Controller,...
A utility struct for ease of use when you want to wrap a piece of code in a Profiler....
The Vector struct represents a three-component mathematical vector or point such as a direction or po...