GameObjects with this component are considered valid for grab / throw interactions by GrabAndThrowManager.cs
Inheritance: MonoBehaviour
        ThrowableObject FindNearestObject(ThrowableObject[] objects, Vector3 position)
        {
            ThrowableObject nearestObject = null;
            float           lowestDist    = Mathf.Infinity;

            foreach (var throwable in objects)
            {
                foreach (var collider in throwable.colliders)
                {
                    float dist = Vector3.Distance(position, collider.ClosestPoint(position));
                    if (dist < GRAB_RANGE && dist < lowestDist)
                    {
                        lowestDist    = dist;
                        nearestObject = throwable;
                    }
                }
            }

            return(nearestObject);
        }
        // For each given hand: Grab, Move, or Release a GameObject
        void UpdateForHand(XRNode handNode, HandData handData)
        {
            InputDevice device         = InputDevices.GetDeviceAtXRNode(handNode);
            bool        deviceHasData  = device.TryGetFeatureValue(CommonUsages.isTracked, out bool deviceIsTracked);
            bool?       isGrabbing     = TryGetIsGrabbing(device);
            bool        isDeviceTapped = isGrabbing ?? false;

            deviceHasData &= (isGrabbing != null);
            deviceHasData &= device.TryGetFeatureValue(CommonUsages.devicePosition, out Vector3 devicePosition);
            deviceHasData &= device.TryGetFeatureValue(CommonUsages.deviceRotation, out Quaternion deviceRotation);

            if (!deviceHasData || !deviceIsTracked)
            {
                return;
            }

            // Update the hand's transform for use below
            handData.transform.SetPositionAndRotation(devicePosition, deviceRotation);

            // If this hand should grab an object, look for one nearby
            if (handData.objectHeld == null && isDeviceTapped && !handData.wasTapped)
            {
                ThrowableObject nearestObject = FindNearestObject(m_throwableObjects, devicePosition);
                if (nearestObject != null)
                {
                    // Grab the object
                    handData.objectHeld = new ObjectHeldData {
                        rigidbody = nearestObject.GetComponent <Rigidbody>()
                    };
                    handData.objectHeld.rigidbody.isKinematic = true;

                    // Record the offset from the hand pose to the object origin pose
                    handData.objectHeld.positionInHandSpace = handData.transform.InverseTransformPoint(handData.objectHeld.rigidbody.position);
                    handData.objectHeld.rotationInHandSpace = Quaternion.Inverse(deviceRotation) * handData.objectHeld.rigidbody.rotation;
                }
            }

            // If this hand is holding an object, update its pose.
            if (handData.objectHeld != null)
            {
                handData.objectHeld.rigidbody.position = handData.transform.TransformPoint(handData.objectHeld.positionInHandSpace);
                handData.objectHeld.rigidbody.rotation = deviceRotation * handData.objectHeld.rotationInHandSpace;
            }

            // If this hand should release the object, derive the new object velocity using the current hand velocity and relative object pose.
            if (handData.objectHeld != null && !isDeviceTapped)
            {
                // Get the device's velocity and angular velocity
                bool hasData = device.TryGetFeatureValue(CommonUsages.deviceVelocity, out Vector3 deviceVelocity);
                hasData &= device.TryGetFeatureValue(CommonUsages.deviceAngularVelocity, out Vector3 deviceAngularVelocity);
                if (!hasData)
                {
                    deviceVelocity        = Vector3.zero;
                    deviceAngularVelocity = Vector3.zero;
                }

                // As the object is released, the hand and object share an angular velocity.
                handData.objectHeld.rigidbody.angularVelocity = deviceAngularVelocity;

                // As the object is released, we apply two velocities - the velocity of the hand relative to the world,
                // and the velocity of the object's center of mass relative to the hand.
                handData.objectHeld.rigidbody.velocity = deviceVelocity +
                                                         Vector3.Cross(handData.objectHeld.rigidbody.angularVelocity, handData.objectHeld.rigidbody.worldCenterOfMass - devicePosition);
                handData.objectHeld.rigidbody.isKinematic = false;
                handData.objectHeld = null;
            }

            handData.wasTapped = isDeviceTapped;
        }