/src/away3d/containers/ObjectContainer3D.as

http://github.com/away3d/away3d-core-fp11 · ActionScript · 776 lines · 470 code · 132 blank · 174 comment · 73 complexity · 75fe9f2b42b83d1c37b4f967d6d09517 MD5 · raw file

  1. package away3d.containers
  2. {
  3. import away3d.arcane;
  4. import away3d.core.base.Object3D;
  5. import away3d.core.partition.Partition3D;
  6. import away3d.events.Object3DEvent;
  7. import away3d.events.Scene3DEvent;
  8. import away3d.library.assets.AssetType;
  9. import away3d.library.assets.IAsset;
  10. import flash.events.Event;
  11. import flash.geom.Matrix3D;
  12. import flash.geom.Vector3D;
  13. use namespace arcane;
  14. /**
  15. * Dispatched when the scene transform matrix of the 3d object changes.
  16. *
  17. * @eventType away3d.events.Object3DEvent
  18. * @see #sceneTransform
  19. */
  20. [Event(name="scenetransformChanged", type="away3d.events.Object3DEvent")]
  21. /**
  22. * Dispatched when the parent scene of the 3d object changes.
  23. *
  24. * @eventType away3d.events.Object3DEvent
  25. * @see #scene
  26. */
  27. [Event(name="sceneChanged", type="away3d.events.Object3DEvent")]
  28. /**
  29. * Dispatched when a user moves the cursor while it is over the 3d object.
  30. *
  31. * @eventType away3d.events.MouseEvent3D
  32. */
  33. [Event(name="mouseMove3d", type="away3d.events.MouseEvent3D")]
  34. /**
  35. * Dispatched when a user presses the left hand mouse button while the cursor is over the 3d object.
  36. *
  37. * @eventType away3d.events.MouseEvent3D
  38. */
  39. [Event(name="mouseDown3d", type="away3d.events.MouseEvent3D")]
  40. /**
  41. * Dispatched when a user releases the left hand mouse button while the cursor is over the 3d object.
  42. *
  43. * @eventType away3d.events.MouseEvent3D
  44. */
  45. [Event(name="mouseUp3d", type="away3d.events.MouseEvent3D")]
  46. /**
  47. * Dispatched when a user moves the cursor over the 3d object.
  48. *
  49. * @eventType away3d.events.MouseEvent3D
  50. */
  51. [Event(name="mouseOver3d", type="away3d.events.MouseEvent3D")]
  52. /**
  53. * Dispatched when a user moves the cursor away from the 3d object.
  54. *
  55. * @eventType away3d.events.MouseEvent3D
  56. */
  57. [Event(name="mouseOut3d", type="away3d.events.MouseEvent3D")]
  58. /**
  59. * ObjectContainer3D is the most basic scene graph node. It can contain other ObjectContainer3Ds.
  60. *
  61. * ObjectContainer3D can have its own scene partition assigned. However, when assigned to a different scene,
  62. * it will loose any partition information, since partitions are tied to a scene.
  63. */
  64. public class ObjectContainer3D extends Object3D implements IAsset
  65. {
  66. /** @private */
  67. arcane var _ancestorsAllowMouseEnabled:Boolean;
  68. arcane var _isRoot:Boolean;
  69. protected var _scene:Scene3D;
  70. protected var _parent:ObjectContainer3D;
  71. protected var _sceneTransform:Matrix3D = new Matrix3D();
  72. protected var _sceneTransformDirty:Boolean = true;
  73. // these vars allow not having to traverse the scene graph to figure out what partition is set
  74. protected var _explicitPartition:Partition3D; // what the user explicitly set as the partition
  75. protected var _implicitPartition:Partition3D; // what is inherited from the parents if it doesn't have its own explicitPartition
  76. protected var _mouseEnabled:Boolean;
  77. private var _sceneTransformChanged:Object3DEvent;
  78. private var _scenechanged:Object3DEvent;
  79. private var _children:Vector.<ObjectContainer3D> = new Vector.<ObjectContainer3D>();
  80. private var _mouseChildren:Boolean = true;
  81. private var _oldScene:Scene3D;
  82. private var _inverseSceneTransform:Matrix3D = new Matrix3D();
  83. private var _inverseSceneTransformDirty:Boolean = true;
  84. private var _scenePosition:Vector3D = new Vector3D();
  85. private var _scenePositionDirty:Boolean = true;
  86. private var _explicitVisibility:Boolean = true;
  87. private var _implicitVisibility:Boolean = true;
  88. private var _listenToSceneTransformChanged:Boolean;
  89. private var _listenToSceneChanged:Boolean;
  90. // visibility passed on from parents
  91. protected var _ignoreTransform:Boolean = false;
  92. /**
  93. * Does not apply any transformations to this object. Allows static objects to be described in world coordinates without any matrix calculations.
  94. */
  95. public function get ignoreTransform():Boolean
  96. {
  97. return _ignoreTransform;
  98. }
  99. public function set ignoreTransform(value:Boolean):void
  100. {
  101. _ignoreTransform = value;
  102. _sceneTransformDirty = !value;
  103. _inverseSceneTransformDirty = !value;
  104. _scenePositionDirty = !value;
  105. if (!value) {
  106. _sceneTransform.identity();
  107. _scenePosition.setTo(0, 0, 0);
  108. }
  109. }
  110. /**
  111. * @private
  112. * The space partition used for this object, possibly inherited from its parent.
  113. */
  114. arcane function get implicitPartition():Partition3D
  115. {
  116. return _implicitPartition;
  117. }
  118. arcane function set implicitPartition(value:Partition3D):void
  119. {
  120. if (value == _implicitPartition)
  121. return;
  122. var i:uint;
  123. var len:uint = _children.length;
  124. var child:ObjectContainer3D;
  125. _implicitPartition = value;
  126. while (i < len) {
  127. child = _children[i++];
  128. // assign implicit partition if no explicit one is given
  129. if (!child._explicitPartition)
  130. child.implicitPartition = value;
  131. }
  132. }
  133. /** @private */
  134. arcane function get isVisible():Boolean
  135. {
  136. return _implicitVisibility && _explicitVisibility;
  137. }
  138. /** @private */
  139. arcane function setParent(value:ObjectContainer3D):void
  140. {
  141. _parent = value;
  142. updateMouseChildren();
  143. if (value == null) {
  144. scene = null;
  145. return;
  146. }
  147. notifySceneTransformChange();
  148. notifySceneChange();
  149. }
  150. private function notifySceneTransformChange():void
  151. {
  152. if (_sceneTransformDirty || _ignoreTransform)
  153. return;
  154. invalidateSceneTransform();
  155. var i:uint;
  156. var len:uint = _children.length;
  157. //act recursively on child objects
  158. while (i < len)
  159. _children[i++].notifySceneTransformChange();
  160. //trigger event if listener exists
  161. if (_listenToSceneTransformChanged) {
  162. if (!_sceneTransformChanged)
  163. _sceneTransformChanged = new Object3DEvent(Object3DEvent.SCENETRANSFORM_CHANGED, this);
  164. dispatchEvent(_sceneTransformChanged);
  165. }
  166. }
  167. private function notifySceneChange():void
  168. {
  169. notifySceneTransformChange();
  170. var i:uint;
  171. var len:uint = _children.length;
  172. //act recursively on child objects
  173. while (i < len)
  174. _children[i++].notifySceneChange();
  175. if (_listenToSceneChanged) {
  176. if (!_scenechanged)
  177. _scenechanged = new Object3DEvent(Object3DEvent.SCENE_CHANGED, this);
  178. dispatchEvent(_scenechanged);
  179. }
  180. }
  181. protected function updateMouseChildren():void
  182. {
  183. if (_parent && !_parent._isRoot) {
  184. // Set implicit mouse enabled if parent its children to be so.
  185. _ancestorsAllowMouseEnabled = parent._ancestorsAllowMouseEnabled && _parent.mouseChildren;
  186. } else
  187. _ancestorsAllowMouseEnabled = mouseChildren;
  188. // Sweep children.
  189. var len:uint = _children.length;
  190. for (var i:uint = 0; i < len; ++i)
  191. _children[i].updateMouseChildren();
  192. }
  193. /**
  194. * Indicates whether the IRenderable should trigger mouse events, and hence should be rendered for hit testing.
  195. */
  196. public function get mouseEnabled():Boolean
  197. {
  198. return _mouseEnabled;
  199. }
  200. public function set mouseEnabled(value:Boolean):void
  201. {
  202. _mouseEnabled = value;
  203. updateMouseChildren();
  204. }
  205. /**
  206. * @inheritDoc
  207. */
  208. override arcane function invalidateTransform():void
  209. {
  210. super.invalidateTransform();
  211. notifySceneTransformChange();
  212. }
  213. /**
  214. * Invalidates the scene transformation matrix, causing it to be updated the next time it's requested.
  215. */
  216. protected function invalidateSceneTransform():void
  217. {
  218. _sceneTransformDirty = !_ignoreTransform;
  219. _inverseSceneTransformDirty = !_ignoreTransform;
  220. _scenePositionDirty = !_ignoreTransform;
  221. }
  222. /**
  223. * Updates the scene transformation matrix.
  224. */
  225. protected function updateSceneTransform():void
  226. {
  227. if (_parent && !_parent._isRoot) {
  228. _sceneTransform.copyFrom(_parent.sceneTransform);
  229. _sceneTransform.prepend(transform);
  230. } else
  231. _sceneTransform.copyFrom(transform);
  232. _sceneTransformDirty = false;
  233. }
  234. /**
  235. *
  236. */
  237. public function get mouseChildren():Boolean
  238. {
  239. return _mouseChildren;
  240. }
  241. public function set mouseChildren(value:Boolean):void
  242. {
  243. _mouseChildren = value;
  244. updateMouseChildren();
  245. }
  246. /**
  247. *
  248. */
  249. public function get visible():Boolean
  250. {
  251. return _explicitVisibility;
  252. }
  253. public function set visible(value:Boolean):void
  254. {
  255. var len:uint = _children.length;
  256. _explicitVisibility = value;
  257. for (var i:uint = 0; i < len; ++i)
  258. _children[i].updateImplicitVisibility();
  259. }
  260. public function get assetType():String
  261. {
  262. return AssetType.CONTAINER;
  263. }
  264. /**
  265. * The global position of the ObjectContainer3D in the scene. The value of the return object should not be changed.
  266. */
  267. public function get scenePosition():Vector3D
  268. {
  269. if (_scenePositionDirty) {
  270. sceneTransform.copyColumnTo(3, _scenePosition);
  271. _scenePositionDirty = false;
  272. }
  273. return _scenePosition;
  274. }
  275. /**
  276. * The minimum extremum of the object along the X-axis.
  277. */
  278. public function get minX():Number
  279. {
  280. var i:uint;
  281. var len:uint = _children.length;
  282. var min:Number = Number.POSITIVE_INFINITY;
  283. var m:Number;
  284. while (i < len) {
  285. var child:ObjectContainer3D = _children[i++];
  286. m = child.minX + child.x;
  287. if (m < min)
  288. min = m;
  289. }
  290. return min;
  291. }
  292. /**
  293. * The minimum extremum of the object along the Y-axis.
  294. */
  295. public function get minY():Number
  296. {
  297. var i:uint;
  298. var len:uint = _children.length;
  299. var min:Number = Number.POSITIVE_INFINITY;
  300. var m:Number;
  301. while (i < len) {
  302. var child:ObjectContainer3D = _children[i++];
  303. m = child.minY + child.y;
  304. if (m < min)
  305. min = m;
  306. }
  307. return min;
  308. }
  309. /**
  310. * The minimum extremum of the object along the Z-axis.
  311. */
  312. public function get minZ():Number
  313. {
  314. var i:uint;
  315. var len:uint = _children.length;
  316. var min:Number = Number.POSITIVE_INFINITY;
  317. var m:Number;
  318. while (i < len) {
  319. var child:ObjectContainer3D = _children[i++];
  320. m = child.minZ + child.z;
  321. if (m < min)
  322. min = m;
  323. }
  324. return min;
  325. }
  326. /**
  327. * The maximum extremum of the object along the X-axis.
  328. */
  329. public function get maxX():Number
  330. {
  331. // todo: this isn't right, doesn't take into account transforms
  332. var i:uint;
  333. var len:uint = _children.length;
  334. var max:Number = Number.NEGATIVE_INFINITY;
  335. var m:Number;
  336. while (i < len) {
  337. var child:ObjectContainer3D = _children[i++];
  338. m = child.maxX + child.x;
  339. if (m > max)
  340. max = m;
  341. }
  342. return max;
  343. }
  344. /**
  345. * The maximum extremum of the object along the Y-axis.
  346. */
  347. public function get maxY():Number
  348. {
  349. var i:uint;
  350. var len:uint = _children.length;
  351. var max:Number = Number.NEGATIVE_INFINITY;
  352. var m:Number;
  353. while (i < len) {
  354. var child:ObjectContainer3D = _children[i++];
  355. m = child.maxY + child.y;
  356. if (m > max)
  357. max = m;
  358. }
  359. return max;
  360. }
  361. /**
  362. * The maximum extremum of the object along the Z-axis.
  363. */
  364. public function get maxZ():Number
  365. {
  366. var i:uint;
  367. var len:uint = _children.length;
  368. var max:Number = Number.NEGATIVE_INFINITY;
  369. var m:Number;
  370. while (i < len) {
  371. var child:ObjectContainer3D = _children[i++];
  372. m = child.maxZ + child.z;
  373. if (m > max)
  374. max = m;
  375. }
  376. return max;
  377. }
  378. /**
  379. * The space partition to be used by the object container and all its recursive children, unless it has its own
  380. * space partition assigned.
  381. */
  382. public function get partition():Partition3D
  383. {
  384. return _explicitPartition;
  385. }
  386. public function set partition(value:Partition3D):void
  387. {
  388. _explicitPartition = value;
  389. implicitPartition = value? value : (_parent? _parent.implicitPartition : null);
  390. }
  391. /**
  392. * The transformation matrix that transforms from model to world space.
  393. */
  394. public function get sceneTransform():Matrix3D
  395. {
  396. if (_sceneTransformDirty)
  397. updateSceneTransform();
  398. return _sceneTransform;
  399. }
  400. /**
  401. * A reference to the Scene3D object to which this object belongs.
  402. */
  403. public function get scene():Scene3D
  404. {
  405. return _scene;
  406. }
  407. public function set scene(value:Scene3D):void
  408. {
  409. var i:uint;
  410. var len:uint = _children.length;
  411. while (i < len)
  412. _children[i++].scene = value;
  413. if (_scene == value)
  414. return;
  415. // test to see if we're switching roots while we're already using a scene partition
  416. if (value == null)
  417. _oldScene = _scene;
  418. if (_explicitPartition && _oldScene && _oldScene != _scene)
  419. partition = null;
  420. if (value)
  421. _oldScene = null;
  422. // end of stupid partition test code
  423. _scene = value;
  424. if (_scene)
  425. _scene.dispatchEvent(new Scene3DEvent(Scene3DEvent.ADDED_TO_SCENE, this));
  426. else if (_oldScene)
  427. _oldScene.dispatchEvent(new Scene3DEvent(Scene3DEvent.REMOVED_FROM_SCENE, this));
  428. }
  429. /**
  430. * The inverse scene transform object that transforms from world to model space.
  431. */
  432. public function get inverseSceneTransform():Matrix3D
  433. {
  434. if (_inverseSceneTransformDirty) {
  435. _inverseSceneTransform.copyFrom(sceneTransform);
  436. _inverseSceneTransform.invert();
  437. _inverseSceneTransformDirty = false;
  438. }
  439. return _inverseSceneTransform;
  440. }
  441. /**
  442. * The parent ObjectContainer3D to which this object's transformation is relative.
  443. */
  444. public function get parent():ObjectContainer3D
  445. {
  446. return _parent;
  447. }
  448. /**
  449. * Creates a new ObjectContainer3D object.
  450. */
  451. public function ObjectContainer3D()
  452. {
  453. super();
  454. }
  455. public function contains(child:ObjectContainer3D):Boolean
  456. {
  457. return _children.indexOf(child) >= 0;
  458. }
  459. /**
  460. * Adds a child ObjectContainer3D to the current object. The child's transformation will become relative to the
  461. * current object's transformation.
  462. * @param child The object to be added as a child.
  463. * @return A reference to the added child object.
  464. */
  465. public function addChild(child:ObjectContainer3D):ObjectContainer3D
  466. {
  467. if (child == null)
  468. throw new Error("Parameter child cannot be null.");
  469. if (child._parent)
  470. child._parent.removeChild(child);
  471. if (!child._explicitPartition)
  472. child.implicitPartition = _implicitPartition;
  473. child.setParent(this);
  474. child.scene = _scene;
  475. child.notifySceneTransformChange();
  476. child.updateMouseChildren();
  477. child.updateImplicitVisibility();
  478. _children.push(child);
  479. return child;
  480. }
  481. /**
  482. * Adds an array of 3d objects to the scene as children of the container
  483. *
  484. * @param ...childarray An array of 3d objects to be added
  485. */
  486. public function addChildren(...childarray):void
  487. {
  488. for each (var child:ObjectContainer3D in childarray)
  489. addChild(child);
  490. }
  491. /**
  492. * Removes a 3d object from the child array of the container
  493. *
  494. * @param child The 3d object to be removed
  495. * @throws Error ObjectContainer3D.removeChild(null)
  496. */
  497. public function removeChild(child:ObjectContainer3D):void
  498. {
  499. if (child == null)
  500. throw new Error("Parameter child cannot be null");
  501. var childIndex:int = _children.indexOf(child);
  502. if (childIndex == -1)
  503. throw new Error("Parameter is not a child of the caller");
  504. removeChildInternal(childIndex, child);
  505. }
  506. /**
  507. * Removes a 3d object from the child array of the container
  508. *
  509. * @param index Index of 3d object to be removed
  510. */
  511. public function removeChildAt(index:uint):void
  512. {
  513. var child:ObjectContainer3D = _children[index];
  514. removeChildInternal(index, child);
  515. }
  516. private function removeChildInternal(childIndex:uint, child:ObjectContainer3D):void
  517. {
  518. // index is important because getChildAt needs to be regular.
  519. _children.splice(childIndex, 1);
  520. // this needs to be nullified before the callbacks!
  521. child.setParent(null);
  522. if (!child._explicitPartition)
  523. child.implicitPartition = null;
  524. }
  525. /**
  526. * Retrieves the child object at the given index.
  527. * @param index The index of the object to be retrieved.
  528. * @return The child object at the given index.
  529. */
  530. public function getChildAt(index:uint):ObjectContainer3D
  531. {
  532. return _children[index];
  533. }
  534. /**
  535. * The amount of child objects of the ObjectContainer3D.
  536. */
  537. public function get numChildren():uint
  538. {
  539. return _children.length;
  540. }
  541. /**
  542. * @inheritDoc
  543. */
  544. override public function lookAt(target:Vector3D, upAxis:Vector3D = null):void
  545. {
  546. super.lookAt(target, upAxis);
  547. notifySceneTransformChange();
  548. }
  549. override public function translateLocal(axis:Vector3D, distance:Number):void
  550. {
  551. super.translateLocal(axis, distance);
  552. notifySceneTransformChange();
  553. }
  554. /**
  555. * @inheritDoc
  556. */
  557. override public function dispose():void
  558. {
  559. if (parent)
  560. parent.removeChild(this);
  561. }
  562. /**
  563. * Disposes the current ObjectContainer3D including all of its children. This is a merely a convenience method.
  564. */
  565. public function disposeWithChildren():void
  566. {
  567. dispose();
  568. while (numChildren > 0)
  569. getChildAt(0).dispose();
  570. }
  571. /**
  572. * Clones this ObjectContainer3D instance along with all it's children, and
  573. * returns the result (which will be a copy of this container, containing copies
  574. * of all it's children.)
  575. */
  576. override public function clone():Object3D
  577. {
  578. var clone:ObjectContainer3D = new ObjectContainer3D();
  579. clone.pivotPoint = pivotPoint;
  580. clone.transform = transform;
  581. clone.partition = partition;
  582. clone.name = name;
  583. var len:uint = _children.length;
  584. for (var i:uint = 0; i < len; ++i)
  585. clone.addChild(ObjectContainer3D(_children[i].clone()));
  586. // todo: implement for all subtypes
  587. return clone;
  588. }
  589. override public function rotate(axis:Vector3D, angle:Number):void
  590. {
  591. super.rotate(axis, angle);
  592. notifySceneTransformChange();
  593. }
  594. /**
  595. * @inheritDoc
  596. */
  597. override public function dispatchEvent(event:Event):Boolean
  598. {
  599. // maybe not the best way to fake bubbling?
  600. var ret:Boolean = super.dispatchEvent(event);
  601. if (event.bubbles) {
  602. if (_parent)
  603. _parent.dispatchEvent(event);
  604. // if it's scene root
  605. else if (_scene)
  606. _scene.dispatchEvent(event);
  607. }
  608. return ret;
  609. }
  610. public function updateImplicitVisibility():void
  611. {
  612. var len:uint = _children.length;
  613. _implicitVisibility = _parent._explicitVisibility && _parent._implicitVisibility;
  614. for (var i:uint = 0; i < len; ++i)
  615. _children[i].updateImplicitVisibility();
  616. }
  617. override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
  618. {
  619. super.addEventListener(type, listener, useCapture, priority, useWeakReference);
  620. switch (type) {
  621. case Object3DEvent.SCENETRANSFORM_CHANGED:
  622. _listenToSceneTransformChanged = true;
  623. break;
  624. case Object3DEvent.SCENE_CHANGED:
  625. _listenToSceneChanged = true;
  626. break;
  627. }
  628. }
  629. override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
  630. {
  631. super.removeEventListener(type, listener, useCapture);
  632. if (hasEventListener(type))
  633. return;
  634. switch (type) {
  635. case Object3DEvent.SCENETRANSFORM_CHANGED:
  636. _listenToSceneTransformChanged = false;
  637. break;
  638. case Object3DEvent.SCENE_CHANGED:
  639. _listenToSceneChanged = false;
  640. break;
  641. }
  642. }
  643. }
  644. }