PageRenderTime 535ms CodeModel.GetById 181ms app.highlight 95ms RepoModel.GetById 254ms app.codeStats 1ms

/src/away3d/animators/PathAnimator.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 455 lines | 275 code | 79 blank | 101 comment | 43 complexity | 32e47ff08aa6ad78966cc43b880986dd MD5 | raw file
  1package away3d.animators
  2{
  3	import away3d.core.base.Object3D;
  4	import away3d.core.math.Vector3DUtils;
  5	import away3d.events.PathEvent;
  6	import away3d.paths.IPath;
  7	import away3d.paths.IPathSegment;
  8	
  9	import flash.events.EventDispatcher;
 10	import flash.geom.Vector3D;
 11	
 12	public class PathAnimator extends EventDispatcher
 13	{
 14		private var _path:IPath;
 15		private var _time:Number;
 16		private var _index:uint = 0;
 17		private var _rotations:Vector.<Vector3D>;
 18		private var _alignToPath:Boolean;
 19		private var _target:Object3D;
 20		private var _lookAtTarget:Object3D;
 21		private var _offset:Vector3D;
 22		private var _tmpOffset:Vector3D;
 23		private var _position:Vector3D = new Vector3D();
 24		private var _lastTime:Number;
 25		private var _from:Number;
 26		private var _to:Number;
 27		private var _bRange:Boolean;
 28		private var _bSegment:Boolean;
 29		private var _bCycle:Boolean;
 30		private var _lastSegment:uint = 0;
 31		private var _rot:Vector3D;
 32		private var _upAxis:Vector3D = new Vector3D(0, 1, 0);
 33		private var _basePosition:Vector3D = new Vector3D(0, 0, 0);
 34		
 35		/**
 36		 * Creates a new <code>PathAnimator</code>
 37		 *
 38		 * @param                 [optional] path                The QuadraticPath to animate onto.
 39		 * @param                 [optional] target              An Object3D, the object to animate along the path. It can be Mesh, Camera, ObjectContainer3D...
 40		 * @param                 [optional] offset              A Vector3D to define the target offset to its location on the path.
 41		 * @param                 [optional] alignToPath         Defines if the object animated along the path is orientated to the path. Default is true.
 42		 * @param                 [optional] lookAtTarget        An Object3D that the target will constantly look at during animation.
 43		 * @param                 [optional] rotations           A Vector.&lt;Vector3D&gt; to define rotations per pathsegments. If PathExtrude is used to simulate the "road", use the very same rotations vector.
 44		 */
 45		function PathAnimator(path:IPath = null, target:Object3D = null, offset:Vector3D = null, alignToPath:Boolean = true, lookAtTarget:Object3D = null, rotations:Vector.<Vector3D> = null)
 46		{
 47			_index = 0;
 48			_time = _lastTime = 0;
 49			
 50			_path = path;
 51			
 52			_target = target;
 53			_alignToPath = alignToPath;
 54			_lookAtTarget = lookAtTarget;
 55			
 56			if (offset)
 57				setOffset(offset.x, offset.y, offset.z);
 58			
 59			this.rotations = rotations;
 60			
 61			if (_lookAtTarget && _alignToPath)
 62				_alignToPath = false;
 63		}
 64		
 65		public function get upAxis():Vector3D
 66		{
 67			return _upAxis;
 68		}
 69		
 70		public function set upAxis(value:Vector3D):void
 71		{
 72			_upAxis = value;
 73		}
 74		
 75		/**
 76		 * sets an optional offset to the position on the path, ideal for cameras or reusing the same <code>Path</code> object for parallel animations
 77		 */
 78		public function setOffset(x:Number = 0, y:Number = 0, z:Number = 0):void
 79		{
 80			_offset ||= new Vector3D();
 81			
 82			_offset.x = x;
 83			_offset.y = y;
 84			_offset.z = z;
 85		}
 86		
 87		/**
 88		 * Calculates the new position and set the object on the path accordingly
 89		 *
 90		 * @param t     A Number  from 0 to 1  (less than one to allow alignToPath)
 91		 */
 92		public function updateProgress(t:Number):void
 93		{
 94			if (!_path)
 95				throw new Error("No Path object set for this class");
 96			
 97			if (t <= 0) {
 98				t = 0;
 99				_lastSegment = 0;
100				
101			} else if (t >= 1) {
102				t = 1;
103				_lastSegment = _path.numSegments - 1;
104			}
105			
106			if (_bCycle && t <= 0.1 && _lastSegment == _path.numSegments - 1)
107				dispatchEvent(new PathEvent(PathEvent.CYCLE));
108			
109			_lastTime = t;
110			
111			var multi:Number = _path.numSegments*t;
112			_index = multi;
113			
114			if (_index == _path.numSegments)
115				index--;
116			
117			if (_offset != null)
118				_target.position = _basePosition;
119			
120			var nT:Number = multi - _index;
121			updatePosition(nT, _path.segments[_index]);
122			
123			var rotate:Boolean;
124			if (_lookAtTarget) {
125				
126				if (_offset) {
127					_target.moveRight(_offset.x);
128					_target.moveUp(_offset.y);
129					_target.moveForward(_offset.z);
130				}
131				_target.lookAt(_lookAtTarget.position);
132				
133			} else if (_alignToPath) {
134				
135				if (_rotations && _rotations.length > 0) {
136					
137					if (_rotations[_index + 1] == null) {
138						
139						_rot.x = _rotations[_rotations.length - 1].x*nT;
140						_rot.y = _rotations[_rotations.length - 1].y*nT;
141						_rot.z = _rotations[_rotations.length - 1].z*nT;
142						
143					} else {
144						
145						_rot.x = _rotations[_index].x + ((_rotations[_index + 1].x - _rotations[_index].x)*nT);
146						_rot.y = _rotations[_index].y + ((_rotations[_index + 1].y - _rotations[_index].y)*nT);
147						_rot.z = _rotations[_index].z + ((_rotations[_index + 1].z - _rotations[_index].z)*nT);
148						
149					}
150					
151					_upAxis.x = 0;
152					_upAxis.y = 1;
153					_upAxis.z = 0;
154					_upAxis = Vector3DUtils.rotatePoint(_upAxis, _rot);
155					
156					_target.lookAt(_basePosition, _upAxis);
157					
158					rotate = true;
159					
160				} else
161					_target.lookAt(_position);
162				
163			}
164			
165			updateObjectPosition(rotate);
166			
167			if (_bSegment && _index > 0 && _lastSegment != _index && t < 1)
168				dispatchEvent(new PathEvent(PathEvent.CHANGE_SEGMENT));
169			
170			if (_bRange && (t >= _from && t <= _to))
171				dispatchEvent(new PathEvent(PathEvent.RANGE));
172			
173			_time = t;
174			_lastSegment = _index;
175		}
176		
177		/**
178		 * Updates a position Vector3D on the path at a given time. Do not use this handler to animate, it's in there to add dummy's or place camera before or after
179		 * the animated object. Use the update() or the automatic tweened animateOnPath() handlers instead.
180		 *
181		 * @param t      Number. A Number  from 0 to 1
182		 * @param out    Vector3D. The Vector3D to update according to the "t" time parameter.
183		 */
184		public function getPositionOnPath(t:Number, out:Vector3D):Vector3D
185		{
186			if (!_path)
187				throw new Error("No Path object set for this class");
188			
189			t = (t < 0)? 0 : (t > 1)? 1 : t;
190			var m:Number = _path.numSegments*t;
191			var i:uint = m;
192			var ps:IPathSegment = _path.segments[i];
193			
194			return ps.getPointOnSegment(m - i, out);
195		}
196		
197		/**
198		 * Returns a position on the path according to duration/elapsed time. Duration variable must be set.
199		 *
200		 * @param        ms            Number. A number representing milliseconds.
201		 * @param        duration        Number. The total duration in milliseconds.
202		 * @param        out            [optional] Vector3D. A Vector3D that will be used to return the position. If none provided, method returns a new Vector3D with this data.
203		 *
204		 * An example of use of this handler would be cases where a given "lap" must be done in a given amount of time and you would want to retrieve the "ideal" time
205		 * based on elapsed time since start of the race. By comparing actual progress to ideal time, you could extract their classement, calculate distance/time between competitors,
206		 * abort the race if goal is impossible to be reached in time etc...
207		 *
208		 *@ returns Vector3D The position at a given elapsed time, compared to total duration.
209		 */
210		public function getPositionOnPathMS(ms:Number, duration:Number, out:Vector3D):Vector3D
211		{
212			if (!_path)
213				throw new Error("No Path object set for this class");
214			
215			var t:Number = Math.abs(ms)/duration;
216			t = (t < 0)? 0 : (t > 1)? 1 : t;
217			var m:Number = _path.numSegments*t;
218			var i:uint = m;
219			var ps:IPathSegment = _path.segments[i];
220			
221			return ps.getPointOnSegment(m - i, out);
222		}
223		
224		/**
225		 * defines if the object animated along the path must be aligned to the path.
226		 */
227		public function set alignToPath(b:Boolean):void
228		{
229			_alignToPath = b;
230		}
231		
232		public function get alignToPath():Boolean
233		{
234			return _alignToPath;
235		}
236		
237		/**
238		 * returns the current interpolated position on the path with no optional offset applied
239		 */
240		public function get position():Vector3D
241		{
242			return _position;
243		}
244		
245		/**
246		 * defines the path to follow
247		 * @see Path
248		 */
249		public function set path(value:IPath):void
250		{
251			_path = value;
252		}
253		
254		public function get path():IPath
255		{
256			return _path;
257		}
258		
259		/**
260		 * Represents the progress of the animation playhead from the start (0) to the end (1) of the animation.
261		 */
262		public function get progress():Number
263		{
264			return _time;
265		}
266		
267		public function set progress(val:Number):void
268		{
269			if (_time == val)
270				return;
271			
272			updateProgress(val);
273		}
274		
275		/**
276		 * returns the segment index that is used at a given time;
277		 * @param     t        [Number]. A Number between 0 and 1. If no params, actual pathanimator time segment index is returned.
278		 */
279		public function getTimeSegment(t:Number = NaN):Number
280		{
281			t = (isNaN(t))? _time : t;
282			return Math.floor(_path.numSegments*t);
283		}
284		
285		/**
286		 * returns the actual interpolated rotation along the path.
287		 */
288		public function get orientation():Vector3D
289		{
290			return _rot;
291		}
292		
293		/**
294		 * sets the object to be animated along the path.
295		 */
296		public function set target(object3d:Object3D):void
297		{
298			_target = object3d;
299		}
300		
301		public function get target():Object3D
302		{
303			return _target;
304		}
305		
306		/**
307		 * sets the object that the animated object will be looking at along the path
308		 */
309		public function set lookAtObject(object3d:Object3D):void
310		{
311			_lookAtTarget = object3d;
312			if (_alignToPath)
313				_alignToPath = false;
314		}
315		
316		public function get lookAtObject():Object3D
317		{
318			return _lookAtTarget;
319		}
320		
321		/**
322		 * sets an optional Vector.&lt;Vector3D&gt; of rotations. if the object3d is animated along a PathExtrude object, use the very same vector to follow the "curves".
323		 */
324		public function set rotations(value:Vector.<Vector3D>):void
325		{
326			_rotations = value;
327			
328			if (_rotations && !_rot) {
329				_rot = new Vector3D();
330				_tmpOffset = new Vector3D();
331			}
332		}
333		
334		/**
335		 * Set the pointer to a given segment along the path
336		 */
337		public function set index(val:uint):void
338		{
339			_index = (val > _path.numSegments - 1)? _path.numSegments - 1 : (val > 0)? val : 0;
340		}
341		
342		public function get index():uint
343		{
344			return _index;
345		}
346		
347		/**
348		 * Default method for adding a cycle event listener. Event fired when the time reaches 1.
349		 *
350		 * @param    listener        The listener function
351		 */
352		public function addOnCycle(listener:Function):void
353		{
354			_lastTime = 0;
355			_bCycle = true;
356			this.addEventListener(PathEvent.CYCLE, listener);
357		}
358		
359		/**
360		 * Default method for removing a cycle event listener
361		 *
362		 * @param        listener        The listener function
363		 */
364		public function removeOnCycle(listener:Function):void
365		{
366			_bCycle = false;
367			this.removeEventListener(PathEvent.CYCLE, listener);
368		}
369		
370		/**
371		 * Default method for adding a range event listener. Event fired when the time is &gt;= from and &lt;= to variables.
372		 *
373		 * @param        listener        The listener function
374		 */
375		//note: If there are requests for this, it could be extended to more than one rangeEvent per path.
376		public function addOnRange(listener:Function, from:Number = 0, to:Number = 0):void
377		{
378			_from = from;
379			_to = to;
380			_bRange = true;
381			this.addEventListener(PathEvent.RANGE, listener);
382		}
383		
384		/**
385		 * Default method for removing a range event listener
386		 *
387		 * @param        listener        The listener function
388		 */
389		public function removeOnRange(listener:Function):void
390		{
391			_from = 0;
392			_to = 0;
393			_bRange = false;
394			this.removeEventListener(PathEvent.RANGE, listener);
395		}
396		
397		/**
398		 * Default method for adding a segmentchange event listener. Event fired when the time pointer enters another PathSegment.
399		 *
400		 * @param        listener        The listener function
401		 */
402		public function addOnChangeSegment(listener:Function):void
403		{
404			_bSegment = true;
405			_lastSegment = 0;
406			this.addEventListener(PathEvent.CHANGE_SEGMENT, listener);
407		}
408		
409		/**
410		 * Default method for removing a range event listener
411		 *
412		 * @param        listener        The listener function
413		 */
414		public function removeOnChangeSegment(listener:Function):void
415		{
416			_bSegment = false;
417			_lastSegment = 0;
418			this.removeEventListener(PathEvent.CHANGE_SEGMENT, listener, false);
419		}
420		
421		private function updatePosition(t:Number, ps:IPathSegment):void
422		{
423			_basePosition = ps.getPointOnSegment(t, _basePosition);
424			
425			_position.x = _basePosition.x;
426			_position.y = _basePosition.y;
427			_position.z = _basePosition.z;
428		}
429		
430		private function updateObjectPosition(rotate:Boolean = false):void
431		{
432			
433			if (rotate && _offset) {
434				
435				_tmpOffset.x = _offset.x;
436				_tmpOffset.y = _offset.y;
437				_tmpOffset.z = _offset.z;
438				_tmpOffset = Vector3DUtils.rotatePoint(_tmpOffset, _rot);
439				
440				_position.x += _tmpOffset.x;
441				_position.y += _tmpOffset.y;
442				_position.z += _tmpOffset.z;
443				
444			} else if (_offset) {
445				
446				_position.x += _offset.x;
447				_position.y += _offset.y;
448				_position.z += _offset.z;
449				
450			}
451			_target.position = _position;
452		}
453	
454	}
455}