The Swartz-Manning’s first exhibit will provide a detailed history of Aaron Swartz Day. https://www.aaronswartzday.org/vr
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Hand.cs 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: The hands used by the player in the vr interaction system
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Collections.ObjectModel;
  11. namespace Valve.VR.InteractionSystem
  12. {
  13. //-------------------------------------------------------------------------
  14. // Links with an appropriate SteamVR controller and facilitates
  15. // interactions with objects in the virtual world.
  16. //-------------------------------------------------------------------------
  17. public class Hand : MonoBehaviour
  18. {
  19. public enum HandType
  20. {
  21. Left,
  22. Right,
  23. Any
  24. };
  25. // The flags used to determine how an object is attached to the hand.
  26. [Flags]
  27. public enum AttachmentFlags
  28. {
  29. SnapOnAttach = 1 << 0, // The object should snap to the position of the specified attachment point on the hand.
  30. DetachOthers = 1 << 1, // Other objects attached to this hand will be detached.
  31. DetachFromOtherHand = 1 << 2, // This object will be detached from the other hand.
  32. ParentToHand = 1 << 3, // The object will be parented to the hand.
  33. };
  34. public const AttachmentFlags defaultAttachmentFlags = AttachmentFlags.ParentToHand |
  35. AttachmentFlags.DetachOthers |
  36. AttachmentFlags.DetachFromOtherHand |
  37. AttachmentFlags.SnapOnAttach;
  38. public Hand otherHand;
  39. public HandType startingHandType;
  40. public Transform hoverSphereTransform;
  41. public float hoverSphereRadius = 0.05f;
  42. public LayerMask hoverLayerMask = -1;
  43. public float hoverUpdateInterval = 0.1f;
  44. public Camera noSteamVRFallbackCamera;
  45. public float noSteamVRFallbackMaxDistanceNoItem = 10.0f;
  46. public float noSteamVRFallbackMaxDistanceWithItem = 0.5f;
  47. private float noSteamVRFallbackInteractorDistance = -1.0f;
  48. public SteamVR_Controller.Device controller;
  49. public GameObject controllerPrefab;
  50. private GameObject controllerObject = null;
  51. public bool showDebugText = false;
  52. public bool spewDebugText = false;
  53. public struct AttachedObject
  54. {
  55. public GameObject attachedObject;
  56. public GameObject originalParent;
  57. public bool isParentedToHand;
  58. }
  59. private List<AttachedObject> attachedObjects = new List<AttachedObject>();
  60. public ReadOnlyCollection<AttachedObject> AttachedObjects
  61. {
  62. get { return attachedObjects.AsReadOnly(); }
  63. }
  64. public bool hoverLocked { get; private set; }
  65. private Interactable _hoveringInteractable;
  66. private TextMesh debugText;
  67. private int prevOverlappingColliders = 0;
  68. private const int ColliderArraySize = 16;
  69. private Collider[] overlappingColliders;
  70. private Player playerInstance;
  71. private GameObject applicationLostFocusObject;
  72. SteamVR_Events.Action inputFocusAction;
  73. //-------------------------------------------------
  74. // The Interactable object this Hand is currently hovering over
  75. //-------------------------------------------------
  76. public Interactable hoveringInteractable
  77. {
  78. get { return _hoveringInteractable; }
  79. set
  80. {
  81. if ( _hoveringInteractable != value )
  82. {
  83. if ( _hoveringInteractable != null )
  84. {
  85. HandDebugLog( "HoverEnd " + _hoveringInteractable.gameObject );
  86. _hoveringInteractable.SendMessage( "OnHandHoverEnd", this, SendMessageOptions.DontRequireReceiver );
  87. //Note: The _hoveringInteractable can change after sending the OnHandHoverEnd message so we need to check it again before broadcasting this message
  88. if ( _hoveringInteractable != null )
  89. {
  90. this.BroadcastMessage( "OnParentHandHoverEnd", _hoveringInteractable, SendMessageOptions.DontRequireReceiver ); // let objects attached to the hand know that a hover has ended
  91. }
  92. }
  93. _hoveringInteractable = value;
  94. if ( _hoveringInteractable != null )
  95. {
  96. HandDebugLog( "HoverBegin " + _hoveringInteractable.gameObject );
  97. _hoveringInteractable.SendMessage( "OnHandHoverBegin", this, SendMessageOptions.DontRequireReceiver );
  98. //Note: The _hoveringInteractable can change after sending the OnHandHoverBegin message so we need to check it again before broadcasting this message
  99. if ( _hoveringInteractable != null )
  100. {
  101. this.BroadcastMessage( "OnParentHandHoverBegin", _hoveringInteractable, SendMessageOptions.DontRequireReceiver ); // let objects attached to the hand know that a hover has begun
  102. }
  103. }
  104. }
  105. }
  106. }
  107. //-------------------------------------------------
  108. // Active GameObject attached to this Hand
  109. //-------------------------------------------------
  110. public GameObject currentAttachedObject
  111. {
  112. get
  113. {
  114. CleanUpAttachedObjectStack();
  115. if ( attachedObjects.Count > 0 )
  116. {
  117. return attachedObjects[attachedObjects.Count - 1].attachedObject;
  118. }
  119. return null;
  120. }
  121. }
  122. //-------------------------------------------------
  123. public Transform GetAttachmentTransform( string attachmentPoint = "" )
  124. {
  125. Transform attachmentTransform = null;
  126. if ( !string.IsNullOrEmpty( attachmentPoint ) )
  127. {
  128. attachmentTransform = transform.Find( attachmentPoint );
  129. }
  130. if ( !attachmentTransform )
  131. {
  132. attachmentTransform = this.transform;
  133. }
  134. return attachmentTransform;
  135. }
  136. //-------------------------------------------------
  137. // Guess the type of this Hand
  138. //
  139. // If startingHandType is Hand.Left or Hand.Right, returns startingHandType.
  140. // If otherHand is non-null and both Hands are linked to controllers, returns
  141. // Hand.Left if this Hand is leftmost relative to the HMD, otherwise Hand.Right.
  142. // Otherwise, returns Hand.Any
  143. //-------------------------------------------------
  144. public HandType GuessCurrentHandType()
  145. {
  146. if ( startingHandType == HandType.Left || startingHandType == HandType.Right )
  147. {
  148. return startingHandType;
  149. }
  150. if ( startingHandType == HandType.Any && otherHand != null && otherHand.controller == null )
  151. {
  152. return HandType.Right;
  153. }
  154. if ( controller == null || otherHand == null || otherHand.controller == null )
  155. {
  156. return startingHandType;
  157. }
  158. if ( controller.index == SteamVR_Controller.GetDeviceIndex( SteamVR_Controller.DeviceRelation.Leftmost ) )
  159. {
  160. return HandType.Left;
  161. }
  162. return HandType.Right;
  163. }
  164. //-------------------------------------------------
  165. // Attach a GameObject to this GameObject
  166. //
  167. // objectToAttach - The GameObject to attach
  168. // flags - The flags to use for attaching the object
  169. // attachmentPoint - Name of the GameObject in the hierarchy of this Hand which should act as the attachment point for this GameObject
  170. //-------------------------------------------------
  171. public void AttachObject( GameObject objectToAttach, AttachmentFlags flags = defaultAttachmentFlags, string attachmentPoint = "" )
  172. {
  173. if ( flags == 0 )
  174. {
  175. flags = defaultAttachmentFlags;
  176. }
  177. //Make sure top object on stack is non-null
  178. CleanUpAttachedObjectStack();
  179. //Detach the object if it is already attached so that it can get re-attached at the top of the stack
  180. DetachObject( objectToAttach );
  181. //Detach from the other hand if requested
  182. if ( ( ( flags & AttachmentFlags.DetachFromOtherHand ) == AttachmentFlags.DetachFromOtherHand ) && otherHand )
  183. {
  184. otherHand.DetachObject( objectToAttach );
  185. }
  186. if ( ( flags & AttachmentFlags.DetachOthers ) == AttachmentFlags.DetachOthers )
  187. {
  188. //Detach all the objects from the stack
  189. while ( attachedObjects.Count > 0 )
  190. {
  191. DetachObject( attachedObjects[0].attachedObject );
  192. }
  193. }
  194. if ( currentAttachedObject )
  195. {
  196. currentAttachedObject.SendMessage( "OnHandFocusLost", this, SendMessageOptions.DontRequireReceiver );
  197. }
  198. AttachedObject attachedObject = new AttachedObject();
  199. attachedObject.attachedObject = objectToAttach;
  200. attachedObject.originalParent = objectToAttach.transform.parent != null ? objectToAttach.transform.parent.gameObject : null;
  201. if ( ( flags & AttachmentFlags.ParentToHand ) == AttachmentFlags.ParentToHand )
  202. {
  203. //Parent the object to the hand
  204. objectToAttach.transform.parent = GetAttachmentTransform( attachmentPoint );
  205. attachedObject.isParentedToHand = true;
  206. }
  207. else
  208. {
  209. attachedObject.isParentedToHand = false;
  210. }
  211. attachedObjects.Add( attachedObject );
  212. if ( ( flags & AttachmentFlags.SnapOnAttach ) == AttachmentFlags.SnapOnAttach )
  213. {
  214. objectToAttach.transform.localPosition = Vector3.zero;
  215. objectToAttach.transform.localRotation = Quaternion.identity;
  216. }
  217. HandDebugLog( "AttachObject " + objectToAttach );
  218. objectToAttach.SendMessage( "OnAttachedToHand", this, SendMessageOptions.DontRequireReceiver );
  219. UpdateHovering();
  220. }
  221. //-------------------------------------------------
  222. // Detach this GameObject from the attached object stack of this Hand
  223. //
  224. // objectToDetach - The GameObject to detach from this Hand
  225. //-------------------------------------------------
  226. public void DetachObject( GameObject objectToDetach, bool restoreOriginalParent = true )
  227. {
  228. int index = attachedObjects.FindIndex( l => l.attachedObject == objectToDetach );
  229. if ( index != -1 )
  230. {
  231. HandDebugLog( "DetachObject " + objectToDetach );
  232. GameObject prevTopObject = currentAttachedObject;
  233. Transform parentTransform = null;
  234. if ( attachedObjects[index].isParentedToHand )
  235. {
  236. if ( restoreOriginalParent && ( attachedObjects[index].originalParent != null ) )
  237. {
  238. parentTransform = attachedObjects[index].originalParent.transform;
  239. }
  240. attachedObjects[index].attachedObject.transform.parent = parentTransform;
  241. }
  242. attachedObjects[index].attachedObject.SetActive( true );
  243. attachedObjects[index].attachedObject.SendMessage( "OnDetachedFromHand", this, SendMessageOptions.DontRequireReceiver );
  244. attachedObjects.RemoveAt( index );
  245. GameObject newTopObject = currentAttachedObject;
  246. //Give focus to the top most object on the stack if it changed
  247. if ( newTopObject != null && newTopObject != prevTopObject )
  248. {
  249. newTopObject.SetActive( true );
  250. newTopObject.SendMessage( "OnHandFocusAcquired", this, SendMessageOptions.DontRequireReceiver );
  251. }
  252. }
  253. CleanUpAttachedObjectStack();
  254. }
  255. //-------------------------------------------------
  256. // Get the world velocity of the VR Hand.
  257. // Note: controller velocity value only updates on controller events (Button but and down) so good for throwing
  258. //-------------------------------------------------
  259. public Vector3 GetTrackedObjectVelocity()
  260. {
  261. if ( controller != null )
  262. {
  263. return transform.parent.TransformVector( controller.velocity );
  264. }
  265. return Vector3.zero;
  266. }
  267. //-------------------------------------------------
  268. // Get the world angular velocity of the VR Hand.
  269. // Note: controller velocity value only updates on controller events (Button but and down) so good for throwing
  270. //-------------------------------------------------
  271. public Vector3 GetTrackedObjectAngularVelocity()
  272. {
  273. if ( controller != null )
  274. {
  275. return transform.parent.TransformVector( controller.angularVelocity );
  276. }
  277. return Vector3.zero;
  278. }
  279. //-------------------------------------------------
  280. private void CleanUpAttachedObjectStack()
  281. {
  282. attachedObjects.RemoveAll( l => l.attachedObject == null );
  283. }
  284. //-------------------------------------------------
  285. void Awake()
  286. {
  287. inputFocusAction = SteamVR_Events.InputFocusAction( OnInputFocus );
  288. if ( hoverSphereTransform == null )
  289. {
  290. hoverSphereTransform = this.transform;
  291. }
  292. applicationLostFocusObject = new GameObject( "_application_lost_focus" );
  293. applicationLostFocusObject.transform.parent = transform;
  294. applicationLostFocusObject.SetActive( false );
  295. }
  296. //-------------------------------------------------
  297. IEnumerator Start()
  298. {
  299. // save off player instance
  300. playerInstance = Player.instance;
  301. if ( !playerInstance )
  302. {
  303. Debug.LogError( "No player instance found in Hand Start()" );
  304. }
  305. // allocate array for colliders
  306. overlappingColliders = new Collider[ColliderArraySize];
  307. // We are a "no SteamVR fallback hand" if we have this camera set
  308. // we'll use the right mouse to look around and left mouse to interact
  309. // - don't need to find the device
  310. if ( noSteamVRFallbackCamera )
  311. {
  312. yield break;
  313. }
  314. //Debug.Log( "Hand - initializing connection routine" );
  315. // Acquire the correct device index for the hand we want to be
  316. // Also for the other hand if we get there first
  317. while ( true )
  318. {
  319. // Don't need to run this every frame
  320. yield return new WaitForSeconds( 1.0f );
  321. // We have a controller now, break out of the loop!
  322. if ( controller != null )
  323. break;
  324. //Debug.Log( "Hand - checking controllers..." );
  325. // Initialize both hands simultaneously
  326. if ( startingHandType == HandType.Left || startingHandType == HandType.Right )
  327. {
  328. // Left/right relationship.
  329. // Wait until we have a clear unique left-right relationship to initialize.
  330. int leftIndex = SteamVR_Controller.GetDeviceIndex( SteamVR_Controller.DeviceRelation.Leftmost );
  331. int rightIndex = SteamVR_Controller.GetDeviceIndex( SteamVR_Controller.DeviceRelation.Rightmost );
  332. if ( leftIndex == -1 || rightIndex == -1 || leftIndex == rightIndex )
  333. {
  334. //Debug.Log( string.Format( "...Left/right hand relationship not yet established: leftIndex={0}, rightIndex={1}", leftIndex, rightIndex ) );
  335. continue;
  336. }
  337. int myIndex = ( startingHandType == HandType.Right ) ? rightIndex : leftIndex;
  338. int otherIndex = ( startingHandType == HandType.Right ) ? leftIndex : rightIndex;
  339. InitController( myIndex );
  340. if ( otherHand )
  341. {
  342. otherHand.InitController( otherIndex );
  343. }
  344. }
  345. else
  346. {
  347. // No left/right relationship. Just wait for a connection
  348. var vr = SteamVR.instance;
  349. for ( int i = 0; i < Valve.VR.OpenVR.k_unMaxTrackedDeviceCount; i++ )
  350. {
  351. if ( vr.hmd.GetTrackedDeviceClass( (uint)i ) != Valve.VR.ETrackedDeviceClass.Controller )
  352. {
  353. //Debug.Log( string.Format( "Hand - device {0} is not a controller", i ) );
  354. continue;
  355. }
  356. var device = SteamVR_Controller.Input( i );
  357. if ( !device.valid )
  358. {
  359. //Debug.Log( string.Format( "Hand - device {0} is not valid", i ) );
  360. continue;
  361. }
  362. if ( ( otherHand != null ) && ( otherHand.controller != null ) )
  363. {
  364. // Other hand is using this index, so we cannot use it.
  365. if ( i == (int)otherHand.controller.index )
  366. {
  367. //Debug.Log( string.Format( "Hand - device {0} is owned by the other hand", i ) );
  368. continue;
  369. }
  370. }
  371. InitController( i );
  372. }
  373. }
  374. }
  375. }
  376. //-------------------------------------------------
  377. private void UpdateHovering()
  378. {
  379. if ( ( noSteamVRFallbackCamera == null ) && ( controller == null ) )
  380. {
  381. return;
  382. }
  383. if ( hoverLocked )
  384. return;
  385. if ( applicationLostFocusObject.activeSelf )
  386. return;
  387. float closestDistance = float.MaxValue;
  388. Interactable closestInteractable = null;
  389. // Pick the closest hovering
  390. float flHoverRadiusScale = playerInstance.transform.lossyScale.x;
  391. float flScaledSphereRadius = hoverSphereRadius * flHoverRadiusScale;
  392. // if we're close to the floor, increase the radius to make things easier to pick up
  393. float handDiff = Mathf.Abs( transform.position.y - playerInstance.trackingOriginTransform.position.y );
  394. float boxMult = Util.RemapNumberClamped( handDiff, 0.0f, 0.5f * flHoverRadiusScale, 5.0f, 1.0f ) * flHoverRadiusScale;
  395. // null out old vals
  396. for ( int i = 0; i < overlappingColliders.Length; ++i )
  397. {
  398. overlappingColliders[i] = null;
  399. }
  400. Physics.OverlapBoxNonAlloc(
  401. hoverSphereTransform.position - new Vector3( 0, flScaledSphereRadius * boxMult - flScaledSphereRadius, 0 ),
  402. new Vector3( flScaledSphereRadius, flScaledSphereRadius * boxMult * 2.0f, flScaledSphereRadius ),
  403. overlappingColliders,
  404. Quaternion.identity,
  405. hoverLayerMask.value
  406. );
  407. // DebugVar
  408. int iActualColliderCount = 0;
  409. foreach ( Collider collider in overlappingColliders )
  410. {
  411. if ( collider == null )
  412. continue;
  413. Interactable contacting = collider.GetComponentInParent<Interactable>();
  414. // Yeah, it's null, skip
  415. if ( contacting == null )
  416. continue;
  417. // Ignore this collider for hovering
  418. IgnoreHovering ignore = collider.GetComponent<IgnoreHovering>();
  419. if ( ignore != null )
  420. {
  421. if ( ignore.onlyIgnoreHand == null || ignore.onlyIgnoreHand == this )
  422. {
  423. continue;
  424. }
  425. }
  426. // Can't hover over the object if it's attached
  427. if ( attachedObjects.FindIndex( l => l.attachedObject == contacting.gameObject ) != -1 )
  428. continue;
  429. // Occupied by another hand, so we can't touch it
  430. if ( otherHand && otherHand.hoveringInteractable == contacting )
  431. continue;
  432. // Best candidate so far...
  433. float distance = Vector3.Distance( contacting.transform.position, hoverSphereTransform.position );
  434. if ( distance < closestDistance )
  435. {
  436. closestDistance = distance;
  437. closestInteractable = contacting;
  438. }
  439. iActualColliderCount++;
  440. }
  441. // Hover on this one
  442. hoveringInteractable = closestInteractable;
  443. if ( iActualColliderCount > 0 && iActualColliderCount != prevOverlappingColliders )
  444. {
  445. prevOverlappingColliders = iActualColliderCount;
  446. HandDebugLog( "Found " + iActualColliderCount + " overlapping colliders." );
  447. }
  448. }
  449. //-------------------------------------------------
  450. private void UpdateNoSteamVRFallback()
  451. {
  452. if ( noSteamVRFallbackCamera )
  453. {
  454. Ray ray = noSteamVRFallbackCamera.ScreenPointToRay( Input.mousePosition );
  455. if ( attachedObjects.Count > 0 )
  456. {
  457. // Holding down the mouse:
  458. // move around a fixed distance from the camera
  459. transform.position = ray.origin + noSteamVRFallbackInteractorDistance * ray.direction;
  460. }
  461. else
  462. {
  463. // Not holding down the mouse:
  464. // cast out a ray to see what we should mouse over
  465. // Don't want to hit the hand and anything underneath it
  466. // So move it back behind the camera when we do the raycast
  467. Vector3 oldPosition = transform.position;
  468. transform.position = noSteamVRFallbackCamera.transform.forward * ( -1000.0f );
  469. RaycastHit raycastHit;
  470. if ( Physics.Raycast( ray, out raycastHit, noSteamVRFallbackMaxDistanceNoItem ) )
  471. {
  472. transform.position = raycastHit.point;
  473. // Remember this distance in case we click and drag the mouse
  474. noSteamVRFallbackInteractorDistance = Mathf.Min( noSteamVRFallbackMaxDistanceNoItem, raycastHit.distance );
  475. }
  476. else if ( noSteamVRFallbackInteractorDistance > 0.0f )
  477. {
  478. // Move it around at the distance we last had a hit
  479. transform.position = ray.origin + Mathf.Min( noSteamVRFallbackMaxDistanceNoItem, noSteamVRFallbackInteractorDistance ) * ray.direction;
  480. }
  481. else
  482. {
  483. // Didn't hit, just leave it where it was
  484. transform.position = oldPosition;
  485. }
  486. }
  487. }
  488. }
  489. //-------------------------------------------------
  490. private void UpdateDebugText()
  491. {
  492. if ( showDebugText )
  493. {
  494. if ( debugText == null )
  495. {
  496. debugText = new GameObject( "_debug_text" ).AddComponent<TextMesh>();
  497. debugText.fontSize = 120;
  498. debugText.characterSize = 0.001f;
  499. debugText.transform.parent = transform;
  500. debugText.transform.localRotation = Quaternion.Euler( 90.0f, 0.0f, 0.0f );
  501. }
  502. if ( GuessCurrentHandType() == HandType.Right )
  503. {
  504. debugText.transform.localPosition = new Vector3( -0.05f, 0.0f, 0.0f );
  505. debugText.alignment = TextAlignment.Right;
  506. debugText.anchor = TextAnchor.UpperRight;
  507. }
  508. else
  509. {
  510. debugText.transform.localPosition = new Vector3( 0.05f, 0.0f, 0.0f );
  511. debugText.alignment = TextAlignment.Left;
  512. debugText.anchor = TextAnchor.UpperLeft;
  513. }
  514. debugText.text = string.Format(
  515. "Hovering: {0}\n" +
  516. "Hover Lock: {1}\n" +
  517. "Attached: {2}\n" +
  518. "Total Attached: {3}\n" +
  519. "Type: {4}\n",
  520. ( hoveringInteractable ? hoveringInteractable.gameObject.name : "null" ),
  521. hoverLocked,
  522. ( currentAttachedObject ? currentAttachedObject.name : "null" ),
  523. attachedObjects.Count,
  524. GuessCurrentHandType().ToString() );
  525. }
  526. else
  527. {
  528. if ( debugText != null )
  529. {
  530. Destroy( debugText.gameObject );
  531. }
  532. }
  533. }
  534. //-------------------------------------------------
  535. void OnEnable()
  536. {
  537. inputFocusAction.enabled = true;
  538. // Stagger updates between hands
  539. float hoverUpdateBegin = ( ( otherHand != null ) && ( otherHand.GetInstanceID() < GetInstanceID() ) ) ? ( 0.5f * hoverUpdateInterval ) : ( 0.0f );
  540. InvokeRepeating( "UpdateHovering", hoverUpdateBegin, hoverUpdateInterval );
  541. InvokeRepeating( "UpdateDebugText", hoverUpdateBegin, hoverUpdateInterval );
  542. }
  543. //-------------------------------------------------
  544. void OnDisable()
  545. {
  546. inputFocusAction.enabled = false;
  547. CancelInvoke();
  548. }
  549. //-------------------------------------------------
  550. void Update()
  551. {
  552. UpdateNoSteamVRFallback();
  553. GameObject attached = currentAttachedObject;
  554. if ( attached )
  555. {
  556. attached.SendMessage( "HandAttachedUpdate", this, SendMessageOptions.DontRequireReceiver );
  557. }
  558. if ( hoveringInteractable )
  559. {
  560. hoveringInteractable.SendMessage( "HandHoverUpdate", this, SendMessageOptions.DontRequireReceiver );
  561. }
  562. }
  563. //-------------------------------------------------
  564. void LateUpdate()
  565. {
  566. //Re-attach the controller if nothing else is attached to the hand
  567. if ( controllerObject != null && attachedObjects.Count == 0 )
  568. {
  569. AttachObject( controllerObject );
  570. }
  571. }
  572. //-------------------------------------------------
  573. private void OnInputFocus( bool hasFocus )
  574. {
  575. if ( hasFocus )
  576. {
  577. DetachObject( applicationLostFocusObject, true );
  578. applicationLostFocusObject.SetActive( false );
  579. UpdateHandPoses();
  580. UpdateHovering();
  581. BroadcastMessage( "OnParentHandInputFocusAcquired", SendMessageOptions.DontRequireReceiver );
  582. }
  583. else
  584. {
  585. applicationLostFocusObject.SetActive( true );
  586. AttachObject( applicationLostFocusObject, AttachmentFlags.ParentToHand );
  587. BroadcastMessage( "OnParentHandInputFocusLost", SendMessageOptions.DontRequireReceiver );
  588. }
  589. }
  590. //-------------------------------------------------
  591. void FixedUpdate()
  592. {
  593. UpdateHandPoses();
  594. }
  595. //-------------------------------------------------
  596. void OnDrawGizmos()
  597. {
  598. Gizmos.color = new Color( 0.5f, 1.0f, 0.5f, 0.9f );
  599. Transform sphereTransform = hoverSphereTransform ? hoverSphereTransform : this.transform;
  600. Gizmos.DrawWireSphere( sphereTransform.position, hoverSphereRadius );
  601. }
  602. //-------------------------------------------------
  603. private void HandDebugLog( string msg )
  604. {
  605. if ( spewDebugText )
  606. {
  607. Debug.Log( "Hand (" + this.name + "): " + msg );
  608. }
  609. }
  610. //-------------------------------------------------
  611. private void UpdateHandPoses()
  612. {
  613. if ( controller != null )
  614. {
  615. SteamVR vr = SteamVR.instance;
  616. if ( vr != null )
  617. {
  618. var pose = new Valve.VR.TrackedDevicePose_t();
  619. var gamePose = new Valve.VR.TrackedDevicePose_t();
  620. var err = vr.compositor.GetLastPoseForTrackedDeviceIndex( controller.index, ref pose, ref gamePose );
  621. if ( err == Valve.VR.EVRCompositorError.None )
  622. {
  623. var t = new SteamVR_Utils.RigidTransform( gamePose.mDeviceToAbsoluteTracking );
  624. transform.localPosition = t.pos;
  625. transform.localRotation = t.rot;
  626. }
  627. }
  628. }
  629. }
  630. //-------------------------------------------------
  631. // Continue to hover over this object indefinitely, whether or not the Hand moves out of its interaction trigger volume.
  632. //
  633. // interactable - The Interactable to hover over indefinitely.
  634. //-------------------------------------------------
  635. public void HoverLock( Interactable interactable )
  636. {
  637. HandDebugLog( "HoverLock " + interactable );
  638. hoverLocked = true;
  639. hoveringInteractable = interactable;
  640. }
  641. //-------------------------------------------------
  642. // Stop hovering over this object indefinitely.
  643. //
  644. // interactable - The hover-locked Interactable to stop hovering over indefinitely.
  645. //-------------------------------------------------
  646. public void HoverUnlock( Interactable interactable )
  647. {
  648. HandDebugLog( "HoverUnlock " + interactable );
  649. if ( hoveringInteractable == interactable )
  650. {
  651. hoverLocked = false;
  652. }
  653. }
  654. //-------------------------------------------------
  655. // Was the standard interaction button just pressed? In VR, this is a trigger press. In 2D fallback, this is a mouse left-click.
  656. //-------------------------------------------------
  657. public bool GetStandardInteractionButtonDown()
  658. {
  659. if ( noSteamVRFallbackCamera )
  660. {
  661. return Input.GetMouseButtonDown( 0 );
  662. }
  663. else if ( controller != null )
  664. {
  665. return controller.GetHairTriggerDown();
  666. }
  667. return false;
  668. }
  669. //-------------------------------------------------
  670. // Was the standard interaction button just released? In VR, this is a trigger press. In 2D fallback, this is a mouse left-click.
  671. //-------------------------------------------------
  672. public bool GetStandardInteractionButtonUp()
  673. {
  674. if ( noSteamVRFallbackCamera )
  675. {
  676. return Input.GetMouseButtonUp( 0 );
  677. }
  678. else if ( controller != null )
  679. {
  680. return controller.GetHairTriggerUp();
  681. }
  682. return false;
  683. }
  684. //-------------------------------------------------
  685. // Is the standard interaction button being pressed? In VR, this is a trigger press. In 2D fallback, this is a mouse left-click.
  686. //-------------------------------------------------
  687. public bool GetStandardInteractionButton()
  688. {
  689. if ( noSteamVRFallbackCamera )
  690. {
  691. return Input.GetMouseButton( 0 );
  692. }
  693. else if ( controller != null )
  694. {
  695. return controller.GetHairTrigger();
  696. }
  697. return false;
  698. }
  699. //-------------------------------------------------
  700. private void InitController( int index )
  701. {
  702. if ( controller == null )
  703. {
  704. controller = SteamVR_Controller.Input( index );
  705. HandDebugLog( "Hand " + name + " connected with device index " + controller.index );
  706. controllerObject = GameObject.Instantiate( controllerPrefab );
  707. controllerObject.SetActive( true );
  708. controllerObject.name = controllerPrefab.name + "_" + this.name;
  709. controllerObject.layer = gameObject.layer;
  710. controllerObject.tag = gameObject.tag;
  711. AttachObject( controllerObject );
  712. controller.TriggerHapticPulse( 800 );
  713. // If the player's scale has been changed the object to attach will be the wrong size.
  714. // To fix this we change the object's scale back to its original, pre-attach scale.
  715. controllerObject.transform.localScale = controllerPrefab.transform.localScale;
  716. this.BroadcastMessage( "OnHandInitialized", index, SendMessageOptions.DontRequireReceiver ); // let child objects know we've initialized
  717. }
  718. }
  719. }
  720. #if UNITY_EDITOR
  721. //-------------------------------------------------------------------------
  722. [UnityEditor.CustomEditor( typeof( Hand ) )]
  723. public class HandEditor : UnityEditor.Editor
  724. {
  725. //-------------------------------------------------
  726. // Custom Inspector GUI allows us to click from within the UI
  727. //-------------------------------------------------
  728. public override void OnInspectorGUI()
  729. {
  730. DrawDefaultInspector();
  731. Hand hand = (Hand)target;
  732. if ( hand.otherHand )
  733. {
  734. if ( hand.otherHand.otherHand != hand )
  735. {
  736. UnityEditor.EditorGUILayout.HelpBox( "The otherHand of this Hand's otherHand is not this Hand.", UnityEditor.MessageType.Warning );
  737. }
  738. if ( hand.startingHandType == Hand.HandType.Left && hand.otherHand.startingHandType != Hand.HandType.Right )
  739. {
  740. UnityEditor.EditorGUILayout.HelpBox( "This is a left Hand but otherHand is not a right Hand.", UnityEditor.MessageType.Warning );
  741. }
  742. if ( hand.startingHandType == Hand.HandType.Right && hand.otherHand.startingHandType != Hand.HandType.Left )
  743. {
  744. UnityEditor.EditorGUILayout.HelpBox( "This is a right Hand but otherHand is not a left Hand.", UnityEditor.MessageType.Warning );
  745. }
  746. if ( hand.startingHandType == Hand.HandType.Any && hand.otherHand.startingHandType != Hand.HandType.Any )
  747. {
  748. UnityEditor.EditorGUILayout.HelpBox( "This is an any-handed Hand but otherHand is not an any-handed Hand.", UnityEditor.MessageType.Warning );
  749. }
  750. }
  751. }
  752. }
  753. #endif
  754. }