MouseCursor.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #if UNITY_EDITOR
  2. using UnityEngine;
  3. using UnityEditor;
  4. using System.Collections.Generic;
  5. namespace O3DWB
  6. {
  7. public class MouseCursor : Singleton<MouseCursor>
  8. {
  9. #region Private Static Variables
  10. private static bool _defaultObjectMaskEnabledState = true;
  11. #endregion
  12. #region Private Variables
  13. private Vector2 _previousPosition;
  14. private Vector2 _offsetSinceLastMouseMove;
  15. private Stack<MouseCursorObjectPickFlags> _objectPickMaskFlagsStack = new Stack<MouseCursorObjectPickFlags>();
  16. private Stack<ObjectMask> _objectMaskStack = new Stack<ObjectMask>();
  17. private Stack<bool> _objectMaskEnabledStates = new Stack<bool>();
  18. #endregion
  19. #region Private Properties
  20. private ObjectMask ObjectMask { get { return _objectMaskStack.Count != 0 ? _objectMaskStack.Peek() : null; } }
  21. #endregion
  22. #region Public Properties
  23. public Vector2 PreviousPosition { get { return _previousPosition; } }
  24. public Vector2 OffsetSinceLastMouseMove { get { return _offsetSinceLastMouseMove; } }
  25. public Vector2 Position { get { return Event.current.mousePosition; } }
  26. public MouseCursorObjectPickFlags ObjectPickMaskFlags { get { return _objectPickMaskFlagsStack.Count != 0 ? _objectPickMaskFlagsStack.Peek() : MouseCursorObjectPickFlags.None; } }
  27. public bool IsObjectMaskEnabled { get { return _objectMaskEnabledStates.Count != 0 ? _objectMaskEnabledStates.Peek() : _defaultObjectMaskEnabledState; } }
  28. #endregion
  29. #region Public Methods
  30. public bool IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags flag)
  31. {
  32. return (ObjectPickMaskFlags & flag) != 0;
  33. }
  34. public void PushObjectMaskEnabledState(bool enabled)
  35. {
  36. _objectMaskEnabledStates.Push(enabled);
  37. }
  38. public bool PopObjectMaskEnabledState()
  39. {
  40. if (_objectMaskEnabledStates.Count != 0) return _objectMaskEnabledStates.Pop();
  41. return _defaultObjectMaskEnabledState;
  42. }
  43. public void PushObjectPickMaskFlags(MouseCursorObjectPickFlags flags)
  44. {
  45. _objectPickMaskFlagsStack.Push(flags);
  46. }
  47. public MouseCursorObjectPickFlags PopObjectPickMaskFlags()
  48. {
  49. if (_objectPickMaskFlagsStack.Count != 0) return _objectPickMaskFlagsStack.Pop();
  50. return MouseCursorObjectPickFlags.None;
  51. }
  52. public void PushObjectMask(ObjectMask objectMask)
  53. {
  54. _objectMaskStack.Push(objectMask);
  55. }
  56. public ObjectMask PopObjectMask()
  57. {
  58. if (_objectMaskStack.Count != 0) return _objectMaskStack.Pop();
  59. return null;
  60. }
  61. public bool IsInsideSceneView()
  62. {
  63. return SceneViewCamera.Camera.pixelRect.Contains(Position);
  64. }
  65. public Ray GetWorldRay()
  66. {
  67. return HandleUtility.GUIPointToWorldRay(Position);
  68. }
  69. public MouseCursorRayHit GetRayHit()
  70. {
  71. return new MouseCursorRayHit(GetGridCellRayHit(), GetObjectRayHitInstances());
  72. }
  73. public MouseCursorRayHit GetRayHitForMeshAndSpriteObjects(List<GameObject> gameObjects)
  74. {
  75. var ray = GetWorldRay();
  76. var hits = new List<GameObjectRayHit>();
  77. foreach (var gameObject in gameObjects)
  78. {
  79. GameObjectRayHit hit = null;
  80. if(gameObject.HasMesh())
  81. {
  82. if (gameObject.RaycastMesh(ray, out hit)) hits.Add(hit);
  83. }
  84. else
  85. if(gameObject.IsSprite())
  86. {
  87. if (gameObject.RaycastSprite(ray, out hit)) hits.Add(hit);
  88. }
  89. }
  90. SortObjectRayHitListByHitDistanceFromCamera(hits);
  91. return new MouseCursorRayHit(null, hits);
  92. }
  93. public MouseCursorRayHit GetCursorRayHitForGridCell()
  94. {
  95. GridCellRayHit gridCellHit = GetGridCellRayHit();
  96. if (gridCellHit == null) return null;
  97. return new MouseCursorRayHit(gridCellHit, new List<GameObjectRayHit>());
  98. }
  99. public MouseCursorRayHit GetCursorRayHitForTerrainObject(GameObject gameObject)
  100. {
  101. if (!gameObject.HasTerrain()) return new MouseCursorRayHit(null, new List<GameObjectRayHit>());
  102. GameObjectRayHit gameObjectRayHit;
  103. if (gameObject.RaycastTerrain(GetWorldRay(), out gameObjectRayHit)) return new MouseCursorRayHit(null, new List<GameObjectRayHit> { gameObjectRayHit });
  104. return new MouseCursorRayHit(null, new List<GameObjectRayHit>());
  105. }
  106. public GridCellRayHit GetGridCellRayHit()
  107. {
  108. Ray ray = GetWorldRay();
  109. float minT;
  110. XZGrid closestSnapGrid = GetClosestHitSnapGridAndMinT(ObjectSnapping.Get().GetAllSnapGrids(), ray, out minT);
  111. if (closestSnapGrid != null) return GetGridCellHit(closestSnapGrid, ray, minT);
  112. else return null;
  113. }
  114. public bool IntersectsPlane(Plane plane, out Vector3 intersectionPoint)
  115. {
  116. intersectionPoint = Vector3.zero;
  117. Ray ray = GetWorldRay();
  118. float t;
  119. if(plane.Raycast(ray, out t))
  120. {
  121. intersectionPoint = ray.GetPoint(t);
  122. return true;
  123. }
  124. return false;
  125. }
  126. public void HandleMouseMoveEvent(Event e)
  127. {
  128. _offsetSinceLastMouseMove = e.mousePosition - _previousPosition;
  129. _previousPosition = e.mousePosition;
  130. }
  131. #endregion
  132. #region Private Methods
  133. private List<GameObjectRayHit> GetObjectRayHitInstances()
  134. {
  135. Ray ray = GetWorldRay();
  136. var gameObjectHits = new List<GameObjectRayHit>();
  137. RaycastAllTerrainObjects(ray, gameObjectHits);
  138. RaycastAllObjectsNoTerrains(ray, gameObjectHits);
  139. ObjectMask activeObjectMask = ObjectMask;
  140. if (activeObjectMask != null && IsObjectMaskEnabled) gameObjectHits.RemoveAll(item => activeObjectMask.IsGameObjectMasked(item.HitObject));
  141. gameObjectHits.RemoveAll(item => !item.HitObject.activeSelf);
  142. SortObjectRayHitListByHitDistanceFromCamera(gameObjectHits);
  143. Vector3 cameraLook = SceneViewCamera.Camera.transform.forward;
  144. while (gameObjectHits.Count > 0)
  145. {
  146. if(gameObjectHits[0].WasBoxHit && gameObjectHits[0].HitObject.HasMesh())
  147. {
  148. GameObjectRayHit meshHit;
  149. if (gameObjectHits[0].HitObject.RaycastMesh(ray, out meshHit))
  150. {
  151. float dot = Vector3.Dot(meshHit.HitNormal, cameraLook);
  152. if (dot > 0.0f)
  153. {
  154. gameObjectHits.RemoveAt(0);
  155. }
  156. else break;
  157. }
  158. else break;
  159. }
  160. else
  161. {
  162. float dot = Vector3.Dot(gameObjectHits[0].HitNormal, cameraLook);
  163. if (dot > 0.0f)
  164. {
  165. gameObjectHits.RemoveAt(0);
  166. }
  167. else break;
  168. }
  169. }
  170. return gameObjectHits;
  171. }
  172. private void RaycastAllTerrainObjects(Ray ray, List<GameObjectRayHit> terrainHits)
  173. {
  174. // Can we pick terrains?
  175. if (!IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectTerrain))
  176. {
  177. // We will use Unity's 'Physics' API for terrain picking because it is reasonable enough
  178. // to expect users to attach terrain colliders to their terrain objects.
  179. RaycastHit[] rayHits = Physics.RaycastAll(ray);
  180. if (rayHits.Length != 0)
  181. {
  182. // Identify all terrain colliders which were picked
  183. foreach (RaycastHit rayHit in rayHits)
  184. {
  185. // Picked a terrain collider?
  186. if (rayHit.collider.GetType() == typeof(TerrainCollider))
  187. {
  188. // Create a game object hit instance and add it to the list
  189. var terrainRayHit = new TerrainRayHit(ray, rayHit);
  190. var gameObjectRayHit = new GameObjectRayHit(ray, rayHit.collider.gameObject, null, null, terrainRayHit, null);
  191. terrainHits.Add(gameObjectRayHit);
  192. }
  193. }
  194. }
  195. }
  196. }
  197. private void RaycastAllObjectsNoTerrains(Ray ray, List<GameObjectRayHit> objectHits)
  198. {
  199. bool canPickMeshObjects = !IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectMesh);
  200. bool canPickBoxes = !IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectBox);
  201. bool canPickSprites = !IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectSprite);
  202. if (canPickMeshObjects && canPickBoxes && canPickSprites)
  203. {
  204. List<GameObjectRayHit> objectMeshHits = Octave3DScene.Get().RaycastAllMesh(ray);
  205. if (objectMeshHits.Count != 0) objectHits.AddRange(objectMeshHits);
  206. List<GameObjectRayHit> objectBoxHits = Octave3DScene.Get().RaycastAllBox(ray);
  207. objectBoxHits.RemoveAll(item => item.HitObject.HasMesh() || item.HitObject.HasSpriteRendererWithSprite());
  208. if (objectBoxHits.Count != 0) objectHits.AddRange(objectBoxHits);
  209. List<GameObjectRayHit> objectSpriteHits = Octave3DScene.Get().RaycastAllSprite(ray);
  210. objectSpriteHits.RemoveAll(item => item.HitObject.HasMesh() || item.HitObject.GetComponent<SpriteRenderer>().IsPixelFullyTransparent(item.HitPoint));
  211. if (objectSpriteHits.Count != 0) objectHits.AddRange(objectSpriteHits);
  212. }
  213. else
  214. {
  215. if (!IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectMesh))
  216. {
  217. List<GameObjectRayHit> objectMeshHits = Octave3DScene.Get().RaycastAllMesh(ray);
  218. if (objectMeshHits.Count != 0) objectHits.AddRange(objectMeshHits);
  219. }
  220. if(!IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectSprite))
  221. {
  222. List<GameObjectRayHit> objectSpriteHits = Octave3DScene.Get().RaycastAllSprite(ray);
  223. objectSpriteHits.RemoveAll(item => objectHits.Contains(item) || item.HitObject.GetComponent<SpriteRenderer>().IsPixelFullyTransparent(item.HitPoint));
  224. if (objectSpriteHits.Count != 0) objectHits.AddRange(objectSpriteHits);
  225. }
  226. if (!IsObjectPickMaskFlagSet(MouseCursorObjectPickFlags.ObjectBox))
  227. {
  228. List<GameObjectRayHit> objectBoxHits = Octave3DScene.Get().RaycastAllBox(ray);
  229. objectBoxHits.RemoveAll(item => objectHits.Contains(item));
  230. if (objectBoxHits.Count != 0) objectHits.AddRange(objectBoxHits);
  231. }
  232. }
  233. }
  234. private XZGrid GetClosestHitSnapGridAndMinT(List<XZGrid> allSnapGrids, Ray ray, out float minT)
  235. {
  236. minT = float.MaxValue;
  237. XZGrid closestSnapGrid = null;
  238. foreach (XZGrid snapGrid in allSnapGrids)
  239. {
  240. float t;
  241. if (snapGrid.Plane.Raycast(ray, out t) & t < minT)
  242. {
  243. minT = t;
  244. closestSnapGrid = snapGrid;
  245. }
  246. }
  247. return closestSnapGrid;
  248. }
  249. private GridCellRayHit GetGridCellHit(XZGrid hitGrid, Ray ray, float t)
  250. {
  251. XZGridCell hitGridCell = hitGrid.GetCellFromPoint(ray.GetPoint(t));
  252. return new GridCellRayHit(ray, t, hitGridCell);
  253. }
  254. private void SortObjectRayHitListByHitDistanceFromCamera(List<GameObjectRayHit> objectRayHitInstances)
  255. {
  256. Vector3 sceneCameraPosition = SceneViewCamera.Camera.transform.position;
  257. objectRayHitInstances.Sort(delegate(GameObjectRayHit firstObjectHit, GameObjectRayHit secondObjectHit)
  258. {
  259. float firstPickPointDistanceFromCamera = (firstObjectHit.HitPoint - sceneCameraPosition).magnitude;
  260. float secondPickPointDistanceFromCamera = (secondObjectHit.HitPoint - sceneCameraPosition).magnitude;
  261. return firstPickPointDistanceFromCamera.CompareTo(secondPickPointDistanceFromCamera);
  262. });
  263. }
  264. #endregion
  265. }
  266. }
  267. #endif