/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

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