animation.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. import { Vector3, Quaternion, Vector2, Matrix, TmpVectors } from "../Maths/math.vector.js";
  2. import { Color3, Color4 } from "../Maths/math.color.js";
  3. import { Scalar } from "../Maths/math.scalar.js";
  4. import { RegisterClass } from "../Misc/typeStore.js";
  5. import { AnimationKeyInterpolation } from "./animationKey.js";
  6. import { AnimationRange } from "./animationRange.js";
  7. import { Node } from "../node.js";
  8. import { Size } from "../Maths/math.size.js";
  9. import { WebRequest } from "../Misc/webRequest.js";
  10. import { SerializationHelper } from "../Misc/decorators.serialization.js";
  11. // Static values to help the garbage collector
  12. // Quaternion
  13. export const _staticOffsetValueQuaternion = Object.freeze(new Quaternion(0, 0, 0, 0));
  14. // Vector3
  15. export const _staticOffsetValueVector3 = Object.freeze(Vector3.Zero());
  16. // Vector2
  17. export const _staticOffsetValueVector2 = Object.freeze(Vector2.Zero());
  18. // Size
  19. export const _staticOffsetValueSize = Object.freeze(Size.Zero());
  20. // Color3
  21. export const _staticOffsetValueColor3 = Object.freeze(Color3.Black());
  22. // Color4
  23. export const _staticOffsetValueColor4 = Object.freeze(new Color4(0, 0, 0, 0));
  24. const evaluateAnimationState = {
  25. key: 0,
  26. repeatCount: 0,
  27. loopMode: 2 /*Animation.ANIMATIONLOOPMODE_CONSTANT*/,
  28. };
  29. /**
  30. * Class used to store any kind of animation
  31. */
  32. export class Animation {
  33. /**
  34. * @internal Internal use
  35. */
  36. static _PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction) {
  37. let dataType = undefined;
  38. if (!isNaN(parseFloat(from)) && isFinite(from)) {
  39. dataType = Animation.ANIMATIONTYPE_FLOAT;
  40. }
  41. else if (from instanceof Quaternion) {
  42. dataType = Animation.ANIMATIONTYPE_QUATERNION;
  43. }
  44. else if (from instanceof Vector3) {
  45. dataType = Animation.ANIMATIONTYPE_VECTOR3;
  46. }
  47. else if (from instanceof Vector2) {
  48. dataType = Animation.ANIMATIONTYPE_VECTOR2;
  49. }
  50. else if (from instanceof Color3) {
  51. dataType = Animation.ANIMATIONTYPE_COLOR3;
  52. }
  53. else if (from instanceof Color4) {
  54. dataType = Animation.ANIMATIONTYPE_COLOR4;
  55. }
  56. else if (from instanceof Size) {
  57. dataType = Animation.ANIMATIONTYPE_SIZE;
  58. }
  59. if (dataType == undefined) {
  60. return null;
  61. }
  62. const animation = new Animation(name, targetProperty, framePerSecond, dataType, loopMode);
  63. const keys = [
  64. { frame: 0, value: from },
  65. { frame: totalFrame, value: to },
  66. ];
  67. animation.setKeys(keys);
  68. if (easingFunction !== undefined) {
  69. animation.setEasingFunction(easingFunction);
  70. }
  71. return animation;
  72. }
  73. /**
  74. * Sets up an animation
  75. * @param property The property to animate
  76. * @param animationType The animation type to apply
  77. * @param framePerSecond The frames per second of the animation
  78. * @param easingFunction The easing function used in the animation
  79. * @returns The created animation
  80. */
  81. static CreateAnimation(property, animationType, framePerSecond, easingFunction) {
  82. const animation = new Animation(property + "Animation", property, framePerSecond, animationType, Animation.ANIMATIONLOOPMODE_CONSTANT);
  83. animation.setEasingFunction(easingFunction);
  84. return animation;
  85. }
  86. /**
  87. * Create and start an animation on a node
  88. * @param name defines the name of the global animation that will be run on all nodes
  89. * @param target defines the target where the animation will take place
  90. * @param targetProperty defines property to animate
  91. * @param framePerSecond defines the number of frame per second yo use
  92. * @param totalFrame defines the number of frames in total
  93. * @param from defines the initial value
  94. * @param to defines the final value
  95. * @param loopMode defines which loop mode you want to use (off by default)
  96. * @param easingFunction defines the easing function to use (linear by default)
  97. * @param onAnimationEnd defines the callback to call when animation end
  98. * @param scene defines the hosting scene
  99. * @returns the animatable created for this animation
  100. */
  101. static CreateAndStartAnimation(name, target, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd, scene) {
  102. const animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
  103. if (!animation) {
  104. return null;
  105. }
  106. if (target.getScene) {
  107. scene = target.getScene();
  108. }
  109. if (!scene) {
  110. return null;
  111. }
  112. return scene.beginDirectAnimation(target, [animation], 0, totalFrame, animation.loopMode === 1, 1.0, onAnimationEnd);
  113. }
  114. /**
  115. * Create and start an animation on a node and its descendants
  116. * @param name defines the name of the global animation that will be run on all nodes
  117. * @param node defines the root node where the animation will take place
  118. * @param directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used
  119. * @param targetProperty defines property to animate
  120. * @param framePerSecond defines the number of frame per second to use
  121. * @param totalFrame defines the number of frames in total
  122. * @param from defines the initial value
  123. * @param to defines the final value
  124. * @param loopMode defines which loop mode you want to use (off by default)
  125. * @param easingFunction defines the easing function to use (linear by default)
  126. * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
  127. * @returns the list of animatables created for all nodes
  128. * @example https://www.babylonjs-playground.com/#MH0VLI
  129. */
  130. static CreateAndStartHierarchyAnimation(name, node, directDescendantsOnly, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
  131. const animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
  132. if (!animation) {
  133. return null;
  134. }
  135. const scene = node.getScene();
  136. return scene.beginDirectHierarchyAnimation(node, directDescendantsOnly, [animation], 0, totalFrame, animation.loopMode === 1, 1.0, onAnimationEnd);
  137. }
  138. /**
  139. * Creates a new animation, merges it with the existing animations and starts it
  140. * @param name Name of the animation
  141. * @param node Node which contains the scene that begins the animations
  142. * @param targetProperty Specifies which property to animate
  143. * @param framePerSecond The frames per second of the animation
  144. * @param totalFrame The total number of frames
  145. * @param from The frame at the beginning of the animation
  146. * @param to The frame at the end of the animation
  147. * @param loopMode Specifies the loop mode of the animation
  148. * @param easingFunction (Optional) The easing function of the animation, which allow custom mathematical formulas for animations
  149. * @param onAnimationEnd Callback to run once the animation is complete
  150. * @returns Nullable animation
  151. */
  152. static CreateMergeAndStartAnimation(name, node, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction, onAnimationEnd) {
  153. const animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
  154. if (!animation) {
  155. return null;
  156. }
  157. node.animations.push(animation);
  158. return node.getScene().beginAnimation(node, 0, totalFrame, animation.loopMode === 1, 1.0, onAnimationEnd);
  159. }
  160. /** @internal */
  161. static MakeAnimationAdditive(sourceAnimation, referenceFrameOrOptions, range, cloneOriginal = false, clonedName) {
  162. let options;
  163. if (typeof referenceFrameOrOptions === "object") {
  164. options = referenceFrameOrOptions;
  165. }
  166. else {
  167. options = {
  168. referenceFrame: referenceFrameOrOptions ?? 0,
  169. range: range,
  170. cloneOriginalAnimation: cloneOriginal,
  171. clonedAnimationName: clonedName,
  172. };
  173. }
  174. let animation = sourceAnimation;
  175. if (options.cloneOriginalAnimation) {
  176. animation = sourceAnimation.clone();
  177. animation.name = options.clonedAnimationName || animation.name;
  178. }
  179. if (!animation._keys.length) {
  180. return animation;
  181. }
  182. const referenceFrame = options.referenceFrame && options.referenceFrame >= 0 ? options.referenceFrame : 0;
  183. let startIndex = 0;
  184. const firstKey = animation._keys[0];
  185. let endIndex = animation._keys.length - 1;
  186. const lastKey = animation._keys[endIndex];
  187. const valueStore = {
  188. referenceValue: firstKey.value,
  189. referencePosition: TmpVectors.Vector3[0],
  190. referenceQuaternion: TmpVectors.Quaternion[0],
  191. referenceScaling: TmpVectors.Vector3[1],
  192. keyPosition: TmpVectors.Vector3[2],
  193. keyQuaternion: TmpVectors.Quaternion[1],
  194. keyScaling: TmpVectors.Vector3[3],
  195. };
  196. let from = firstKey.frame;
  197. let to = lastKey.frame;
  198. if (options.range) {
  199. const rangeValue = animation.getRange(options.range);
  200. if (rangeValue) {
  201. from = rangeValue.from;
  202. to = rangeValue.to;
  203. }
  204. }
  205. else {
  206. from = options.fromFrame ?? from;
  207. to = options.toFrame ?? to;
  208. }
  209. if (from !== firstKey.frame) {
  210. startIndex = animation.createKeyForFrame(from);
  211. }
  212. if (to !== lastKey.frame) {
  213. endIndex = animation.createKeyForFrame(to);
  214. }
  215. // There's only one key, so use it
  216. if (animation._keys.length === 1) {
  217. const value = animation._getKeyValue(animation._keys[0]);
  218. valueStore.referenceValue = value.clone ? value.clone() : value;
  219. }
  220. // Reference frame is before the first frame, so just use the first frame
  221. else if (referenceFrame <= firstKey.frame) {
  222. const value = animation._getKeyValue(firstKey.value);
  223. valueStore.referenceValue = value.clone ? value.clone() : value;
  224. }
  225. // Reference frame is after the last frame, so just use the last frame
  226. else if (referenceFrame >= lastKey.frame) {
  227. const value = animation._getKeyValue(lastKey.value);
  228. valueStore.referenceValue = value.clone ? value.clone() : value;
  229. }
  230. // Interpolate the reference value from the animation
  231. else {
  232. evaluateAnimationState.key = 0;
  233. const value = animation._interpolate(referenceFrame, evaluateAnimationState);
  234. valueStore.referenceValue = value.clone ? value.clone() : value;
  235. }
  236. // Conjugate the quaternion
  237. if (animation.dataType === Animation.ANIMATIONTYPE_QUATERNION) {
  238. valueStore.referenceValue.normalize().conjugateInPlace();
  239. }
  240. // Decompose matrix and conjugate the quaternion
  241. else if (animation.dataType === Animation.ANIMATIONTYPE_MATRIX) {
  242. valueStore.referenceValue.decompose(valueStore.referenceScaling, valueStore.referenceQuaternion, valueStore.referencePosition);
  243. valueStore.referenceQuaternion.normalize().conjugateInPlace();
  244. }
  245. let startFrame = Number.MAX_VALUE;
  246. const clippedKeys = options.clipKeys ? [] : null;
  247. // Subtract the reference value from all of the key values
  248. for (let index = startIndex; index <= endIndex; index++) {
  249. let key = animation._keys[index];
  250. if (clippedKeys) {
  251. key = {
  252. frame: key.frame,
  253. value: key.value.clone ? key.value.clone() : key.value,
  254. inTangent: key.inTangent,
  255. outTangent: key.outTangent,
  256. interpolation: key.interpolation,
  257. lockedTangent: key.lockedTangent,
  258. };
  259. if (startFrame === Number.MAX_VALUE) {
  260. startFrame = key.frame;
  261. }
  262. key.frame -= startFrame;
  263. clippedKeys.push(key);
  264. }
  265. // If this key was duplicated to create a frame 0 key, skip it because its value has already been updated
  266. if (index && animation.dataType !== Animation.ANIMATIONTYPE_FLOAT && key.value === firstKey.value) {
  267. continue;
  268. }
  269. switch (animation.dataType) {
  270. case Animation.ANIMATIONTYPE_MATRIX:
  271. key.value.decompose(valueStore.keyScaling, valueStore.keyQuaternion, valueStore.keyPosition);
  272. valueStore.keyPosition.subtractInPlace(valueStore.referencePosition);
  273. valueStore.keyScaling.divideInPlace(valueStore.referenceScaling);
  274. valueStore.referenceQuaternion.multiplyToRef(valueStore.keyQuaternion, valueStore.keyQuaternion);
  275. Matrix.ComposeToRef(valueStore.keyScaling, valueStore.keyQuaternion, valueStore.keyPosition, key.value);
  276. break;
  277. case Animation.ANIMATIONTYPE_QUATERNION:
  278. valueStore.referenceValue.multiplyToRef(key.value, key.value);
  279. break;
  280. case Animation.ANIMATIONTYPE_VECTOR2:
  281. case Animation.ANIMATIONTYPE_VECTOR3:
  282. case Animation.ANIMATIONTYPE_COLOR3:
  283. case Animation.ANIMATIONTYPE_COLOR4:
  284. key.value.subtractToRef(valueStore.referenceValue, key.value);
  285. break;
  286. case Animation.ANIMATIONTYPE_SIZE:
  287. key.value.width -= valueStore.referenceValue.width;
  288. key.value.height -= valueStore.referenceValue.height;
  289. break;
  290. default:
  291. key.value -= valueStore.referenceValue;
  292. }
  293. }
  294. if (clippedKeys) {
  295. animation.setKeys(clippedKeys, true);
  296. }
  297. return animation;
  298. }
  299. /**
  300. * Transition property of an host to the target Value
  301. * @param property The property to transition
  302. * @param targetValue The target Value of the property
  303. * @param host The object where the property to animate belongs
  304. * @param scene Scene used to run the animation
  305. * @param frameRate Framerate (in frame/s) to use
  306. * @param transition The transition type we want to use
  307. * @param duration The duration of the animation, in milliseconds
  308. * @param onAnimationEnd Callback trigger at the end of the animation
  309. * @returns Nullable animation
  310. */
  311. static TransitionTo(property, targetValue, host, scene, frameRate, transition, duration, onAnimationEnd = null) {
  312. if (duration <= 0) {
  313. host[property] = targetValue;
  314. if (onAnimationEnd) {
  315. onAnimationEnd();
  316. }
  317. return null;
  318. }
  319. const endFrame = frameRate * (duration / 1000);
  320. transition.setKeys([
  321. {
  322. frame: 0,
  323. value: host[property].clone ? host[property].clone() : host[property],
  324. },
  325. {
  326. frame: endFrame,
  327. value: targetValue,
  328. },
  329. ]);
  330. if (!host.animations) {
  331. host.animations = [];
  332. }
  333. host.animations.push(transition);
  334. const animation = scene.beginAnimation(host, 0, endFrame, false);
  335. animation.onAnimationEnd = onAnimationEnd;
  336. return animation;
  337. }
  338. /**
  339. * Return the array of runtime animations currently using this animation
  340. */
  341. get runtimeAnimations() {
  342. return this._runtimeAnimations;
  343. }
  344. /**
  345. * Specifies if any of the runtime animations are currently running
  346. */
  347. get hasRunningRuntimeAnimations() {
  348. for (const runtimeAnimation of this._runtimeAnimations) {
  349. if (!runtimeAnimation.isStopped()) {
  350. return true;
  351. }
  352. }
  353. return false;
  354. }
  355. /**
  356. * Initializes the animation
  357. * @param name Name of the animation
  358. * @param targetProperty Property to animate
  359. * @param framePerSecond The frames per second of the animation
  360. * @param dataType The data type of the animation
  361. * @param loopMode The loop mode of the animation
  362. * @param enableBlending Specifies if blending should be enabled
  363. */
  364. constructor(
  365. /**Name of the animation */
  366. name,
  367. /**Property to animate */
  368. targetProperty,
  369. /**The frames per second of the animation */
  370. framePerSecond,
  371. /**The data type of the animation */
  372. dataType,
  373. /**The loop mode of the animation */
  374. loopMode,
  375. /**Specifies if blending should be enabled */
  376. enableBlending) {
  377. this.name = name;
  378. this.targetProperty = targetProperty;
  379. this.framePerSecond = framePerSecond;
  380. this.dataType = dataType;
  381. this.loopMode = loopMode;
  382. this.enableBlending = enableBlending;
  383. /**
  384. * Stores the easing function of the animation
  385. */
  386. this._easingFunction = null;
  387. /**
  388. * @internal Internal use only
  389. */
  390. this._runtimeAnimations = new Array();
  391. /**
  392. * The set of event that will be linked to this animation
  393. */
  394. this._events = new Array();
  395. /**
  396. * Stores the blending speed of the animation
  397. */
  398. this.blendingSpeed = 0.01;
  399. /**
  400. * Stores the animation ranges for the animation
  401. */
  402. this._ranges = {};
  403. this.targetPropertyPath = targetProperty.split(".");
  404. this.dataType = dataType;
  405. this.loopMode = loopMode === undefined ? Animation.ANIMATIONLOOPMODE_CYCLE : loopMode;
  406. this.uniqueId = Animation._UniqueIdGenerator++;
  407. }
  408. // Methods
  409. /**
  410. * Converts the animation to a string
  411. * @param fullDetails support for multiple levels of logging within scene loading
  412. * @returns String form of the animation
  413. */
  414. toString(fullDetails) {
  415. let ret = "Name: " + this.name + ", property: " + this.targetProperty;
  416. ret += ", datatype: " + ["Float", "Vector3", "Quaternion", "Matrix", "Color3", "Vector2"][this.dataType];
  417. ret += ", nKeys: " + (this._keys ? this._keys.length : "none");
  418. ret += ", nRanges: " + (this._ranges ? Object.keys(this._ranges).length : "none");
  419. if (fullDetails) {
  420. ret += ", Ranges: {";
  421. let first = true;
  422. for (const name in this._ranges) {
  423. if (first) {
  424. ret += ", ";
  425. first = false;
  426. }
  427. ret += name;
  428. }
  429. ret += "}";
  430. }
  431. return ret;
  432. }
  433. /**
  434. * Add an event to this animation
  435. * @param event Event to add
  436. */
  437. addEvent(event) {
  438. this._events.push(event);
  439. this._events.sort((a, b) => a.frame - b.frame);
  440. }
  441. /**
  442. * Remove all events found at the given frame
  443. * @param frame The frame to remove events from
  444. */
  445. removeEvents(frame) {
  446. for (let index = 0; index < this._events.length; index++) {
  447. if (this._events[index].frame === frame) {
  448. this._events.splice(index, 1);
  449. index--;
  450. }
  451. }
  452. }
  453. /**
  454. * Retrieves all the events from the animation
  455. * @returns Events from the animation
  456. */
  457. getEvents() {
  458. return this._events;
  459. }
  460. /**
  461. * Creates an animation range
  462. * @param name Name of the animation range
  463. * @param from Starting frame of the animation range
  464. * @param to Ending frame of the animation
  465. */
  466. createRange(name, from, to) {
  467. // check name not already in use; could happen for bones after serialized
  468. if (!this._ranges[name]) {
  469. this._ranges[name] = new AnimationRange(name, from, to);
  470. }
  471. }
  472. /**
  473. * Deletes an animation range by name
  474. * @param name Name of the animation range to delete
  475. * @param deleteFrames Specifies if the key frames for the range should also be deleted (true) or not (false)
  476. */
  477. deleteRange(name, deleteFrames = true) {
  478. const range = this._ranges[name];
  479. if (!range) {
  480. return;
  481. }
  482. if (deleteFrames) {
  483. const from = range.from;
  484. const to = range.to;
  485. // this loop MUST go high to low for multiple splices to work
  486. for (let key = this._keys.length - 1; key >= 0; key--) {
  487. if (this._keys[key].frame >= from && this._keys[key].frame <= to) {
  488. this._keys.splice(key, 1);
  489. }
  490. }
  491. }
  492. this._ranges[name] = null; // said much faster than 'delete this._range[name]'
  493. }
  494. /**
  495. * Gets the animation range by name, or null if not defined
  496. * @param name Name of the animation range
  497. * @returns Nullable animation range
  498. */
  499. getRange(name) {
  500. return this._ranges[name];
  501. }
  502. /**
  503. * Gets the key frames from the animation
  504. * @returns The key frames of the animation
  505. */
  506. getKeys() {
  507. return this._keys;
  508. }
  509. /**
  510. * Gets the highest frame of the animation
  511. * @returns Highest frame of the animation
  512. */
  513. getHighestFrame() {
  514. let ret = 0;
  515. for (let key = 0, nKeys = this._keys.length; key < nKeys; key++) {
  516. if (ret < this._keys[key].frame) {
  517. ret = this._keys[key].frame;
  518. }
  519. }
  520. return ret;
  521. }
  522. /**
  523. * Gets the easing function of the animation
  524. * @returns Easing function of the animation
  525. */
  526. getEasingFunction() {
  527. return this._easingFunction;
  528. }
  529. /**
  530. * Sets the easing function of the animation
  531. * @param easingFunction A custom mathematical formula for animation
  532. */
  533. setEasingFunction(easingFunction) {
  534. this._easingFunction = easingFunction;
  535. }
  536. /**
  537. * Interpolates a scalar linearly
  538. * @param startValue Start value of the animation curve
  539. * @param endValue End value of the animation curve
  540. * @param gradient Scalar amount to interpolate
  541. * @returns Interpolated scalar value
  542. */
  543. floatInterpolateFunction(startValue, endValue, gradient) {
  544. return Scalar.Lerp(startValue, endValue, gradient);
  545. }
  546. /**
  547. * Interpolates a scalar cubically
  548. * @param startValue Start value of the animation curve
  549. * @param outTangent End tangent of the animation
  550. * @param endValue End value of the animation curve
  551. * @param inTangent Start tangent of the animation curve
  552. * @param gradient Scalar amount to interpolate
  553. * @returns Interpolated scalar value
  554. */
  555. floatInterpolateFunctionWithTangents(startValue, outTangent, endValue, inTangent, gradient) {
  556. return Scalar.Hermite(startValue, outTangent, endValue, inTangent, gradient);
  557. }
  558. /**
  559. * Interpolates a quaternion using a spherical linear interpolation
  560. * @param startValue Start value of the animation curve
  561. * @param endValue End value of the animation curve
  562. * @param gradient Scalar amount to interpolate
  563. * @returns Interpolated quaternion value
  564. */
  565. quaternionInterpolateFunction(startValue, endValue, gradient) {
  566. return Quaternion.Slerp(startValue, endValue, gradient);
  567. }
  568. /**
  569. * Interpolates a quaternion cubically
  570. * @param startValue Start value of the animation curve
  571. * @param outTangent End tangent of the animation curve
  572. * @param endValue End value of the animation curve
  573. * @param inTangent Start tangent of the animation curve
  574. * @param gradient Scalar amount to interpolate
  575. * @returns Interpolated quaternion value
  576. */
  577. quaternionInterpolateFunctionWithTangents(startValue, outTangent, endValue, inTangent, gradient) {
  578. return Quaternion.Hermite(startValue, outTangent, endValue, inTangent, gradient).normalize();
  579. }
  580. /**
  581. * Interpolates a Vector3 linearly
  582. * @param startValue Start value of the animation curve
  583. * @param endValue End value of the animation curve
  584. * @param gradient Scalar amount to interpolate (value between 0 and 1)
  585. * @returns Interpolated scalar value
  586. */
  587. vector3InterpolateFunction(startValue, endValue, gradient) {
  588. return Vector3.Lerp(startValue, endValue, gradient);
  589. }
  590. /**
  591. * Interpolates a Vector3 cubically
  592. * @param startValue Start value of the animation curve
  593. * @param outTangent End tangent of the animation
  594. * @param endValue End value of the animation curve
  595. * @param inTangent Start tangent of the animation curve
  596. * @param gradient Scalar amount to interpolate (value between 0 and 1)
  597. * @returns InterpolatedVector3 value
  598. */
  599. vector3InterpolateFunctionWithTangents(startValue, outTangent, endValue, inTangent, gradient) {
  600. return Vector3.Hermite(startValue, outTangent, endValue, inTangent, gradient);
  601. }
  602. /**
  603. * Interpolates a Vector2 linearly
  604. * @param startValue Start value of the animation curve
  605. * @param endValue End value of the animation curve
  606. * @param gradient Scalar amount to interpolate (value between 0 and 1)
  607. * @returns Interpolated Vector2 value
  608. */
  609. vector2InterpolateFunction(startValue, endValue, gradient) {
  610. return Vector2.Lerp(startValue, endValue, gradient);
  611. }
  612. /**
  613. * Interpolates a Vector2 cubically
  614. * @param startValue Start value of the animation curve
  615. * @param outTangent End tangent of the animation
  616. * @param endValue End value of the animation curve
  617. * @param inTangent Start tangent of the animation curve
  618. * @param gradient Scalar amount to interpolate (value between 0 and 1)
  619. * @returns Interpolated Vector2 value
  620. */
  621. vector2InterpolateFunctionWithTangents(startValue, outTangent, endValue, inTangent, gradient) {
  622. return Vector2.Hermite(startValue, outTangent, endValue, inTangent, gradient);
  623. }
  624. /**
  625. * Interpolates a size linearly
  626. * @param startValue Start value of the animation curve
  627. * @param endValue End value of the animation curve
  628. * @param gradient Scalar amount to interpolate
  629. * @returns Interpolated Size value
  630. */
  631. sizeInterpolateFunction(startValue, endValue, gradient) {
  632. return Size.Lerp(startValue, endValue, gradient);
  633. }
  634. /**
  635. * Interpolates a Color3 linearly
  636. * @param startValue Start value of the animation curve
  637. * @param endValue End value of the animation curve
  638. * @param gradient Scalar amount to interpolate
  639. * @returns Interpolated Color3 value
  640. */
  641. color3InterpolateFunction(startValue, endValue, gradient) {
  642. return Color3.Lerp(startValue, endValue, gradient);
  643. }
  644. /**
  645. * Interpolates a Color3 cubically
  646. * @param startValue Start value of the animation curve
  647. * @param outTangent End tangent of the animation
  648. * @param endValue End value of the animation curve
  649. * @param inTangent Start tangent of the animation curve
  650. * @param gradient Scalar amount to interpolate
  651. * @returns interpolated value
  652. */
  653. color3InterpolateFunctionWithTangents(startValue, outTangent, endValue, inTangent, gradient) {
  654. return Color3.Hermite(startValue, outTangent, endValue, inTangent, gradient);
  655. }
  656. /**
  657. * Interpolates a Color4 linearly
  658. * @param startValue Start value of the animation curve
  659. * @param endValue End value of the animation curve
  660. * @param gradient Scalar amount to interpolate
  661. * @returns Interpolated Color3 value
  662. */
  663. color4InterpolateFunction(startValue, endValue, gradient) {
  664. return Color4.Lerp(startValue, endValue, gradient);
  665. }
  666. /**
  667. * Interpolates a Color4 cubically
  668. * @param startValue Start value of the animation curve
  669. * @param outTangent End tangent of the animation
  670. * @param endValue End value of the animation curve
  671. * @param inTangent Start tangent of the animation curve
  672. * @param gradient Scalar amount to interpolate
  673. * @returns interpolated value
  674. */
  675. color4InterpolateFunctionWithTangents(startValue, outTangent, endValue, inTangent, gradient) {
  676. return Color4.Hermite(startValue, outTangent, endValue, inTangent, gradient);
  677. }
  678. /**
  679. * @internal Internal use only
  680. */
  681. _getKeyValue(value) {
  682. if (typeof value === "function") {
  683. return value();
  684. }
  685. return value;
  686. }
  687. /**
  688. * Evaluate the animation value at a given frame
  689. * @param currentFrame defines the frame where we want to evaluate the animation
  690. * @returns the animation value
  691. */
  692. evaluate(currentFrame) {
  693. evaluateAnimationState.key = 0;
  694. return this._interpolate(currentFrame, evaluateAnimationState);
  695. }
  696. /**
  697. * @internal Internal use only
  698. */
  699. _interpolate(currentFrame, state, searchClosestKeyOnly = false) {
  700. if (state.loopMode === Animation.ANIMATIONLOOPMODE_CONSTANT && state.repeatCount > 0) {
  701. return state.highLimitValue.clone ? state.highLimitValue.clone() : state.highLimitValue;
  702. }
  703. const keys = this._keys;
  704. const keysLength = keys.length;
  705. let key = state.key;
  706. while (key >= 0 && currentFrame < keys[key].frame) {
  707. --key;
  708. }
  709. while (key + 1 <= keysLength - 1 && currentFrame >= keys[key + 1].frame) {
  710. ++key;
  711. }
  712. state.key = key;
  713. if (key < 0) {
  714. return searchClosestKeyOnly ? undefined : this._getKeyValue(keys[0].value);
  715. }
  716. else if (key + 1 > keysLength - 1) {
  717. return searchClosestKeyOnly ? undefined : this._getKeyValue(keys[keysLength - 1].value);
  718. }
  719. const startKey = keys[key];
  720. const endKey = keys[key + 1];
  721. if (searchClosestKeyOnly && (currentFrame === startKey.frame || currentFrame === endKey.frame)) {
  722. return undefined;
  723. }
  724. const startValue = this._getKeyValue(startKey.value);
  725. const endValue = this._getKeyValue(endKey.value);
  726. if (startKey.interpolation === AnimationKeyInterpolation.STEP) {
  727. if (endKey.frame > currentFrame) {
  728. return startValue;
  729. }
  730. else {
  731. return endValue;
  732. }
  733. }
  734. const useTangent = startKey.outTangent !== undefined && endKey.inTangent !== undefined;
  735. const frameDelta = endKey.frame - startKey.frame;
  736. // gradient : percent of currentFrame between the frame inf and the frame sup
  737. let gradient = (currentFrame - startKey.frame) / frameDelta;
  738. // check for easingFunction and correction of gradient
  739. const easingFunction = startKey.easingFunction || this.getEasingFunction();
  740. if (easingFunction !== null) {
  741. gradient = easingFunction.ease(gradient);
  742. }
  743. switch (this.dataType) {
  744. // Float
  745. case Animation.ANIMATIONTYPE_FLOAT: {
  746. const floatValue = useTangent
  747. ? this.floatInterpolateFunctionWithTangents(startValue, startKey.outTangent * frameDelta, endValue, endKey.inTangent * frameDelta, gradient)
  748. : this.floatInterpolateFunction(startValue, endValue, gradient);
  749. switch (state.loopMode) {
  750. case Animation.ANIMATIONLOOPMODE_CYCLE:
  751. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  752. case Animation.ANIMATIONLOOPMODE_YOYO:
  753. return floatValue;
  754. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  755. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  756. return (state.offsetValue ?? 0) * state.repeatCount + floatValue;
  757. }
  758. break;
  759. }
  760. // Quaternion
  761. case Animation.ANIMATIONTYPE_QUATERNION: {
  762. const quatValue = useTangent
  763. ? this.quaternionInterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient)
  764. : this.quaternionInterpolateFunction(startValue, endValue, gradient);
  765. switch (state.loopMode) {
  766. case Animation.ANIMATIONLOOPMODE_CYCLE:
  767. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  768. case Animation.ANIMATIONLOOPMODE_YOYO:
  769. return quatValue;
  770. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  771. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  772. return quatValue.addInPlace((state.offsetValue || _staticOffsetValueQuaternion).scale(state.repeatCount));
  773. }
  774. return quatValue;
  775. }
  776. // Vector3
  777. case Animation.ANIMATIONTYPE_VECTOR3: {
  778. const vec3Value = useTangent
  779. ? this.vector3InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient)
  780. : this.vector3InterpolateFunction(startValue, endValue, gradient);
  781. switch (state.loopMode) {
  782. case Animation.ANIMATIONLOOPMODE_CYCLE:
  783. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  784. case Animation.ANIMATIONLOOPMODE_YOYO:
  785. return vec3Value;
  786. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  787. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  788. return vec3Value.add((state.offsetValue || _staticOffsetValueVector3).scale(state.repeatCount));
  789. }
  790. break;
  791. }
  792. // Vector2
  793. case Animation.ANIMATIONTYPE_VECTOR2: {
  794. const vec2Value = useTangent
  795. ? this.vector2InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient)
  796. : this.vector2InterpolateFunction(startValue, endValue, gradient);
  797. switch (state.loopMode) {
  798. case Animation.ANIMATIONLOOPMODE_CYCLE:
  799. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  800. case Animation.ANIMATIONLOOPMODE_YOYO:
  801. return vec2Value;
  802. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  803. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  804. return vec2Value.add((state.offsetValue || _staticOffsetValueVector2).scale(state.repeatCount));
  805. }
  806. break;
  807. }
  808. // Size
  809. case Animation.ANIMATIONTYPE_SIZE: {
  810. switch (state.loopMode) {
  811. case Animation.ANIMATIONLOOPMODE_CYCLE:
  812. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  813. case Animation.ANIMATIONLOOPMODE_YOYO:
  814. return this.sizeInterpolateFunction(startValue, endValue, gradient);
  815. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  816. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  817. return this.sizeInterpolateFunction(startValue, endValue, gradient).add((state.offsetValue || _staticOffsetValueSize).scale(state.repeatCount));
  818. }
  819. break;
  820. }
  821. // Color3
  822. case Animation.ANIMATIONTYPE_COLOR3: {
  823. const color3Value = useTangent
  824. ? this.color3InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient)
  825. : this.color3InterpolateFunction(startValue, endValue, gradient);
  826. switch (state.loopMode) {
  827. case Animation.ANIMATIONLOOPMODE_CYCLE:
  828. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  829. case Animation.ANIMATIONLOOPMODE_YOYO:
  830. return color3Value;
  831. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  832. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  833. return color3Value.add((state.offsetValue || _staticOffsetValueColor3).scale(state.repeatCount));
  834. }
  835. break;
  836. }
  837. // Color4
  838. case Animation.ANIMATIONTYPE_COLOR4: {
  839. const color4Value = useTangent
  840. ? this.color4InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient)
  841. : this.color4InterpolateFunction(startValue, endValue, gradient);
  842. switch (state.loopMode) {
  843. case Animation.ANIMATIONLOOPMODE_CYCLE:
  844. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  845. case Animation.ANIMATIONLOOPMODE_YOYO:
  846. return color4Value;
  847. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  848. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT:
  849. return color4Value.add((state.offsetValue || _staticOffsetValueColor4).scale(state.repeatCount));
  850. }
  851. break;
  852. }
  853. // Matrix
  854. case Animation.ANIMATIONTYPE_MATRIX: {
  855. switch (state.loopMode) {
  856. case Animation.ANIMATIONLOOPMODE_CYCLE:
  857. case Animation.ANIMATIONLOOPMODE_CONSTANT:
  858. case Animation.ANIMATIONLOOPMODE_YOYO: {
  859. if (Animation.AllowMatricesInterpolation) {
  860. return this.matrixInterpolateFunction(startValue, endValue, gradient, state.workValue);
  861. }
  862. return startValue;
  863. }
  864. case Animation.ANIMATIONLOOPMODE_RELATIVE:
  865. case Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT: {
  866. return startValue;
  867. }
  868. }
  869. break;
  870. }
  871. }
  872. return 0;
  873. }
  874. /**
  875. * Defines the function to use to interpolate matrices
  876. * @param startValue defines the start matrix
  877. * @param endValue defines the end matrix
  878. * @param gradient defines the gradient between both matrices
  879. * @param result defines an optional target matrix where to store the interpolation
  880. * @returns the interpolated matrix
  881. */
  882. matrixInterpolateFunction(startValue, endValue, gradient, result) {
  883. if (Animation.AllowMatrixDecomposeForInterpolation) {
  884. if (result) {
  885. Matrix.DecomposeLerpToRef(startValue, endValue, gradient, result);
  886. return result;
  887. }
  888. return Matrix.DecomposeLerp(startValue, endValue, gradient);
  889. }
  890. if (result) {
  891. Matrix.LerpToRef(startValue, endValue, gradient, result);
  892. return result;
  893. }
  894. return Matrix.Lerp(startValue, endValue, gradient);
  895. }
  896. /**
  897. * Makes a copy of the animation
  898. * @returns Cloned animation
  899. */
  900. clone() {
  901. const clone = new Animation(this.name, this.targetPropertyPath.join("."), this.framePerSecond, this.dataType, this.loopMode);
  902. clone.enableBlending = this.enableBlending;
  903. clone.blendingSpeed = this.blendingSpeed;
  904. if (this._keys) {
  905. clone.setKeys(this._keys);
  906. }
  907. if (this._ranges) {
  908. clone._ranges = {};
  909. for (const name in this._ranges) {
  910. const range = this._ranges[name];
  911. if (!range) {
  912. continue;
  913. }
  914. clone._ranges[name] = range.clone();
  915. }
  916. }
  917. return clone;
  918. }
  919. /**
  920. * Sets the key frames of the animation
  921. * @param values The animation key frames to set
  922. * @param dontClone Whether to clone the keys or not (default is false, so the array of keys is cloned)
  923. */
  924. setKeys(values, dontClone = false) {
  925. this._keys = !dontClone ? values.slice(0) : values;
  926. }
  927. /**
  928. * Creates a key for the frame passed as a parameter and adds it to the animation IF a key doesn't already exist for that frame
  929. * @param frame Frame number
  930. * @returns The key index if the key was added or the index of the pre existing key if the frame passed as parameter already has a corresponding key
  931. */
  932. createKeyForFrame(frame) {
  933. // Find the key corresponding to frame
  934. evaluateAnimationState.key = 0;
  935. const value = this._interpolate(frame, evaluateAnimationState, true);
  936. if (!value) {
  937. // A key corresponding to this frame already exists
  938. return this._keys[evaluateAnimationState.key].frame === frame ? evaluateAnimationState.key : evaluateAnimationState.key + 1;
  939. }
  940. // The frame is between two keys, so create a new key
  941. const newKey = {
  942. frame,
  943. value: value.clone ? value.clone() : value,
  944. };
  945. this._keys.splice(evaluateAnimationState.key + 1, 0, newKey);
  946. return evaluateAnimationState.key + 1;
  947. }
  948. /**
  949. * Serializes the animation to an object
  950. * @returns Serialized object
  951. */
  952. serialize() {
  953. const serializationObject = {};
  954. serializationObject.name = this.name;
  955. serializationObject.property = this.targetProperty;
  956. serializationObject.framePerSecond = this.framePerSecond;
  957. serializationObject.dataType = this.dataType;
  958. serializationObject.loopBehavior = this.loopMode;
  959. serializationObject.enableBlending = this.enableBlending;
  960. serializationObject.blendingSpeed = this.blendingSpeed;
  961. const dataType = this.dataType;
  962. serializationObject.keys = [];
  963. const keys = this.getKeys();
  964. for (let index = 0; index < keys.length; index++) {
  965. const animationKey = keys[index];
  966. const key = {};
  967. key.frame = animationKey.frame;
  968. switch (dataType) {
  969. case Animation.ANIMATIONTYPE_FLOAT:
  970. key.values = [animationKey.value];
  971. if (animationKey.inTangent !== undefined) {
  972. key.values.push(animationKey.inTangent);
  973. }
  974. if (animationKey.outTangent !== undefined) {
  975. if (animationKey.inTangent === undefined) {
  976. key.values.push(undefined);
  977. }
  978. key.values.push(animationKey.outTangent);
  979. }
  980. if (animationKey.interpolation !== undefined) {
  981. if (animationKey.inTangent === undefined) {
  982. key.values.push(undefined);
  983. }
  984. if (animationKey.outTangent === undefined) {
  985. key.values.push(undefined);
  986. }
  987. key.values.push(animationKey.interpolation);
  988. }
  989. break;
  990. case Animation.ANIMATIONTYPE_QUATERNION:
  991. case Animation.ANIMATIONTYPE_MATRIX:
  992. case Animation.ANIMATIONTYPE_VECTOR3:
  993. case Animation.ANIMATIONTYPE_COLOR3:
  994. case Animation.ANIMATIONTYPE_COLOR4:
  995. key.values = animationKey.value.asArray();
  996. if (animationKey.inTangent != undefined) {
  997. key.values.push(animationKey.inTangent.asArray());
  998. }
  999. if (animationKey.outTangent != undefined) {
  1000. if (animationKey.inTangent === undefined) {
  1001. key.values.push(undefined);
  1002. }
  1003. key.values.push(animationKey.outTangent.asArray());
  1004. }
  1005. if (animationKey.interpolation !== undefined) {
  1006. if (animationKey.inTangent === undefined) {
  1007. key.values.push(undefined);
  1008. }
  1009. if (animationKey.outTangent === undefined) {
  1010. key.values.push(undefined);
  1011. }
  1012. key.values.push(animationKey.interpolation);
  1013. }
  1014. break;
  1015. }
  1016. serializationObject.keys.push(key);
  1017. }
  1018. serializationObject.ranges = [];
  1019. for (const name in this._ranges) {
  1020. const source = this._ranges[name];
  1021. if (!source) {
  1022. continue;
  1023. }
  1024. const range = {};
  1025. range.name = name;
  1026. range.from = source.from;
  1027. range.to = source.to;
  1028. serializationObject.ranges.push(range);
  1029. }
  1030. return serializationObject;
  1031. }
  1032. /**
  1033. * @internal
  1034. */
  1035. static _UniversalLerp(left, right, amount) {
  1036. const constructor = left.constructor;
  1037. if (constructor.Lerp) {
  1038. // Lerp supported
  1039. return constructor.Lerp(left, right, amount);
  1040. }
  1041. else if (constructor.Slerp) {
  1042. // Slerp supported
  1043. return constructor.Slerp(left, right, amount);
  1044. }
  1045. else if (left.toFixed) {
  1046. // Number
  1047. return left * (1.0 - amount) + amount * right;
  1048. }
  1049. else {
  1050. // Blending not supported
  1051. return right;
  1052. }
  1053. }
  1054. /**
  1055. * Parses an animation object and creates an animation
  1056. * @param parsedAnimation Parsed animation object
  1057. * @returns Animation object
  1058. */
  1059. static Parse(parsedAnimation) {
  1060. const animation = new Animation(parsedAnimation.name, parsedAnimation.property, parsedAnimation.framePerSecond, parsedAnimation.dataType, parsedAnimation.loopBehavior);
  1061. const dataType = parsedAnimation.dataType;
  1062. const keys = [];
  1063. let data;
  1064. let index;
  1065. if (parsedAnimation.enableBlending) {
  1066. animation.enableBlending = parsedAnimation.enableBlending;
  1067. }
  1068. if (parsedAnimation.blendingSpeed) {
  1069. animation.blendingSpeed = parsedAnimation.blendingSpeed;
  1070. }
  1071. for (index = 0; index < parsedAnimation.keys.length; index++) {
  1072. const key = parsedAnimation.keys[index];
  1073. let inTangent = undefined;
  1074. let outTangent = undefined;
  1075. let interpolation = undefined;
  1076. switch (dataType) {
  1077. case Animation.ANIMATIONTYPE_FLOAT:
  1078. data = key.values[0];
  1079. if (key.values.length >= 2) {
  1080. inTangent = key.values[1];
  1081. }
  1082. if (key.values.length >= 3) {
  1083. outTangent = key.values[2];
  1084. }
  1085. if (key.values.length >= 4) {
  1086. interpolation = key.values[3];
  1087. }
  1088. break;
  1089. case Animation.ANIMATIONTYPE_QUATERNION:
  1090. data = Quaternion.FromArray(key.values);
  1091. if (key.values.length >= 8) {
  1092. const _inTangent = Quaternion.FromArray(key.values.slice(4, 8));
  1093. if (!_inTangent.equals(Quaternion.Zero())) {
  1094. inTangent = _inTangent;
  1095. }
  1096. }
  1097. if (key.values.length >= 12) {
  1098. const _outTangent = Quaternion.FromArray(key.values.slice(8, 12));
  1099. if (!_outTangent.equals(Quaternion.Zero())) {
  1100. outTangent = _outTangent;
  1101. }
  1102. }
  1103. if (key.values.length >= 13) {
  1104. interpolation = key.values[12];
  1105. }
  1106. break;
  1107. case Animation.ANIMATIONTYPE_MATRIX:
  1108. data = Matrix.FromArray(key.values);
  1109. if (key.values.length >= 17) {
  1110. interpolation = key.values[16];
  1111. }
  1112. break;
  1113. case Animation.ANIMATIONTYPE_COLOR3:
  1114. data = Color3.FromArray(key.values);
  1115. if (key.values[3]) {
  1116. inTangent = Color3.FromArray(key.values[3]);
  1117. }
  1118. if (key.values[4]) {
  1119. outTangent = Color3.FromArray(key.values[4]);
  1120. }
  1121. if (key.values[5]) {
  1122. interpolation = key.values[5];
  1123. }
  1124. break;
  1125. case Animation.ANIMATIONTYPE_COLOR4:
  1126. data = Color4.FromArray(key.values);
  1127. if (key.values[4]) {
  1128. inTangent = Color4.FromArray(key.values[4]);
  1129. }
  1130. if (key.values[5]) {
  1131. outTangent = Color4.FromArray(key.values[5]);
  1132. }
  1133. if (key.values[6]) {
  1134. interpolation = Color4.FromArray(key.values[6]);
  1135. }
  1136. break;
  1137. case Animation.ANIMATIONTYPE_VECTOR3:
  1138. default:
  1139. data = Vector3.FromArray(key.values);
  1140. if (key.values[3]) {
  1141. inTangent = Vector3.FromArray(key.values[3]);
  1142. }
  1143. if (key.values[4]) {
  1144. outTangent = Vector3.FromArray(key.values[4]);
  1145. }
  1146. if (key.values[5]) {
  1147. interpolation = key.values[5];
  1148. }
  1149. break;
  1150. }
  1151. const keyData = {};
  1152. keyData.frame = key.frame;
  1153. keyData.value = data;
  1154. if (inTangent != undefined) {
  1155. keyData.inTangent = inTangent;
  1156. }
  1157. if (outTangent != undefined) {
  1158. keyData.outTangent = outTangent;
  1159. }
  1160. if (interpolation != undefined) {
  1161. keyData.interpolation = interpolation;
  1162. }
  1163. keys.push(keyData);
  1164. }
  1165. animation.setKeys(keys);
  1166. if (parsedAnimation.ranges) {
  1167. for (index = 0; index < parsedAnimation.ranges.length; index++) {
  1168. data = parsedAnimation.ranges[index];
  1169. animation.createRange(data.name, data.from, data.to);
  1170. }
  1171. }
  1172. return animation;
  1173. }
  1174. /**
  1175. * Appends the serialized animations from the source animations
  1176. * @param source Source containing the animations
  1177. * @param destination Target to store the animations
  1178. */
  1179. static AppendSerializedAnimations(source, destination) {
  1180. SerializationHelper.AppendSerializedAnimations(source, destination);
  1181. }
  1182. /**
  1183. * Creates a new animation or an array of animations from a snippet saved in a remote file
  1184. * @param name defines the name of the animation to create (can be null or empty to use the one from the json data)
  1185. * @param url defines the url to load from
  1186. * @returns a promise that will resolve to the new animation or an array of animations
  1187. */
  1188. static ParseFromFileAsync(name, url) {
  1189. return new Promise((resolve, reject) => {
  1190. const request = new WebRequest();
  1191. request.addEventListener("readystatechange", () => {
  1192. if (request.readyState == 4) {
  1193. if (request.status == 200) {
  1194. let serializationObject = JSON.parse(request.responseText);
  1195. if (serializationObject.animations) {
  1196. serializationObject = serializationObject.animations;
  1197. }
  1198. if (serializationObject.length) {
  1199. const output = [];
  1200. for (const serializedAnimation of serializationObject) {
  1201. output.push(this.Parse(serializedAnimation));
  1202. }
  1203. resolve(output);
  1204. }
  1205. else {
  1206. const output = this.Parse(serializationObject);
  1207. if (name) {
  1208. output.name = name;
  1209. }
  1210. resolve(output);
  1211. }
  1212. }
  1213. else {
  1214. reject("Unable to load the animation");
  1215. }
  1216. }
  1217. });
  1218. request.open("GET", url);
  1219. request.send();
  1220. });
  1221. }
  1222. /**
  1223. * Creates an animation or an array of animations from a snippet saved by the Inspector
  1224. * @param snippetId defines the snippet to load
  1225. * @returns a promise that will resolve to the new animation or a new array of animations
  1226. */
  1227. static ParseFromSnippetAsync(snippetId) {
  1228. return new Promise((resolve, reject) => {
  1229. const request = new WebRequest();
  1230. request.addEventListener("readystatechange", () => {
  1231. if (request.readyState == 4) {
  1232. if (request.status == 200) {
  1233. const snippet = JSON.parse(JSON.parse(request.responseText).jsonPayload);
  1234. if (snippet.animations) {
  1235. const serializationObject = JSON.parse(snippet.animations);
  1236. const outputs = [];
  1237. for (const serializedAnimation of serializationObject.animations) {
  1238. const output = this.Parse(serializedAnimation);
  1239. output.snippetId = snippetId;
  1240. outputs.push(output);
  1241. }
  1242. resolve(outputs);
  1243. }
  1244. else {
  1245. const serializationObject = JSON.parse(snippet.animation);
  1246. const output = this.Parse(serializationObject);
  1247. output.snippetId = snippetId;
  1248. resolve(output);
  1249. }
  1250. }
  1251. else {
  1252. reject("Unable to load the snippet " + snippetId);
  1253. }
  1254. }
  1255. });
  1256. request.open("GET", this.SnippetUrl + "/" + snippetId.replace(/#/g, "/"));
  1257. request.send();
  1258. });
  1259. }
  1260. }
  1261. Animation._UniqueIdGenerator = 0;
  1262. /**
  1263. * Use matrix interpolation instead of using direct key value when animating matrices
  1264. */
  1265. Animation.AllowMatricesInterpolation = false;
  1266. /**
  1267. * When matrix interpolation is enabled, this boolean forces the system to use Matrix.DecomposeLerp instead of Matrix.Lerp. Interpolation is more precise but slower
  1268. */
  1269. Animation.AllowMatrixDecomposeForInterpolation = true;
  1270. /** Define the Url to load snippets */
  1271. Animation.SnippetUrl = `https://snippet.babylonjs.com`;
  1272. // Statics
  1273. /**
  1274. * Float animation type
  1275. */
  1276. Animation.ANIMATIONTYPE_FLOAT = 0;
  1277. /**
  1278. * Vector3 animation type
  1279. */
  1280. Animation.ANIMATIONTYPE_VECTOR3 = 1;
  1281. /**
  1282. * Quaternion animation type
  1283. */
  1284. Animation.ANIMATIONTYPE_QUATERNION = 2;
  1285. /**
  1286. * Matrix animation type
  1287. */
  1288. Animation.ANIMATIONTYPE_MATRIX = 3;
  1289. /**
  1290. * Color3 animation type
  1291. */
  1292. Animation.ANIMATIONTYPE_COLOR3 = 4;
  1293. /**
  1294. * Color3 animation type
  1295. */
  1296. Animation.ANIMATIONTYPE_COLOR4 = 7;
  1297. /**
  1298. * Vector2 animation type
  1299. */
  1300. Animation.ANIMATIONTYPE_VECTOR2 = 5;
  1301. /**
  1302. * Size animation type
  1303. */
  1304. Animation.ANIMATIONTYPE_SIZE = 6;
  1305. /**
  1306. * Relative Loop Mode
  1307. */
  1308. Animation.ANIMATIONLOOPMODE_RELATIVE = 0;
  1309. /**
  1310. * Cycle Loop Mode
  1311. */
  1312. Animation.ANIMATIONLOOPMODE_CYCLE = 1;
  1313. /**
  1314. * Constant Loop Mode
  1315. */
  1316. Animation.ANIMATIONLOOPMODE_CONSTANT = 2;
  1317. /**
  1318. * Yoyo Loop Mode
  1319. */
  1320. Animation.ANIMATIONLOOPMODE_YOYO = 4;
  1321. /**
  1322. * Relative Loop Mode (add to current value of animated object, unlike ANIMATIONLOOPMODE_RELATIVE)
  1323. */
  1324. Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT = 5;
  1325. /**
  1326. * Creates an animation or an array of animations from a snippet saved by the Inspector
  1327. * @deprecated Please use ParseFromSnippetAsync instead
  1328. * @param snippetId defines the snippet to load
  1329. * @returns a promise that will resolve to the new animation or a new array of animations
  1330. */
  1331. Animation.CreateFromSnippetAsync = Animation.ParseFromSnippetAsync;
  1332. RegisterClass("BABYLON.Animation", Animation);
  1333. Node._AnimationRangeFactory = (name, from, to) => new AnimationRange(name, from, to);
  1334. //# sourceMappingURL=animation.js.map