/src/away3d/core/managers/Mouse3DManager.as

http://github.com/away3d/away3d-core-fp11 · ActionScript · 380 lines · 290 code · 51 blank · 39 comment · 54 complexity · 59e8713d3467e0752f1081afb763ee94 MD5 · raw file

  1. package away3d.core.managers
  2. {
  3. import away3d.*;
  4. import away3d.containers.*;
  5. import away3d.core.pick.*;
  6. import away3d.events.*;
  7. import flash.display.*;
  8. import flash.events.*;
  9. import flash.geom.*;
  10. import flash.utils.*;
  11. use namespace arcane;
  12. /**
  13. * Mouse3DManager enforces a singleton pattern and is not intended to be instanced.
  14. * it provides a manager class for detecting 3D mouse hits on View3D objects and sending out 3D mouse events.
  15. */
  16. public class Mouse3DManager
  17. {
  18. private static var _view3Ds:Dictionary;
  19. private static var _view3DLookup:Vector.<View3D>;
  20. private static var _viewCount:int = 0;
  21. private var _activeView:View3D;
  22. private var _updateDirty:Boolean = true;
  23. private var _nullVector:Vector3D = new Vector3D();
  24. protected static var _collidingObject:PickingCollisionVO;
  25. private static var _previousCollidingObject:PickingCollisionVO;
  26. private static var _collidingViewObjects:Vector.<PickingCollisionVO>;
  27. private static var _queuedEvents:Vector.<MouseEvent3D> = new Vector.<MouseEvent3D>();
  28. private var _mouseMoveEvent:MouseEvent = new MouseEvent(MouseEvent.MOUSE_MOVE);
  29. private static var _mouseUp:MouseEvent3D = new MouseEvent3D(MouseEvent3D.MOUSE_UP);
  30. private static var _mouseClick:MouseEvent3D = new MouseEvent3D(MouseEvent3D.CLICK);
  31. private static var _mouseOut:MouseEvent3D = new MouseEvent3D(MouseEvent3D.MOUSE_OUT);
  32. private static var _mouseDown:MouseEvent3D = new MouseEvent3D(MouseEvent3D.MOUSE_DOWN);
  33. private static var _mouseMove:MouseEvent3D = new MouseEvent3D(MouseEvent3D.MOUSE_MOVE);
  34. private static var _mouseOver:MouseEvent3D = new MouseEvent3D(MouseEvent3D.MOUSE_OVER);
  35. private static var _mouseWheel:MouseEvent3D = new MouseEvent3D(MouseEvent3D.MOUSE_WHEEL);
  36. private static var _mouseDoubleClick:MouseEvent3D = new MouseEvent3D(MouseEvent3D.DOUBLE_CLICK);
  37. private var _forceMouseMove:Boolean;
  38. private var _mousePicker:IPicker = PickingType.RAYCAST_FIRST_ENCOUNTERED;
  39. private var _childDepth:int = 0;
  40. private static var _previousCollidingView:int = -1;
  41. private static var _collidingView:int = -1;
  42. private var _collidingDownObject:PickingCollisionVO;
  43. private var _collidingUpObject:PickingCollisionVO;
  44. /**
  45. * Creates a new <code>Mouse3DManager</code> object.
  46. */
  47. public function Mouse3DManager()
  48. {
  49. if (!_view3Ds) {
  50. _view3Ds = new Dictionary();
  51. _view3DLookup = new Vector.<View3D>();
  52. }
  53. }
  54. // ---------------------------------------------------------------------
  55. // Interface.
  56. // ---------------------------------------------------------------------
  57. public function updateCollider(view:View3D):void
  58. {
  59. _previousCollidingView = _collidingView;
  60. if (view) {
  61. // Clear the current colliding objects for multiple views if backBuffer just cleared
  62. if (view.stage3DProxy.bufferClear)
  63. _collidingViewObjects = new Vector.<PickingCollisionVO>(_viewCount);
  64. var p:Point = view.localToGlobal(new Point(view.mouseX, view.mouseY));
  65. if (!view.shareContext) {
  66. if (view == _activeView && (_forceMouseMove || _updateDirty)) { // If forceMouseMove is off, and no 2D mouse events dirtied the update, don't update either.
  67. _collidingObject = _mousePicker.getViewCollision(p.x, p.y, view);
  68. }
  69. } else {
  70. //if (view.getBounds(view.parent).contains((view.mouseX + view.x)/view.parent.scaleX, (view.mouseY + view.y)/view.parent.scaleY)) {
  71. if (!_collidingViewObjects)
  72. _collidingViewObjects = new Vector.<PickingCollisionVO>(_viewCount);
  73. _collidingObject = _collidingViewObjects[_view3Ds[view]] = _mousePicker.getViewCollision(p.x, p.y, view);
  74. //}
  75. }
  76. }
  77. }
  78. public function fireMouseEvents():void
  79. {
  80. var i:uint;
  81. var len:uint;
  82. var event:MouseEvent3D;
  83. var dispatcher:ObjectContainer3D;
  84. // If multiple view are used, determine the best hit based on the depth intersection.
  85. if (_collidingViewObjects) {
  86. _collidingObject = null;
  87. // Get the top-most view colliding object
  88. var distance:Number = Infinity;
  89. var view:View3D;
  90. for (var v:int = _viewCount - 1; v >= 0; v--) {
  91. view = _view3DLookup[v];
  92. if (_collidingViewObjects[v] && (view.layeredView || _collidingViewObjects[v].rayEntryDistance < distance)) {
  93. distance = _collidingViewObjects[v].rayEntryDistance;
  94. _collidingObject = _collidingViewObjects[v];
  95. if (view.layeredView)
  96. break;
  97. }
  98. }
  99. }
  100. // If colliding object has changed, queue over/out events.
  101. if (_collidingObject != _previousCollidingObject) {
  102. if (_previousCollidingObject)
  103. queueDispatch(_mouseOut, _mouseMoveEvent, _previousCollidingObject);
  104. if (_collidingObject)
  105. queueDispatch(_mouseOver, _mouseMoveEvent, _collidingObject);
  106. }
  107. // Fire mouse move events here if forceMouseMove is on.
  108. if (_forceMouseMove && _collidingObject)
  109. queueDispatch(_mouseMove, _mouseMoveEvent, _collidingObject);
  110. // Dispatch all queued events.
  111. len = _queuedEvents.length;
  112. for (i = 0; i < len; ++i) {
  113. // Only dispatch from first implicitly enabled object ( one that is not a child of a mouseChildren = false hierarchy ).
  114. event = _queuedEvents[i];
  115. dispatcher = event.object;
  116. while (dispatcher && !dispatcher._ancestorsAllowMouseEnabled)
  117. dispatcher = dispatcher.parent;
  118. if (dispatcher)
  119. dispatcher.dispatchEvent(event);
  120. }
  121. _queuedEvents.length = 0;
  122. _updateDirty = false;
  123. _previousCollidingObject = _collidingObject;
  124. }
  125. public function addViewLayer(view:View3D):void
  126. {
  127. var stg:Stage = view.stage;
  128. // Add instance to mouse3dmanager to fire mouse events for multiple views
  129. if (!view.stage3DProxy.mouse3DManager)
  130. view.stage3DProxy.mouse3DManager = this;
  131. if (!hasKey(view))
  132. _view3Ds[view] = 0;
  133. _childDepth = 0;
  134. traverseDisplayObjects(stg);
  135. _viewCount = _childDepth;
  136. }
  137. public function enableMouseListeners(view:View3D):void
  138. {
  139. view.addEventListener(MouseEvent.CLICK, onClick);
  140. view.addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
  141. view.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
  142. view.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
  143. view.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  144. view.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
  145. view.addEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
  146. view.addEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
  147. }
  148. public function disableMouseListeners(view:View3D):void
  149. {
  150. view.removeEventListener(MouseEvent.CLICK, onClick);
  151. view.removeEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
  152. view.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
  153. view.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
  154. view.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  155. view.removeEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
  156. view.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOver);
  157. view.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOut);
  158. }
  159. public function dispose():void
  160. {
  161. _mousePicker.dispose();
  162. }
  163. // ---------------------------------------------------------------------
  164. // Private.
  165. // ---------------------------------------------------------------------
  166. private function queueDispatch(event:MouseEvent3D, sourceEvent:MouseEvent, collider:PickingCollisionVO = null):void
  167. {
  168. // 2D properties.
  169. event.ctrlKey = sourceEvent.ctrlKey;
  170. event.altKey = sourceEvent.altKey;
  171. event.shiftKey = sourceEvent.shiftKey;
  172. event.delta = sourceEvent.delta;
  173. event.screenX = sourceEvent.localX;
  174. event.screenY = sourceEvent.localY;
  175. collider ||= _collidingObject;
  176. // 3D properties.
  177. if (collider) {
  178. // Object.
  179. event.object = collider.entity;
  180. event.renderable = collider.renderable;
  181. // UV.
  182. event.uv = collider.uv;
  183. // Position.
  184. event.localPosition = collider.localPosition? collider.localPosition.clone() : null;
  185. // Normal.
  186. event.localNormal = collider.localNormal? collider.localNormal.clone() : null;
  187. // Face index.
  188. event.index = collider.index;
  189. // SubGeometryIndex.
  190. event.subGeometryIndex = collider.subGeometryIndex;
  191. } else {
  192. // Set all to null.
  193. event.uv = null;
  194. event.object = null;
  195. event.localPosition = _nullVector;
  196. event.localNormal = _nullVector;
  197. event.index = 0;
  198. event.subGeometryIndex = 0;
  199. }
  200. // Store event to be dispatched later.
  201. _queuedEvents.push(event);
  202. }
  203. private function reThrowEvent(event:MouseEvent):void
  204. {
  205. if (!_activeView || (_activeView && !_activeView.shareContext))
  206. return;
  207. for (var v:* in _view3Ds) {
  208. if (v != _activeView && _view3Ds[v] < _view3Ds[_activeView])
  209. v.dispatchEvent(event);
  210. }
  211. }
  212. private function hasKey(view:View3D):Boolean
  213. {
  214. for (var v:* in _view3Ds) {
  215. if (v === view)
  216. return true;
  217. }
  218. return false;
  219. }
  220. private function traverseDisplayObjects(container:DisplayObjectContainer):void
  221. {
  222. var childCount:int = container.numChildren;
  223. var c:int = 0;
  224. var child:DisplayObject;
  225. for (c = 0; c < childCount; c++) {
  226. child = container.getChildAt(c);
  227. for (var v:* in _view3Ds) {
  228. if (child == v) {
  229. _view3Ds[child] = _childDepth;
  230. _view3DLookup[_childDepth] = v;
  231. _childDepth++;
  232. }
  233. }
  234. if (child is DisplayObjectContainer)
  235. traverseDisplayObjects(child as DisplayObjectContainer);
  236. }
  237. }
  238. // ---------------------------------------------------------------------
  239. // Listeners.
  240. // ---------------------------------------------------------------------
  241. private function onMouseMove(event:MouseEvent):void
  242. {
  243. if (_collidingObject)
  244. queueDispatch(_mouseMove, _mouseMoveEvent = event);
  245. else
  246. reThrowEvent(event);
  247. _updateDirty = true;
  248. }
  249. private function onMouseOut(event:MouseEvent):void
  250. {
  251. _activeView = null;
  252. if (_collidingObject)
  253. queueDispatch(_mouseOut, event, _collidingObject);
  254. _updateDirty = true;
  255. }
  256. private function onMouseOver(event:MouseEvent):void
  257. {
  258. _activeView = (event.currentTarget as View3D);
  259. if (_collidingObject && _previousCollidingObject != _collidingObject)
  260. queueDispatch(_mouseOver, event, _collidingObject);
  261. else
  262. reThrowEvent(event);
  263. _updateDirty = true;
  264. }
  265. private function onClick(event:MouseEvent):void
  266. {
  267. if (_collidingObject) {
  268. queueDispatch(_mouseClick, event);
  269. } else
  270. reThrowEvent(event);
  271. _updateDirty = true;
  272. }
  273. private function onDoubleClick(event:MouseEvent):void
  274. {
  275. if (_collidingObject)
  276. queueDispatch(_mouseDoubleClick, event);
  277. else
  278. reThrowEvent(event);
  279. _updateDirty = true;
  280. }
  281. private function onMouseDown(event:MouseEvent):void
  282. {
  283. _activeView = (event.currentTarget as View3D);
  284. updateCollider(_activeView); // ensures collision check is done with correct mouse coordinates on mobile
  285. if (_collidingObject) {
  286. queueDispatch(_mouseDown, event);
  287. _previousCollidingObject = _collidingObject;
  288. } else
  289. reThrowEvent(event);
  290. _updateDirty = true;
  291. }
  292. private function onMouseUp(event:MouseEvent):void
  293. {
  294. if (_collidingObject) {
  295. queueDispatch(_mouseUp, event);
  296. _previousCollidingObject = _collidingObject;
  297. } else
  298. reThrowEvent(event);
  299. _updateDirty = true;
  300. }
  301. private function onMouseWheel(event:MouseEvent):void
  302. {
  303. if (_collidingObject)
  304. queueDispatch(_mouseWheel, event);
  305. else
  306. reThrowEvent(event);
  307. _updateDirty = true;
  308. }
  309. // ---------------------------------------------------------------------
  310. // Getters & setters.
  311. // ---------------------------------------------------------------------
  312. public function get forceMouseMove():Boolean
  313. {
  314. return _forceMouseMove;
  315. }
  316. public function set forceMouseMove(value:Boolean):void
  317. {
  318. _forceMouseMove = value;
  319. }
  320. public function get mousePicker():IPicker
  321. {
  322. return _mousePicker;
  323. }
  324. public function set mousePicker(value:IPicker):void
  325. {
  326. _mousePicker = value;
  327. }
  328. }
  329. }