holopy3/Assets/SteamVR/InteractionSystem/Core/Scripts/Hand.cs

1686 lines
70 KiB
C#
Raw Permalink Normal View History

2020-12-10 14:25:12 +00:00
//======= Copyright (c) Valve Corporation, All rights reserved. ===============
//
// Purpose: The hands used by the player in the vr interaction system
//
//=============================================================================
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine.Events;
using System.Threading;
namespace Valve.VR.InteractionSystem
{
//-------------------------------------------------------------------------
// Links with an appropriate SteamVR controller and facilitates
// interactions with objects in the virtual world.
//-------------------------------------------------------------------------
public class Hand : MonoBehaviour
{
// The flags used to determine how an object is attached to the hand.
[Flags]
public enum AttachmentFlags
{
SnapOnAttach = 1 << 0, // The object should snap to the position of the specified attachment point on the hand.
DetachOthers = 1 << 1, // Other objects attached to this hand will be detached.
DetachFromOtherHand = 1 << 2, // This object will be detached from the other hand.
ParentToHand = 1 << 3, // The object will be parented to the hand.
VelocityMovement = 1 << 4, // The object will attempt to move to match the position and rotation of the hand.
TurnOnKinematic = 1 << 5, // The object will not respond to external physics.
TurnOffGravity = 1 << 6, // The object will not respond to external physics.
AllowSidegrade = 1 << 7, // The object is able to switch from a pinch grab to a grip grab. Decreases likelyhood of a good throw but also decreases likelyhood of accidental drop
};
public const AttachmentFlags defaultAttachmentFlags = AttachmentFlags.ParentToHand |
AttachmentFlags.DetachOthers |
AttachmentFlags.DetachFromOtherHand |
AttachmentFlags.TurnOnKinematic |
AttachmentFlags.SnapOnAttach;
public Hand otherHand;
public SteamVR_Input_Sources handType;
public SteamVR_Behaviour_Pose trackedObject;
public SteamVR_Action_Boolean grabPinchAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("GrabPinch");
public SteamVR_Action_Boolean grabGripAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("GrabGrip");
public SteamVR_Action_Vibration hapticAction = SteamVR_Input.GetAction<SteamVR_Action_Vibration>("Haptic");
public SteamVR_Action_Boolean uiInteractAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("InteractUI");
public bool useHoverSphere = true;
public Transform hoverSphereTransform;
public float hoverSphereRadius = 0.05f;
public LayerMask hoverLayerMask = -1;
public float hoverUpdateInterval = 0.1f;
public bool useControllerHoverComponent = true;
public string controllerHoverComponent = "tip";
public float controllerHoverRadius = 0.075f;
public bool useFingerJointHover = true;
public SteamVR_Skeleton_JointIndexEnum fingerJointHover = SteamVR_Skeleton_JointIndexEnum.indexTip;
public float fingerJointHoverRadius = 0.025f;
[Tooltip("A transform on the hand to center attached objects on")]
public Transform objectAttachmentPoint;
public Camera noSteamVRFallbackCamera;
public float noSteamVRFallbackMaxDistanceNoItem = 10.0f;
public float noSteamVRFallbackMaxDistanceWithItem = 0.5f;
private float noSteamVRFallbackInteractorDistance = -1.0f;
public GameObject renderModelPrefab;
[HideInInspector]
public List<RenderModel> renderModels = new List<RenderModel>();
[HideInInspector]
public RenderModel mainRenderModel;
[HideInInspector]
public RenderModel hoverhighlightRenderModel;
public bool showDebugText = false;
public bool spewDebugText = false;
public bool showDebugInteractables = false;
public struct AttachedObject
{
public GameObject attachedObject;
public Interactable interactable;
public Rigidbody attachedRigidbody;
public CollisionDetectionMode collisionDetectionMode;
public bool attachedRigidbodyWasKinematic;
public bool attachedRigidbodyUsedGravity;
public GameObject originalParent;
public bool isParentedToHand;
public GrabTypes grabbedWithType;
public AttachmentFlags attachmentFlags;
public Vector3 initialPositionalOffset;
public Quaternion initialRotationalOffset;
public Transform attachedOffsetTransform;
public Transform handAttachmentPointTransform;
public Vector3 easeSourcePosition;
public Quaternion easeSourceRotation;
public float attachTime;
public AllowTeleportWhileAttachedToHand allowTeleportWhileAttachedToHand;
public bool HasAttachFlag(AttachmentFlags flag)
{
return (attachmentFlags & flag) == flag;
}
}
private List<AttachedObject> attachedObjects = new List<AttachedObject>();
public ReadOnlyCollection<AttachedObject> AttachedObjects
{
get { return attachedObjects.AsReadOnly(); }
}
public bool hoverLocked { get; private set; }
private Interactable _hoveringInteractable;
private TextMesh debugText;
private int prevOverlappingColliders = 0;
private const int ColliderArraySize = 32;
private Collider[] overlappingColliders;
private Player playerInstance;
private GameObject applicationLostFocusObject;
private SteamVR_Events.Action inputFocusAction;
public bool isActive
{
get
{
if (trackedObject != null)
return trackedObject.isActive;
return this.gameObject.activeInHierarchy;
}
}
public bool isPoseValid
{
get
{
return trackedObject.isValid;
}
}
//-------------------------------------------------
// The Interactable object this Hand is currently hovering over
//-------------------------------------------------
public Interactable hoveringInteractable
{
get { return _hoveringInteractable; }
set
{
if (_hoveringInteractable != value)
{
if (_hoveringInteractable != null)
{
if (spewDebugText)
HandDebugLog("HoverEnd " + _hoveringInteractable.gameObject);
_hoveringInteractable.SendMessage("OnHandHoverEnd", this, SendMessageOptions.DontRequireReceiver);
//Note: The _hoveringInteractable can change after sending the OnHandHoverEnd message so we need to check it again before broadcasting this message
if (_hoveringInteractable != null)
{
this.BroadcastMessage("OnParentHandHoverEnd", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); // let objects attached to the hand know that a hover has ended
}
}
_hoveringInteractable = value;
if (_hoveringInteractable != null)
{
if (spewDebugText)
HandDebugLog("HoverBegin " + _hoveringInteractable.gameObject);
_hoveringInteractable.SendMessage("OnHandHoverBegin", this, SendMessageOptions.DontRequireReceiver);
//Note: The _hoveringInteractable can change after sending the OnHandHoverBegin message so we need to check it again before broadcasting this message
if (_hoveringInteractable != null)
{
this.BroadcastMessage("OnParentHandHoverBegin", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); // let objects attached to the hand know that a hover has begun
}
}
}
}
}
//-------------------------------------------------
// Active GameObject attached to this Hand
//-------------------------------------------------
public GameObject currentAttachedObject
{
get
{
CleanUpAttachedObjectStack();
if (attachedObjects.Count > 0)
{
return attachedObjects[attachedObjects.Count - 1].attachedObject;
}
return null;
}
}
public AttachedObject? currentAttachedObjectInfo
{
get
{
CleanUpAttachedObjectStack();
if (attachedObjects.Count > 0)
{
return attachedObjects[attachedObjects.Count - 1];
}
return null;
}
}
public AllowTeleportWhileAttachedToHand currentAttachedTeleportManager
{
get
{
if (currentAttachedObjectInfo.HasValue)
return currentAttachedObjectInfo.Value.allowTeleportWhileAttachedToHand;
return null;
}
}
public SteamVR_Behaviour_Skeleton skeleton
{
get
{
if (mainRenderModel != null)
return mainRenderModel.GetSkeleton();
return null;
}
}
public void ShowController(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetControllerVisibility(true, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetControllerVisibility(true, permanent);
}
public void HideController(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetControllerVisibility(false, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetControllerVisibility(false, permanent);
}
public void ShowSkeleton(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetHandVisibility(true, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandVisibility(true, permanent);
}
public void HideSkeleton(bool permanent = false)
{
if (mainRenderModel != null)
mainRenderModel.SetHandVisibility(false, permanent);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandVisibility(false, permanent);
}
public bool HasSkeleton()
{
return mainRenderModel != null && mainRenderModel.GetSkeleton() != null;
}
public void Show()
{
SetVisibility(true);
}
public void Hide()
{
SetVisibility(false);
}
public void SetVisibility(bool visible)
{
if (mainRenderModel != null)
mainRenderModel.SetVisibility(visible);
}
public void SetSkeletonRangeOfMotion(EVRSkeletalMotionRange newRangeOfMotion, float blendOverSeconds = 0.1f)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].SetSkeletonRangeOfMotion(newRangeOfMotion, blendOverSeconds);
}
}
public void SetTemporarySkeletonRangeOfMotion(SkeletalMotionRangeChange temporaryRangeOfMotionChange, float blendOverSeconds = 0.1f)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].SetTemporarySkeletonRangeOfMotion(temporaryRangeOfMotionChange, blendOverSeconds);
}
}
public void ResetTemporarySkeletonRangeOfMotion(float blendOverSeconds = 0.1f)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].ResetTemporarySkeletonRangeOfMotion(blendOverSeconds);
}
}
public void SetAnimationState(int stateValue)
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].SetAnimationState(stateValue);
}
}
public void StopAnimation()
{
for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
{
renderModels[renderModelIndex].StopAnimation();
}
}
//-------------------------------------------------
// Attach a GameObject to this GameObject
//
// objectToAttach - The GameObject to attach
// flags - The flags to use for attaching the object
// attachmentPoint - Name of the GameObject in the hierarchy of this Hand which should act as the attachment point for this GameObject
//-------------------------------------------------
public void AttachObject(GameObject objectToAttach, GrabTypes grabbedWithType, AttachmentFlags flags = defaultAttachmentFlags, Transform attachmentOffset = null)
{
AttachedObject attachedObject = new AttachedObject();
attachedObject.attachmentFlags = flags;
attachedObject.attachedOffsetTransform = attachmentOffset;
attachedObject.attachTime = Time.time;
if (flags == 0)
{
flags = defaultAttachmentFlags;
}
//Make sure top object on stack is non-null
CleanUpAttachedObjectStack();
//Detach the object if it is already attached so that it can get re-attached at the top of the stack
if (ObjectIsAttached(objectToAttach))
DetachObject(objectToAttach);
//Detach from the other hand if requested
if (attachedObject.HasAttachFlag(AttachmentFlags.DetachFromOtherHand))
{
if (otherHand != null)
otherHand.DetachObject(objectToAttach);
}
if (attachedObject.HasAttachFlag(AttachmentFlags.DetachOthers))
{
//Detach all the objects from the stack
while (attachedObjects.Count > 0)
{
DetachObject(attachedObjects[0].attachedObject);
}
}
if (currentAttachedObject)
{
currentAttachedObject.SendMessage("OnHandFocusLost", this, SendMessageOptions.DontRequireReceiver);
}
attachedObject.attachedObject = objectToAttach;
attachedObject.interactable = objectToAttach.GetComponent<Interactable>();
attachedObject.allowTeleportWhileAttachedToHand = objectToAttach.GetComponent<AllowTeleportWhileAttachedToHand>();
attachedObject.handAttachmentPointTransform = this.transform;
if (attachedObject.interactable != null)
{
if (attachedObject.interactable.attachEaseIn)
{
attachedObject.easeSourcePosition = attachedObject.attachedObject.transform.position;
attachedObject.easeSourceRotation = attachedObject.attachedObject.transform.rotation;
attachedObject.interactable.snapAttachEaseInCompleted = false;
}
if (attachedObject.interactable.useHandObjectAttachmentPoint)
attachedObject.handAttachmentPointTransform = objectAttachmentPoint;
if (attachedObject.interactable.hideHandOnAttach)
Hide();
if (attachedObject.interactable.hideSkeletonOnAttach && mainRenderModel != null && mainRenderModel.displayHandByDefault)
HideSkeleton();
if (attachedObject.interactable.hideControllerOnAttach && mainRenderModel != null && mainRenderModel.displayControllerByDefault)
HideController();
if (attachedObject.interactable.handAnimationOnPickup != 0)
SetAnimationState(attachedObject.interactable.handAnimationOnPickup);
if (attachedObject.interactable.setRangeOfMotionOnPickup != SkeletalMotionRangeChange.None)
SetTemporarySkeletonRangeOfMotion(attachedObject.interactable.setRangeOfMotionOnPickup);
}
attachedObject.originalParent = objectToAttach.transform.parent != null ? objectToAttach.transform.parent.gameObject : null;
attachedObject.attachedRigidbody = objectToAttach.GetComponent<Rigidbody>();
if (attachedObject.attachedRigidbody != null)
{
if (attachedObject.interactable.attachedToHand != null) //already attached to another hand
{
//if it was attached to another hand, get the flags from that hand
for (int attachedIndex = 0; attachedIndex < attachedObject.interactable.attachedToHand.attachedObjects.Count; attachedIndex++)
{
AttachedObject attachedObjectInList = attachedObject.interactable.attachedToHand.attachedObjects[attachedIndex];
if (attachedObjectInList.interactable == attachedObject.interactable)
{
attachedObject.attachedRigidbodyWasKinematic = attachedObjectInList.attachedRigidbodyWasKinematic;
attachedObject.attachedRigidbodyUsedGravity = attachedObjectInList.attachedRigidbodyUsedGravity;
attachedObject.originalParent = attachedObjectInList.originalParent;
}
}
}
else
{
attachedObject.attachedRigidbodyWasKinematic = attachedObject.attachedRigidbody.isKinematic;
attachedObject.attachedRigidbodyUsedGravity = attachedObject.attachedRigidbody.useGravity;
}
}
attachedObject.grabbedWithType = grabbedWithType;
if (attachedObject.HasAttachFlag(AttachmentFlags.ParentToHand))
{
//Parent the object to the hand
objectToAttach.transform.parent = this.transform;
attachedObject.isParentedToHand = true;
}
else
{
attachedObject.isParentedToHand = false;
}
if (attachedObject.HasAttachFlag(AttachmentFlags.SnapOnAttach))
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
SteamVR_Skeleton_PoseSnapshot pose = attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton);
//snap the object to the center of the attach point
objectToAttach.transform.position = this.transform.TransformPoint(pose.position);
objectToAttach.transform.rotation = this.transform.rotation * pose.rotation;
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
}
else
{
if (attachmentOffset != null)
{
//offset the object from the hand by the positional and rotational difference between the offset transform and the attached object
Quaternion rotDiff = Quaternion.Inverse(attachmentOffset.transform.rotation) * objectToAttach.transform.rotation;
objectToAttach.transform.rotation = attachedObject.handAttachmentPointTransform.rotation * rotDiff;
Vector3 posDiff = objectToAttach.transform.position - attachmentOffset.transform.position;
objectToAttach.transform.position = attachedObject.handAttachmentPointTransform.position + posDiff;
}
else
{
//snap the object to the center of the attach point
objectToAttach.transform.rotation = attachedObject.handAttachmentPointTransform.rotation;
objectToAttach.transform.position = attachedObject.handAttachmentPointTransform.position;
}
Transform followPoint = objectToAttach.transform;
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(followPoint.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * followPoint.rotation;
}
}
else
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
}
else
{
if (attachmentOffset != null)
{
//get the initial positional and rotational offsets between the hand and the offset transform
Quaternion rotDiff = Quaternion.Inverse(attachmentOffset.transform.rotation) * objectToAttach.transform.rotation;
Quaternion targetRotation = attachedObject.handAttachmentPointTransform.rotation * rotDiff;
Quaternion rotationPositionBy = targetRotation * Quaternion.Inverse(objectToAttach.transform.rotation);
Vector3 posDiff = (rotationPositionBy * objectToAttach.transform.position) - (rotationPositionBy * attachmentOffset.transform.position);
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(attachedObject.handAttachmentPointTransform.position + posDiff);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * (attachedObject.handAttachmentPointTransform.rotation * rotDiff);
}
else
{
attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
}
}
}
if (attachedObject.HasAttachFlag(AttachmentFlags.TurnOnKinematic))
{
if (attachedObject.attachedRigidbody != null)
{
attachedObject.collisionDetectionMode = attachedObject.attachedRigidbody.collisionDetectionMode;
if (attachedObject.collisionDetectionMode == CollisionDetectionMode.Continuous)
attachedObject.attachedRigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
attachedObject.attachedRigidbody.isKinematic = true;
}
}
if (attachedObject.HasAttachFlag(AttachmentFlags.TurnOffGravity))
{
if (attachedObject.attachedRigidbody != null)
{
attachedObject.attachedRigidbody.useGravity = false;
}
}
if (attachedObject.interactable != null && attachedObject.interactable.attachEaseIn)
{
attachedObject.attachedObject.transform.position = attachedObject.easeSourcePosition;
attachedObject.attachedObject.transform.rotation = attachedObject.easeSourceRotation;
}
attachedObjects.Add(attachedObject);
UpdateHovering();
if (spewDebugText)
HandDebugLog("AttachObject " + objectToAttach);
objectToAttach.SendMessage("OnAttachedToHand", this, SendMessageOptions.DontRequireReceiver);
}
public bool ObjectIsAttached(GameObject go)
{
for (int attachedIndex = 0; attachedIndex < attachedObjects.Count; attachedIndex++)
{
if (attachedObjects[attachedIndex].attachedObject == go)
return true;
}
return false;
}
public void ForceHoverUnlock()
{
hoverLocked = false;
}
//-------------------------------------------------
// Detach this GameObject from the attached object stack of this Hand
//
// objectToDetach - The GameObject to detach from this Hand
//-------------------------------------------------
public void DetachObject(GameObject objectToDetach, bool restoreOriginalParent = true)
{
int index = attachedObjects.FindIndex(l => l.attachedObject == objectToDetach);
if (index != -1)
{
if (spewDebugText)
HandDebugLog("DetachObject " + objectToDetach);
GameObject prevTopObject = currentAttachedObject;
if (attachedObjects[index].interactable != null)
{
if (attachedObjects[index].interactable.hideHandOnAttach)
Show();
if (attachedObjects[index].interactable.hideSkeletonOnAttach && mainRenderModel != null && mainRenderModel.displayHandByDefault)
ShowSkeleton();
if (attachedObjects[index].interactable.hideControllerOnAttach && mainRenderModel != null && mainRenderModel.displayControllerByDefault)
ShowController();
if (attachedObjects[index].interactable.handAnimationOnPickup != 0)
StopAnimation();
if (attachedObjects[index].interactable.setRangeOfMotionOnPickup != SkeletalMotionRangeChange.None)
ResetTemporarySkeletonRangeOfMotion();
}
Transform parentTransform = null;
if (attachedObjects[index].isParentedToHand)
{
if (restoreOriginalParent && (attachedObjects[index].originalParent != null))
{
parentTransform = attachedObjects[index].originalParent.transform;
}
if (attachedObjects[index].attachedObject != null)
{
attachedObjects[index].attachedObject.transform.parent = parentTransform;
}
}
if (attachedObjects[index].HasAttachFlag(AttachmentFlags.TurnOnKinematic))
{
if (attachedObjects[index].attachedRigidbody != null)
{
attachedObjects[index].attachedRigidbody.isKinematic = attachedObjects[index].attachedRigidbodyWasKinematic;
attachedObjects[index].attachedRigidbody.collisionDetectionMode = attachedObjects[index].collisionDetectionMode;
}
}
if (attachedObjects[index].HasAttachFlag(AttachmentFlags.TurnOffGravity))
{
if (attachedObjects[index].attachedObject != null)
{
if (attachedObjects[index].attachedRigidbody != null)
attachedObjects[index].attachedRigidbody.useGravity = attachedObjects[index].attachedRigidbodyUsedGravity;
}
}
if (attachedObjects[index].interactable != null && attachedObjects[index].interactable.handFollowTransform && HasSkeleton())
{
skeleton.transform.localPosition = Vector3.zero;
skeleton.transform.localRotation = Quaternion.identity;
}
if (attachedObjects[index].attachedObject != null)
{
if (attachedObjects[index].interactable == null || (attachedObjects[index].interactable != null && attachedObjects[index].interactable.isDestroying == false))
attachedObjects[index].attachedObject.SetActive(true);
attachedObjects[index].attachedObject.SendMessage("OnDetachedFromHand", this, SendMessageOptions.DontRequireReceiver);
}
attachedObjects.RemoveAt(index);
CleanUpAttachedObjectStack();
GameObject newTopObject = currentAttachedObject;
hoverLocked = false;
//Give focus to the top most object on the stack if it changed
if (newTopObject != null && newTopObject != prevTopObject)
{
newTopObject.SetActive(true);
newTopObject.SendMessage("OnHandFocusAcquired", this, SendMessageOptions.DontRequireReceiver);
}
}
CleanUpAttachedObjectStack();
if (mainRenderModel != null)
mainRenderModel.MatchHandToTransform(mainRenderModel.transform);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.MatchHandToTransform(hoverhighlightRenderModel.transform);
}
//-------------------------------------------------
// Get the world velocity of the VR Hand.
//-------------------------------------------------
public Vector3 GetTrackedObjectVelocity(float timeOffset = 0)
{
if (trackedObject == null)
{
Vector3 velocityTarget, angularTarget;
GetUpdatedAttachedVelocities(currentAttachedObjectInfo.Value, out velocityTarget, out angularTarget);
return velocityTarget;
}
if (isActive)
{
if (timeOffset == 0)
return Player.instance.trackingOriginTransform.TransformVector(trackedObject.GetVelocity());
else
{
Vector3 velocity;
Vector3 angularVelocity;
trackedObject.GetVelocitiesAtTimeOffset(timeOffset, out velocity, out angularVelocity);
return Player.instance.trackingOriginTransform.TransformVector(velocity);
}
}
return Vector3.zero;
}
//-------------------------------------------------
// Get the world space angular velocity of the VR Hand.
//-------------------------------------------------
public Vector3 GetTrackedObjectAngularVelocity(float timeOffset = 0)
{
if (trackedObject == null)
{
Vector3 velocityTarget, angularTarget;
GetUpdatedAttachedVelocities(currentAttachedObjectInfo.Value, out velocityTarget, out angularTarget);
return angularTarget;
}
if (isActive)
{
if (timeOffset == 0)
return Player.instance.trackingOriginTransform.TransformDirection(trackedObject.GetAngularVelocity());
else
{
Vector3 velocity;
Vector3 angularVelocity;
trackedObject.GetVelocitiesAtTimeOffset(timeOffset, out velocity, out angularVelocity);
return Player.instance.trackingOriginTransform.TransformDirection(angularVelocity);
}
}
return Vector3.zero;
}
public void GetEstimatedPeakVelocities(out Vector3 velocity, out Vector3 angularVelocity)
{
trackedObject.GetEstimatedPeakVelocities(out velocity, out angularVelocity);
velocity = Player.instance.trackingOriginTransform.TransformVector(velocity);
angularVelocity = Player.instance.trackingOriginTransform.TransformDirection(angularVelocity);
}
//-------------------------------------------------
private void CleanUpAttachedObjectStack()
{
attachedObjects.RemoveAll(l => l.attachedObject == null);
}
//-------------------------------------------------
protected virtual void Awake()
{
inputFocusAction = SteamVR_Events.InputFocusAction(OnInputFocus);
if (hoverSphereTransform == null)
hoverSphereTransform = this.transform;
if (objectAttachmentPoint == null)
objectAttachmentPoint = this.transform;
applicationLostFocusObject = new GameObject("_application_lost_focus");
applicationLostFocusObject.transform.parent = transform;
applicationLostFocusObject.SetActive(false);
if (trackedObject == null)
{
trackedObject = this.gameObject.GetComponent<SteamVR_Behaviour_Pose>();
if (trackedObject != null)
trackedObject.onTransformUpdatedEvent += OnTransformUpdated;
}
}
protected virtual void OnDestroy()
{
if (trackedObject != null)
{
trackedObject.onTransformUpdatedEvent -= OnTransformUpdated;
}
}
protected virtual void OnTransformUpdated(SteamVR_Behaviour_Pose updatedPose, SteamVR_Input_Sources updatedSource)
{
HandFollowUpdate();
}
//-------------------------------------------------
protected virtual IEnumerator Start()
{
// save off player instance
playerInstance = Player.instance;
if (!playerInstance)
{
Debug.LogError("<b>[SteamVR Interaction]</b> No player instance found in Hand Start()", this);
}
if (this.gameObject.layer == 0)
Debug.LogWarning("<b>[SteamVR Interaction]</b> Hand is on default layer. This puts unnecessary strain on hover checks as it is always true for hand colliders (which are then ignored).", this);
else
hoverLayerMask &= ~(1 << this.gameObject.layer); //ignore self for hovering
// allocate array for colliders
overlappingColliders = new Collider[ColliderArraySize];
// We are a "no SteamVR fallback hand" if we have this camera set
// we'll use the right mouse to look around and left mouse to interact
// - don't need to find the device
if (noSteamVRFallbackCamera)
{
yield break;
}
//Debug.Log( "<b>[SteamVR Interaction]</b> Hand - initializing connection routine" );
while (true)
{
if (isPoseValid)
{
InitController();
break;
}
yield return null;
}
}
//-------------------------------------------------
protected virtual void UpdateHovering()
{
if ((noSteamVRFallbackCamera == null) && (isActive == false))
{
return;
}
if (hoverLocked)
return;
if (applicationLostFocusObject.activeSelf)
return;
float closestDistance = float.MaxValue;
Interactable closestInteractable = null;
if (useHoverSphere)
{
float scaledHoverRadius = hoverSphereRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(hoverSphereTransform));
CheckHoveringForTransform(hoverSphereTransform.position, scaledHoverRadius, ref closestDistance, ref closestInteractable, Color.green);
}
if (useControllerHoverComponent && mainRenderModel != null && mainRenderModel.IsControllerVisibile())
{
float scaledHoverRadius = controllerHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
CheckHoveringForTransform(mainRenderModel.GetControllerPosition(controllerHoverComponent), scaledHoverRadius / 2f, ref closestDistance, ref closestInteractable, Color.blue);
}
if (useFingerJointHover && mainRenderModel != null && mainRenderModel.IsHandVisibile())
{
float scaledHoverRadius = fingerJointHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
CheckHoveringForTransform(mainRenderModel.GetBonePosition((int)fingerJointHover), scaledHoverRadius / 2f, ref closestDistance, ref closestInteractable, Color.yellow);
}
// Hover on this one
hoveringInteractable = closestInteractable;
}
protected virtual bool CheckHoveringForTransform(Vector3 hoverPosition, float hoverRadius, ref float closestDistance, ref Interactable closestInteractable, Color debugColor)
{
bool foundCloser = false;
// null out old vals
for (int i = 0; i < overlappingColliders.Length; ++i)
{
overlappingColliders[i] = null;
}
int numColliding = Physics.OverlapSphereNonAlloc(hoverPosition, hoverRadius, overlappingColliders, hoverLayerMask.value);
if (numColliding >= ColliderArraySize)
Debug.LogWarning("<b>[SteamVR Interaction]</b> This hand is overlapping the max number of colliders: " + ColliderArraySize + ". Some collisions may be missed. Increase ColliderArraySize on Hand.cs");
// DebugVar
int iActualColliderCount = 0;
// Pick the closest hovering
for (int colliderIndex = 0; colliderIndex < overlappingColliders.Length; colliderIndex++)
{
Collider collider = overlappingColliders[colliderIndex];
if (collider == null)
continue;
Interactable contacting = collider.GetComponentInParent<Interactable>();
// Yeah, it's null, skip
if (contacting == null)
continue;
// Ignore this collider for hovering
IgnoreHovering ignore = collider.GetComponent<IgnoreHovering>();
if (ignore != null)
{
if (ignore.onlyIgnoreHand == null || ignore.onlyIgnoreHand == this)
{
continue;
}
}
// Can't hover over the object if it's attached
bool hoveringOverAttached = false;
for (int attachedIndex = 0; attachedIndex < attachedObjects.Count; attachedIndex++)
{
if (attachedObjects[attachedIndex].attachedObject == contacting.gameObject)
{
hoveringOverAttached = true;
break;
}
}
if (hoveringOverAttached)
continue;
// Best candidate so far...
float distance = Vector3.Distance(contacting.transform.position, hoverPosition);
//float distance = Vector3.Distance(collider.bounds.center, hoverPosition);
bool lowerPriority = false;
if (closestInteractable != null)
{ // compare to closest interactable to check priority
lowerPriority = contacting.hoverPriority < closestInteractable.hoverPriority;
}
bool isCloser = (distance < closestDistance);
if (isCloser && !lowerPriority)
{
closestDistance = distance;
closestInteractable = contacting;
foundCloser = true;
}
iActualColliderCount++;
}
if (showDebugInteractables && foundCloser)
{
Debug.DrawLine(hoverPosition, closestInteractable.transform.position, debugColor, .05f, false);
}
if (iActualColliderCount > 0 && iActualColliderCount != prevOverlappingColliders)
{
prevOverlappingColliders = iActualColliderCount;
if (spewDebugText)
HandDebugLog("Found " + iActualColliderCount + " overlapping colliders.");
}
return foundCloser;
}
//-------------------------------------------------
protected virtual void UpdateNoSteamVRFallback()
{
if (noSteamVRFallbackCamera)
{
Ray ray = noSteamVRFallbackCamera.ScreenPointToRay(Input.mousePosition);
if (attachedObjects.Count > 0)
{
// Holding down the mouse:
// move around a fixed distance from the camera
transform.position = ray.origin + noSteamVRFallbackInteractorDistance * ray.direction;
}
else
{
// Not holding down the mouse:
// cast out a ray to see what we should mouse over
// Don't want to hit the hand and anything underneath it
// So move it back behind the camera when we do the raycast
Vector3 oldPosition = transform.position;
transform.position = noSteamVRFallbackCamera.transform.forward * (-1000.0f);
RaycastHit raycastHit;
if (Physics.Raycast(ray, out raycastHit, noSteamVRFallbackMaxDistanceNoItem))
{
transform.position = raycastHit.point;
// Remember this distance in case we click and drag the mouse
noSteamVRFallbackInteractorDistance = Mathf.Min(noSteamVRFallbackMaxDistanceNoItem, raycastHit.distance);
}
else if (noSteamVRFallbackInteractorDistance > 0.0f)
{
// Move it around at the distance we last had a hit
transform.position = ray.origin + Mathf.Min(noSteamVRFallbackMaxDistanceNoItem, noSteamVRFallbackInteractorDistance) * ray.direction;
}
else
{
// Didn't hit, just leave it where it was
transform.position = oldPosition;
}
}
}
}
//-------------------------------------------------
private void UpdateDebugText()
{
if (showDebugText)
{
if (debugText == null)
{
debugText = new GameObject("_debug_text").AddComponent<TextMesh>();
debugText.fontSize = 120;
debugText.characterSize = 0.001f;
debugText.transform.parent = transform;
debugText.transform.localRotation = Quaternion.Euler(90.0f, 0.0f, 0.0f);
}
if (handType == SteamVR_Input_Sources.RightHand)
{
debugText.transform.localPosition = new Vector3(-0.05f, 0.0f, 0.0f);
debugText.alignment = TextAlignment.Right;
debugText.anchor = TextAnchor.UpperRight;
}
else
{
debugText.transform.localPosition = new Vector3(0.05f, 0.0f, 0.0f);
debugText.alignment = TextAlignment.Left;
debugText.anchor = TextAnchor.UpperLeft;
}
debugText.text = string.Format(
"Hovering: {0}\n" +
"Hover Lock: {1}\n" +
"Attached: {2}\n" +
"Total Attached: {3}\n" +
"Type: {4}\n",
(hoveringInteractable ? hoveringInteractable.gameObject.name : "null"),
hoverLocked,
(currentAttachedObject ? currentAttachedObject.name : "null"),
attachedObjects.Count,
handType.ToString());
}
else
{
if (debugText != null)
{
Destroy(debugText.gameObject);
}
}
}
//-------------------------------------------------
protected virtual void OnEnable()
{
inputFocusAction.enabled = true;
// Stagger updates between hands
float hoverUpdateBegin = ((otherHand != null) && (otherHand.GetInstanceID() < GetInstanceID())) ? (0.5f * hoverUpdateInterval) : (0.0f);
InvokeRepeating("UpdateHovering", hoverUpdateBegin, hoverUpdateInterval);
InvokeRepeating("UpdateDebugText", hoverUpdateBegin, hoverUpdateInterval);
}
//-------------------------------------------------
protected virtual void OnDisable()
{
inputFocusAction.enabled = false;
CancelInvoke();
}
//-------------------------------------------------
protected virtual void Update()
{
UpdateNoSteamVRFallback();
GameObject attachedObject = currentAttachedObject;
if (attachedObject != null)
{
attachedObject.SendMessage("HandAttachedUpdate", this, SendMessageOptions.DontRequireReceiver);
}
if (hoveringInteractable)
{
hoveringInteractable.SendMessage("HandHoverUpdate", this, SendMessageOptions.DontRequireReceiver);
}
}
/// <summary>
/// Returns true when the hand is currently hovering over the interactable passed in
/// </summary>
public bool IsStillHovering(Interactable interactable)
{
return hoveringInteractable == interactable;
}
protected virtual void HandFollowUpdate()
{
GameObject attachedObject = currentAttachedObject;
if (attachedObject != null)
{
if (currentAttachedObjectInfo.Value.interactable != null)
{
SteamVR_Skeleton_PoseSnapshot pose = null;
if (currentAttachedObjectInfo.Value.interactable.skeletonPoser != null && HasSkeleton())
{
pose = currentAttachedObjectInfo.Value.interactable.skeletonPoser.GetBlendedPose(skeleton);
}
if (currentAttachedObjectInfo.Value.interactable.handFollowTransform)
{
Quaternion targetHandRotation;
Vector3 targetHandPosition;
if (pose == null)
{
Quaternion offset = Quaternion.Inverse(this.transform.rotation) * currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation;
targetHandRotation = currentAttachedObjectInfo.Value.interactable.transform.rotation * Quaternion.Inverse(offset);
Vector3 worldOffset = (this.transform.position - currentAttachedObjectInfo.Value.handAttachmentPointTransform.position);
Quaternion rotationDiff = mainRenderModel.GetHandRotation() * Quaternion.Inverse(this.transform.rotation);
Vector3 localOffset = rotationDiff * worldOffset;
targetHandPosition = currentAttachedObjectInfo.Value.interactable.transform.position + localOffset;
}
else
{
Transform objectT = currentAttachedObjectInfo.Value.attachedObject.transform;
Vector3 oldItemPos = objectT.position;
Quaternion oldItemRot = objectT.transform.rotation;
objectT.position = TargetItemPosition(currentAttachedObjectInfo.Value);
objectT.rotation = TargetItemRotation(currentAttachedObjectInfo.Value);
Vector3 localSkelePos = objectT.InverseTransformPoint(transform.position);
Quaternion localSkeleRot = Quaternion.Inverse(objectT.rotation) * transform.rotation;
objectT.position = oldItemPos;
objectT.rotation = oldItemRot;
targetHandPosition = objectT.TransformPoint(localSkelePos);
targetHandRotation = objectT.rotation * localSkeleRot;
}
if (mainRenderModel != null)
mainRenderModel.SetHandRotation(targetHandRotation);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandRotation(targetHandRotation);
if (mainRenderModel != null)
mainRenderModel.SetHandPosition(targetHandPosition);
if (hoverhighlightRenderModel != null)
hoverhighlightRenderModel.SetHandPosition(targetHandPosition);
}
}
}
}
protected virtual void FixedUpdate()
{
if (currentAttachedObject != null)
{
AttachedObject attachedInfo = currentAttachedObjectInfo.Value;
if (attachedInfo.attachedObject != null)
{
if (attachedInfo.HasAttachFlag(AttachmentFlags.VelocityMovement))
{
if (attachedInfo.interactable.attachEaseIn == false || attachedInfo.interactable.snapAttachEaseInCompleted)
UpdateAttachedVelocity(attachedInfo);
/*if (attachedInfo.interactable.handFollowTransformPosition)
{
skeleton.transform.position = TargetSkeletonPosition(attachedInfo);
skeleton.transform.rotation = attachedInfo.attachedObject.transform.rotation * attachedInfo.skeletonLockRotation;
}*/
}
else
{
if (attachedInfo.HasAttachFlag(AttachmentFlags.ParentToHand))
{
attachedInfo.attachedObject.transform.position = TargetItemPosition(attachedInfo);
attachedInfo.attachedObject.transform.rotation = TargetItemRotation(attachedInfo);
}
}
if (attachedInfo.interactable.attachEaseIn)
{
float t = Util.RemapNumberClamped(Time.time, attachedInfo.attachTime, attachedInfo.attachTime + attachedInfo.interactable.snapAttachEaseInTime, 0.0f, 1.0f);
if (t < 1.0f)
{
if (attachedInfo.HasAttachFlag(AttachmentFlags.VelocityMovement))
{
attachedInfo.attachedRigidbody.velocity = Vector3.zero;
attachedInfo.attachedRigidbody.angularVelocity = Vector3.zero;
}
t = attachedInfo.interactable.snapAttachEaseInCurve.Evaluate(t);
attachedInfo.attachedObject.transform.position = Vector3.Lerp(attachedInfo.easeSourcePosition, TargetItemPosition(attachedInfo), t);
attachedInfo.attachedObject.transform.rotation = Quaternion.Lerp(attachedInfo.easeSourceRotation, TargetItemRotation(attachedInfo), t);
}
else if (!attachedInfo.interactable.snapAttachEaseInCompleted)
{
attachedInfo.interactable.gameObject.SendMessage("OnThrowableAttachEaseInCompleted", this, SendMessageOptions.DontRequireReceiver);
attachedInfo.interactable.snapAttachEaseInCompleted = true;
}
}
}
}
}
protected const float MaxVelocityChange = 10f;
protected const float VelocityMagic = 6000f;
protected const float AngularVelocityMagic = 50f;
protected const float MaxAngularVelocityChange = 20f;
protected void UpdateAttachedVelocity(AttachedObject attachedObjectInfo)
{
Vector3 velocityTarget, angularTarget;
bool success = GetUpdatedAttachedVelocities(attachedObjectInfo, out velocityTarget, out angularTarget);
if (success)
{
float scale = SteamVR_Utils.GetLossyScale(currentAttachedObjectInfo.Value.handAttachmentPointTransform);
float maxAngularVelocityChange = MaxAngularVelocityChange * scale;
float maxVelocityChange = MaxVelocityChange * scale;
attachedObjectInfo.attachedRigidbody.velocity = Vector3.MoveTowards(attachedObjectInfo.attachedRigidbody.velocity, velocityTarget, maxVelocityChange);
attachedObjectInfo.attachedRigidbody.angularVelocity = Vector3.MoveTowards(attachedObjectInfo.attachedRigidbody.angularVelocity, angularTarget, maxAngularVelocityChange);
}
}
/// <summary>
/// Snap an attached object to its target position and rotation. Good for error correction.
/// </summary>
public void ResetAttachedTransform(AttachedObject attachedObject)
{
attachedObject.attachedObject.transform.position = TargetItemPosition(attachedObject);
attachedObject.attachedObject.transform.rotation = TargetItemRotation(attachedObject);
}
protected Vector3 TargetItemPosition(AttachedObject attachedObject)
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
Vector3 tp = attachedObject.handAttachmentPointTransform.InverseTransformPoint(transform.TransformPoint(attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton).position));
//tp.x *= -1;
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.TransformPoint(tp);
}
else
{
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.TransformPoint(attachedObject.initialPositionalOffset);
}
}
protected Quaternion TargetItemRotation(AttachedObject attachedObject)
{
if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
{
Quaternion tr = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * (transform.rotation * attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton).rotation);
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation * tr;
}
else
{
return currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation * attachedObject.initialRotationalOffset;
}
}
protected bool GetUpdatedAttachedVelocities(AttachedObject attachedObjectInfo, out Vector3 velocityTarget, out Vector3 angularTarget)
{
bool realNumbers = false;
float velocityMagic = VelocityMagic;
float angularVelocityMagic = AngularVelocityMagic;
Vector3 targetItemPosition = TargetItemPosition(attachedObjectInfo);
Vector3 positionDelta = (targetItemPosition - attachedObjectInfo.attachedRigidbody.position);
velocityTarget = (positionDelta * velocityMagic * Time.deltaTime);
if (float.IsNaN(velocityTarget.x) == false && float.IsInfinity(velocityTarget.x) == false)
{
if (noSteamVRFallbackCamera)
velocityTarget /= 10; //hacky fix for fallback
realNumbers = true;
}
else
velocityTarget = Vector3.zero;
Quaternion targetItemRotation = TargetItemRotation(attachedObjectInfo);
Quaternion rotationDelta = targetItemRotation * Quaternion.Inverse(attachedObjectInfo.attachedObject.transform.rotation);
float angle;
Vector3 axis;
rotationDelta.ToAngleAxis(out angle, out axis);
if (angle > 180)
angle -= 360;
if (angle != 0 && float.IsNaN(axis.x) == false && float.IsInfinity(axis.x) == false)
{
angularTarget = angle * axis * angularVelocityMagic * Time.deltaTime;
if (noSteamVRFallbackCamera)
angularTarget /= 10; //hacky fix for fallback
realNumbers &= true;
}
else
angularTarget = Vector3.zero;
return realNumbers;
}
//-------------------------------------------------
protected virtual void OnInputFocus(bool hasFocus)
{
if (hasFocus)
{
DetachObject(applicationLostFocusObject, true);
applicationLostFocusObject.SetActive(false);
UpdateHovering();
BroadcastMessage("OnParentHandInputFocusAcquired", SendMessageOptions.DontRequireReceiver);
}
else
{
applicationLostFocusObject.SetActive(true);
AttachObject(applicationLostFocusObject, GrabTypes.Scripted, AttachmentFlags.ParentToHand);
BroadcastMessage("OnParentHandInputFocusLost", SendMessageOptions.DontRequireReceiver);
}
}
//-------------------------------------------------
protected virtual void OnDrawGizmos()
{
if (useHoverSphere && hoverSphereTransform != null)
{
Gizmos.color = Color.green;
float scaledHoverRadius = hoverSphereRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(hoverSphereTransform));
Gizmos.DrawWireSphere(hoverSphereTransform.position, scaledHoverRadius / 2);
}
if (useControllerHoverComponent && mainRenderModel != null && mainRenderModel.IsControllerVisibile())
{
Gizmos.color = Color.blue;
float scaledHoverRadius = controllerHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
Gizmos.DrawWireSphere(mainRenderModel.GetControllerPosition(controllerHoverComponent), scaledHoverRadius / 2);
}
if (useFingerJointHover && mainRenderModel != null && mainRenderModel.IsHandVisibile())
{
Gizmos.color = Color.yellow;
float scaledHoverRadius = fingerJointHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
Gizmos.DrawWireSphere(mainRenderModel.GetBonePosition((int)fingerJointHover), scaledHoverRadius / 2);
}
}
//-------------------------------------------------
private void HandDebugLog(string msg)
{
if (spewDebugText)
{
Debug.Log("<b>[SteamVR Interaction]</b> Hand (" + this.name + "): " + msg);
}
}
//-------------------------------------------------
// Continue to hover over this object indefinitely, whether or not the Hand moves out of its interaction trigger volume.
//
// interactable - The Interactable to hover over indefinitely.
//-------------------------------------------------
public void HoverLock(Interactable interactable)
{
if (spewDebugText)
HandDebugLog("HoverLock " + interactable);
hoverLocked = true;
hoveringInteractable = interactable;
}
//-------------------------------------------------
// Stop hovering over this object indefinitely.
//
// interactable - The hover-locked Interactable to stop hovering over indefinitely.
//-------------------------------------------------
public void HoverUnlock(Interactable interactable)
{
if (spewDebugText)
HandDebugLog("HoverUnlock " + interactable);
if (hoveringInteractable == interactable)
{
hoverLocked = false;
}
}
public void TriggerHapticPulse(ushort microSecondsDuration)
{
float seconds = (float)microSecondsDuration / 1000000f;
hapticAction.Execute(0, seconds, 1f / seconds, 1, handType);
}
public void TriggerHapticPulse(float duration, float frequency, float amplitude)
{
hapticAction.Execute(0, duration, frequency, amplitude, handType);
}
public void ShowGrabHint()
{
ControllerButtonHints.ShowButtonHint(this, grabGripAction); //todo: assess
}
public void HideGrabHint()
{
ControllerButtonHints.HideButtonHint(this, grabGripAction); //todo: assess
}
public void ShowGrabHint(string text)
{
ControllerButtonHints.ShowTextHint(this, grabGripAction, text);
}
public GrabTypes GetGrabStarting(GrabTypes explicitType = GrabTypes.None)
{
if (explicitType != GrabTypes.None)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButtonDown(0))
return explicitType;
else
return GrabTypes.None;
}
if (explicitType == GrabTypes.Pinch && grabPinchAction.GetStateDown(handType))
return GrabTypes.Pinch;
if (explicitType == GrabTypes.Grip && grabGripAction.GetStateDown(handType))
return GrabTypes.Grip;
}
else
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButtonDown(0))
return GrabTypes.Grip;
else
return GrabTypes.None;
}
if (grabPinchAction != null && grabPinchAction.GetStateDown(handType))
return GrabTypes.Pinch;
if (grabGripAction != null && grabGripAction.GetStateDown(handType))
return GrabTypes.Grip;
}
return GrabTypes.None;
}
public GrabTypes GetGrabEnding(GrabTypes explicitType = GrabTypes.None)
{
if (explicitType != GrabTypes.None)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButtonUp(0))
return explicitType;
else
return GrabTypes.None;
}
if (explicitType == GrabTypes.Pinch && grabPinchAction.GetStateUp(handType))
return GrabTypes.Pinch;
if (explicitType == GrabTypes.Grip && grabGripAction.GetStateUp(handType))
return GrabTypes.Grip;
}
else
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButtonUp(0))
return GrabTypes.Grip;
else
return GrabTypes.None;
}
if (grabPinchAction.GetStateUp(handType))
return GrabTypes.Pinch;
if (grabGripAction.GetStateUp(handType))
return GrabTypes.Grip;
}
return GrabTypes.None;
}
public bool IsGrabEnding(GameObject attachedObject)
{
for (int attachedObjectIndex = 0; attachedObjectIndex < attachedObjects.Count; attachedObjectIndex++)
{
if (attachedObjects[attachedObjectIndex].attachedObject == attachedObject)
{
return IsGrabbingWithType(attachedObjects[attachedObjectIndex].grabbedWithType) == false;
}
}
return false;
}
public bool IsGrabbingWithType(GrabTypes type)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButton(0))
return true;
else
return false;
}
switch (type)
{
case GrabTypes.Pinch:
return grabPinchAction.GetState(handType);
case GrabTypes.Grip:
return grabGripAction.GetState(handType);
default:
return false;
}
}
public bool IsGrabbingWithOppositeType(GrabTypes type)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButton(0))
return true;
else
return false;
}
switch (type)
{
case GrabTypes.Pinch:
return grabGripAction.GetState(handType);
case GrabTypes.Grip:
return grabPinchAction.GetState(handType);
default:
return false;
}
}
public GrabTypes GetBestGrabbingType()
{
return GetBestGrabbingType(GrabTypes.None);
}
public GrabTypes GetBestGrabbingType(GrabTypes preferred, bool forcePreference = false)
{
if (noSteamVRFallbackCamera)
{
if (Input.GetMouseButton(0))
return preferred;
else
return GrabTypes.None;
}
if (preferred == GrabTypes.Pinch)
{
if (grabPinchAction.GetState(handType))
return GrabTypes.Pinch;
else if (forcePreference)
return GrabTypes.None;
}
if (preferred == GrabTypes.Grip)
{
if (grabGripAction.GetState(handType))
return GrabTypes.Grip;
else if (forcePreference)
return GrabTypes.None;
}
if (grabPinchAction.GetState(handType))
return GrabTypes.Pinch;
if (grabGripAction.GetState(handType))
return GrabTypes.Grip;
return GrabTypes.None;
}
//-------------------------------------------------
private void InitController()
{
if (spewDebugText)
HandDebugLog("Hand " + name + " connected with type " + handType.ToString());
bool hadOldRendermodel = mainRenderModel != null;
EVRSkeletalMotionRange oldRM_rom = EVRSkeletalMotionRange.WithController;
if (hadOldRendermodel)
oldRM_rom = mainRenderModel.GetSkeletonRangeOfMotion;
foreach (RenderModel r in renderModels)
{
if (r != null)
Destroy(r.gameObject);
}
renderModels.Clear();
GameObject renderModelInstance = GameObject.Instantiate(renderModelPrefab);
renderModelInstance.layer = gameObject.layer;
renderModelInstance.tag = gameObject.tag;
renderModelInstance.transform.parent = this.transform;
renderModelInstance.transform.localPosition = Vector3.zero;
renderModelInstance.transform.localRotation = Quaternion.identity;
renderModelInstance.transform.localScale = renderModelPrefab.transform.localScale;
//TriggerHapticPulse(800); //pulse on controller init
int deviceIndex = trackedObject.GetDeviceIndex();
mainRenderModel = renderModelInstance.GetComponent<RenderModel>();
renderModels.Add(mainRenderModel);
if (hadOldRendermodel)
mainRenderModel.SetSkeletonRangeOfMotion(oldRM_rom);
this.BroadcastMessage("SetInputSource", handType, SendMessageOptions.DontRequireReceiver); // let child objects know we've initialized
this.BroadcastMessage("OnHandInitialized", deviceIndex, SendMessageOptions.DontRequireReceiver); // let child objects know we've initialized
}
public void SetRenderModel(GameObject prefab)
{
renderModelPrefab = prefab;
if (mainRenderModel != null && isPoseValid)
InitController();
}
public void SetHoverRenderModel(RenderModel hoverRenderModel)
{
hoverhighlightRenderModel = hoverRenderModel;
renderModels.Add(hoverRenderModel);
}
public int GetDeviceIndex()
{
return trackedObject.GetDeviceIndex();
}
}
[System.Serializable]
public class HandEvent : UnityEvent<Hand> { }
}