2020-04-29 19:40:05 +02: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 ) ;
}
}
2020-05-01 04:37:20 +02:00
if ( attachedInfo . interactable ! = null & & attachedInfo . interactable . attachEaseIn )
2020-04-29 19:40:05 +02:00
{
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 > { }
}