Hand.cs 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681
  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. using UnityEngine.Events;
  12. using System.Threading;
  13. namespace Valve.VR.InteractionSystem
  14. {
  15. //-------------------------------------------------------------------------
  16. // Links with an appropriate SteamVR controller and facilitates
  17. // interactions with objects in the virtual world.
  18. //-------------------------------------------------------------------------
  19. public class Hand : MonoBehaviour
  20. {
  21. // The flags used to determine how an object is attached to the hand.
  22. [Flags]
  23. public enum AttachmentFlags
  24. {
  25. SnapOnAttach = 1 << 0, // The object should snap to the position of the specified attachment point on the hand.
  26. DetachOthers = 1 << 1, // Other objects attached to this hand will be detached.
  27. DetachFromOtherHand = 1 << 2, // This object will be detached from the other hand.
  28. ParentToHand = 1 << 3, // The object will be parented to the hand.
  29. VelocityMovement = 1 << 4, // The object will attempt to move to match the position and rotation of the hand.
  30. TurnOnKinematic = 1 << 5, // The object will not respond to external physics.
  31. TurnOffGravity = 1 << 6, // The object will not respond to external physics.
  32. 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
  33. };
  34. public const AttachmentFlags defaultAttachmentFlags = AttachmentFlags.ParentToHand |
  35. AttachmentFlags.DetachOthers |
  36. AttachmentFlags.DetachFromOtherHand |
  37. AttachmentFlags.TurnOnKinematic |
  38. AttachmentFlags.SnapOnAttach;
  39. public Hand otherHand;
  40. public SteamVR_Input_Sources handType;
  41. public SteamVR_Behaviour_Pose trackedObject;
  42. public SteamVR_Action_Boolean grabPinchAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("GrabPinch");
  43. public SteamVR_Action_Boolean grabGripAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("GrabGrip");
  44. public SteamVR_Action_Vibration hapticAction = SteamVR_Input.GetAction<SteamVR_Action_Vibration>("Haptic");
  45. public SteamVR_Action_Boolean uiInteractAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("InteractUI");
  46. public bool useHoverSphere = true;
  47. public Transform hoverSphereTransform;
  48. public float hoverSphereRadius = 0.05f;
  49. public LayerMask hoverLayerMask = -1;
  50. public float hoverUpdateInterval = 0.1f;
  51. public bool useControllerHoverComponent = true;
  52. public string controllerHoverComponent = "tip";
  53. public float controllerHoverRadius = 0.075f;
  54. public bool useFingerJointHover = true;
  55. public SteamVR_Skeleton_JointIndexEnum fingerJointHover = SteamVR_Skeleton_JointIndexEnum.indexTip;
  56. public float fingerJointHoverRadius = 0.025f;
  57. [Tooltip("A transform on the hand to center attached objects on")]
  58. public Transform objectAttachmentPoint;
  59. public Camera noSteamVRFallbackCamera;
  60. public float noSteamVRFallbackMaxDistanceNoItem = 10.0f;
  61. public float noSteamVRFallbackMaxDistanceWithItem = 0.5f;
  62. private float noSteamVRFallbackInteractorDistance = -1.0f;
  63. public GameObject renderModelPrefab;
  64. protected List<RenderModel> renderModels = new List<RenderModel>();
  65. protected RenderModel mainRenderModel;
  66. protected RenderModel hoverhighlightRenderModel;
  67. public bool showDebugText = false;
  68. public bool spewDebugText = false;
  69. public bool showDebugInteractables = false;
  70. public struct AttachedObject
  71. {
  72. public GameObject attachedObject;
  73. public Interactable interactable;
  74. public Rigidbody attachedRigidbody;
  75. public CollisionDetectionMode collisionDetectionMode;
  76. public bool attachedRigidbodyWasKinematic;
  77. public bool attachedRigidbodyUsedGravity;
  78. public GameObject originalParent;
  79. public bool isParentedToHand;
  80. public GrabTypes grabbedWithType;
  81. public AttachmentFlags attachmentFlags;
  82. public Vector3 initialPositionalOffset;
  83. public Quaternion initialRotationalOffset;
  84. public Transform attachedOffsetTransform;
  85. public Transform handAttachmentPointTransform;
  86. public Vector3 easeSourcePosition;
  87. public Quaternion easeSourceRotation;
  88. public float attachTime;
  89. public bool HasAttachFlag(AttachmentFlags flag)
  90. {
  91. return (attachmentFlags & flag) == flag;
  92. }
  93. }
  94. private List<AttachedObject> attachedObjects = new List<AttachedObject>();
  95. public ReadOnlyCollection<AttachedObject> AttachedObjects
  96. {
  97. get { return attachedObjects.AsReadOnly(); }
  98. }
  99. public bool hoverLocked { get; private set; }
  100. private Interactable _hoveringInteractable;
  101. private TextMesh debugText;
  102. private int prevOverlappingColliders = 0;
  103. private const int ColliderArraySize = 16;
  104. private Collider[] overlappingColliders;
  105. private Player playerInstance;
  106. private GameObject applicationLostFocusObject;
  107. private SteamVR_Events.Action inputFocusAction;
  108. public bool isActive
  109. {
  110. get
  111. {
  112. if (trackedObject != null)
  113. return trackedObject.isActive;
  114. return this.gameObject.activeInHierarchy;
  115. }
  116. }
  117. public bool isPoseValid
  118. {
  119. get
  120. {
  121. return trackedObject.isValid;
  122. }
  123. }
  124. //-------------------------------------------------
  125. // The Interactable object this Hand is currently hovering over
  126. //-------------------------------------------------
  127. public Interactable hoveringInteractable
  128. {
  129. get { return _hoveringInteractable; }
  130. set
  131. {
  132. if (_hoveringInteractable != value)
  133. {
  134. if (_hoveringInteractable != null)
  135. {
  136. if (spewDebugText)
  137. HandDebugLog("HoverEnd " + _hoveringInteractable.gameObject);
  138. _hoveringInteractable.SendMessage("OnHandHoverEnd", this, SendMessageOptions.DontRequireReceiver);
  139. //Note: The _hoveringInteractable can change after sending the OnHandHoverEnd message so we need to check it again before broadcasting this message
  140. if (_hoveringInteractable != null)
  141. {
  142. this.BroadcastMessage("OnParentHandHoverEnd", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); // let objects attached to the hand know that a hover has ended
  143. }
  144. }
  145. _hoveringInteractable = value;
  146. if (_hoveringInteractable != null)
  147. {
  148. if (spewDebugText)
  149. HandDebugLog("HoverBegin " + _hoveringInteractable.gameObject);
  150. _hoveringInteractable.SendMessage("OnHandHoverBegin", this, SendMessageOptions.DontRequireReceiver);
  151. //Note: The _hoveringInteractable can change after sending the OnHandHoverBegin message so we need to check it again before broadcasting this message
  152. if (_hoveringInteractable != null)
  153. {
  154. this.BroadcastMessage("OnParentHandHoverBegin", _hoveringInteractable, SendMessageOptions.DontRequireReceiver); // let objects attached to the hand know that a hover has begun
  155. }
  156. }
  157. }
  158. }
  159. }
  160. //-------------------------------------------------
  161. // Active GameObject attached to this Hand
  162. //-------------------------------------------------
  163. public GameObject currentAttachedObject
  164. {
  165. get
  166. {
  167. CleanUpAttachedObjectStack();
  168. if (attachedObjects.Count > 0)
  169. {
  170. return attachedObjects[attachedObjects.Count - 1].attachedObject;
  171. }
  172. return null;
  173. }
  174. }
  175. public AttachedObject? currentAttachedObjectInfo
  176. {
  177. get
  178. {
  179. CleanUpAttachedObjectStack();
  180. if (attachedObjects.Count > 0)
  181. {
  182. return attachedObjects[attachedObjects.Count - 1];
  183. }
  184. return null;
  185. }
  186. }
  187. public SteamVR_Behaviour_Skeleton skeleton
  188. {
  189. get
  190. {
  191. if (mainRenderModel != null)
  192. return mainRenderModel.GetSkeleton();
  193. return null;
  194. }
  195. }
  196. public void ShowController(bool permanent = false)
  197. {
  198. if (mainRenderModel != null)
  199. mainRenderModel.SetControllerVisibility(true, permanent);
  200. if (hoverhighlightRenderModel != null)
  201. hoverhighlightRenderModel.SetControllerVisibility(true, permanent);
  202. }
  203. public void HideController(bool permanent = false)
  204. {
  205. if (mainRenderModel != null)
  206. mainRenderModel.SetControllerVisibility(false, permanent);
  207. if (hoverhighlightRenderModel != null)
  208. hoverhighlightRenderModel.SetControllerVisibility(false, permanent);
  209. }
  210. public void ShowSkeleton(bool permanent = false)
  211. {
  212. if (mainRenderModel != null)
  213. mainRenderModel.SetHandVisibility(true, permanent);
  214. if (hoverhighlightRenderModel != null)
  215. hoverhighlightRenderModel.SetHandVisibility(true, permanent);
  216. }
  217. public void HideSkeleton(bool permanent = false)
  218. {
  219. if (mainRenderModel != null)
  220. mainRenderModel.SetHandVisibility(false, permanent);
  221. if (hoverhighlightRenderModel != null)
  222. hoverhighlightRenderModel.SetHandVisibility(false, permanent);
  223. }
  224. public bool HasSkeleton()
  225. {
  226. return mainRenderModel != null && mainRenderModel.GetSkeleton() != null;
  227. }
  228. public void Show()
  229. {
  230. SetVisibility(true);
  231. }
  232. public void Hide()
  233. {
  234. SetVisibility(false);
  235. }
  236. public void SetVisibility(bool visible)
  237. {
  238. if (mainRenderModel != null)
  239. mainRenderModel.SetVisibility(visible);
  240. }
  241. public void SetSkeletonRangeOfMotion(EVRSkeletalMotionRange newRangeOfMotion, float blendOverSeconds = 0.1f)
  242. {
  243. for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
  244. {
  245. renderModels[renderModelIndex].SetSkeletonRangeOfMotion(newRangeOfMotion, blendOverSeconds);
  246. }
  247. }
  248. public void SetTemporarySkeletonRangeOfMotion(SkeletalMotionRangeChange temporaryRangeOfMotionChange, float blendOverSeconds = 0.1f)
  249. {
  250. for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
  251. {
  252. renderModels[renderModelIndex].SetTemporarySkeletonRangeOfMotion(temporaryRangeOfMotionChange, blendOverSeconds);
  253. }
  254. }
  255. public void ResetTemporarySkeletonRangeOfMotion(float blendOverSeconds = 0.1f)
  256. {
  257. for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
  258. {
  259. renderModels[renderModelIndex].ResetTemporarySkeletonRangeOfMotion(blendOverSeconds);
  260. }
  261. }
  262. public void SetAnimationState(int stateValue)
  263. {
  264. for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
  265. {
  266. renderModels[renderModelIndex].SetAnimationState(stateValue);
  267. }
  268. }
  269. public void StopAnimation()
  270. {
  271. for (int renderModelIndex = 0; renderModelIndex < renderModels.Count; renderModelIndex++)
  272. {
  273. renderModels[renderModelIndex].StopAnimation();
  274. }
  275. }
  276. //-------------------------------------------------
  277. // Attach a GameObject to this GameObject
  278. //
  279. // objectToAttach - The GameObject to attach
  280. // flags - The flags to use for attaching the object
  281. // attachmentPoint - Name of the GameObject in the hierarchy of this Hand which should act as the attachment point for this GameObject
  282. //-------------------------------------------------
  283. public void AttachObject(GameObject objectToAttach, GrabTypes grabbedWithType, AttachmentFlags flags = defaultAttachmentFlags, Transform attachmentOffset = null)
  284. {
  285. AttachedObject attachedObject = new AttachedObject();
  286. attachedObject.attachmentFlags = flags;
  287. attachedObject.attachedOffsetTransform = attachmentOffset;
  288. attachedObject.attachTime = Time.time;
  289. if (flags == 0)
  290. {
  291. flags = defaultAttachmentFlags;
  292. }
  293. //Make sure top object on stack is non-null
  294. CleanUpAttachedObjectStack();
  295. //Detach the object if it is already attached so that it can get re-attached at the top of the stack
  296. if(ObjectIsAttached(objectToAttach))
  297. DetachObject(objectToAttach);
  298. //Detach from the other hand if requested
  299. if (attachedObject.HasAttachFlag(AttachmentFlags.DetachFromOtherHand))
  300. {
  301. if (otherHand != null)
  302. otherHand.DetachObject(objectToAttach);
  303. }
  304. if (attachedObject.HasAttachFlag(AttachmentFlags.DetachOthers))
  305. {
  306. //Detach all the objects from the stack
  307. while (attachedObjects.Count > 0)
  308. {
  309. DetachObject(attachedObjects[0].attachedObject);
  310. }
  311. }
  312. if (currentAttachedObject)
  313. {
  314. currentAttachedObject.SendMessage("OnHandFocusLost", this, SendMessageOptions.DontRequireReceiver);
  315. }
  316. attachedObject.attachedObject = objectToAttach;
  317. attachedObject.interactable = objectToAttach.GetComponent<Interactable>();
  318. attachedObject.handAttachmentPointTransform = this.transform;
  319. if (attachedObject.interactable != null)
  320. {
  321. if (attachedObject.interactable.attachEaseIn)
  322. {
  323. attachedObject.easeSourcePosition = attachedObject.attachedObject.transform.position;
  324. attachedObject.easeSourceRotation = attachedObject.attachedObject.transform.rotation;
  325. attachedObject.interactable.snapAttachEaseInCompleted = false;
  326. }
  327. if (attachedObject.interactable.useHandObjectAttachmentPoint)
  328. attachedObject.handAttachmentPointTransform = objectAttachmentPoint;
  329. if (attachedObject.interactable.hideHandOnAttach)
  330. Hide();
  331. if (attachedObject.interactable.hideSkeletonOnAttach && mainRenderModel != null && mainRenderModel.displayHandByDefault)
  332. HideSkeleton();
  333. if (attachedObject.interactable.hideControllerOnAttach && mainRenderModel != null && mainRenderModel.displayControllerByDefault)
  334. HideController();
  335. if (attachedObject.interactable.handAnimationOnPickup != 0)
  336. SetAnimationState(attachedObject.interactable.handAnimationOnPickup);
  337. if (attachedObject.interactable.setRangeOfMotionOnPickup != SkeletalMotionRangeChange.None)
  338. SetTemporarySkeletonRangeOfMotion(attachedObject.interactable.setRangeOfMotionOnPickup);
  339. }
  340. attachedObject.originalParent = objectToAttach.transform.parent != null ? objectToAttach.transform.parent.gameObject : null;
  341. attachedObject.attachedRigidbody = objectToAttach.GetComponent<Rigidbody>();
  342. if (attachedObject.attachedRigidbody != null)
  343. {
  344. if (attachedObject.interactable.attachedToHand != null) //already attached to another hand
  345. {
  346. //if it was attached to another hand, get the flags from that hand
  347. for (int attachedIndex = 0; attachedIndex < attachedObject.interactable.attachedToHand.attachedObjects.Count; attachedIndex++)
  348. {
  349. AttachedObject attachedObjectInList = attachedObject.interactable.attachedToHand.attachedObjects[attachedIndex];
  350. if (attachedObjectInList.interactable == attachedObject.interactable)
  351. {
  352. attachedObject.attachedRigidbodyWasKinematic = attachedObjectInList.attachedRigidbodyWasKinematic;
  353. attachedObject.attachedRigidbodyUsedGravity = attachedObjectInList.attachedRigidbodyUsedGravity;
  354. attachedObject.originalParent = attachedObjectInList.originalParent;
  355. }
  356. }
  357. }
  358. else
  359. {
  360. attachedObject.attachedRigidbodyWasKinematic = attachedObject.attachedRigidbody.isKinematic;
  361. attachedObject.attachedRigidbodyUsedGravity = attachedObject.attachedRigidbody.useGravity;
  362. }
  363. }
  364. attachedObject.grabbedWithType = grabbedWithType;
  365. if (attachedObject.HasAttachFlag(AttachmentFlags.ParentToHand))
  366. {
  367. //Parent the object to the hand
  368. objectToAttach.transform.parent = this.transform;
  369. attachedObject.isParentedToHand = true;
  370. }
  371. else
  372. {
  373. attachedObject.isParentedToHand = false;
  374. }
  375. if (attachedObject.HasAttachFlag(AttachmentFlags.SnapOnAttach))
  376. {
  377. if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
  378. {
  379. SteamVR_Skeleton_PoseSnapshot pose = attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton);
  380. //snap the object to the center of the attach point
  381. objectToAttach.transform.position = this.transform.TransformPoint(pose.position);
  382. objectToAttach.transform.rotation = this.transform.rotation * pose.rotation;
  383. attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
  384. attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
  385. }
  386. else
  387. {
  388. if (attachmentOffset != null)
  389. {
  390. //offset the object from the hand by the positional and rotational difference between the offset transform and the attached object
  391. Quaternion rotDiff = Quaternion.Inverse(attachmentOffset.transform.rotation) * objectToAttach.transform.rotation;
  392. objectToAttach.transform.rotation = attachedObject.handAttachmentPointTransform.rotation * rotDiff;
  393. Vector3 posDiff = objectToAttach.transform.position - attachmentOffset.transform.position;
  394. objectToAttach.transform.position = attachedObject.handAttachmentPointTransform.position + posDiff;
  395. }
  396. else
  397. {
  398. //snap the object to the center of the attach point
  399. objectToAttach.transform.rotation = attachedObject.handAttachmentPointTransform.rotation;
  400. objectToAttach.transform.position = attachedObject.handAttachmentPointTransform.position;
  401. }
  402. Transform followPoint = objectToAttach.transform;
  403. attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(followPoint.position);
  404. attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * followPoint.rotation;
  405. }
  406. }
  407. else
  408. {
  409. if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
  410. {
  411. attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
  412. attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
  413. }
  414. else
  415. {
  416. if (attachmentOffset != null)
  417. {
  418. //get the initial positional and rotational offsets between the hand and the offset transform
  419. Quaternion rotDiff = Quaternion.Inverse(attachmentOffset.transform.rotation) * objectToAttach.transform.rotation;
  420. Quaternion targetRotation = attachedObject.handAttachmentPointTransform.rotation * rotDiff;
  421. Quaternion rotationPositionBy = targetRotation * Quaternion.Inverse(objectToAttach.transform.rotation);
  422. Vector3 posDiff = (rotationPositionBy * objectToAttach.transform.position) - (rotationPositionBy * attachmentOffset.transform.position);
  423. attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(attachedObject.handAttachmentPointTransform.position + posDiff);
  424. attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * (attachedObject.handAttachmentPointTransform.rotation * rotDiff);
  425. }
  426. else
  427. {
  428. attachedObject.initialPositionalOffset = attachedObject.handAttachmentPointTransform.InverseTransformPoint(objectToAttach.transform.position);
  429. attachedObject.initialRotationalOffset = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * objectToAttach.transform.rotation;
  430. }
  431. }
  432. }
  433. if (attachedObject.HasAttachFlag(AttachmentFlags.TurnOnKinematic))
  434. {
  435. if (attachedObject.attachedRigidbody != null)
  436. {
  437. attachedObject.collisionDetectionMode = attachedObject.attachedRigidbody.collisionDetectionMode;
  438. if (attachedObject.collisionDetectionMode == CollisionDetectionMode.Continuous)
  439. attachedObject.attachedRigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete;
  440. attachedObject.attachedRigidbody.isKinematic = true;
  441. }
  442. }
  443. if (attachedObject.HasAttachFlag(AttachmentFlags.TurnOffGravity))
  444. {
  445. if (attachedObject.attachedRigidbody != null)
  446. {
  447. attachedObject.attachedRigidbody.useGravity = false;
  448. }
  449. }
  450. if (attachedObject.interactable != null && attachedObject.interactable.attachEaseIn)
  451. {
  452. attachedObject.attachedObject.transform.position = attachedObject.easeSourcePosition;
  453. attachedObject.attachedObject.transform.rotation = attachedObject.easeSourceRotation;
  454. }
  455. attachedObjects.Add(attachedObject);
  456. UpdateHovering();
  457. if (spewDebugText)
  458. HandDebugLog("AttachObject " + objectToAttach);
  459. objectToAttach.SendMessage("OnAttachedToHand", this, SendMessageOptions.DontRequireReceiver);
  460. }
  461. public bool ObjectIsAttached(GameObject go)
  462. {
  463. for (int attachedIndex = 0; attachedIndex < attachedObjects.Count; attachedIndex++)
  464. {
  465. if (attachedObjects[attachedIndex].attachedObject == go)
  466. return true;
  467. }
  468. return false;
  469. }
  470. public void ForceHoverUnlock()
  471. {
  472. hoverLocked = false;
  473. }
  474. //-------------------------------------------------
  475. // Detach this GameObject from the attached object stack of this Hand
  476. //
  477. // objectToDetach - The GameObject to detach from this Hand
  478. //-------------------------------------------------
  479. public void DetachObject(GameObject objectToDetach, bool restoreOriginalParent = true)
  480. {
  481. int index = attachedObjects.FindIndex(l => l.attachedObject == objectToDetach);
  482. if (index != -1)
  483. {
  484. if (spewDebugText)
  485. HandDebugLog("DetachObject " + objectToDetach);
  486. GameObject prevTopObject = currentAttachedObject;
  487. if (attachedObjects[index].interactable != null)
  488. {
  489. if (attachedObjects[index].interactable.hideHandOnAttach)
  490. Show();
  491. if (attachedObjects[index].interactable.hideSkeletonOnAttach && mainRenderModel != null && mainRenderModel.displayHandByDefault)
  492. ShowSkeleton();
  493. if (attachedObjects[index].interactable.hideControllerOnAttach && mainRenderModel != null && mainRenderModel.displayControllerByDefault)
  494. ShowController();
  495. if (attachedObjects[index].interactable.handAnimationOnPickup != 0)
  496. StopAnimation();
  497. if (attachedObjects[index].interactable.setRangeOfMotionOnPickup != SkeletalMotionRangeChange.None)
  498. ResetTemporarySkeletonRangeOfMotion();
  499. }
  500. Transform parentTransform = null;
  501. if (attachedObjects[index].isParentedToHand)
  502. {
  503. if (restoreOriginalParent && (attachedObjects[index].originalParent != null))
  504. {
  505. parentTransform = attachedObjects[index].originalParent.transform;
  506. }
  507. if (attachedObjects[index].attachedObject != null)
  508. {
  509. attachedObjects[index].attachedObject.transform.parent = parentTransform;
  510. }
  511. }
  512. if (attachedObjects[index].HasAttachFlag(AttachmentFlags.TurnOnKinematic))
  513. {
  514. if (attachedObjects[index].attachedRigidbody != null)
  515. {
  516. attachedObjects[index].attachedRigidbody.isKinematic = attachedObjects[index].attachedRigidbodyWasKinematic;
  517. attachedObjects[index].attachedRigidbody.collisionDetectionMode = attachedObjects[index].collisionDetectionMode;
  518. }
  519. }
  520. if (attachedObjects[index].HasAttachFlag(AttachmentFlags.TurnOffGravity))
  521. {
  522. if (attachedObjects[index].attachedObject != null)
  523. {
  524. if (attachedObjects[index].attachedRigidbody != null)
  525. attachedObjects[index].attachedRigidbody.useGravity = attachedObjects[index].attachedRigidbodyUsedGravity;
  526. }
  527. }
  528. if (attachedObjects[index].interactable != null && attachedObjects[index].interactable.handFollowTransform && HasSkeleton())
  529. {
  530. skeleton.transform.localPosition = Vector3.zero;
  531. skeleton.transform.localRotation = Quaternion.identity;
  532. }
  533. if (attachedObjects[index].attachedObject != null)
  534. {
  535. if (attachedObjects[index].interactable == null || (attachedObjects[index].interactable != null && attachedObjects[index].interactable.isDestroying == false))
  536. attachedObjects[index].attachedObject.SetActive(true);
  537. attachedObjects[index].attachedObject.SendMessage("OnDetachedFromHand", this, SendMessageOptions.DontRequireReceiver);
  538. }
  539. attachedObjects.RemoveAt(index);
  540. CleanUpAttachedObjectStack();
  541. GameObject newTopObject = currentAttachedObject;
  542. hoverLocked = false;
  543. //Give focus to the top most object on the stack if it changed
  544. if (newTopObject != null && newTopObject != prevTopObject)
  545. {
  546. newTopObject.SetActive(true);
  547. newTopObject.SendMessage("OnHandFocusAcquired", this, SendMessageOptions.DontRequireReceiver);
  548. }
  549. }
  550. CleanUpAttachedObjectStack();
  551. if (mainRenderModel != null)
  552. mainRenderModel.MatchHandToTransform(mainRenderModel.transform);
  553. if (hoverhighlightRenderModel != null)
  554. hoverhighlightRenderModel.MatchHandToTransform(hoverhighlightRenderModel.transform);
  555. }
  556. //-------------------------------------------------
  557. // Get the world velocity of the VR Hand.
  558. //-------------------------------------------------
  559. public Vector3 GetTrackedObjectVelocity(float timeOffset = 0)
  560. {
  561. if (trackedObject == null)
  562. {
  563. Vector3 velocityTarget, angularTarget;
  564. GetUpdatedAttachedVelocities(currentAttachedObjectInfo.Value, out velocityTarget, out angularTarget);
  565. return velocityTarget;
  566. }
  567. if (isActive)
  568. {
  569. if (timeOffset == 0)
  570. return Player.instance.trackingOriginTransform.TransformVector(trackedObject.GetVelocity());
  571. else
  572. {
  573. Vector3 velocity;
  574. Vector3 angularVelocity;
  575. trackedObject.GetVelocitiesAtTimeOffset(timeOffset, out velocity, out angularVelocity);
  576. return Player.instance.trackingOriginTransform.TransformVector(velocity);
  577. }
  578. }
  579. return Vector3.zero;
  580. }
  581. //-------------------------------------------------
  582. // Get the world space angular velocity of the VR Hand.
  583. //-------------------------------------------------
  584. public Vector3 GetTrackedObjectAngularVelocity(float timeOffset = 0)
  585. {
  586. if (trackedObject == null)
  587. {
  588. Vector3 velocityTarget, angularTarget;
  589. GetUpdatedAttachedVelocities(currentAttachedObjectInfo.Value, out velocityTarget, out angularTarget);
  590. return angularTarget;
  591. }
  592. if (isActive)
  593. {
  594. if (timeOffset == 0)
  595. return Player.instance.trackingOriginTransform.TransformDirection(trackedObject.GetAngularVelocity());
  596. else
  597. {
  598. Vector3 velocity;
  599. Vector3 angularVelocity;
  600. trackedObject.GetVelocitiesAtTimeOffset(timeOffset, out velocity, out angularVelocity);
  601. return Player.instance.trackingOriginTransform.TransformDirection(angularVelocity);
  602. }
  603. }
  604. return Vector3.zero;
  605. }
  606. public void GetEstimatedPeakVelocities(out Vector3 velocity, out Vector3 angularVelocity)
  607. {
  608. trackedObject.GetEstimatedPeakVelocities(out velocity, out angularVelocity);
  609. velocity = Player.instance.trackingOriginTransform.TransformVector(velocity);
  610. angularVelocity = Player.instance.trackingOriginTransform.TransformDirection(angularVelocity);
  611. }
  612. //-------------------------------------------------
  613. private void CleanUpAttachedObjectStack()
  614. {
  615. attachedObjects.RemoveAll(l => l.attachedObject == null);
  616. }
  617. //-------------------------------------------------
  618. protected virtual void Awake()
  619. {
  620. inputFocusAction = SteamVR_Events.InputFocusAction(OnInputFocus);
  621. if (hoverSphereTransform == null)
  622. hoverSphereTransform = this.transform;
  623. if (objectAttachmentPoint == null)
  624. objectAttachmentPoint = this.transform;
  625. applicationLostFocusObject = new GameObject("_application_lost_focus");
  626. applicationLostFocusObject.transform.parent = transform;
  627. applicationLostFocusObject.SetActive(false);
  628. if (trackedObject == null)
  629. {
  630. trackedObject = this.gameObject.GetComponent<SteamVR_Behaviour_Pose>();
  631. if (trackedObject != null)
  632. trackedObject.onTransformUpdatedEvent += OnTransformUpdated;
  633. }
  634. }
  635. protected virtual void OnDestroy()
  636. {
  637. if (trackedObject != null)
  638. {
  639. trackedObject.onTransformUpdatedEvent -= OnTransformUpdated;
  640. }
  641. }
  642. protected virtual void OnTransformUpdated(SteamVR_Behaviour_Pose updatedPose, SteamVR_Input_Sources updatedSource)
  643. {
  644. HandFollowUpdate();
  645. }
  646. //-------------------------------------------------
  647. protected virtual IEnumerator Start()
  648. {
  649. // save off player instance
  650. playerInstance = Player.instance;
  651. if (!playerInstance)
  652. {
  653. Debug.LogError("<b>[SteamVR Interaction]</b> No player instance found in Hand Start()");
  654. }
  655. // allocate array for colliders
  656. overlappingColliders = new Collider[ColliderArraySize];
  657. // We are a "no SteamVR fallback hand" if we have this camera set
  658. // we'll use the right mouse to look around and left mouse to interact
  659. // - don't need to find the device
  660. if (noSteamVRFallbackCamera)
  661. {
  662. yield break;
  663. }
  664. //Debug.Log( "<b>[SteamVR Interaction]</b> Hand - initializing connection routine" );
  665. while (true)
  666. {
  667. if (isPoseValid)
  668. {
  669. InitController();
  670. break;
  671. }
  672. yield return null;
  673. }
  674. }
  675. //-------------------------------------------------
  676. protected virtual void UpdateHovering()
  677. {
  678. if ((noSteamVRFallbackCamera == null) && (isActive == false))
  679. {
  680. return;
  681. }
  682. if (hoverLocked)
  683. return;
  684. if (applicationLostFocusObject.activeSelf)
  685. return;
  686. float closestDistance = float.MaxValue;
  687. Interactable closestInteractable = null;
  688. if (useHoverSphere)
  689. {
  690. float scaledHoverRadius = hoverSphereRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(hoverSphereTransform));
  691. CheckHoveringForTransform(hoverSphereTransform.position, scaledHoverRadius, ref closestDistance, ref closestInteractable, Color.green);
  692. }
  693. if (useControllerHoverComponent && mainRenderModel != null && mainRenderModel.IsControllerVisibile())
  694. {
  695. float scaledHoverRadius = controllerHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
  696. CheckHoveringForTransform(mainRenderModel.GetControllerPosition(controllerHoverComponent), scaledHoverRadius / 2f, ref closestDistance, ref closestInteractable, Color.blue);
  697. }
  698. if (useFingerJointHover && mainRenderModel != null && mainRenderModel.IsHandVisibile())
  699. {
  700. float scaledHoverRadius = fingerJointHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
  701. CheckHoveringForTransform(mainRenderModel.GetBonePosition((int)fingerJointHover), scaledHoverRadius / 2f, ref closestDistance, ref closestInteractable, Color.yellow);
  702. }
  703. // Hover on this one
  704. hoveringInteractable = closestInteractable;
  705. }
  706. protected virtual bool CheckHoveringForTransform(Vector3 hoverPosition, float hoverRadius, ref float closestDistance, ref Interactable closestInteractable, Color debugColor)
  707. {
  708. bool foundCloser = false;
  709. // null out old vals
  710. for (int i = 0; i < overlappingColliders.Length; ++i)
  711. {
  712. overlappingColliders[i] = null;
  713. }
  714. int numColliding = Physics.OverlapSphereNonAlloc(hoverPosition, hoverRadius, overlappingColliders, hoverLayerMask.value);
  715. if (numColliding == ColliderArraySize)
  716. 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");
  717. // DebugVar
  718. int iActualColliderCount = 0;
  719. // Pick the closest hovering
  720. for (int colliderIndex = 0; colliderIndex < overlappingColliders.Length; colliderIndex++)
  721. {
  722. Collider collider = overlappingColliders[colliderIndex];
  723. if (collider == null)
  724. continue;
  725. Interactable contacting = collider.GetComponentInParent<Interactable>();
  726. // Yeah, it's null, skip
  727. if (contacting == null)
  728. continue;
  729. // Ignore this collider for hovering
  730. IgnoreHovering ignore = collider.GetComponent<IgnoreHovering>();
  731. if (ignore != null)
  732. {
  733. if (ignore.onlyIgnoreHand == null || ignore.onlyIgnoreHand == this)
  734. {
  735. continue;
  736. }
  737. }
  738. // Can't hover over the object if it's attached
  739. bool hoveringOverAttached = false;
  740. for (int attachedIndex = 0; attachedIndex < attachedObjects.Count; attachedIndex++)
  741. {
  742. if (attachedObjects[attachedIndex].attachedObject == contacting.gameObject)
  743. {
  744. hoveringOverAttached = true;
  745. break;
  746. }
  747. }
  748. if (hoveringOverAttached)
  749. continue;
  750. // Occupied by another hand, so we can't touch it
  751. if (otherHand && otherHand.hoveringInteractable == contacting)
  752. continue;
  753. // Best candidate so far...
  754. float distance = Vector3.Distance(contacting.transform.position, hoverPosition);
  755. if (distance < closestDistance)
  756. {
  757. closestDistance = distance;
  758. closestInteractable = contacting;
  759. foundCloser = true;
  760. }
  761. iActualColliderCount++;
  762. }
  763. if (showDebugInteractables && foundCloser)
  764. {
  765. Debug.DrawLine(hoverPosition, closestInteractable.transform.position, debugColor, .05f, false);
  766. }
  767. if (iActualColliderCount > 0 && iActualColliderCount != prevOverlappingColliders)
  768. {
  769. prevOverlappingColliders = iActualColliderCount;
  770. if (spewDebugText)
  771. HandDebugLog("Found " + iActualColliderCount + " overlapping colliders.");
  772. }
  773. return foundCloser;
  774. }
  775. //-------------------------------------------------
  776. protected virtual void UpdateNoSteamVRFallback()
  777. {
  778. if (noSteamVRFallbackCamera)
  779. {
  780. Ray ray = noSteamVRFallbackCamera.ScreenPointToRay(Input.mousePosition);
  781. if (attachedObjects.Count > 0)
  782. {
  783. // Holding down the mouse:
  784. // move around a fixed distance from the camera
  785. transform.position = ray.origin + noSteamVRFallbackInteractorDistance * ray.direction;
  786. }
  787. else
  788. {
  789. // Not holding down the mouse:
  790. // cast out a ray to see what we should mouse over
  791. // Don't want to hit the hand and anything underneath it
  792. // So move it back behind the camera when we do the raycast
  793. Vector3 oldPosition = transform.position;
  794. transform.position = noSteamVRFallbackCamera.transform.forward * (-1000.0f);
  795. RaycastHit raycastHit;
  796. if (Physics.Raycast(ray, out raycastHit, noSteamVRFallbackMaxDistanceNoItem))
  797. {
  798. transform.position = raycastHit.point;
  799. // Remember this distance in case we click and drag the mouse
  800. noSteamVRFallbackInteractorDistance = Mathf.Min(noSteamVRFallbackMaxDistanceNoItem, raycastHit.distance);
  801. }
  802. else if (noSteamVRFallbackInteractorDistance > 0.0f)
  803. {
  804. // Move it around at the distance we last had a hit
  805. transform.position = ray.origin + Mathf.Min(noSteamVRFallbackMaxDistanceNoItem, noSteamVRFallbackInteractorDistance) * ray.direction;
  806. }
  807. else
  808. {
  809. // Didn't hit, just leave it where it was
  810. transform.position = oldPosition;
  811. }
  812. }
  813. }
  814. }
  815. //-------------------------------------------------
  816. private void UpdateDebugText()
  817. {
  818. if (showDebugText)
  819. {
  820. if (debugText == null)
  821. {
  822. debugText = new GameObject("_debug_text").AddComponent<TextMesh>();
  823. debugText.fontSize = 120;
  824. debugText.characterSize = 0.001f;
  825. debugText.transform.parent = transform;
  826. debugText.transform.localRotation = Quaternion.Euler(90.0f, 0.0f, 0.0f);
  827. }
  828. if (handType == SteamVR_Input_Sources.RightHand)
  829. {
  830. debugText.transform.localPosition = new Vector3(-0.05f, 0.0f, 0.0f);
  831. debugText.alignment = TextAlignment.Right;
  832. debugText.anchor = TextAnchor.UpperRight;
  833. }
  834. else
  835. {
  836. debugText.transform.localPosition = new Vector3(0.05f, 0.0f, 0.0f);
  837. debugText.alignment = TextAlignment.Left;
  838. debugText.anchor = TextAnchor.UpperLeft;
  839. }
  840. debugText.text = string.Format(
  841. "Hovering: {0}\n" +
  842. "Hover Lock: {1}\n" +
  843. "Attached: {2}\n" +
  844. "Total Attached: {3}\n" +
  845. "Type: {4}\n",
  846. (hoveringInteractable ? hoveringInteractable.gameObject.name : "null"),
  847. hoverLocked,
  848. (currentAttachedObject ? currentAttachedObject.name : "null"),
  849. attachedObjects.Count,
  850. handType.ToString());
  851. }
  852. else
  853. {
  854. if (debugText != null)
  855. {
  856. Destroy(debugText.gameObject);
  857. }
  858. }
  859. }
  860. //-------------------------------------------------
  861. protected virtual void OnEnable()
  862. {
  863. inputFocusAction.enabled = true;
  864. // Stagger updates between hands
  865. float hoverUpdateBegin = ((otherHand != null) && (otherHand.GetInstanceID() < GetInstanceID())) ? (0.5f * hoverUpdateInterval) : (0.0f);
  866. InvokeRepeating("UpdateHovering", hoverUpdateBegin, hoverUpdateInterval);
  867. InvokeRepeating("UpdateDebugText", hoverUpdateBegin, hoverUpdateInterval);
  868. }
  869. //-------------------------------------------------
  870. protected virtual void OnDisable()
  871. {
  872. inputFocusAction.enabled = false;
  873. CancelInvoke();
  874. }
  875. //-------------------------------------------------
  876. protected virtual void Update()
  877. {
  878. UpdateNoSteamVRFallback();
  879. GameObject attachedObject = currentAttachedObject;
  880. if (attachedObject != null)
  881. {
  882. attachedObject.SendMessage("HandAttachedUpdate", this, SendMessageOptions.DontRequireReceiver);
  883. }
  884. if (hoveringInteractable)
  885. {
  886. hoveringInteractable.SendMessage("HandHoverUpdate", this, SendMessageOptions.DontRequireReceiver);
  887. }
  888. }
  889. /// <summary>
  890. /// Returns true when the hand is currently hovering over the interactable passed in
  891. /// </summary>
  892. public bool IsStillHovering(Interactable interactable)
  893. {
  894. return hoveringInteractable == interactable;
  895. }
  896. protected virtual void HandFollowUpdate()
  897. {
  898. GameObject attachedObject = currentAttachedObject;
  899. if (attachedObject != null)
  900. {
  901. if (currentAttachedObjectInfo.Value.interactable != null)
  902. {
  903. SteamVR_Skeleton_PoseSnapshot pose = null;
  904. if (currentAttachedObjectInfo.Value.interactable.skeletonPoser != null && HasSkeleton())
  905. {
  906. pose = currentAttachedObjectInfo.Value.interactable.skeletonPoser.GetBlendedPose(skeleton);
  907. }
  908. if (currentAttachedObjectInfo.Value.interactable.handFollowTransform)
  909. {
  910. Quaternion targetHandRotation;
  911. Vector3 targetHandPosition;
  912. if (pose == null)
  913. {
  914. Quaternion offset = Quaternion.Inverse(this.transform.rotation) * currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation;
  915. targetHandRotation = currentAttachedObjectInfo.Value.interactable.transform.rotation * Quaternion.Inverse(offset);
  916. Vector3 worldOffset = (this.transform.position - currentAttachedObjectInfo.Value.handAttachmentPointTransform.position);
  917. Quaternion rotationDiff = mainRenderModel.GetHandRotation() * Quaternion.Inverse(this.transform.rotation);
  918. Vector3 localOffset = rotationDiff * worldOffset;
  919. targetHandPosition = currentAttachedObjectInfo.Value.interactable.transform.position + localOffset;
  920. }
  921. else
  922. {
  923. Transform objectT = currentAttachedObjectInfo.Value.attachedObject.transform;
  924. Vector3 oldItemPos = objectT.position;
  925. Quaternion oldItemRot = objectT.transform.rotation;
  926. objectT.position = TargetItemPosition(currentAttachedObjectInfo.Value);
  927. objectT.rotation = TargetItemRotation(currentAttachedObjectInfo.Value);
  928. Vector3 localSkelePos = objectT.InverseTransformPoint(transform.position);
  929. Quaternion localSkeleRot = Quaternion.Inverse(objectT.rotation) * transform.rotation;
  930. objectT.position = oldItemPos;
  931. objectT.rotation = oldItemRot;
  932. targetHandPosition = objectT.TransformPoint(localSkelePos);
  933. targetHandRotation = objectT.rotation * localSkeleRot;
  934. }
  935. if (mainRenderModel != null)
  936. mainRenderModel.SetHandRotation(targetHandRotation);
  937. if (hoverhighlightRenderModel != null)
  938. hoverhighlightRenderModel.SetHandRotation(targetHandRotation);
  939. if (mainRenderModel != null)
  940. mainRenderModel.SetHandPosition(targetHandPosition);
  941. if (hoverhighlightRenderModel != null)
  942. hoverhighlightRenderModel.SetHandPosition(targetHandPosition);
  943. }
  944. }
  945. }
  946. }
  947. protected virtual void FixedUpdate()
  948. {
  949. if (currentAttachedObject != null)
  950. {
  951. AttachedObject attachedInfo = currentAttachedObjectInfo.Value;
  952. if (attachedInfo.attachedObject != null)
  953. {
  954. if (attachedInfo.HasAttachFlag(AttachmentFlags.VelocityMovement))
  955. {
  956. if(attachedInfo.interactable.attachEaseIn == false || attachedInfo.interactable.snapAttachEaseInCompleted)
  957. UpdateAttachedVelocity(attachedInfo);
  958. /*if (attachedInfo.interactable.handFollowTransformPosition)
  959. {
  960. skeleton.transform.position = TargetSkeletonPosition(attachedInfo);
  961. skeleton.transform.rotation = attachedInfo.attachedObject.transform.rotation * attachedInfo.skeletonLockRotation;
  962. }*/
  963. }else
  964. {
  965. if (attachedInfo.HasAttachFlag(AttachmentFlags.ParentToHand))
  966. {
  967. attachedInfo.attachedObject.transform.position = TargetItemPosition(attachedInfo);
  968. attachedInfo.attachedObject.transform.rotation = TargetItemRotation(attachedInfo);
  969. }
  970. }
  971. if (attachedInfo.interactable.attachEaseIn)
  972. {
  973. float t = Util.RemapNumberClamped(Time.time, attachedInfo.attachTime, attachedInfo.attachTime + attachedInfo.interactable.snapAttachEaseInTime, 0.0f, 1.0f);
  974. if (t < 1.0f)
  975. {
  976. if (attachedInfo.HasAttachFlag(AttachmentFlags.VelocityMovement))
  977. {
  978. attachedInfo.attachedRigidbody.velocity = Vector3.zero;
  979. attachedInfo.attachedRigidbody.angularVelocity = Vector3.zero;
  980. }
  981. t = attachedInfo.interactable.snapAttachEaseInCurve.Evaluate(t);
  982. attachedInfo.attachedObject.transform.position = Vector3.Lerp(attachedInfo.easeSourcePosition, TargetItemPosition(attachedInfo), t);
  983. attachedInfo.attachedObject.transform.rotation = Quaternion.Lerp(attachedInfo.easeSourceRotation, TargetItemRotation(attachedInfo), t);
  984. }
  985. else if (!attachedInfo.interactable.snapAttachEaseInCompleted)
  986. {
  987. attachedInfo.interactable.gameObject.SendMessage("OnThrowableAttachEaseInCompleted", this, SendMessageOptions.DontRequireReceiver);
  988. attachedInfo.interactable.snapAttachEaseInCompleted = true;
  989. }
  990. }
  991. }
  992. }
  993. }
  994. protected const float MaxVelocityChange = 10f;
  995. protected const float VelocityMagic = 6000f;
  996. protected const float AngularVelocityMagic = 50f;
  997. protected const float MaxAngularVelocityChange = 20f;
  998. protected void UpdateAttachedVelocity(AttachedObject attachedObjectInfo)
  999. {
  1000. Vector3 velocityTarget, angularTarget;
  1001. bool success = GetUpdatedAttachedVelocities(attachedObjectInfo, out velocityTarget, out angularTarget);
  1002. if (success)
  1003. {
  1004. float scale = SteamVR_Utils.GetLossyScale(currentAttachedObjectInfo.Value.handAttachmentPointTransform);
  1005. float maxAngularVelocityChange = MaxAngularVelocityChange * scale;
  1006. float maxVelocityChange = MaxVelocityChange * scale;
  1007. attachedObjectInfo.attachedRigidbody.velocity = Vector3.MoveTowards(attachedObjectInfo.attachedRigidbody.velocity, velocityTarget, maxVelocityChange);
  1008. attachedObjectInfo.attachedRigidbody.angularVelocity = Vector3.MoveTowards(attachedObjectInfo.attachedRigidbody.angularVelocity, angularTarget, maxAngularVelocityChange);
  1009. }
  1010. }
  1011. protected Vector3 TargetItemPosition(AttachedObject attachedObject)
  1012. {
  1013. if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
  1014. {
  1015. Vector3 tp = attachedObject.handAttachmentPointTransform.InverseTransformPoint(transform.TransformPoint(attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton).position));
  1016. //tp.x *= -1;
  1017. return currentAttachedObjectInfo.Value.handAttachmentPointTransform.TransformPoint(tp);
  1018. }
  1019. else
  1020. {
  1021. return currentAttachedObjectInfo.Value.handAttachmentPointTransform.TransformPoint(attachedObject.initialPositionalOffset);
  1022. }
  1023. }
  1024. protected Quaternion TargetItemRotation(AttachedObject attachedObject)
  1025. {
  1026. if (attachedObject.interactable != null && attachedObject.interactable.skeletonPoser != null && HasSkeleton())
  1027. {
  1028. Quaternion tr = Quaternion.Inverse(attachedObject.handAttachmentPointTransform.rotation) * (transform.rotation * attachedObject.interactable.skeletonPoser.GetBlendedPose(skeleton).rotation);
  1029. return currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation * tr;
  1030. }
  1031. else
  1032. {
  1033. return currentAttachedObjectInfo.Value.handAttachmentPointTransform.rotation * attachedObject.initialRotationalOffset;
  1034. }
  1035. }
  1036. protected bool GetUpdatedAttachedVelocities(AttachedObject attachedObjectInfo, out Vector3 velocityTarget, out Vector3 angularTarget)
  1037. {
  1038. bool realNumbers = false;
  1039. float velocityMagic = VelocityMagic;
  1040. float angularVelocityMagic = AngularVelocityMagic;
  1041. Vector3 targetItemPosition = TargetItemPosition(attachedObjectInfo);
  1042. Vector3 positionDelta = (targetItemPosition - attachedObjectInfo.attachedRigidbody.position);
  1043. velocityTarget = (positionDelta * velocityMagic * Time.deltaTime);
  1044. if (float.IsNaN(velocityTarget.x) == false && float.IsInfinity(velocityTarget.x) == false)
  1045. {
  1046. if (noSteamVRFallbackCamera)
  1047. velocityTarget /= 10; //hacky fix for fallback
  1048. realNumbers = true;
  1049. }
  1050. else
  1051. velocityTarget = Vector3.zero;
  1052. Quaternion targetItemRotation = TargetItemRotation(attachedObjectInfo);
  1053. Quaternion rotationDelta = targetItemRotation * Quaternion.Inverse(attachedObjectInfo.attachedObject.transform.rotation);
  1054. float angle;
  1055. Vector3 axis;
  1056. rotationDelta.ToAngleAxis(out angle, out axis);
  1057. if (angle > 180)
  1058. angle -= 360;
  1059. if (angle != 0 && float.IsNaN(axis.x) == false && float.IsInfinity(axis.x) == false)
  1060. {
  1061. angularTarget = angle * axis * angularVelocityMagic * Time.deltaTime;
  1062. if (noSteamVRFallbackCamera)
  1063. angularTarget /= 10; //hacky fix for fallback
  1064. realNumbers &= true;
  1065. }
  1066. else
  1067. angularTarget = Vector3.zero;
  1068. return realNumbers;
  1069. }
  1070. //-------------------------------------------------
  1071. protected virtual void OnInputFocus(bool hasFocus)
  1072. {
  1073. if (hasFocus)
  1074. {
  1075. DetachObject(applicationLostFocusObject, true);
  1076. applicationLostFocusObject.SetActive(false);
  1077. UpdateHovering();
  1078. BroadcastMessage("OnParentHandInputFocusAcquired", SendMessageOptions.DontRequireReceiver);
  1079. }
  1080. else
  1081. {
  1082. applicationLostFocusObject.SetActive(true);
  1083. AttachObject(applicationLostFocusObject, GrabTypes.Scripted, AttachmentFlags.ParentToHand);
  1084. BroadcastMessage("OnParentHandInputFocusLost", SendMessageOptions.DontRequireReceiver);
  1085. }
  1086. }
  1087. //-------------------------------------------------
  1088. protected virtual void OnDrawGizmos()
  1089. {
  1090. if (useHoverSphere && hoverSphereTransform != null)
  1091. {
  1092. Gizmos.color = Color.green;
  1093. float scaledHoverRadius = hoverSphereRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(hoverSphereTransform));
  1094. Gizmos.DrawWireSphere(hoverSphereTransform.position, scaledHoverRadius/2);
  1095. }
  1096. if (useControllerHoverComponent && mainRenderModel != null && mainRenderModel.IsControllerVisibile())
  1097. {
  1098. Gizmos.color = Color.blue;
  1099. float scaledHoverRadius = controllerHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
  1100. Gizmos.DrawWireSphere(mainRenderModel.GetControllerPosition(controllerHoverComponent), scaledHoverRadius/2);
  1101. }
  1102. if (useFingerJointHover && mainRenderModel != null && mainRenderModel.IsHandVisibile())
  1103. {
  1104. Gizmos.color = Color.yellow;
  1105. float scaledHoverRadius = fingerJointHoverRadius * Mathf.Abs(SteamVR_Utils.GetLossyScale(this.transform));
  1106. Gizmos.DrawWireSphere(mainRenderModel.GetBonePosition((int)fingerJointHover), scaledHoverRadius/2);
  1107. }
  1108. }
  1109. //-------------------------------------------------
  1110. private void HandDebugLog(string msg)
  1111. {
  1112. if (spewDebugText)
  1113. {
  1114. Debug.Log("<b>[SteamVR Interaction]</b> Hand (" + this.name + "): " + msg);
  1115. }
  1116. }
  1117. //-------------------------------------------------
  1118. // Continue to hover over this object indefinitely, whether or not the Hand moves out of its interaction trigger volume.
  1119. //
  1120. // interactable - The Interactable to hover over indefinitely.
  1121. //-------------------------------------------------
  1122. public void HoverLock(Interactable interactable)
  1123. {
  1124. if (spewDebugText)
  1125. HandDebugLog("HoverLock " + interactable);
  1126. hoverLocked = true;
  1127. hoveringInteractable = interactable;
  1128. }
  1129. //-------------------------------------------------
  1130. // Stop hovering over this object indefinitely.
  1131. //
  1132. // interactable - The hover-locked Interactable to stop hovering over indefinitely.
  1133. //-------------------------------------------------
  1134. public void HoverUnlock(Interactable interactable)
  1135. {
  1136. if (spewDebugText)
  1137. HandDebugLog("HoverUnlock " + interactable);
  1138. if (hoveringInteractable == interactable)
  1139. {
  1140. hoverLocked = false;
  1141. }
  1142. }
  1143. public void TriggerHapticPulse(ushort microSecondsDuration)
  1144. {
  1145. float seconds = (float)microSecondsDuration / 1000000f;
  1146. hapticAction.Execute(0, seconds, 1f / seconds, 1, handType);
  1147. }
  1148. public void TriggerHapticPulse(float duration, float frequency, float amplitude)
  1149. {
  1150. hapticAction.Execute(0, duration, frequency, amplitude, handType);
  1151. }
  1152. public void ShowGrabHint()
  1153. {
  1154. ControllerButtonHints.ShowButtonHint(this, grabGripAction); //todo: assess
  1155. }
  1156. public void HideGrabHint()
  1157. {
  1158. ControllerButtonHints.HideButtonHint(this, grabGripAction); //todo: assess
  1159. }
  1160. public void ShowGrabHint(string text)
  1161. {
  1162. ControllerButtonHints.ShowTextHint(this, grabGripAction, text);
  1163. }
  1164. public GrabTypes GetGrabStarting(GrabTypes explicitType = GrabTypes.None)
  1165. {
  1166. if (explicitType != GrabTypes.None)
  1167. {
  1168. if (noSteamVRFallbackCamera)
  1169. {
  1170. if (Input.GetMouseButtonDown(0))
  1171. return explicitType;
  1172. }
  1173. if (explicitType == GrabTypes.Pinch && grabPinchAction.GetStateDown(handType))
  1174. return GrabTypes.Pinch;
  1175. if (explicitType == GrabTypes.Grip && grabGripAction.GetStateDown(handType))
  1176. return GrabTypes.Grip;
  1177. }
  1178. else
  1179. {
  1180. if (noSteamVRFallbackCamera)
  1181. {
  1182. if (Input.GetMouseButtonDown(0))
  1183. return GrabTypes.Grip;
  1184. }
  1185. if (grabPinchAction.GetStateDown(handType))
  1186. return GrabTypes.Pinch;
  1187. if (grabGripAction.GetStateDown(handType))
  1188. return GrabTypes.Grip;
  1189. }
  1190. return GrabTypes.None;
  1191. }
  1192. public GrabTypes GetGrabEnding(GrabTypes explicitType = GrabTypes.None)
  1193. {
  1194. if (explicitType != GrabTypes.None)
  1195. {
  1196. if (noSteamVRFallbackCamera)
  1197. {
  1198. if (Input.GetMouseButtonUp(0))
  1199. return explicitType;
  1200. }
  1201. if (explicitType == GrabTypes.Pinch && grabPinchAction.GetStateUp(handType))
  1202. return GrabTypes.Pinch;
  1203. if (explicitType == GrabTypes.Grip && grabGripAction.GetStateUp(handType))
  1204. return GrabTypes.Grip;
  1205. }
  1206. else
  1207. {
  1208. if (noSteamVRFallbackCamera)
  1209. {
  1210. if (Input.GetMouseButtonUp(0))
  1211. return GrabTypes.Grip;
  1212. }
  1213. if (grabPinchAction.GetStateUp(handType))
  1214. return GrabTypes.Pinch;
  1215. if (grabGripAction.GetStateUp(handType))
  1216. return GrabTypes.Grip;
  1217. }
  1218. return GrabTypes.None;
  1219. }
  1220. public bool IsGrabEnding(GameObject attachedObject)
  1221. {
  1222. for (int attachedObjectIndex = 0; attachedObjectIndex < attachedObjects.Count; attachedObjectIndex++)
  1223. {
  1224. if (attachedObjects[attachedObjectIndex].attachedObject == attachedObject)
  1225. {
  1226. return IsGrabbingWithType(attachedObjects[attachedObjectIndex].grabbedWithType) == false;
  1227. }
  1228. }
  1229. return false;
  1230. }
  1231. public bool IsGrabbingWithType(GrabTypes type)
  1232. {
  1233. if (noSteamVRFallbackCamera)
  1234. {
  1235. if (Input.GetMouseButton(0))
  1236. return true;
  1237. }
  1238. switch (type)
  1239. {
  1240. case GrabTypes.Pinch:
  1241. return grabPinchAction.GetState(handType);
  1242. case GrabTypes.Grip:
  1243. return grabGripAction.GetState(handType);
  1244. default:
  1245. return false;
  1246. }
  1247. }
  1248. public bool IsGrabbingWithOppositeType(GrabTypes type)
  1249. {
  1250. if (noSteamVRFallbackCamera)
  1251. {
  1252. if (Input.GetMouseButton(0))
  1253. return true;
  1254. }
  1255. switch (type)
  1256. {
  1257. case GrabTypes.Pinch:
  1258. return grabGripAction.GetState(handType);
  1259. case GrabTypes.Grip:
  1260. return grabPinchAction.GetState(handType);
  1261. default:
  1262. return false;
  1263. }
  1264. }
  1265. public GrabTypes GetBestGrabbingType()
  1266. {
  1267. return GetBestGrabbingType(GrabTypes.None);
  1268. }
  1269. public GrabTypes GetBestGrabbingType(GrabTypes preferred, bool forcePreference = false)
  1270. {
  1271. if (noSteamVRFallbackCamera)
  1272. {
  1273. if (Input.GetMouseButton(0))
  1274. return preferred;
  1275. }
  1276. if (preferred == GrabTypes.Pinch)
  1277. {
  1278. if (grabPinchAction.GetState(handType))
  1279. return GrabTypes.Pinch;
  1280. else if (forcePreference)
  1281. return GrabTypes.None;
  1282. }
  1283. if (preferred == GrabTypes.Grip)
  1284. {
  1285. if (grabGripAction.GetState(handType))
  1286. return GrabTypes.Grip;
  1287. else if (forcePreference)
  1288. return GrabTypes.None;
  1289. }
  1290. if (grabPinchAction.GetState(handType))
  1291. return GrabTypes.Pinch;
  1292. if (grabGripAction.GetState(handType))
  1293. return GrabTypes.Grip;
  1294. return GrabTypes.None;
  1295. }
  1296. //-------------------------------------------------
  1297. private void InitController()
  1298. {
  1299. if (spewDebugText)
  1300. HandDebugLog("Hand " + name + " connected with type " + handType.ToString());
  1301. bool hadOldRendermodel = mainRenderModel != null;
  1302. EVRSkeletalMotionRange oldRM_rom = EVRSkeletalMotionRange.WithController;
  1303. if(hadOldRendermodel)
  1304. oldRM_rom = mainRenderModel.GetSkeletonRangeOfMotion;
  1305. foreach (RenderModel r in renderModels)
  1306. {
  1307. if (r != null)
  1308. Destroy(r.gameObject);
  1309. }
  1310. renderModels.Clear();
  1311. GameObject renderModelInstance = GameObject.Instantiate(renderModelPrefab);
  1312. renderModelInstance.layer = gameObject.layer;
  1313. renderModelInstance.tag = gameObject.tag;
  1314. renderModelInstance.transform.parent = this.transform;
  1315. renderModelInstance.transform.localPosition = Vector3.zero;
  1316. renderModelInstance.transform.localRotation = Quaternion.identity;
  1317. renderModelInstance.transform.localScale = renderModelPrefab.transform.localScale;
  1318. //TriggerHapticPulse(800); //pulse on controller init
  1319. int deviceIndex = trackedObject.GetDeviceIndex();
  1320. mainRenderModel = renderModelInstance.GetComponent<RenderModel>();
  1321. renderModels.Add(mainRenderModel);
  1322. if (hadOldRendermodel)
  1323. mainRenderModel.SetSkeletonRangeOfMotion(oldRM_rom);
  1324. this.BroadcastMessage("SetInputSource", handType, SendMessageOptions.DontRequireReceiver); // let child objects know we've initialized
  1325. this.BroadcastMessage("OnHandInitialized", deviceIndex, SendMessageOptions.DontRequireReceiver); // let child objects know we've initialized
  1326. }
  1327. public void SetRenderModel(GameObject prefab)
  1328. {
  1329. renderModelPrefab = prefab;
  1330. if (mainRenderModel != null && isPoseValid)
  1331. InitController();
  1332. }
  1333. public void SetHoverRenderModel(RenderModel hoverRenderModel)
  1334. {
  1335. hoverhighlightRenderModel = hoverRenderModel;
  1336. renderModels.Add(hoverRenderModel);
  1337. }
  1338. public int GetDeviceIndex()
  1339. {
  1340. return trackedObject.GetDeviceIndex();
  1341. }
  1342. }
  1343. [System.Serializable]
  1344. public class HandEvent : UnityEvent<Hand> { }
  1345. #if UNITY_EDITOR
  1346. //-------------------------------------------------------------------------
  1347. [UnityEditor.CustomEditor(typeof(Hand))]
  1348. public class HandEditor : UnityEditor.Editor
  1349. {
  1350. //-------------------------------------------------
  1351. // Custom Inspector GUI allows us to click from within the UI
  1352. //-------------------------------------------------
  1353. public override void OnInspectorGUI()
  1354. {
  1355. DrawDefaultInspector();
  1356. /*
  1357. Hand hand = (Hand)target;
  1358. if (hand.otherHand)
  1359. {
  1360. if (hand.otherHand.otherHand != hand)
  1361. {
  1362. UnityEditor.EditorGUILayout.HelpBox("The otherHand of this Hand's otherHand is not this Hand.", UnityEditor.MessageType.Warning);
  1363. }
  1364. if (hand.handType == SteamVR_Input_Sources.LeftHand && hand.otherHand && hand.otherHand.handType != SteamVR_Input_Sources.RightHand)
  1365. {
  1366. UnityEditor.EditorGUILayout.HelpBox("This is a left Hand but otherHand is not a right Hand.", UnityEditor.MessageType.Warning);
  1367. }
  1368. if (hand.handType == SteamVR_Input_Sources.RightHand && hand.otherHand && hand.otherHand.handType != SteamVR_Input_Sources.LeftHand)
  1369. {
  1370. UnityEditor.EditorGUILayout.HelpBox("This is a right Hand but otherHand is not a left Hand.", UnityEditor.MessageType.Warning);
  1371. }
  1372. if (hand.handType == SteamVR_Input_Sources.Any && hand.otherHand && hand.otherHand.handType != SteamVR_Input_Sources.Any)
  1373. {
  1374. UnityEditor.EditorGUILayout.HelpBox("This is an any-handed Hand but otherHand is not an any-handed Hand.", UnityEditor.MessageType.Warning);
  1375. }
  1376. }
  1377. */ //removing for now because it conflicts with other input sources (trackers and such)
  1378. }
  1379. }
  1380. #endif
  1381. }