PageRenderTime 133ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/GoogleVR/Scripts/UI/GvrGaze.cs

https://gitlab.com/admin-github-cloud/gvr-unity-sdk
C# | 290 lines | 209 code | 29 blank | 52 comment | 83 complexity | 20768fa7246774cff8a0b3ad68c7bbae MD5 | raw file
  1. // Copyright 2015 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. using UnityEngine;
  15. using System.Linq;
  16. /// Class that can perform gaze-based selection, as a simple alternative to the
  17. /// more complicated path of using _GazeInputModule_ and the rest of **uGUI**.
  18. [RequireComponent(typeof(Camera))]
  19. public class GvrGaze : MonoBehaviour {
  20. /// The active Gaze Pointer for this camera. Must have IGvrGazePointer.
  21. /// The IGvrGazePointer responds to events from this class.
  22. public GameObject PointerObject {
  23. get {
  24. return pointerObject;
  25. }
  26. set {
  27. if (value != null) {
  28. // Retrieve the IGvrGazePointer component.
  29. var ptr = value.GetComponents<MonoBehaviour>()
  30. .Select(c => c as IGvrGazePointer)
  31. .Where(c => c != null)
  32. .FirstOrDefault();
  33. if (ptr != null) {
  34. if (pointer != null) {
  35. if (isTriggered) {
  36. pointer.OnGazeTriggerEnd(cam);
  37. }
  38. if (currentGazeObject != null) {
  39. pointer.OnGazeExit(cam, currentGazeObject);
  40. }
  41. pointer.OnGazeDisabled();
  42. }
  43. pointerObject = value;
  44. pointer = ptr;
  45. pointer.OnGazeEnabled();
  46. if (currentGazeObject != null) {
  47. pointer.OnGazeStart(cam, currentGazeObject, lastIntersectPosition,
  48. currentTarget != null);
  49. }
  50. if (isTriggered) {
  51. pointer.OnGazeTriggerStart(cam);
  52. }
  53. } else {
  54. Debug.LogError("Object must have component which implements IGvrGazePointer.");
  55. }
  56. } else {
  57. if (pointer != null) {
  58. if (isTriggered) {
  59. pointer.OnGazeTriggerEnd(cam);
  60. }
  61. if (currentTarget != null) {
  62. pointer.OnGazeExit(cam, currentGazeObject);
  63. }
  64. }
  65. pointer = null;
  66. pointerObject = null;
  67. }
  68. }
  69. }
  70. [SerializeField][HideInInspector]
  71. private GameObject pointerObject;
  72. private IGvrGazePointer pointer;
  73. // Convenient accessor to the camera component used throughout this script.
  74. public Camera cam { get; private set; }
  75. /// The layers to use for finding objects which intersect the user's gaze.
  76. public LayerMask mask = -1;
  77. // Current target detected the user is "gazing" at.
  78. private IGvrGazeResponder currentTarget;
  79. private GameObject currentGazeObject;
  80. private Vector3 lastIntersectPosition;
  81. // Trigger state.
  82. private bool isTriggered;
  83. void Awake() {
  84. cam = GetComponent<Camera>();
  85. PointerObject = pointerObject;
  86. }
  87. void OnEnable() {
  88. if (pointer != null) {
  89. pointer.OnGazeEnabled();
  90. }
  91. }
  92. void OnDisable() {
  93. // Is there a current target?
  94. if (currentTarget != null) {
  95. currentTarget.OnGazeExit();
  96. }
  97. // Tell pointer to exit target.
  98. if (pointer != null) {
  99. // Is there a pending trigger?
  100. if (isTriggered) {
  101. pointer.OnGazeTriggerEnd(cam);
  102. }
  103. if (currentGazeObject != null) {
  104. pointer.OnGazeExit(cam, currentGazeObject);
  105. }
  106. pointer.OnGazeDisabled();
  107. }
  108. currentGazeObject = null;
  109. currentTarget = null;
  110. isTriggered = false;
  111. }
  112. void LateUpdate () {
  113. GvrViewer.Instance.UpdateState();
  114. HandleGaze();
  115. HandleTrigger();
  116. }
  117. private void HandleGaze() {
  118. // Retrieve GazePointer radius.
  119. float innerRadius = 0.0f;
  120. float outerRadius = 0.0f;
  121. if (pointer != null) {
  122. pointer.GetPointerRadius(out innerRadius, out outerRadius);
  123. }
  124. // Find what object the user is looking at.
  125. Vector3 intersectPosition;
  126. IGvrGazeResponder target = null;
  127. GameObject targetObject = FindGazeTarget(innerRadius, out target, out intersectPosition);
  128. // Found a target?
  129. if (targetObject != null) {
  130. lastIntersectPosition = intersectPosition;
  131. // Is the object new?
  132. if (targetObject != currentGazeObject) {
  133. if (pointer != null) {
  134. pointer.OnGazeExit(cam, currentGazeObject);
  135. }
  136. if (currentTarget != null) {
  137. // Replace with current object.
  138. currentTarget.OnGazeExit();
  139. }
  140. // Save new object.
  141. currentTarget = target;
  142. currentGazeObject = targetObject;
  143. // Inform pointer and target of gaze.
  144. if (pointer != null) {
  145. pointer.OnGazeStart(cam, currentGazeObject, intersectPosition,
  146. currentTarget != null);
  147. }
  148. if (currentTarget != null) {
  149. currentTarget.OnGazeEnter();
  150. }
  151. } else {
  152. // Same object, inform pointer of new intersection.
  153. if (pointer != null) {
  154. pointer.OnGazeStay(cam, currentGazeObject, intersectPosition,
  155. currentTarget != null);
  156. }
  157. }
  158. } else {
  159. // Failed to find an object by inner radius.
  160. if (currentGazeObject != null) {
  161. // Already gazing an object? Check against outer radius.
  162. if (IsGazeNearObject(outerRadius, currentGazeObject, out intersectPosition)) {
  163. // Still gazing.
  164. if (pointer != null) {
  165. pointer.OnGazeStay(cam, currentGazeObject, intersectPosition, currentTarget != null);
  166. }
  167. } else {
  168. // No longer gazing any object.
  169. if (pointer != null) {
  170. pointer.OnGazeExit(cam, currentGazeObject);
  171. }
  172. if (currentTarget != null) {
  173. currentTarget.OnGazeExit();
  174. }
  175. currentTarget = null;
  176. currentGazeObject = null;
  177. }
  178. }
  179. }
  180. }
  181. private GameObject FindGazeTarget(float radius, out IGvrGazeResponder responder,
  182. out Vector3 intersectPosition) {
  183. RaycastHit hit;
  184. GameObject targetObject = null;
  185. bool hitResult = false;
  186. // Use Raycast or SphereCast?
  187. if (radius > 0.0f) {
  188. // Cast a sphere against the scene.
  189. hitResult = Physics.SphereCast(transform.position,
  190. radius, transform.forward, out hit, cam.farClipPlane, mask);
  191. } else {
  192. // Cast a Ray against the scene.
  193. Ray ray = new Ray(transform.position, transform.forward);
  194. hitResult = Physics.Raycast(ray, out hit, cam.farClipPlane, mask);
  195. }
  196. // Found anything?
  197. if (hitResult) {
  198. // Set object and IGvrGazeResponder if any.
  199. targetObject = hit.collider.gameObject;
  200. responder = targetObject.GetComponent(typeof(IGvrGazeResponder))
  201. as IGvrGazeResponder;
  202. intersectPosition = transform.position + transform.forward * hit.distance;
  203. } else {
  204. // Nothing? Reset variables.
  205. intersectPosition = Vector3.zero;
  206. responder = null;
  207. }
  208. return targetObject;
  209. }
  210. private bool IsGazeNearObject(float radius, GameObject target, out Vector3 intersectPosition) {
  211. RaycastHit[] hits;
  212. // Use Raycast or SphereCast?
  213. if (radius > 0.0f) {
  214. // Cast a sphere against the scene.
  215. hits = Physics.SphereCastAll(transform.position,
  216. radius, transform.forward, cam.farClipPlane, mask);
  217. } else {
  218. // Cast a Ray against the object.
  219. RaycastHit hitInfo;
  220. Ray ray = new Ray(transform.position, transform.forward);
  221. if (target.GetComponent<Collider>().Raycast(ray, out hitInfo, cam.farClipPlane)) {
  222. hits = new RaycastHit[1];
  223. hits[0] = hitInfo;
  224. } else {
  225. hits = new RaycastHit[0];
  226. }
  227. }
  228. // Iterate all intersected objects to find the object we are looking for.
  229. foreach (RaycastHit hit in hits) {
  230. if (hit.collider.gameObject == target) {
  231. // Found our object, save intersection position.
  232. intersectPosition = transform.position + transform.forward * hit.distance;
  233. return true;
  234. }
  235. }
  236. // Desired object was not intersected.
  237. intersectPosition = Vector3.zero;
  238. return false;
  239. }
  240. private void HandleTrigger() {
  241. // If trigger isn't already held.
  242. if (!isTriggered) {
  243. if (GvrViewer.Instance.Triggered || Input.GetMouseButtonDown(0)) {
  244. // Trigger started.
  245. isTriggered = true;
  246. if (pointer != null) {
  247. pointer.OnGazeTriggerStart(cam);
  248. }
  249. }
  250. } else if (!GvrViewer.Instance.Triggered && !Input.GetMouseButton(0)) {
  251. // Trigger ended.
  252. if (pointer != null) {
  253. pointer.OnGazeTriggerEnd(cam);
  254. }
  255. if (currentTarget != null) {
  256. currentTarget.OnGazeTrigger();
  257. }
  258. isTriggered = false;
  259. }
  260. }
  261. }