Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
d2871d28eThis file is pinned to the selected release. Download the original to open it in your design tool.
/******************************************************************************
* Copyright (C) Leap Motion, Inc. 2011-2018. *
* Leap Motion proprietary and confidential. *
* *
* Use subject to the terms of the Leap Motion SDK Agreement available at *
* https://developer.leapmotion.com/sdk_agreement, or another agreement *
* between Leap Motion and you, your company or other organization. *
******************************************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Leap.Unity.Interaction {
/// <summary>
/// This pose handler is the default implementation of IGraspedPoseHandler provided by
/// the Interaction Engine and is most likely the only implementation you will need. It
/// uses a Kabsch solve from frame to frame based on the points at which any grasping
/// controller are grasping the interaction object to determine where the object should
/// move and rotate in the grasp. Note that IGraspedPoseHandlers only determine the
/// target position and rotation of a held object; actually moving the object is the
/// domain of an IGraspedMovementHandler.
/// </summary>
public class KabschGraspedPose : IGraspedPoseHandler {
public const int NUM_FINGERS = 5;
public const int NUM_BONES = 4;
public enum SolveMethod {
SixDegreeSolve,
PivotAroundOrigin
}
private SolveMethod _solveMethod;
private InteractionBehaviour _intObj;
private KabschSolver _kabsch;
private List<Vector3> _points, _refPoints;
private Vector3 _controllerCentroid, _objectCentroid;
private float _manipulatorCount;
private Dictionary<InteractionController, PosePointCollection> _controllerToPoints;
public KabschGraspedPose(InteractionBehaviour interactionObj) {
_intObj = interactionObj;
_kabsch = new KabschSolver();
_points = new List<Vector3>(20); _refPoints = new List<Vector3>(20);
_controllerToPoints = new Dictionary<InteractionController, PosePointCollection>();
}
public void AddController(InteractionController controller) {
var newPoints = PosePointCollection.Create(_intObj.rigidbody.position,
_intObj.rigidbody.rotation);
_controllerToPoints[controller] = newPoints;
for (int i = 0; i < controller.graspManipulatorPoints.Count; i++) {
Vector3 manipulatorPosition = controller.graspManipulatorPoints[i];
newPoints.SetWorldPosition(i, manipulatorPosition);
}
}
public void RemoveController(InteractionController controller) {
var collection = _controllerToPoints[controller];
_controllerToPoints.Remove(controller);
// Return the collection to the pool so it can be re-used.
PosePointCollection.Return(collection);
}
public void ClearControllers() {
foreach (var controllerPointsPair in _controllerToPoints) {
PosePointCollection.Return(controllerPointsPair.Value);
}
_controllerToPoints.Clear();
}
public void GetGraspedPosition(out Vector3 newPosition, out Quaternion newRotation) {
_points.Clear(); _refPoints.Clear();
Vector3 bodyPosition = _intObj.rigidbody.position;
Quaternion bodyRotation = _intObj.rigidbody.rotation;
Matrix4x4 it = Matrix4x4.TRS(bodyPosition, bodyRotation, Vector3.one);
_controllerCentroid = Vector3.zero; _objectCentroid = Vector3.zero; _manipulatorCount = 0f;
foreach (var controllerPointPair in _controllerToPoints) {
InteractionController controller = controllerPointPair.Key;
PosePointCollection points = _controllerToPoints[controller];
for (int i = 0; i < controller.graspManipulatorPoints.Count; i++) {
Vector3 originalManipulatorPos = points.GetLocalPosition(i);
Vector3 currentManipulatorPos = controller.graspManipulatorPoints[i];
// Perform the solve such that the objects' positions are matched to the new
// manipulator positions.
Vector3 point1 = (it.MultiplyPoint3x4(originalManipulatorPos) - bodyPosition);
Vector3 point2 = (currentManipulatorPos - bodyPosition);
if (_intObj.isPositionLocked) {
// Only rotate the object, pivoting around its origin.
_solveMethod = SolveMethod.PivotAroundOrigin;
_objectCentroid += point1;
_controllerCentroid += point2;
_manipulatorCount += 1F;
}
else {
// Do normal Kabsch solve.
_solveMethod = SolveMethod.SixDegreeSolve;
_points.Add(point1); _refPoints.Add(point2);
}
}
}
Matrix4x4 kabschTransform = PerformSolve(bodyPosition);
newPosition = bodyPosition + kabschTransform.GetVector3();
newRotation = kabschTransform.GetQuaternion() * bodyRotation;
}
private Matrix4x4 PerformSolve(Vector3 position) {
switch (_solveMethod) {
case SolveMethod.SixDegreeSolve:
return _kabsch.SolveKabsch(_points, _refPoints);
case SolveMethod.PivotAroundOrigin:
_objectCentroid /= _manipulatorCount;
_controllerCentroid /= _manipulatorCount;
if (!_objectCentroid.Equals(_controllerCentroid)) {
return Matrix4x4.TRS(Vector3.zero, Quaternion.FromToRotation(_objectCentroid, _controllerCentroid), Vector3.one);
}
else {
return Matrix4x4.identity;
}
default:
return _kabsch.SolveKabsch(_points, _refPoints);
}
}
protected class PosePointCollection {
// Without a pool, you might end up with 2 instances per object
// With a pool, likely there will only ever be 2 instances!
private static Stack<PosePointCollection> _posePointCollectionPool = new Stack<PosePointCollection>();
private List<Vector3> _localPositions;
private Matrix4x4 _inverseTransformMatrix;
public static PosePointCollection Create(Vector3 position,
Quaternion rotation) {
PosePointCollection collection;
if (_posePointCollectionPool.Count != 0) {
collection = _posePointCollectionPool.Pop();
} else {
collection = new PosePointCollection();
}
collection.Initialize(position, rotation);
return collection;
}
public static void Return(PosePointCollection posePointCollection) {
_posePointCollectionPool.Push(posePointCollection);
}
private PosePointCollection() {
_localPositions = new List<Vector3>();
}
private void Initialize(Vector3 position, Quaternion rotation) {
_inverseTransformMatrix = Matrix4x4.TRS(position, rotation, Vector3.one).inverse;
}
public void SetWorldPosition(int index, Vector3 worldPosition) {
Vector3 localPosition = _inverseTransformMatrix.MultiplyPoint3x4(worldPosition);
if (index > _localPositions.Count) {
Debug.LogError("SetWorldPosition requires setting indices in order from 0.");
}
if (_localPositions.Count == index) {
_localPositions.Add(localPosition);
}
else {
_localPositions[index] = localPosition;
}
}
public Vector3 GetLocalPosition(int index) {
return _localPositions[index];
}
}
}
}