PageRenderTime 49ms CodeModel.GetById 16ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 1ms

/src/away3d/entities/Entity.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 440 lines | 268 code | 67 blank | 105 comment | 37 complexity | 59074dd68783e1167907e71eb2769be5 MD5 | raw file
  1package away3d.entities
  2{
  3	import away3d.arcane;
  4	import away3d.bounds.*;
  5	import away3d.containers.*;
  6	import away3d.core.math.Matrix3DUtils;
  7	import away3d.core.partition.*;
  8	import away3d.core.pick.*;
  9	import away3d.errors.*;
 10	import away3d.library.assets.*;
 11	
 12	import flash.geom.*;
 13	
 14	use namespace arcane;
 15	
 16	/**
 17	 * The Entity class provides an abstract base class for all scene graph objects that are considered having a
 18	 * "presence" in the scene, in the sense that it can be considered an actual object with a position and a size (even
 19	 * if infinite or idealised), rather than a grouping.
 20	 * Entities can be partitioned in a space partitioning system and in turn collected by an EntityCollector.
 21	 *
 22	 * @see away3d.partition.Partition3D
 23	 * @see away3d.core.traverse.EntityCollector
 24	 */
 25	public class Entity extends ObjectContainer3D
 26	{
 27		private var _showBounds:Boolean;
 28		private var _partitionNode:EntityNode;
 29		private var _boundsIsShown:Boolean = false;
 30		private var _shaderPickingDetails:Boolean;
 31		
 32		arcane var _pickingCollisionVO:PickingCollisionVO;
 33		arcane var _pickingCollider:IPickingCollider;
 34		arcane var _staticNode:Boolean;
 35		
 36		protected var _bounds:BoundingVolumeBase;
 37		protected var _boundsInvalid:Boolean = true;
 38		private var _worldBounds:BoundingVolumeBase;
 39		private var _worldBoundsInvalid:Boolean = true;
 40
 41		override public function set ignoreTransform(value:Boolean):void
 42		{
 43			if (_scene)
 44				_scene.invalidateEntityBounds(this);
 45			super.ignoreTransform = value;
 46		}
 47		
 48		/**
 49		 * Used by the shader-based picking system to determine whether a separate render pass is made in order
 50		 * to offer more details for the picking collision object, including local position, normal vector and uv value.
 51		 * Defaults to false.
 52		 *
 53		 * @see away3d.core.pick.ShaderPicker
 54		 */
 55		public function get shaderPickingDetails():Boolean
 56		{
 57			return _shaderPickingDetails;
 58		}
 59		
 60		public function set shaderPickingDetails(value:Boolean):void
 61		{
 62			_shaderPickingDetails = value;
 63		}
 64		
 65		/**
 66		 * Defines whether or not the object will be moved or animated at runtime. This property is used by some partitioning systems to improve performance.
 67		 * Warning: if set to true, they may not be processed by certain partition systems using static visibility lists, unless they're specifically assigned to the visibility list.
 68		 */
 69		public function get staticNode():Boolean
 70		{
 71			return _staticNode;
 72		}
 73		
 74		public function set staticNode(value:Boolean):void
 75		{
 76			_staticNode = value;
 77		}
 78		
 79		/**
 80		 * Returns a unique picking collision value object for the entity.
 81		 */
 82		public function get pickingCollisionVO():PickingCollisionVO
 83		{
 84			if (!_pickingCollisionVO)
 85				_pickingCollisionVO = new PickingCollisionVO(this);
 86			
 87			return _pickingCollisionVO;
 88		}
 89		
 90		/**
 91		 * Tests if a collision occurs before shortestCollisionDistance, using the data stored in PickingCollisionVO.
 92		 * @param shortestCollisionDistance
 93		 * @return
 94		 */
 95		arcane function collidesBefore(shortestCollisionDistance:Number, findClosest:Boolean):Boolean
 96		{
 97			shortestCollisionDistance = shortestCollisionDistance;
 98			findClosest = findClosest;
 99			return true;
100		}
101		
102		/**
103		 *
104		 */
105		public function get showBounds():Boolean
106		{
107			return _showBounds;
108		}
109		
110		public function set showBounds(value:Boolean):void
111		{
112			if (value == _showBounds)
113				return;
114			
115			_showBounds = value;
116			
117			if (_showBounds)
118				addBounds();
119			else
120				removeBounds();
121		}
122		
123		/**
124		 * @inheritDoc
125		 */
126		override public function get minX():Number
127		{
128			if (_boundsInvalid)
129				updateBounds();
130			
131			return _bounds.min.x;
132		}
133		
134		/**
135		 * @inheritDoc
136		 */
137		override public function get minY():Number
138		{
139			if (_boundsInvalid)
140				updateBounds();
141			
142			return _bounds.min.y;
143		}
144		
145		/**
146		 * @inheritDoc
147		 */
148		override public function get minZ():Number
149		{
150			if (_boundsInvalid)
151				updateBounds();
152			
153			return _bounds.min.z;
154		}
155		
156		/**
157		 * @inheritDoc
158		 */
159		override public function get maxX():Number
160		{
161			if (_boundsInvalid)
162				updateBounds();
163			
164			return _bounds.max.x;
165		}
166		
167		/**
168		 * @inheritDoc
169		 */
170		override public function get maxY():Number
171		{
172			if (_boundsInvalid)
173				updateBounds();
174			
175			return _bounds.max.y;
176		}
177		
178		/**
179		 * @inheritDoc
180		 */
181		override public function get maxZ():Number
182		{
183			if (_boundsInvalid)
184				updateBounds();
185			
186			return _bounds.max.z;
187		}
188		
189		/**
190		 * The bounding volume approximating the volume occupied by the Entity.
191		 */
192		public function get bounds():BoundingVolumeBase
193		{
194			if (_boundsInvalid)
195				updateBounds();
196			
197			return _bounds;
198		}
199		
200		public function set bounds(value:BoundingVolumeBase):void
201		{
202			removeBounds();
203			_bounds = value;
204			_worldBounds = value.clone();
205			invalidateBounds();
206			if (_showBounds)
207				addBounds();
208		}
209		
210		public function get worldBounds():BoundingVolumeBase
211		{
212			if (_worldBoundsInvalid)
213				updateWorldBounds();
214			
215			return _worldBounds;
216		}
217		
218		private function updateWorldBounds():void
219		{
220			_worldBounds.transformFrom(bounds, sceneTransform);
221			_worldBoundsInvalid = false;
222		}
223		
224		/**
225		 * @inheritDoc
226		 */
227		override arcane function set implicitPartition(value:Partition3D):void
228		{
229			if (value == _implicitPartition)
230				return;
231			
232			if (_implicitPartition)
233				notifyPartitionUnassigned();
234			
235			super.implicitPartition = value;
236			
237			notifyPartitionAssigned();
238		}
239		
240		/**
241		 * @inheritDoc
242		 */
243		override public function set scene(value:Scene3D):void
244		{
245			if (value == _scene)
246				return;
247			
248			if (_scene)
249				_scene.unregisterEntity(this);
250			
251			// callback to notify object has been spawned. Casts to please FDT
252			if (value)
253				value.registerEntity(this);
254			
255			super.scene = value;
256		}
257		
258		override public function get assetType():String
259		{
260			return AssetType.ENTITY;
261		}
262		
263		/**
264		 * Used by the raycast-based picking system to determine how the geometric contents of an entity are processed
265		 * in order to offer more details for the picking collision object, including local position, normal vector and uv value.
266		 * Defaults to null.
267		 *
268		 * @see away3d.core.pick.RaycastPicker
269		 */
270		public function get pickingCollider():IPickingCollider
271		{
272			return _pickingCollider;
273		}
274		
275		public function set pickingCollider(value:IPickingCollider):void
276		{
277			_pickingCollider = value;
278		}
279		
280		/**
281		 * Creates a new Entity object.
282		 */
283		public function Entity()
284		{
285			super();
286			
287			_bounds = getDefaultBoundingVolume();
288			_worldBounds = getDefaultBoundingVolume();
289		}
290		
291		/**
292		 * Gets a concrete EntityPartition3DNode subclass that is associated with this Entity instance
293		 */
294		public function getEntityPartitionNode():EntityNode
295		{
296			return _partitionNode ||= createEntityPartitionNode();
297		}
298		
299		public function isIntersectingRay(rayPosition:Vector3D, rayDirection:Vector3D):Boolean
300		{
301			if(!pickingCollisionVO.localRayPosition) pickingCollisionVO.localRayPosition = new Vector3D();
302			if(!pickingCollisionVO.localRayDirection) pickingCollisionVO.localRayDirection = new Vector3D();
303			if(!pickingCollisionVO.localNormal) pickingCollisionVO.localNormal = new Vector3D();
304
305			// convert ray to entity space
306			var localRayPosition:Vector3D = pickingCollisionVO.localRayPosition;
307			var localRayDirection:Vector3D = pickingCollisionVO.localRayDirection;
308			Matrix3DUtils.transformVector(inverseSceneTransform, rayPosition, localRayPosition);
309			Matrix3DUtils.deltaTransformVector(inverseSceneTransform, rayDirection, localRayDirection);
310
311			// check for ray-bounds collision
312			var rayEntryDistance:Number = bounds.rayIntersection(localRayPosition, localRayDirection, pickingCollisionVO.localNormal);
313			if (rayEntryDistance < 0)
314				return false;
315			
316			// Store collision data.
317			pickingCollisionVO.rayEntryDistance = rayEntryDistance;
318			pickingCollisionVO.rayPosition = rayPosition;
319			pickingCollisionVO.rayDirection = rayDirection;
320			pickingCollisionVO.rayOriginIsInsideBounds = rayEntryDistance == 0;
321			
322			return true;
323		}
324		
325		/**
326		 * Factory method that returns the current partition node. Needs to be overridden by concrete subclasses
327		 * such as Mesh to return the correct concrete subtype of EntityPartition3DNode (for Mesh = MeshPartition3DNode,
328		 * most IRenderables (particles fe) would return RenderablePartition3DNode, I suppose)
329		 */
330		protected function createEntityPartitionNode():EntityNode
331		{
332			throw new AbstractMethodError();
333		}
334		
335		/**
336		 * Creates the default bounding box to be used by this type of Entity.
337		 * @return
338		 */
339		protected function getDefaultBoundingVolume():BoundingVolumeBase
340		{
341			// point lights should be using sphere bounds
342			// directional lights should be using null bounds
343			return new AxisAlignedBoundingBox();
344		}
345		
346		/**
347		 * Updates the bounding volume for the object. Overriding methods need to set invalid flag to false!
348		 */
349		protected function updateBounds():void
350		{
351			throw new AbstractMethodError();
352		}
353		
354		/**
355		 * @inheritDoc
356		 */
357		override protected function invalidateSceneTransform():void
358		{
359			if (!_ignoreTransform) {
360				super.invalidateSceneTransform();
361				_worldBoundsInvalid = true;
362				notifySceneBoundsInvalid();
363			}
364		}
365		
366		/**
367		 * Invalidates the bounding volume, causing to be updated when requested.
368		 */
369		protected function invalidateBounds():void
370		{
371			_boundsInvalid = true;
372			_worldBoundsInvalid = true;
373			notifySceneBoundsInvalid();
374		}
375		
376		override protected function updateMouseChildren():void
377		{
378			// If there is a parent and this child does not have a triangle collider, use its parent's triangle collider.
379			if (_parent && !pickingCollider) {
380				if (_parent is Entity) {
381					var collider:IPickingCollider = Entity(_parent).pickingCollider;
382					if (collider)
383						pickingCollider = collider;
384				}
385			}
386			
387			super.updateMouseChildren();
388		}
389		
390		/**
391		 * Notify the scene that the global scene bounds have changed, so it can be repartitioned.
392		 */
393		private function notifySceneBoundsInvalid():void
394		{
395			if (_scene)
396				_scene.invalidateEntityBounds(this);
397		}
398		
399		/**
400		 * Notify the scene that a new partition was assigned.
401		 */
402		private function notifyPartitionAssigned():void
403		{
404			if (_scene)
405				_scene.registerPartition(this); //_onAssignPartitionCallback(this);
406		}
407		
408		/**
409		 * Notify the scene that a partition was unassigned.
410		 */
411		private function notifyPartitionUnassigned():void
412		{
413			if (_scene)
414				_scene.unregisterPartition(this);
415		}
416		
417		private function addBounds():void
418		{
419			if (!_boundsIsShown) {
420				_boundsIsShown = true;
421				addChild(_bounds.boundingRenderable);
422			}
423		}
424		
425		private function removeBounds():void
426		{
427			if (_boundsIsShown) {
428				_boundsIsShown = false;
429				removeChild(_bounds.boundingRenderable);
430				_bounds.disposeRenderable();
431			}
432		}
433		
434		arcane function internalUpdate():void
435		{
436			if (_controller)
437				_controller.update();
438		}
439	}
440}