animatable.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. import { Animation } from "./animation.js";
  2. import { RuntimeAnimation } from "./runtimeAnimation.js";
  3. import { Observable } from "../Misc/observable.js";
  4. import { Scene } from "../scene.js";
  5. import { Matrix, Quaternion, Vector3, TmpVectors } from "../Maths/math.vector.js";
  6. import { PrecisionDate } from "../Misc/precisionDate.js";
  7. import { Bone } from "../Bones/bone.js";
  8. /**
  9. * Class used to store an actual running animation
  10. */
  11. export class Animatable {
  12. /**
  13. * Gets the root Animatable used to synchronize and normalize animations
  14. */
  15. get syncRoot() {
  16. return this._syncRoot;
  17. }
  18. /**
  19. * Gets the current frame of the first RuntimeAnimation
  20. * Used to synchronize Animatables
  21. */
  22. get masterFrame() {
  23. if (this._runtimeAnimations.length === 0) {
  24. return 0;
  25. }
  26. return this._runtimeAnimations[0].currentFrame;
  27. }
  28. /**
  29. * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
  30. */
  31. get weight() {
  32. return this._weight;
  33. }
  34. set weight(value) {
  35. if (value === -1) {
  36. // -1 is ok and means no weight
  37. this._weight = -1;
  38. return;
  39. }
  40. // Else weight must be in [0, 1] range
  41. this._weight = Math.min(Math.max(value, 0), 1.0);
  42. }
  43. /**
  44. * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
  45. */
  46. get speedRatio() {
  47. return this._speedRatio;
  48. }
  49. set speedRatio(value) {
  50. for (let index = 0; index < this._runtimeAnimations.length; index++) {
  51. const animation = this._runtimeAnimations[index];
  52. animation._prepareForSpeedRatioChange(value);
  53. }
  54. this._speedRatio = value;
  55. // Resync _manualJumpDelay in case goToFrame was called before speedRatio was set.
  56. if (this._goToFrame !== null) {
  57. this.goToFrame(this._goToFrame);
  58. }
  59. }
  60. /**
  61. * Gets the elapsed time since the animatable started in milliseconds
  62. */
  63. get elapsedTime() {
  64. return this._localDelayOffset === null ? 0 : this._scene._animationTime - this._localDelayOffset;
  65. }
  66. /**
  67. * Creates a new Animatable
  68. * @param scene defines the hosting scene
  69. * @param target defines the target object
  70. * @param fromFrame defines the starting frame number (default is 0)
  71. * @param toFrame defines the ending frame number (default is 100)
  72. * @param loopAnimation defines if the animation must loop (default is false)
  73. * @param speedRatio defines the factor to apply to animation speed (default is 1)
  74. * @param onAnimationEnd defines a callback to call when animation ends if it is not looping
  75. * @param animations defines a group of animation to add to the new Animatable
  76. * @param onAnimationLoop defines a callback to call when animation loops
  77. * @param isAdditive defines whether the animation should be evaluated additively
  78. * @param playOrder defines the order in which this animatable should be processed in the list of active animatables (default: 0)
  79. */
  80. constructor(scene,
  81. /** defines the target object */
  82. target,
  83. /** defines the starting frame number (default is 0) */
  84. fromFrame = 0,
  85. /** defines the ending frame number (default is 100) */
  86. toFrame = 100,
  87. /** defines if the animation must loop (default is false) */
  88. loopAnimation = false, speedRatio = 1.0,
  89. /** defines a callback to call when animation ends if it is not looping */
  90. onAnimationEnd, animations,
  91. /** defines a callback to call when animation loops */
  92. onAnimationLoop,
  93. /** defines whether the animation should be evaluated additively */
  94. isAdditive = false,
  95. /** defines the order in which this animatable should be processed in the list of active animatables (default: 0) */
  96. playOrder = 0) {
  97. this.target = target;
  98. this.fromFrame = fromFrame;
  99. this.toFrame = toFrame;
  100. this.loopAnimation = loopAnimation;
  101. this.onAnimationEnd = onAnimationEnd;
  102. this.onAnimationLoop = onAnimationLoop;
  103. this.isAdditive = isAdditive;
  104. this.playOrder = playOrder;
  105. this._localDelayOffset = null;
  106. this._pausedDelay = null;
  107. this._manualJumpDelay = null;
  108. /** @hidden */
  109. this._runtimeAnimations = new Array();
  110. this._paused = false;
  111. this._speedRatio = 1;
  112. this._weight = -1.0;
  113. this._syncRoot = null;
  114. this._frameToSyncFromJump = null;
  115. this._goToFrame = null;
  116. /**
  117. * Gets or sets a boolean indicating if the animatable must be disposed and removed at the end of the animation.
  118. * This will only apply for non looping animation (default is true)
  119. */
  120. this.disposeOnEnd = true;
  121. /**
  122. * Gets a boolean indicating if the animation has started
  123. */
  124. this.animationStarted = false;
  125. /**
  126. * Observer raised when the animation ends
  127. */
  128. this.onAnimationEndObservable = new Observable();
  129. /**
  130. * Observer raised when the animation loops
  131. */
  132. this.onAnimationLoopObservable = new Observable();
  133. this._scene = scene;
  134. if (animations) {
  135. this.appendAnimations(target, animations);
  136. }
  137. this._speedRatio = speedRatio;
  138. scene._activeAnimatables.push(this);
  139. }
  140. // Methods
  141. /**
  142. * Synchronize and normalize current Animatable with a source Animatable
  143. * This is useful when using animation weights and when animations are not of the same length
  144. * @param root defines the root Animatable to synchronize with (null to stop synchronizing)
  145. * @returns the current Animatable
  146. */
  147. syncWith(root) {
  148. this._syncRoot = root;
  149. if (root) {
  150. // Make sure this animatable will animate after the root
  151. const index = this._scene._activeAnimatables.indexOf(this);
  152. if (index > -1) {
  153. this._scene._activeAnimatables.splice(index, 1);
  154. this._scene._activeAnimatables.push(this);
  155. }
  156. }
  157. return this;
  158. }
  159. /**
  160. * Gets the list of runtime animations
  161. * @returns an array of RuntimeAnimation
  162. */
  163. getAnimations() {
  164. return this._runtimeAnimations;
  165. }
  166. /**
  167. * Adds more animations to the current animatable
  168. * @param target defines the target of the animations
  169. * @param animations defines the new animations to add
  170. */
  171. appendAnimations(target, animations) {
  172. for (let index = 0; index < animations.length; index++) {
  173. const animation = animations[index];
  174. const newRuntimeAnimation = new RuntimeAnimation(target, animation, this._scene, this);
  175. newRuntimeAnimation._onLoop = () => {
  176. this.onAnimationLoopObservable.notifyObservers(this);
  177. if (this.onAnimationLoop) {
  178. this.onAnimationLoop();
  179. }
  180. };
  181. this._runtimeAnimations.push(newRuntimeAnimation);
  182. }
  183. }
  184. /**
  185. * Gets the source animation for a specific property
  186. * @param property defines the property to look for
  187. * @returns null or the source animation for the given property
  188. */
  189. getAnimationByTargetProperty(property) {
  190. const runtimeAnimations = this._runtimeAnimations;
  191. for (let index = 0; index < runtimeAnimations.length; index++) {
  192. if (runtimeAnimations[index].animation.targetProperty === property) {
  193. return runtimeAnimations[index].animation;
  194. }
  195. }
  196. return null;
  197. }
  198. /**
  199. * Gets the runtime animation for a specific property
  200. * @param property defines the property to look for
  201. * @returns null or the runtime animation for the given property
  202. */
  203. getRuntimeAnimationByTargetProperty(property) {
  204. const runtimeAnimations = this._runtimeAnimations;
  205. for (let index = 0; index < runtimeAnimations.length; index++) {
  206. if (runtimeAnimations[index].animation.targetProperty === property) {
  207. return runtimeAnimations[index];
  208. }
  209. }
  210. return null;
  211. }
  212. /**
  213. * Resets the animatable to its original state
  214. */
  215. reset() {
  216. const runtimeAnimations = this._runtimeAnimations;
  217. for (let index = 0; index < runtimeAnimations.length; index++) {
  218. runtimeAnimations[index].reset(true);
  219. }
  220. this._localDelayOffset = null;
  221. this._pausedDelay = null;
  222. }
  223. /**
  224. * Allows the animatable to blend with current running animations
  225. * @see https://doc.babylonjs.com/features/featuresDeepDive/animation/advanced_animations#animation-blending
  226. * @param blendingSpeed defines the blending speed to use
  227. */
  228. enableBlending(blendingSpeed) {
  229. const runtimeAnimations = this._runtimeAnimations;
  230. for (let index = 0; index < runtimeAnimations.length; index++) {
  231. runtimeAnimations[index].animation.enableBlending = true;
  232. runtimeAnimations[index].animation.blendingSpeed = blendingSpeed;
  233. }
  234. }
  235. /**
  236. * Disable animation blending
  237. * @see https://doc.babylonjs.com/features/featuresDeepDive/animation/advanced_animations#animation-blending
  238. */
  239. disableBlending() {
  240. const runtimeAnimations = this._runtimeAnimations;
  241. for (let index = 0; index < runtimeAnimations.length; index++) {
  242. runtimeAnimations[index].animation.enableBlending = false;
  243. }
  244. }
  245. /**
  246. * Jump directly to a given frame
  247. * @param frame defines the frame to jump to
  248. */
  249. goToFrame(frame) {
  250. const runtimeAnimations = this._runtimeAnimations;
  251. if (runtimeAnimations[0]) {
  252. const fps = runtimeAnimations[0].animation.framePerSecond;
  253. this._frameToSyncFromJump = this._frameToSyncFromJump ?? runtimeAnimations[0].currentFrame;
  254. const delay = this.speedRatio === 0 ? 0 : (((frame - this._frameToSyncFromJump) / fps) * 1000) / this.speedRatio;
  255. this._manualJumpDelay = -delay;
  256. }
  257. for (let index = 0; index < runtimeAnimations.length; index++) {
  258. runtimeAnimations[index].goToFrame(frame);
  259. }
  260. this._goToFrame = frame;
  261. }
  262. /**
  263. * Returns true if the animations for this animatable are paused
  264. */
  265. get paused() {
  266. return this._paused;
  267. }
  268. /**
  269. * Pause the animation
  270. */
  271. pause() {
  272. if (this._paused) {
  273. return;
  274. }
  275. this._paused = true;
  276. }
  277. /**
  278. * Restart the animation
  279. */
  280. restart() {
  281. this._paused = false;
  282. }
  283. _raiseOnAnimationEnd() {
  284. if (this.onAnimationEnd) {
  285. this.onAnimationEnd();
  286. }
  287. this.onAnimationEndObservable.notifyObservers(this);
  288. }
  289. /**
  290. * Stop and delete the current animation
  291. * @param animationName defines a string used to only stop some of the runtime animations instead of all
  292. * @param targetMask a function that determines if the animation should be stopped based on its target (all animations will be stopped if both this and animationName are empty)
  293. * @param useGlobalSplice if true, the animatables will be removed by the caller of this function (false by default)
  294. */
  295. stop(animationName, targetMask, useGlobalSplice = false) {
  296. if (animationName || targetMask) {
  297. const idx = this._scene._activeAnimatables.indexOf(this);
  298. if (idx > -1) {
  299. const runtimeAnimations = this._runtimeAnimations;
  300. for (let index = runtimeAnimations.length - 1; index >= 0; index--) {
  301. const runtimeAnimation = runtimeAnimations[index];
  302. if (animationName && runtimeAnimation.animation.name != animationName) {
  303. continue;
  304. }
  305. if (targetMask && !targetMask(runtimeAnimation.target)) {
  306. continue;
  307. }
  308. runtimeAnimation.dispose();
  309. runtimeAnimations.splice(index, 1);
  310. }
  311. if (runtimeAnimations.length == 0) {
  312. if (!useGlobalSplice) {
  313. this._scene._activeAnimatables.splice(idx, 1);
  314. }
  315. this._raiseOnAnimationEnd();
  316. }
  317. }
  318. }
  319. else {
  320. const index = this._scene._activeAnimatables.indexOf(this);
  321. if (index > -1) {
  322. if (!useGlobalSplice) {
  323. this._scene._activeAnimatables.splice(index, 1);
  324. }
  325. const runtimeAnimations = this._runtimeAnimations;
  326. for (let index = 0; index < runtimeAnimations.length; index++) {
  327. runtimeAnimations[index].dispose();
  328. }
  329. this._runtimeAnimations.length = 0;
  330. this._raiseOnAnimationEnd();
  331. }
  332. }
  333. }
  334. /**
  335. * Wait asynchronously for the animation to end
  336. * @returns a promise which will be fulfilled when the animation ends
  337. */
  338. waitAsync() {
  339. return new Promise((resolve) => {
  340. this.onAnimationEndObservable.add(() => {
  341. resolve(this);
  342. }, undefined, undefined, this, true);
  343. });
  344. }
  345. /**
  346. * @internal
  347. */
  348. _animate(delay) {
  349. if (this._paused) {
  350. this.animationStarted = false;
  351. if (this._pausedDelay === null) {
  352. this._pausedDelay = delay;
  353. }
  354. return true;
  355. }
  356. if (this._localDelayOffset === null) {
  357. this._localDelayOffset = delay;
  358. this._pausedDelay = null;
  359. }
  360. else if (this._pausedDelay !== null) {
  361. this._localDelayOffset += delay - this._pausedDelay;
  362. this._pausedDelay = null;
  363. }
  364. if (this._manualJumpDelay !== null) {
  365. this._localDelayOffset += this._manualJumpDelay;
  366. this._manualJumpDelay = null;
  367. this._frameToSyncFromJump = null;
  368. }
  369. this._goToFrame = null;
  370. if (this._weight === 0) {
  371. // We consider that an animatable with a weight === 0 is "actively" paused
  372. return true;
  373. }
  374. // Animating
  375. let running = false;
  376. const runtimeAnimations = this._runtimeAnimations;
  377. let index;
  378. for (index = 0; index < runtimeAnimations.length; index++) {
  379. const animation = runtimeAnimations[index];
  380. const isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
  381. running = running || isRunning;
  382. }
  383. this.animationStarted = running;
  384. if (!running) {
  385. if (this.disposeOnEnd) {
  386. // Remove from active animatables
  387. index = this._scene._activeAnimatables.indexOf(this);
  388. this._scene._activeAnimatables.splice(index, 1);
  389. // Dispose all runtime animations
  390. for (index = 0; index < runtimeAnimations.length; index++) {
  391. runtimeAnimations[index].dispose();
  392. }
  393. }
  394. this._raiseOnAnimationEnd();
  395. if (this.disposeOnEnd) {
  396. this.onAnimationEnd = null;
  397. this.onAnimationLoop = null;
  398. this.onAnimationLoopObservable.clear();
  399. this.onAnimationEndObservable.clear();
  400. }
  401. }
  402. return running;
  403. }
  404. }
  405. Scene.prototype._animate = function (customDeltaTime) {
  406. if (!this.animationsEnabled) {
  407. return;
  408. }
  409. // Getting time
  410. const now = PrecisionDate.Now;
  411. if (!this._animationTimeLast) {
  412. if (this._pendingData.length > 0) {
  413. return;
  414. }
  415. this._animationTimeLast = now;
  416. }
  417. this.deltaTime = customDeltaTime !== undefined ? customDeltaTime : this.useConstantAnimationDeltaTime ? 16.0 : (now - this._animationTimeLast) * this.animationTimeScale;
  418. this._animationTimeLast = now;
  419. const animatables = this._activeAnimatables;
  420. if (animatables.length === 0) {
  421. return;
  422. }
  423. this._animationTime += this.deltaTime;
  424. const animationTime = this._animationTime;
  425. for (let index = 0; index < animatables.length; index++) {
  426. const animatable = animatables[index];
  427. if (!animatable._animate(animationTime) && animatable.disposeOnEnd) {
  428. index--; // Array was updated
  429. }
  430. }
  431. // Late animation bindings
  432. this._processLateAnimationBindings();
  433. };
  434. Scene.prototype.sortActiveAnimatables = function () {
  435. this._activeAnimatables.sort((a, b) => {
  436. return a.playOrder - b.playOrder;
  437. });
  438. };
  439. Scene.prototype.beginWeightedAnimation = function (target, from, to, weight = 1.0, loop, speedRatio = 1.0, onAnimationEnd, animatable, targetMask, onAnimationLoop, isAdditive = false) {
  440. const returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false, targetMask, onAnimationLoop, isAdditive);
  441. returnedAnimatable.weight = weight;
  442. return returnedAnimatable;
  443. };
  444. Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio = 1.0, onAnimationEnd, animatable, stopCurrent = true, targetMask, onAnimationLoop, isAdditive = false) {
  445. if (from > to && speedRatio > 0) {
  446. speedRatio *= -1;
  447. }
  448. if (stopCurrent) {
  449. this.stopAnimation(target, undefined, targetMask);
  450. }
  451. if (!animatable) {
  452. animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, undefined, onAnimationLoop, isAdditive);
  453. }
  454. const shouldRunTargetAnimations = targetMask ? targetMask(target) : true;
  455. // Local animations
  456. if (target.animations && shouldRunTargetAnimations) {
  457. animatable.appendAnimations(target, target.animations);
  458. }
  459. // Children animations
  460. if (target.getAnimatables) {
  461. const animatables = target.getAnimatables();
  462. for (let index = 0; index < animatables.length; index++) {
  463. this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, onAnimationLoop);
  464. }
  465. }
  466. animatable.reset();
  467. return animatable;
  468. };
  469. Scene.prototype.beginHierarchyAnimation = function (target, directDescendantsOnly, from, to, loop, speedRatio = 1.0, onAnimationEnd, animatable, stopCurrent = true, targetMask, onAnimationLoop, isAdditive = false) {
  470. const children = target.getDescendants(directDescendantsOnly);
  471. const result = [];
  472. result.push(this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, undefined, isAdditive));
  473. for (const child of children) {
  474. result.push(this.beginAnimation(child, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, undefined, isAdditive));
  475. }
  476. return result;
  477. };
  478. Scene.prototype.beginDirectAnimation = function (target, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive = false) {
  479. if (speedRatio === undefined) {
  480. speedRatio = 1.0;
  481. }
  482. if (from > to && speedRatio > 0) {
  483. speedRatio *= -1;
  484. }
  485. else if (to > from && speedRatio < 0) {
  486. const temp = to;
  487. to = from;
  488. from = temp;
  489. }
  490. const animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, animations, onAnimationLoop, isAdditive);
  491. return animatable;
  492. };
  493. Scene.prototype.beginDirectHierarchyAnimation = function (target, directDescendantsOnly, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive = false) {
  494. const children = target.getDescendants(directDescendantsOnly);
  495. const result = [];
  496. result.push(this.beginDirectAnimation(target, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive));
  497. for (const child of children) {
  498. result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive));
  499. }
  500. return result;
  501. };
  502. Scene.prototype.getAnimatableByTarget = function (target) {
  503. for (let index = 0; index < this._activeAnimatables.length; index++) {
  504. if (this._activeAnimatables[index].target === target) {
  505. return this._activeAnimatables[index];
  506. }
  507. }
  508. return null;
  509. };
  510. Scene.prototype.getAllAnimatablesByTarget = function (target) {
  511. const result = [];
  512. for (let index = 0; index < this._activeAnimatables.length; index++) {
  513. if (this._activeAnimatables[index].target === target) {
  514. result.push(this._activeAnimatables[index]);
  515. }
  516. }
  517. return result;
  518. };
  519. /**
  520. * Will stop the animation of the given target
  521. * @param target - the target
  522. * @param animationName - the name of the animation to stop (all animations will be stopped if both this and targetMask are empty)
  523. * @param targetMask - a function that determines if the animation should be stopped based on its target (all animations will be stopped if both this and animationName are empty)
  524. */
  525. Scene.prototype.stopAnimation = function (target, animationName, targetMask) {
  526. const animatables = this.getAllAnimatablesByTarget(target);
  527. for (const animatable of animatables) {
  528. animatable.stop(animationName, targetMask);
  529. }
  530. };
  531. /**
  532. * Stops and removes all animations that have been applied to the scene
  533. */
  534. Scene.prototype.stopAllAnimations = function () {
  535. if (this._activeAnimatables) {
  536. for (let i = 0; i < this._activeAnimatables.length; i++) {
  537. this._activeAnimatables[i].stop(undefined, undefined, true);
  538. }
  539. this._activeAnimatables.length = 0;
  540. }
  541. for (const group of this.animationGroups) {
  542. group.stop();
  543. }
  544. };
  545. Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation, originalValue) {
  546. const target = runtimeAnimation.target;
  547. this._registeredForLateAnimationBindings.pushNoDuplicate(target);
  548. if (!target._lateAnimationHolders) {
  549. target._lateAnimationHolders = {};
  550. }
  551. if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
  552. target._lateAnimationHolders[runtimeAnimation.targetPath] = {
  553. totalWeight: 0,
  554. totalAdditiveWeight: 0,
  555. animations: [],
  556. additiveAnimations: [],
  557. originalValue: originalValue,
  558. };
  559. }
  560. if (runtimeAnimation.isAdditive) {
  561. target._lateAnimationHolders[runtimeAnimation.targetPath].additiveAnimations.push(runtimeAnimation);
  562. target._lateAnimationHolders[runtimeAnimation.targetPath].totalAdditiveWeight += runtimeAnimation.weight;
  563. }
  564. else {
  565. target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
  566. target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
  567. }
  568. };
  569. Scene.prototype._processLateAnimationBindingsForMatrices = function (holder) {
  570. if (holder.totalWeight === 0 && holder.totalAdditiveWeight === 0) {
  571. return holder.originalValue;
  572. }
  573. let normalizer = 1.0;
  574. const finalPosition = TmpVectors.Vector3[0];
  575. const finalScaling = TmpVectors.Vector3[1];
  576. const finalQuaternion = TmpVectors.Quaternion[0];
  577. let startIndex = 0;
  578. const originalAnimation = holder.animations[0];
  579. const originalValue = holder.originalValue;
  580. let scale = 1;
  581. let skipOverride = false;
  582. if (holder.totalWeight < 1.0) {
  583. // We need to mix the original value in
  584. scale = 1.0 - holder.totalWeight;
  585. originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
  586. }
  587. else {
  588. startIndex = 1;
  589. // We need to normalize the weights
  590. normalizer = holder.totalWeight;
  591. scale = originalAnimation.weight / normalizer;
  592. if (scale == 1) {
  593. if (holder.totalAdditiveWeight) {
  594. skipOverride = true;
  595. }
  596. else {
  597. return originalAnimation.currentValue;
  598. }
  599. }
  600. originalAnimation.currentValue.decompose(finalScaling, finalQuaternion, finalPosition);
  601. }
  602. // Add up the override animations
  603. if (!skipOverride) {
  604. finalScaling.scaleInPlace(scale);
  605. finalPosition.scaleInPlace(scale);
  606. finalQuaternion.scaleInPlace(scale);
  607. for (let animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
  608. const runtimeAnimation = holder.animations[animIndex];
  609. if (runtimeAnimation.weight === 0) {
  610. continue;
  611. }
  612. scale = runtimeAnimation.weight / normalizer;
  613. const currentPosition = TmpVectors.Vector3[2];
  614. const currentScaling = TmpVectors.Vector3[3];
  615. const currentQuaternion = TmpVectors.Quaternion[1];
  616. runtimeAnimation.currentValue.decompose(currentScaling, currentQuaternion, currentPosition);
  617. currentScaling.scaleAndAddToRef(scale, finalScaling);
  618. currentQuaternion.scaleAndAddToRef(Quaternion.Dot(finalQuaternion, currentQuaternion) > 0 ? scale : -scale, finalQuaternion);
  619. currentPosition.scaleAndAddToRef(scale, finalPosition);
  620. }
  621. finalQuaternion.normalize();
  622. }
  623. // Add up the additive animations
  624. for (let animIndex = 0; animIndex < holder.additiveAnimations.length; animIndex++) {
  625. const runtimeAnimation = holder.additiveAnimations[animIndex];
  626. if (runtimeAnimation.weight === 0) {
  627. continue;
  628. }
  629. const currentPosition = TmpVectors.Vector3[2];
  630. const currentScaling = TmpVectors.Vector3[3];
  631. const currentQuaternion = TmpVectors.Quaternion[1];
  632. runtimeAnimation.currentValue.decompose(currentScaling, currentQuaternion, currentPosition);
  633. currentScaling.multiplyToRef(finalScaling, currentScaling);
  634. Vector3.LerpToRef(finalScaling, currentScaling, runtimeAnimation.weight, finalScaling);
  635. finalQuaternion.multiplyToRef(currentQuaternion, currentQuaternion);
  636. Quaternion.SlerpToRef(finalQuaternion, currentQuaternion, runtimeAnimation.weight, finalQuaternion);
  637. currentPosition.scaleAndAddToRef(runtimeAnimation.weight, finalPosition);
  638. }
  639. const workValue = originalAnimation ? originalAnimation._animationState.workValue : TmpVectors.Matrix[0].clone();
  640. Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, workValue);
  641. return workValue;
  642. };
  643. Scene.prototype._processLateAnimationBindingsForQuaternions = function (holder, refQuaternion) {
  644. if (holder.totalWeight === 0 && holder.totalAdditiveWeight === 0) {
  645. return refQuaternion;
  646. }
  647. const originalAnimation = holder.animations[0];
  648. const originalValue = holder.originalValue;
  649. let cumulativeQuaternion = refQuaternion;
  650. if (holder.totalWeight === 0 && holder.totalAdditiveWeight > 0) {
  651. cumulativeQuaternion.copyFrom(originalValue);
  652. }
  653. else if (holder.animations.length === 1) {
  654. Quaternion.SlerpToRef(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight), cumulativeQuaternion);
  655. if (holder.totalAdditiveWeight === 0) {
  656. return cumulativeQuaternion;
  657. }
  658. }
  659. else if (holder.animations.length > 1) {
  660. // Add up the override animations
  661. let normalizer = 1.0;
  662. let quaternions;
  663. let weights;
  664. if (holder.totalWeight < 1.0) {
  665. const scale = 1.0 - holder.totalWeight;
  666. quaternions = [];
  667. weights = [];
  668. quaternions.push(originalValue);
  669. weights.push(scale);
  670. }
  671. else {
  672. if (holder.animations.length === 2) {
  673. // Slerp as soon as we can
  674. Quaternion.SlerpToRef(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight, refQuaternion);
  675. if (holder.totalAdditiveWeight === 0) {
  676. return refQuaternion;
  677. }
  678. }
  679. quaternions = [];
  680. weights = [];
  681. normalizer = holder.totalWeight;
  682. }
  683. for (let animIndex = 0; animIndex < holder.animations.length; animIndex++) {
  684. const runtimeAnimation = holder.animations[animIndex];
  685. quaternions.push(runtimeAnimation.currentValue);
  686. weights.push(runtimeAnimation.weight / normalizer);
  687. }
  688. // https://gamedev.stackexchange.com/questions/62354/method-for-interpolation-between-3-quaternions
  689. let cumulativeAmount = 0;
  690. for (let index = 0; index < quaternions.length;) {
  691. if (!index) {
  692. Quaternion.SlerpToRef(quaternions[index], quaternions[index + 1], weights[index + 1] / (weights[index] + weights[index + 1]), refQuaternion);
  693. cumulativeQuaternion = refQuaternion;
  694. cumulativeAmount = weights[index] + weights[index + 1];
  695. index += 2;
  696. continue;
  697. }
  698. cumulativeAmount += weights[index];
  699. Quaternion.SlerpToRef(cumulativeQuaternion, quaternions[index], weights[index] / cumulativeAmount, cumulativeQuaternion);
  700. index++;
  701. }
  702. }
  703. // Add up the additive animations
  704. for (let animIndex = 0; animIndex < holder.additiveAnimations.length; animIndex++) {
  705. const runtimeAnimation = holder.additiveAnimations[animIndex];
  706. if (runtimeAnimation.weight === 0) {
  707. continue;
  708. }
  709. cumulativeQuaternion.multiplyToRef(runtimeAnimation.currentValue, TmpVectors.Quaternion[0]);
  710. Quaternion.SlerpToRef(cumulativeQuaternion, TmpVectors.Quaternion[0], runtimeAnimation.weight, cumulativeQuaternion);
  711. }
  712. return cumulativeQuaternion;
  713. };
  714. Scene.prototype._processLateAnimationBindings = function () {
  715. if (!this._registeredForLateAnimationBindings.length) {
  716. return;
  717. }
  718. for (let index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
  719. const target = this._registeredForLateAnimationBindings.data[index];
  720. for (const path in target._lateAnimationHolders) {
  721. const holder = target._lateAnimationHolders[path];
  722. const originalAnimation = holder.animations[0];
  723. const originalValue = holder.originalValue;
  724. if (originalValue === undefined || originalValue === null) {
  725. continue;
  726. }
  727. const matrixDecomposeMode = Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
  728. let finalValue = target[path];
  729. if (matrixDecomposeMode) {
  730. finalValue = this._processLateAnimationBindingsForMatrices(holder);
  731. }
  732. else {
  733. const quaternionMode = originalValue.w !== undefined;
  734. if (quaternionMode) {
  735. finalValue = this._processLateAnimationBindingsForQuaternions(holder, finalValue || Quaternion.Identity());
  736. }
  737. else {
  738. let startIndex = 0;
  739. let normalizer = 1.0;
  740. const originalAnimationIsLoopRelativeFromCurrent = originalAnimation && originalAnimation._animationState.loopMode === Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT;
  741. if (holder.totalWeight < 1.0) {
  742. // We need to mix the original value in
  743. if (originalAnimationIsLoopRelativeFromCurrent) {
  744. finalValue = originalValue.clone ? originalValue.clone() : originalValue;
  745. }
  746. else if (originalAnimation && originalValue.scale) {
  747. finalValue = originalValue.scale(1.0 - holder.totalWeight);
  748. }
  749. else if (originalAnimation) {
  750. finalValue = originalValue * (1.0 - holder.totalWeight);
  751. }
  752. else if (originalValue.clone) {
  753. finalValue = originalValue.clone();
  754. }
  755. else {
  756. finalValue = originalValue;
  757. }
  758. }
  759. else if (originalAnimation) {
  760. // We need to normalize the weights
  761. normalizer = holder.totalWeight;
  762. const scale = originalAnimation.weight / normalizer;
  763. if (scale !== 1) {
  764. if (originalAnimation.currentValue.scale) {
  765. finalValue = originalAnimation.currentValue.scale(scale);
  766. }
  767. else {
  768. finalValue = originalAnimation.currentValue * scale;
  769. }
  770. }
  771. else {
  772. finalValue = originalAnimation.currentValue;
  773. }
  774. if (originalAnimationIsLoopRelativeFromCurrent) {
  775. if (finalValue.addToRef) {
  776. finalValue.addToRef(originalValue, finalValue);
  777. }
  778. else {
  779. finalValue += originalValue;
  780. }
  781. }
  782. startIndex = 1;
  783. }
  784. // Add up the override animations
  785. for (let animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
  786. const runtimeAnimation = holder.animations[animIndex];
  787. const scale = runtimeAnimation.weight / normalizer;
  788. if (!scale) {
  789. continue;
  790. }
  791. else if (runtimeAnimation.currentValue.scaleAndAddToRef) {
  792. runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
  793. }
  794. else {
  795. finalValue += runtimeAnimation.currentValue * scale;
  796. }
  797. }
  798. // Add up the additive animations
  799. for (let animIndex = 0; animIndex < holder.additiveAnimations.length; animIndex++) {
  800. const runtimeAnimation = holder.additiveAnimations[animIndex];
  801. const scale = runtimeAnimation.weight;
  802. if (!scale) {
  803. continue;
  804. }
  805. else if (runtimeAnimation.currentValue.scaleAndAddToRef) {
  806. runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
  807. }
  808. else {
  809. finalValue += runtimeAnimation.currentValue * scale;
  810. }
  811. }
  812. }
  813. }
  814. target[path] = finalValue;
  815. }
  816. target._lateAnimationHolders = {};
  817. }
  818. this._registeredForLateAnimationBindings.reset();
  819. };
  820. Bone.prototype.copyAnimationRange = function (source, rangeName, frameOffset, rescaleAsRequired = false, skelDimensionsRatio = null) {
  821. // all animation may be coming from a library skeleton, so may need to create animation
  822. if (this.animations.length === 0) {
  823. this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
  824. this.animations[0].setKeys([]);
  825. }
  826. // get animation info / verify there is such a range from the source bone
  827. const sourceRange = source.animations[0].getRange(rangeName);
  828. if (!sourceRange) {
  829. return false;
  830. }
  831. const from = sourceRange.from;
  832. const to = sourceRange.to;
  833. const sourceKeys = source.animations[0].getKeys();
  834. // rescaling prep
  835. const sourceBoneLength = source.length;
  836. const sourceParent = source.getParent();
  837. const parent = this.getParent();
  838. const parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
  839. const parentRatio = parentScalingReqd && parent && sourceParent ? parent.length / sourceParent.length : 1;
  840. const dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);
  841. const destKeys = this.animations[0].getKeys();
  842. // loop vars declaration
  843. let orig;
  844. let origTranslation;
  845. let mat;
  846. for (let key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
  847. orig = sourceKeys[key];
  848. if (orig.frame >= from && orig.frame <= to) {
  849. if (rescaleAsRequired) {
  850. mat = orig.value.clone();
  851. // scale based on parent ratio, when bone has parent
  852. if (parentScalingReqd) {
  853. origTranslation = mat.getTranslation();
  854. mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
  855. // scale based on skeleton dimension ratio when root bone, and value is passed
  856. }
  857. else if (dimensionsScalingReqd && skelDimensionsRatio) {
  858. origTranslation = mat.getTranslation();
  859. mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));
  860. // use original when root bone, and no data for skelDimensionsRatio
  861. }
  862. else {
  863. mat = orig.value;
  864. }
  865. }
  866. else {
  867. mat = orig.value;
  868. }
  869. destKeys.push({ frame: orig.frame + frameOffset, value: mat });
  870. }
  871. }
  872. this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
  873. return true;
  874. };
  875. //# sourceMappingURL=animatable.js.map