Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
c30bb7b85This 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 Leap.Unity.Interaction;
using Leap.Unity.Query;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Leap.Unity.Examples {
[AddComponentMenu("")]
public class TransformTool : MonoBehaviour {
[Tooltip("The scene's InteractionManager, used to get InteractionControllers and "
+ "manage handle state.")]
public InteractionManager interactionManager;
[Tooltip("The target object to be moved by this tool.")]
public Transform target;
private Vector3 _moveBuffer = Vector3.zero;
private Quaternion _rotateBuffer = Quaternion.identity;
private HashSet<TransformHandle> _transformHandles = new HashSet<TransformHandle>();
private enum ToolState { Idle, Translating, Rotating }
private ToolState _toolState = ToolState.Idle;
private HashSet<TransformHandle> _activeHandles = new HashSet<TransformHandle>();
private HashSet<TranslationAxis> _activeTranslationAxes = new HashSet<TranslationAxis>();
void Start() {
if (interactionManager == null) {
interactionManager = InteractionManager.instance;
}
foreach (var handle in GetComponentsInChildren<TransformHandle>()) {
_transformHandles.Add(handle);
}
// PhysicsCallbacks is useful for creating explicit pre-physics and post-physics
// behaviour.
PhysicsCallbacks.OnPostPhysics += onPostPhysics;
}
void Update() {
// Enable or disable handles based on hand proximity and tool state.
updateHandles();
}
#region Handle Movement / Rotation
/// <summary>
/// Transform handles call this method to notify the tool that they were used
/// to move the target object.
/// </summary>
public void NotifyHandleMovement(Vector3 deltaPosition) {
_moveBuffer += deltaPosition;
}
/// <summary>
/// Transform handles call this method to notify the tool that they were used
/// to rotate the target object.
/// </summary>
public void NotifyHandleRotation(Quaternion deltaRotation) {
_rotateBuffer = deltaRotation * _rotateBuffer;
}
private void onPostPhysics() {
// Hooked up via PhysicsCallbacks in Start(), this method will run after
// FixedUpdate and after PhysX has run. We take the opportunity to immediately
// manipulate the target object's and this object's transforms using the
// accumulated information about movement and rotation from the Transform Handles.
// Apply accumulated movement and rotation to target object.
target.transform.rotation = _rotateBuffer * target.transform.rotation;
this.transform.rotation = target.transform.rotation;
// Match this transform with the target object's (this will move child
// TransformHandles' transforms).
target.transform.position += _moveBuffer;
this.transform.position = target.transform.position;
// Explicitly sync TransformHandles' rigidbodies with their transforms,
// which moved along with this object's transform because they are children of it.
foreach (var handle in _transformHandles) {
handle.syncRigidbodyWithTransform();
}
// Reset movement and rotation buffers.
_moveBuffer = Vector3.zero;
_rotateBuffer = Quaternion.identity;
}
#endregion
#region Handle Visibility
private void updateHandles() {
switch (_toolState) {
case ToolState.Idle:
// Find the closest handle to any InteractionHand.
TransformHandle closestHandleToAnyHand = null;
float closestHandleDist = float.PositiveInfinity;
foreach (var intController in interactionManager.interactionControllers
.Query()
.Where(controller => controller.isTracked)) {
if (!intController.isPrimaryHovering) continue;
TransformHandle testHandle = intController.primaryHoveredObject
.gameObject
.GetComponent<TransformHandle>();
if (testHandle == null || !_transformHandles.Contains(testHandle)) continue;
float testDist = intController.primaryHoverDistance;
if (testDist < closestHandleDist) {
closestHandleToAnyHand = testHandle;
closestHandleDist = testDist;
}
}
// While idle, only show the closest handle to any hand, hide other handles.
foreach (var handle in _transformHandles) {
if (closestHandleToAnyHand != null && handle == closestHandleToAnyHand) {
handle.EnsureVisible();
}
else {
handle.EnsureHidden();
}
}
break;
case ToolState.Translating:
// While translating, show all translation handles except the other handle
// on the same axis, and hide rotation handles.
foreach (var handle in _transformHandles) {
if (handle is TransformTranslationHandle) {
var translateHandle = handle as TransformTranslationHandle;
if (!_activeHandles.Contains(translateHandle)
&& _activeTranslationAxes.Contains(translateHandle.axis)) {
handle.EnsureHidden();
}
else {
handle.EnsureVisible();
}
}
else {
handle.EnsureHidden();
}
}
break;
case ToolState.Rotating:
// While rotating, only show the active rotating handle.
foreach (var handle in _transformHandles) {
if (_activeHandles.Contains(handle)) {
handle.EnsureVisible();
}
else {
handle.EnsureHidden();
}
}
break;
}
}
/// <summary>
/// Called by handles when they are grasped.
/// </summary>
/// <param name="handle"></param>
public void NotifyHandleActivated(TransformHandle handle) {
switch (_toolState) {
case ToolState.Idle:
_activeHandles.Add(handle);
if (handle is TransformTranslationHandle) {
_toolState = ToolState.Translating;
_activeTranslationAxes.Add(((TransformTranslationHandle)handle).axis);
}
else {
_toolState = ToolState.Rotating;
}
break;
case ToolState.Translating:
if (handle is TransformRotationHandle) {
Debug.LogError("Error: Can't rotate a transform while it is already being "
+ "translated.");
}
else {
_activeHandles.Add(handle);
_activeTranslationAxes.Add(((TransformTranslationHandle)handle).axis);
}
break;
case ToolState.Rotating:
Debug.LogError("Error: Only one handle can be active while a transform is being "
+ "rotated.");
break;
}
}
/// <summary>
/// Called by Handles when they are released.
/// </summary>
public void NotifyHandleDeactivated(TransformHandle handle) {
if (handle is TransformTranslationHandle) {
_activeTranslationAxes.Remove(((TransformTranslationHandle)handle).axis);
}
_activeHandles.Remove(handle);
switch (_toolState) {
case ToolState.Idle:
Debug.LogWarning("Warning: Handle was deactived while Tool was already idle.");
break;
default:
if (_activeHandles.Count == 0) {
_toolState = ToolState.Idle;
}
break;
}
}
#endregion
}
}