ModelExperimentalAnimation.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. import defaultValue from "../../Core/defaultValue.js";
  2. import defined from "../../Core/defined.js";
  3. import Event from "../../Core/Event.js";
  4. import JulianDate from "../../Core/JulianDate.js";
  5. import ModelAnimationLoop from ".././ModelAnimationLoop.js";
  6. import ModelAnimationState from ".././ModelAnimationState.js";
  7. import ModelExperimentalAnimationChannel from "../ModelExperimental/ModelExperimentalAnimationChannel.js";
  8. /**
  9. * An active animation derived from a glTF asset. An active animation is an
  10. * animation that is either currently playing or scheduled to be played due to
  11. * being added to a model's {@link ModelExperimentalAnimationCollection}. An active animation
  12. * is an instance of an animation; for example, there can be multiple active
  13. * animations for the same glTF animation, each with a different start time.
  14. * <p>
  15. * Create this by calling {@link ModelExperimentalAnimationCollection#add}.
  16. * </p>
  17. *
  18. * @alias ModelExperimentalAnimation
  19. * @internalConstructor
  20. * @class
  21. *
  22. * @see ModelExperimentalAnimationCollection#add
  23. */
  24. function ModelExperimentalAnimation(model, animation, options) {
  25. this._animation = animation;
  26. this._name = animation.name;
  27. this._runtimeChannels = undefined;
  28. this._startTime = JulianDate.clone(options.startTime);
  29. this._delay = defaultValue(options.delay, 0.0); // in seconds
  30. this._stopTime = JulianDate.clone(options.stopTime);
  31. /**
  32. * When <code>true</code>, the animation is removed after it stops playing.
  33. * This is slightly more efficient that not removing it, but if, for example,
  34. * time is reversed, the animation is not played again.
  35. *
  36. * @type {Boolean}
  37. * @default false
  38. */
  39. this.removeOnStop = defaultValue(options.removeOnStop, false);
  40. this._multiplier = defaultValue(options.multiplier, 1.0);
  41. this._reverse = defaultValue(options.reverse, false);
  42. this._loop = defaultValue(options.loop, ModelAnimationLoop.NONE);
  43. /**
  44. * The event fired when this animation is started. This can be used, for
  45. * example, to play a sound or start a particle system, when the animation starts.
  46. * <p>
  47. * This event is fired at the end of the frame after the scene is rendered.
  48. * </p>
  49. *
  50. * @type {Event}
  51. * @default new Event()
  52. *
  53. * @example
  54. * animation.start.addEventListener(function(model, animation) {
  55. * console.log('Animation started: ' + animation.name);
  56. * });
  57. */
  58. this.start = new Event();
  59. /**
  60. * The event fired when on each frame when this animation is updated. The
  61. * current time of the animation, relative to the glTF animation time span, is
  62. * passed to the event, which allows, for example, starting new animations at a
  63. * specific time relative to a playing animation.
  64. * <p>
  65. * This event is fired at the end of the frame after the scene is rendered.
  66. * </p>
  67. *
  68. * @type {Event}
  69. * @default new Event()
  70. *
  71. * @example
  72. * animation.update.addEventListener(function(model, animation, time) {
  73. * console.log('Animation updated: ' + animation.name + '. glTF animation time: ' + time);
  74. * });
  75. */
  76. this.update = new Event();
  77. /**
  78. * The event fired when this animation is stopped. This can be used, for
  79. * example, to play a sound or start a particle system, when the animation stops.
  80. * <p>
  81. * This event is fired at the end of the frame after the scene is rendered.
  82. * </p>
  83. *
  84. * @type {Event}
  85. * @default new Event()
  86. *
  87. * @example
  88. * animation.stop.addEventListener(function(model, animation) {
  89. * console.log('Animation stopped: ' + animation.name);
  90. * });
  91. */
  92. this.stop = new Event();
  93. this._state = ModelAnimationState.STOPPED;
  94. // Set during animation update
  95. this._computedStartTime = undefined;
  96. this._duration = undefined;
  97. // To avoid allocations in ModelExperimentalAnimationCollection.update
  98. const that = this;
  99. this._raiseStartEvent = function () {
  100. that.start.raiseEvent(model, that);
  101. };
  102. this._updateEventTime = 0.0;
  103. this._raiseUpdateEvent = function () {
  104. that.update.raiseEvent(model, that, that._updateEventTime);
  105. };
  106. this._raiseStopEvent = function () {
  107. that.stop.raiseEvent(model, that);
  108. };
  109. this._model = model;
  110. this._localStartTime = undefined;
  111. this._localStopTime = undefined;
  112. initialize(this);
  113. }
  114. Object.defineProperties(ModelExperimentalAnimation.prototype, {
  115. /**
  116. * The glTF animation.
  117. *
  118. * @memberof ModelExperimentalAnimation.prototype
  119. *
  120. * @type {ModelComponents.Animation}
  121. * @readonly
  122. *
  123. * @private
  124. */
  125. animation: {
  126. get: function () {
  127. return this._animation;
  128. },
  129. },
  130. /**
  131. * The name that identifies this animation in the model, if it exists.
  132. *
  133. * @memberof ModelExperimentalAnimation.prototype
  134. *
  135. * @type {String}
  136. * @readonly
  137. */
  138. name: {
  139. get: function () {
  140. return this._name;
  141. },
  142. },
  143. /**
  144. * The runtime animation channels for this animation.
  145. *
  146. * @memberof ModelExperimentalAnimation.prototype
  147. *
  148. * @type {ModelExperimentalAnimationChannel[]}
  149. * @readonly
  150. *
  151. * @private
  152. */
  153. runtimeChannels: {
  154. get: function () {
  155. return this._runtimeChannels;
  156. },
  157. },
  158. /**
  159. * The {@link ModelExperimental} that owns this animation.
  160. *
  161. * @memberof ModelExperimentalAnimation.prototype
  162. *
  163. * @type {ModelExperimental}
  164. * @readonly
  165. *
  166. * @private
  167. */
  168. model: {
  169. get: function () {
  170. return this._model;
  171. },
  172. },
  173. /**
  174. * The starting point of the animation in local animation time. This is the minimum
  175. * time value across all of the keyframes belonging to this animation.
  176. *
  177. * @memberof ModelExperimentalAnimation.prototype
  178. *
  179. * @type {Number}
  180. * @readonly
  181. *
  182. * @private
  183. */
  184. localStartTime: {
  185. get: function () {
  186. return this._localStartTime;
  187. },
  188. },
  189. /**
  190. * The stopping point of the animation in local animation time. This is the maximum
  191. * time value across all of the keyframes belonging to this animation.
  192. *
  193. * @memberof ModelExperimentalAnimation.prototype
  194. *
  195. * @type {Number}
  196. * @readonly
  197. *
  198. * @private
  199. */
  200. localStopTime: {
  201. get: function () {
  202. return this._localStopTime;
  203. },
  204. },
  205. /**
  206. * The scene time to start playing this animation. When this is <code>undefined</code>,
  207. * the animation starts at the next frame.
  208. *
  209. * @memberof ModelExperimentalAnimation.prototype
  210. *
  211. * @type {JulianDate}
  212. * @readonly
  213. *
  214. * @default undefined
  215. */
  216. startTime: {
  217. get: function () {
  218. return this._startTime;
  219. },
  220. },
  221. /**
  222. * The delay, in seconds, from {@link ModelExperimentalAnimation#startTime} to start playing.
  223. *
  224. * @memberof ModelExperimentalAnimation.prototype
  225. *
  226. * @type {Number}
  227. * @readonly
  228. *
  229. * @default undefined
  230. */
  231. delay: {
  232. get: function () {
  233. return this._delay;
  234. },
  235. },
  236. /**
  237. * The scene time to stop playing this animation. When this is <code>undefined</code>,
  238. * the animation is played for its full duration and perhaps repeated depending on
  239. * {@link ModelExperimentalAnimation#loop}.
  240. *
  241. * @memberof ModelExperimentalAnimation.prototype
  242. *
  243. * @type {JulianDate}
  244. * @readonly
  245. *
  246. * @default undefined
  247. */
  248. stopTime: {
  249. get: function () {
  250. return this._stopTime;
  251. },
  252. },
  253. /**
  254. * Values greater than <code>1.0</code> increase the speed that the animation is played relative
  255. * to the scene clock speed; values less than <code>1.0</code> decrease the speed. A value of
  256. * <code>1.0</code> plays the animation at the speed in the glTF animation mapped to the scene
  257. * clock speed. For example, if the scene is played at 2x real-time, a two-second glTF animation
  258. * will play in one second even if <code>multiplier</code> is <code>1.0</code>.
  259. *
  260. * @memberof ModelExperimentalAnimation.prototype
  261. *
  262. * @type {Number}
  263. * @readonly
  264. *
  265. * @default 1.0
  266. */
  267. multiplier: {
  268. get: function () {
  269. return this._multiplier;
  270. },
  271. },
  272. /**
  273. * When <code>true</code>, the animation is played in reverse.
  274. *
  275. * @memberof ModelExperimentalAnimation.prototype
  276. *
  277. * @type {Boolean}
  278. * @readonly
  279. *
  280. * @default false
  281. */
  282. reverse: {
  283. get: function () {
  284. return this._reverse;
  285. },
  286. },
  287. /**
  288. * Determines if and how the animation is looped.
  289. *
  290. * @memberof ModelExperimentalAnimation.prototype
  291. *
  292. * @type {ModelAnimationLoop}
  293. * @readonly
  294. *
  295. * @default {@link ModelAnimationLoop.NONE}
  296. */
  297. loop: {
  298. get: function () {
  299. return this._loop;
  300. },
  301. },
  302. });
  303. function initialize(runtimeAnimation) {
  304. let localStartTime = Number.MAX_VALUE;
  305. let localStopTime = -Number.MAX_VALUE;
  306. const sceneGraph = runtimeAnimation._model.sceneGraph;
  307. const animation = runtimeAnimation._animation;
  308. const channels = animation.channels;
  309. const length = channels.length;
  310. const runtimeChannels = [];
  311. for (let i = 0; i < length; i++) {
  312. const channel = channels[i];
  313. const target = channel.target;
  314. // Ignore this channel if the target is invalid, i.e. if the node
  315. // it references doesn't exist.
  316. if (!defined(target)) {
  317. continue;
  318. }
  319. const nodeIndex = target.node.index;
  320. const runtimeNode = sceneGraph._runtimeNodes[nodeIndex];
  321. const runtimeChannel = new ModelExperimentalAnimationChannel({
  322. channel: channel,
  323. runtimeAnimation: runtimeAnimation,
  324. runtimeNode: runtimeNode,
  325. });
  326. const times = channel.sampler.input;
  327. localStartTime = Math.min(localStartTime, times[0]);
  328. localStopTime = Math.max(localStopTime, times[times.length - 1]);
  329. runtimeChannels.push(runtimeChannel);
  330. }
  331. runtimeAnimation._runtimeChannels = runtimeChannels;
  332. runtimeAnimation._localStartTime = localStartTime;
  333. runtimeAnimation._localStopTime = localStopTime;
  334. }
  335. /**
  336. * Evaluate all animation channels to advance this animation.
  337. *
  338. * @param {Number} time The local animation time.
  339. *
  340. * @private
  341. */
  342. ModelExperimentalAnimation.prototype.animate = function (time) {
  343. const runtimeChannels = this._runtimeChannels;
  344. const length = runtimeChannels.length;
  345. for (let i = 0; i < length; i++) {
  346. runtimeChannels[i].animate(time);
  347. }
  348. };
  349. export default ModelExperimentalAnimation;