Interactable.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. //======= Copyright (c) Valve Corporation, All rights reserved. ===============
  2. //
  3. // Purpose: This object will get hover events and can be attached to the hands
  4. //
  5. //=============================================================================
  6. using UnityEngine;
  7. using UnityEngine.Events;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. namespace Valve.VR.InteractionSystem
  11. {
  12. //-------------------------------------------------------------------------
  13. public class Interactable : MonoBehaviour
  14. {
  15. [Tooltip("Activates an action set on attach and deactivates on detach")]
  16. public SteamVR_ActionSet activateActionSetOnAttach;
  17. [Tooltip("Hide the whole hand on attachment and show on detach")]
  18. public bool hideHandOnAttach = true;
  19. [Tooltip("Hide the skeleton part of the hand on attachment and show on detach")]
  20. public bool hideSkeletonOnAttach = false;
  21. [Tooltip("Hide the controller part of the hand on attachment and show on detach")]
  22. public bool hideControllerOnAttach = false;
  23. [Tooltip("The integer in the animator to trigger on pickup. 0 for none")]
  24. public int handAnimationOnPickup = 0;
  25. [Tooltip("The range of motion to set on the skeleton. None for no change.")]
  26. public SkeletalMotionRangeChange setRangeOfMotionOnPickup = SkeletalMotionRangeChange.None;
  27. public delegate void OnAttachedToHandDelegate(Hand hand);
  28. public delegate void OnDetachedFromHandDelegate(Hand hand);
  29. public event OnAttachedToHandDelegate onAttachedToHand;
  30. public event OnDetachedFromHandDelegate onDetachedFromHand;
  31. [Tooltip("Specify whether you want to snap to the hand's object attachment point, or just the raw hand")]
  32. public bool useHandObjectAttachmentPoint = true;
  33. public bool attachEaseIn = false;
  34. [HideInInspector]
  35. public AnimationCurve snapAttachEaseInCurve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
  36. public float snapAttachEaseInTime = 0.15f;
  37. public bool snapAttachEaseInCompleted = false;
  38. // [Tooltip("The skeleton pose to apply when grabbing. Can only set this or handFollowTransform.")]
  39. [HideInInspector]
  40. public SteamVR_Skeleton_Poser skeletonPoser;
  41. [Tooltip("Should the rendered hand lock on to and follow the object")]
  42. public bool handFollowTransform= true;
  43. [Tooltip("Set whether or not you want this interactible to highlight when hovering over it")]
  44. public bool highlightOnHover = true;
  45. protected MeshRenderer[] highlightRenderers;
  46. protected MeshRenderer[] existingRenderers;
  47. protected GameObject highlightHolder;
  48. protected SkinnedMeshRenderer[] highlightSkinnedRenderers;
  49. protected SkinnedMeshRenderer[] existingSkinnedRenderers;
  50. protected static Material highlightMat;
  51. [Tooltip("An array of child gameObjects to not render a highlight for. Things like transparent parts, vfx, etc.")]
  52. public GameObject[] hideHighlight;
  53. [System.NonSerialized]
  54. public Hand attachedToHand;
  55. [System.NonSerialized]
  56. public Hand hoveringHand;
  57. public bool isDestroying { get; protected set; }
  58. public bool isHovering { get; protected set; }
  59. public bool wasHovering { get; protected set; }
  60. private void Awake()
  61. {
  62. skeletonPoser = GetComponent<SteamVR_Skeleton_Poser>();
  63. }
  64. protected virtual void Start()
  65. {
  66. highlightMat = (Material)Resources.Load("SteamVR_HoverHighlight", typeof(Material));
  67. if (highlightMat == null)
  68. Debug.LogError("<b>[SteamVR Interaction]</b> Hover Highlight Material is missing. Please create a material named 'SteamVR_HoverHighlight' and place it in a Resources folder");
  69. if (skeletonPoser != null)
  70. {
  71. if (useHandObjectAttachmentPoint)
  72. {
  73. //Debug.LogWarning("<b>[SteamVR Interaction]</b> SkeletonPose and useHandObjectAttachmentPoint both set at the same time. Ignoring useHandObjectAttachmentPoint.");
  74. useHandObjectAttachmentPoint = false;
  75. }
  76. }
  77. }
  78. protected virtual bool ShouldIgnoreHighlight(Component component)
  79. {
  80. return ShouldIgnore(component.gameObject);
  81. }
  82. protected virtual bool ShouldIgnore(GameObject check)
  83. {
  84. for (int ignoreIndex = 0; ignoreIndex < hideHighlight.Length; ignoreIndex++)
  85. {
  86. if (check == hideHighlight[ignoreIndex])
  87. return true;
  88. }
  89. return false;
  90. }
  91. protected virtual void CreateHighlightRenderers()
  92. {
  93. existingSkinnedRenderers = this.GetComponentsInChildren<SkinnedMeshRenderer>(true);
  94. highlightHolder = new GameObject("Highlighter");
  95. highlightSkinnedRenderers = new SkinnedMeshRenderer[existingSkinnedRenderers.Length];
  96. for (int skinnedIndex = 0; skinnedIndex < existingSkinnedRenderers.Length; skinnedIndex++)
  97. {
  98. SkinnedMeshRenderer existingSkinned = existingSkinnedRenderers[skinnedIndex];
  99. if (ShouldIgnoreHighlight(existingSkinned))
  100. continue;
  101. GameObject newSkinnedHolder = new GameObject("SkinnedHolder");
  102. newSkinnedHolder.transform.parent = highlightHolder.transform;
  103. SkinnedMeshRenderer newSkinned = newSkinnedHolder.AddComponent<SkinnedMeshRenderer>();
  104. Material[] materials = new Material[existingSkinned.sharedMaterials.Length];
  105. for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++)
  106. {
  107. materials[materialIndex] = highlightMat;
  108. }
  109. newSkinned.sharedMaterials = materials;
  110. newSkinned.sharedMesh = existingSkinned.sharedMesh;
  111. newSkinned.rootBone = existingSkinned.rootBone;
  112. newSkinned.updateWhenOffscreen = existingSkinned.updateWhenOffscreen;
  113. newSkinned.bones = existingSkinned.bones;
  114. highlightSkinnedRenderers[skinnedIndex] = newSkinned;
  115. }
  116. MeshFilter[] existingFilters = this.GetComponentsInChildren<MeshFilter>(true);
  117. existingRenderers = new MeshRenderer[existingFilters.Length];
  118. highlightRenderers = new MeshRenderer[existingFilters.Length];
  119. for (int filterIndex = 0; filterIndex < existingFilters.Length; filterIndex++)
  120. {
  121. MeshFilter existingFilter = existingFilters[filterIndex];
  122. MeshRenderer existingRenderer = existingFilter.GetComponent<MeshRenderer>();
  123. if (existingFilter == null || existingRenderer == null || ShouldIgnoreHighlight(existingFilter))
  124. continue;
  125. GameObject newFilterHolder = new GameObject("FilterHolder");
  126. newFilterHolder.transform.parent = highlightHolder.transform;
  127. MeshFilter newFilter = newFilterHolder.AddComponent<MeshFilter>();
  128. newFilter.sharedMesh = existingFilter.sharedMesh;
  129. MeshRenderer newRenderer = newFilterHolder.AddComponent<MeshRenderer>();
  130. Material[] materials = new Material[existingRenderer.sharedMaterials.Length];
  131. for (int materialIndex = 0; materialIndex < materials.Length; materialIndex++)
  132. {
  133. materials[materialIndex] = highlightMat;
  134. }
  135. newRenderer.sharedMaterials = materials;
  136. highlightRenderers[filterIndex] = newRenderer;
  137. existingRenderers[filterIndex] = existingRenderer;
  138. }
  139. }
  140. protected virtual void UpdateHighlightRenderers()
  141. {
  142. if (highlightHolder == null)
  143. return;
  144. for (int skinnedIndex = 0; skinnedIndex < existingSkinnedRenderers.Length; skinnedIndex++)
  145. {
  146. SkinnedMeshRenderer existingSkinned = existingSkinnedRenderers[skinnedIndex];
  147. SkinnedMeshRenderer highlightSkinned = highlightSkinnedRenderers[skinnedIndex];
  148. if (existingSkinned != null && highlightSkinned != null && attachedToHand == false)
  149. {
  150. highlightSkinned.transform.position = existingSkinned.transform.position;
  151. highlightSkinned.transform.rotation = existingSkinned.transform.rotation;
  152. highlightSkinned.transform.localScale = existingSkinned.transform.lossyScale;
  153. highlightSkinned.localBounds = existingSkinned.localBounds;
  154. highlightSkinned.enabled = isHovering && existingSkinned.enabled && existingSkinned.gameObject.activeInHierarchy;
  155. int blendShapeCount = existingSkinned.sharedMesh.blendShapeCount;
  156. for (int blendShapeIndex = 0; blendShapeIndex < blendShapeCount; blendShapeIndex++)
  157. {
  158. highlightSkinned.SetBlendShapeWeight(blendShapeIndex, existingSkinned.GetBlendShapeWeight(blendShapeIndex));
  159. }
  160. }
  161. else if (highlightSkinned != null)
  162. highlightSkinned.enabled = false;
  163. }
  164. for (int rendererIndex = 0; rendererIndex < highlightRenderers.Length; rendererIndex++)
  165. {
  166. MeshRenderer existingRenderer = existingRenderers[rendererIndex];
  167. MeshRenderer highlightRenderer = highlightRenderers[rendererIndex];
  168. if (existingRenderer != null && highlightRenderer != null && attachedToHand == false)
  169. {
  170. highlightRenderer.transform.position = existingRenderer.transform.position;
  171. highlightRenderer.transform.rotation = existingRenderer.transform.rotation;
  172. highlightRenderer.transform.localScale = existingRenderer.transform.lossyScale;
  173. highlightRenderer.enabled = isHovering && existingRenderer.enabled && existingRenderer.gameObject.activeInHierarchy;
  174. }
  175. else if (highlightRenderer != null)
  176. highlightRenderer.enabled = false;
  177. }
  178. }
  179. /// <summary>
  180. /// Called when a Hand starts hovering over this object
  181. /// </summary>
  182. protected virtual void OnHandHoverBegin(Hand hand)
  183. {
  184. wasHovering = isHovering;
  185. isHovering = true;
  186. hoveringHand = hand;
  187. if (highlightOnHover == true)
  188. {
  189. CreateHighlightRenderers();
  190. UpdateHighlightRenderers();
  191. }
  192. }
  193. /// <summary>
  194. /// Called when a Hand stops hovering over this object
  195. /// </summary>
  196. private void OnHandHoverEnd(Hand hand)
  197. {
  198. wasHovering = isHovering;
  199. isHovering = false;
  200. if (highlightOnHover && highlightHolder != null)
  201. Destroy(highlightHolder);
  202. }
  203. protected virtual void Update()
  204. {
  205. if (highlightOnHover)
  206. {
  207. UpdateHighlightRenderers();
  208. if (isHovering == false && highlightHolder != null)
  209. Destroy(highlightHolder);
  210. }
  211. }
  212. protected float blendToPoseTime = 0.1f;
  213. protected float releasePoseBlendTime = 0.2f;
  214. protected virtual void OnAttachedToHand(Hand hand)
  215. {
  216. if (activateActionSetOnAttach != null)
  217. activateActionSetOnAttach.Activate(hand.handType);
  218. if (onAttachedToHand != null)
  219. {
  220. onAttachedToHand.Invoke(hand);
  221. }
  222. if (skeletonPoser != null && hand.skeleton != null)
  223. {
  224. hand.skeleton.BlendToPoser(skeletonPoser, blendToPoseTime);
  225. }
  226. attachedToHand = hand;
  227. }
  228. protected virtual void OnDetachedFromHand(Hand hand)
  229. {
  230. if (activateActionSetOnAttach != null)
  231. {
  232. if (hand.otherHand == null || hand.otherHand.currentAttachedObjectInfo.HasValue == false ||
  233. (hand.otherHand.currentAttachedObjectInfo.Value.interactable != null &&
  234. hand.otherHand.currentAttachedObjectInfo.Value.interactable.activateActionSetOnAttach != this.activateActionSetOnAttach))
  235. {
  236. activateActionSetOnAttach.Deactivate(hand.handType);
  237. }
  238. }
  239. if (onDetachedFromHand != null)
  240. {
  241. onDetachedFromHand.Invoke(hand);
  242. }
  243. if (skeletonPoser != null)
  244. {
  245. if (hand.skeleton != null)
  246. hand.skeleton.BlendToSkeleton(releasePoseBlendTime);
  247. }
  248. attachedToHand = null;
  249. }
  250. protected virtual void OnDestroy()
  251. {
  252. isDestroying = true;
  253. if (attachedToHand != null)
  254. {
  255. attachedToHand.DetachObject(this.gameObject, false);
  256. attachedToHand.skeleton.BlendToSkeleton(0.1f);
  257. }
  258. if (highlightHolder != null)
  259. Destroy(highlightHolder);
  260. }
  261. protected virtual void OnDisable()
  262. {
  263. isDestroying = true;
  264. if (attachedToHand != null)
  265. {
  266. attachedToHand.ForceHoverUnlock();
  267. }
  268. if (highlightHolder != null)
  269. Destroy(highlightHolder);
  270. }
  271. }
  272. }