PageRenderTime 29ms CodeModel.GetById 11ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

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