PageRenderTime 40ms CodeModel.GetById 15ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

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