Interactable.cs 14 KB

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