/src/away3d/core/managers/Mouse3DManager.as
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}