123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875 |
- import { Animation } from "./animation.js";
- import { RuntimeAnimation } from "./runtimeAnimation.js";
- import { Observable } from "../Misc/observable.js";
- import { Scene } from "../scene.js";
- import { Matrix, Quaternion, Vector3, TmpVectors } from "../Maths/math.vector.js";
- import { PrecisionDate } from "../Misc/precisionDate.js";
- import { Bone } from "../Bones/bone.js";
- /**
- * Class used to store an actual running animation
- */
- export class Animatable {
- /**
- * Gets the root Animatable used to synchronize and normalize animations
- */
- get syncRoot() {
- return this._syncRoot;
- }
- /**
- * Gets the current frame of the first RuntimeAnimation
- * Used to synchronize Animatables
- */
- get masterFrame() {
- if (this._runtimeAnimations.length === 0) {
- return 0;
- }
- return this._runtimeAnimations[0].currentFrame;
- }
- /**
- * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
- */
- get weight() {
- return this._weight;
- }
- set weight(value) {
- if (value === -1) {
- // -1 is ok and means no weight
- this._weight = -1;
- return;
- }
- // Else weight must be in [0, 1] range
- this._weight = Math.min(Math.max(value, 0), 1.0);
- }
- /**
- * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
- */
- get speedRatio() {
- return this._speedRatio;
- }
- set speedRatio(value) {
- for (let index = 0; index < this._runtimeAnimations.length; index++) {
- const animation = this._runtimeAnimations[index];
- animation._prepareForSpeedRatioChange(value);
- }
- this._speedRatio = value;
- // Resync _manualJumpDelay in case goToFrame was called before speedRatio was set.
- if (this._goToFrame !== null) {
- this.goToFrame(this._goToFrame);
- }
- }
- /**
- * Gets the elapsed time since the animatable started in milliseconds
- */
- get elapsedTime() {
- return this._localDelayOffset === null ? 0 : this._scene._animationTime - this._localDelayOffset;
- }
- /**
- * Creates a new Animatable
- * @param scene defines the hosting scene
- * @param target defines the target object
- * @param fromFrame defines the starting frame number (default is 0)
- * @param toFrame defines the ending frame number (default is 100)
- * @param loopAnimation defines if the animation must loop (default is false)
- * @param speedRatio defines the factor to apply to animation speed (default is 1)
- * @param onAnimationEnd defines a callback to call when animation ends if it is not looping
- * @param animations defines a group of animation to add to the new Animatable
- * @param onAnimationLoop defines a callback to call when animation loops
- * @param isAdditive defines whether the animation should be evaluated additively
- * @param playOrder defines the order in which this animatable should be processed in the list of active animatables (default: 0)
- */
- constructor(scene,
- /** defines the target object */
- target,
- /** defines the starting frame number (default is 0) */
- fromFrame = 0,
- /** defines the ending frame number (default is 100) */
- toFrame = 100,
- /** defines if the animation must loop (default is false) */
- loopAnimation = false, speedRatio = 1.0,
- /** defines a callback to call when animation ends if it is not looping */
- onAnimationEnd, animations,
- /** defines a callback to call when animation loops */
- onAnimationLoop,
- /** defines whether the animation should be evaluated additively */
- isAdditive = false,
- /** defines the order in which this animatable should be processed in the list of active animatables (default: 0) */
- playOrder = 0) {
- this.target = target;
- this.fromFrame = fromFrame;
- this.toFrame = toFrame;
- this.loopAnimation = loopAnimation;
- this.onAnimationEnd = onAnimationEnd;
- this.onAnimationLoop = onAnimationLoop;
- this.isAdditive = isAdditive;
- this.playOrder = playOrder;
- this._localDelayOffset = null;
- this._pausedDelay = null;
- this._manualJumpDelay = null;
- /** @hidden */
- this._runtimeAnimations = new Array();
- this._paused = false;
- this._speedRatio = 1;
- this._weight = -1.0;
- this._syncRoot = null;
- this._frameToSyncFromJump = null;
- this._goToFrame = null;
- /**
- * Gets or sets a boolean indicating if the animatable must be disposed and removed at the end of the animation.
- * This will only apply for non looping animation (default is true)
- */
- this.disposeOnEnd = true;
- /**
- * Gets a boolean indicating if the animation has started
- */
- this.animationStarted = false;
- /**
- * Observer raised when the animation ends
- */
- this.onAnimationEndObservable = new Observable();
- /**
- * Observer raised when the animation loops
- */
- this.onAnimationLoopObservable = new Observable();
- this._scene = scene;
- if (animations) {
- this.appendAnimations(target, animations);
- }
- this._speedRatio = speedRatio;
- scene._activeAnimatables.push(this);
- }
- // Methods
- /**
- * Synchronize and normalize current Animatable with a source Animatable
- * This is useful when using animation weights and when animations are not of the same length
- * @param root defines the root Animatable to synchronize with (null to stop synchronizing)
- * @returns the current Animatable
- */
- syncWith(root) {
- this._syncRoot = root;
- if (root) {
- // Make sure this animatable will animate after the root
- const index = this._scene._activeAnimatables.indexOf(this);
- if (index > -1) {
- this._scene._activeAnimatables.splice(index, 1);
- this._scene._activeAnimatables.push(this);
- }
- }
- return this;
- }
- /**
- * Gets the list of runtime animations
- * @returns an array of RuntimeAnimation
- */
- getAnimations() {
- return this._runtimeAnimations;
- }
- /**
- * Adds more animations to the current animatable
- * @param target defines the target of the animations
- * @param animations defines the new animations to add
- */
- appendAnimations(target, animations) {
- for (let index = 0; index < animations.length; index++) {
- const animation = animations[index];
- const newRuntimeAnimation = new RuntimeAnimation(target, animation, this._scene, this);
- newRuntimeAnimation._onLoop = () => {
- this.onAnimationLoopObservable.notifyObservers(this);
- if (this.onAnimationLoop) {
- this.onAnimationLoop();
- }
- };
- this._runtimeAnimations.push(newRuntimeAnimation);
- }
- }
- /**
- * Gets the source animation for a specific property
- * @param property defines the property to look for
- * @returns null or the source animation for the given property
- */
- getAnimationByTargetProperty(property) {
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = 0; index < runtimeAnimations.length; index++) {
- if (runtimeAnimations[index].animation.targetProperty === property) {
- return runtimeAnimations[index].animation;
- }
- }
- return null;
- }
- /**
- * Gets the runtime animation for a specific property
- * @param property defines the property to look for
- * @returns null or the runtime animation for the given property
- */
- getRuntimeAnimationByTargetProperty(property) {
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = 0; index < runtimeAnimations.length; index++) {
- if (runtimeAnimations[index].animation.targetProperty === property) {
- return runtimeAnimations[index];
- }
- }
- return null;
- }
- /**
- * Resets the animatable to its original state
- */
- reset() {
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = 0; index < runtimeAnimations.length; index++) {
- runtimeAnimations[index].reset(true);
- }
- this._localDelayOffset = null;
- this._pausedDelay = null;
- }
- /**
- * Allows the animatable to blend with current running animations
- * @see https://doc.babylonjs.com/features/featuresDeepDive/animation/advanced_animations#animation-blending
- * @param blendingSpeed defines the blending speed to use
- */
- enableBlending(blendingSpeed) {
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = 0; index < runtimeAnimations.length; index++) {
- runtimeAnimations[index].animation.enableBlending = true;
- runtimeAnimations[index].animation.blendingSpeed = blendingSpeed;
- }
- }
- /**
- * Disable animation blending
- * @see https://doc.babylonjs.com/features/featuresDeepDive/animation/advanced_animations#animation-blending
- */
- disableBlending() {
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = 0; index < runtimeAnimations.length; index++) {
- runtimeAnimations[index].animation.enableBlending = false;
- }
- }
- /**
- * Jump directly to a given frame
- * @param frame defines the frame to jump to
- */
- goToFrame(frame) {
- const runtimeAnimations = this._runtimeAnimations;
- if (runtimeAnimations[0]) {
- const fps = runtimeAnimations[0].animation.framePerSecond;
- this._frameToSyncFromJump = this._frameToSyncFromJump ?? runtimeAnimations[0].currentFrame;
- const delay = this.speedRatio === 0 ? 0 : (((frame - this._frameToSyncFromJump) / fps) * 1000) / this.speedRatio;
- this._manualJumpDelay = -delay;
- }
- for (let index = 0; index < runtimeAnimations.length; index++) {
- runtimeAnimations[index].goToFrame(frame);
- }
- this._goToFrame = frame;
- }
- /**
- * Returns true if the animations for this animatable are paused
- */
- get paused() {
- return this._paused;
- }
- /**
- * Pause the animation
- */
- pause() {
- if (this._paused) {
- return;
- }
- this._paused = true;
- }
- /**
- * Restart the animation
- */
- restart() {
- this._paused = false;
- }
- _raiseOnAnimationEnd() {
- if (this.onAnimationEnd) {
- this.onAnimationEnd();
- }
- this.onAnimationEndObservable.notifyObservers(this);
- }
- /**
- * Stop and delete the current animation
- * @param animationName defines a string used to only stop some of the runtime animations instead of all
- * @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)
- * @param useGlobalSplice if true, the animatables will be removed by the caller of this function (false by default)
- */
- stop(animationName, targetMask, useGlobalSplice = false) {
- if (animationName || targetMask) {
- const idx = this._scene._activeAnimatables.indexOf(this);
- if (idx > -1) {
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = runtimeAnimations.length - 1; index >= 0; index--) {
- const runtimeAnimation = runtimeAnimations[index];
- if (animationName && runtimeAnimation.animation.name != animationName) {
- continue;
- }
- if (targetMask && !targetMask(runtimeAnimation.target)) {
- continue;
- }
- runtimeAnimation.dispose();
- runtimeAnimations.splice(index, 1);
- }
- if (runtimeAnimations.length == 0) {
- if (!useGlobalSplice) {
- this._scene._activeAnimatables.splice(idx, 1);
- }
- this._raiseOnAnimationEnd();
- }
- }
- }
- else {
- const index = this._scene._activeAnimatables.indexOf(this);
- if (index > -1) {
- if (!useGlobalSplice) {
- this._scene._activeAnimatables.splice(index, 1);
- }
- const runtimeAnimations = this._runtimeAnimations;
- for (let index = 0; index < runtimeAnimations.length; index++) {
- runtimeAnimations[index].dispose();
- }
- this._runtimeAnimations.length = 0;
- this._raiseOnAnimationEnd();
- }
- }
- }
- /**
- * Wait asynchronously for the animation to end
- * @returns a promise which will be fulfilled when the animation ends
- */
- waitAsync() {
- return new Promise((resolve) => {
- this.onAnimationEndObservable.add(() => {
- resolve(this);
- }, undefined, undefined, this, true);
- });
- }
- /**
- * @internal
- */
- _animate(delay) {
- if (this._paused) {
- this.animationStarted = false;
- if (this._pausedDelay === null) {
- this._pausedDelay = delay;
- }
- return true;
- }
- if (this._localDelayOffset === null) {
- this._localDelayOffset = delay;
- this._pausedDelay = null;
- }
- else if (this._pausedDelay !== null) {
- this._localDelayOffset += delay - this._pausedDelay;
- this._pausedDelay = null;
- }
- if (this._manualJumpDelay !== null) {
- this._localDelayOffset += this._manualJumpDelay;
- this._manualJumpDelay = null;
- this._frameToSyncFromJump = null;
- }
- this._goToFrame = null;
- if (this._weight === 0) {
- // We consider that an animatable with a weight === 0 is "actively" paused
- return true;
- }
- // Animating
- let running = false;
- const runtimeAnimations = this._runtimeAnimations;
- let index;
- for (index = 0; index < runtimeAnimations.length; index++) {
- const animation = runtimeAnimations[index];
- const isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
- running = running || isRunning;
- }
- this.animationStarted = running;
- if (!running) {
- if (this.disposeOnEnd) {
- // Remove from active animatables
- index = this._scene._activeAnimatables.indexOf(this);
- this._scene._activeAnimatables.splice(index, 1);
- // Dispose all runtime animations
- for (index = 0; index < runtimeAnimations.length; index++) {
- runtimeAnimations[index].dispose();
- }
- }
- this._raiseOnAnimationEnd();
- if (this.disposeOnEnd) {
- this.onAnimationEnd = null;
- this.onAnimationLoop = null;
- this.onAnimationLoopObservable.clear();
- this.onAnimationEndObservable.clear();
- }
- }
- return running;
- }
- }
- Scene.prototype._animate = function (customDeltaTime) {
- if (!this.animationsEnabled) {
- return;
- }
- // Getting time
- const now = PrecisionDate.Now;
- if (!this._animationTimeLast) {
- if (this._pendingData.length > 0) {
- return;
- }
- this._animationTimeLast = now;
- }
- this.deltaTime = customDeltaTime !== undefined ? customDeltaTime : this.useConstantAnimationDeltaTime ? 16.0 : (now - this._animationTimeLast) * this.animationTimeScale;
- this._animationTimeLast = now;
- const animatables = this._activeAnimatables;
- if (animatables.length === 0) {
- return;
- }
- this._animationTime += this.deltaTime;
- const animationTime = this._animationTime;
- for (let index = 0; index < animatables.length; index++) {
- const animatable = animatables[index];
- if (!animatable._animate(animationTime) && animatable.disposeOnEnd) {
- index--; // Array was updated
- }
- }
- // Late animation bindings
- this._processLateAnimationBindings();
- };
- Scene.prototype.sortActiveAnimatables = function () {
- this._activeAnimatables.sort((a, b) => {
- return a.playOrder - b.playOrder;
- });
- };
- Scene.prototype.beginWeightedAnimation = function (target, from, to, weight = 1.0, loop, speedRatio = 1.0, onAnimationEnd, animatable, targetMask, onAnimationLoop, isAdditive = false) {
- const returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false, targetMask, onAnimationLoop, isAdditive);
- returnedAnimatable.weight = weight;
- return returnedAnimatable;
- };
- Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio = 1.0, onAnimationEnd, animatable, stopCurrent = true, targetMask, onAnimationLoop, isAdditive = false) {
- if (from > to && speedRatio > 0) {
- speedRatio *= -1;
- }
- if (stopCurrent) {
- this.stopAnimation(target, undefined, targetMask);
- }
- if (!animatable) {
- animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, undefined, onAnimationLoop, isAdditive);
- }
- const shouldRunTargetAnimations = targetMask ? targetMask(target) : true;
- // Local animations
- if (target.animations && shouldRunTargetAnimations) {
- animatable.appendAnimations(target, target.animations);
- }
- // Children animations
- if (target.getAnimatables) {
- const animatables = target.getAnimatables();
- for (let index = 0; index < animatables.length; index++) {
- this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, onAnimationLoop);
- }
- }
- animatable.reset();
- return animatable;
- };
- Scene.prototype.beginHierarchyAnimation = function (target, directDescendantsOnly, from, to, loop, speedRatio = 1.0, onAnimationEnd, animatable, stopCurrent = true, targetMask, onAnimationLoop, isAdditive = false) {
- const children = target.getDescendants(directDescendantsOnly);
- const result = [];
- result.push(this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, undefined, isAdditive));
- for (const child of children) {
- result.push(this.beginAnimation(child, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, undefined, isAdditive));
- }
- return result;
- };
- Scene.prototype.beginDirectAnimation = function (target, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive = false) {
- if (speedRatio === undefined) {
- speedRatio = 1.0;
- }
- if (from > to && speedRatio > 0) {
- speedRatio *= -1;
- }
- else if (to > from && speedRatio < 0) {
- const temp = to;
- to = from;
- from = temp;
- }
- const animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, animations, onAnimationLoop, isAdditive);
- return animatable;
- };
- Scene.prototype.beginDirectHierarchyAnimation = function (target, directDescendantsOnly, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive = false) {
- const children = target.getDescendants(directDescendantsOnly);
- const result = [];
- result.push(this.beginDirectAnimation(target, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive));
- for (const child of children) {
- result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop, isAdditive));
- }
- return result;
- };
- Scene.prototype.getAnimatableByTarget = function (target) {
- for (let index = 0; index < this._activeAnimatables.length; index++) {
- if (this._activeAnimatables[index].target === target) {
- return this._activeAnimatables[index];
- }
- }
- return null;
- };
- Scene.prototype.getAllAnimatablesByTarget = function (target) {
- const result = [];
- for (let index = 0; index < this._activeAnimatables.length; index++) {
- if (this._activeAnimatables[index].target === target) {
- result.push(this._activeAnimatables[index]);
- }
- }
- return result;
- };
- /**
- * Will stop the animation of the given target
- * @param target - the target
- * @param animationName - the name of the animation to stop (all animations will be stopped if both this and targetMask are empty)
- * @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)
- */
- Scene.prototype.stopAnimation = function (target, animationName, targetMask) {
- const animatables = this.getAllAnimatablesByTarget(target);
- for (const animatable of animatables) {
- animatable.stop(animationName, targetMask);
- }
- };
- /**
- * Stops and removes all animations that have been applied to the scene
- */
- Scene.prototype.stopAllAnimations = function () {
- if (this._activeAnimatables) {
- for (let i = 0; i < this._activeAnimatables.length; i++) {
- this._activeAnimatables[i].stop(undefined, undefined, true);
- }
- this._activeAnimatables.length = 0;
- }
- for (const group of this.animationGroups) {
- group.stop();
- }
- };
- Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation, originalValue) {
- const target = runtimeAnimation.target;
- this._registeredForLateAnimationBindings.pushNoDuplicate(target);
- if (!target._lateAnimationHolders) {
- target._lateAnimationHolders = {};
- }
- if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
- target._lateAnimationHolders[runtimeAnimation.targetPath] = {
- totalWeight: 0,
- totalAdditiveWeight: 0,
- animations: [],
- additiveAnimations: [],
- originalValue: originalValue,
- };
- }
- if (runtimeAnimation.isAdditive) {
- target._lateAnimationHolders[runtimeAnimation.targetPath].additiveAnimations.push(runtimeAnimation);
- target._lateAnimationHolders[runtimeAnimation.targetPath].totalAdditiveWeight += runtimeAnimation.weight;
- }
- else {
- target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
- target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
- }
- };
- Scene.prototype._processLateAnimationBindingsForMatrices = function (holder) {
- if (holder.totalWeight === 0 && holder.totalAdditiveWeight === 0) {
- return holder.originalValue;
- }
- let normalizer = 1.0;
- const finalPosition = TmpVectors.Vector3[0];
- const finalScaling = TmpVectors.Vector3[1];
- const finalQuaternion = TmpVectors.Quaternion[0];
- let startIndex = 0;
- const originalAnimation = holder.animations[0];
- const originalValue = holder.originalValue;
- let scale = 1;
- let skipOverride = false;
- if (holder.totalWeight < 1.0) {
- // We need to mix the original value in
- scale = 1.0 - holder.totalWeight;
- originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
- }
- else {
- startIndex = 1;
- // We need to normalize the weights
- normalizer = holder.totalWeight;
- scale = originalAnimation.weight / normalizer;
- if (scale == 1) {
- if (holder.totalAdditiveWeight) {
- skipOverride = true;
- }
- else {
- return originalAnimation.currentValue;
- }
- }
- originalAnimation.currentValue.decompose(finalScaling, finalQuaternion, finalPosition);
- }
- // Add up the override animations
- if (!skipOverride) {
- finalScaling.scaleInPlace(scale);
- finalPosition.scaleInPlace(scale);
- finalQuaternion.scaleInPlace(scale);
- for (let animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
- const runtimeAnimation = holder.animations[animIndex];
- if (runtimeAnimation.weight === 0) {
- continue;
- }
- scale = runtimeAnimation.weight / normalizer;
- const currentPosition = TmpVectors.Vector3[2];
- const currentScaling = TmpVectors.Vector3[3];
- const currentQuaternion = TmpVectors.Quaternion[1];
- runtimeAnimation.currentValue.decompose(currentScaling, currentQuaternion, currentPosition);
- currentScaling.scaleAndAddToRef(scale, finalScaling);
- currentQuaternion.scaleAndAddToRef(Quaternion.Dot(finalQuaternion, currentQuaternion) > 0 ? scale : -scale, finalQuaternion);
- currentPosition.scaleAndAddToRef(scale, finalPosition);
- }
- finalQuaternion.normalize();
- }
- // Add up the additive animations
- for (let animIndex = 0; animIndex < holder.additiveAnimations.length; animIndex++) {
- const runtimeAnimation = holder.additiveAnimations[animIndex];
- if (runtimeAnimation.weight === 0) {
- continue;
- }
- const currentPosition = TmpVectors.Vector3[2];
- const currentScaling = TmpVectors.Vector3[3];
- const currentQuaternion = TmpVectors.Quaternion[1];
- runtimeAnimation.currentValue.decompose(currentScaling, currentQuaternion, currentPosition);
- currentScaling.multiplyToRef(finalScaling, currentScaling);
- Vector3.LerpToRef(finalScaling, currentScaling, runtimeAnimation.weight, finalScaling);
- finalQuaternion.multiplyToRef(currentQuaternion, currentQuaternion);
- Quaternion.SlerpToRef(finalQuaternion, currentQuaternion, runtimeAnimation.weight, finalQuaternion);
- currentPosition.scaleAndAddToRef(runtimeAnimation.weight, finalPosition);
- }
- const workValue = originalAnimation ? originalAnimation._animationState.workValue : TmpVectors.Matrix[0].clone();
- Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, workValue);
- return workValue;
- };
- Scene.prototype._processLateAnimationBindingsForQuaternions = function (holder, refQuaternion) {
- if (holder.totalWeight === 0 && holder.totalAdditiveWeight === 0) {
- return refQuaternion;
- }
- const originalAnimation = holder.animations[0];
- const originalValue = holder.originalValue;
- let cumulativeQuaternion = refQuaternion;
- if (holder.totalWeight === 0 && holder.totalAdditiveWeight > 0) {
- cumulativeQuaternion.copyFrom(originalValue);
- }
- else if (holder.animations.length === 1) {
- Quaternion.SlerpToRef(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight), cumulativeQuaternion);
- if (holder.totalAdditiveWeight === 0) {
- return cumulativeQuaternion;
- }
- }
- else if (holder.animations.length > 1) {
- // Add up the override animations
- let normalizer = 1.0;
- let quaternions;
- let weights;
- if (holder.totalWeight < 1.0) {
- const scale = 1.0 - holder.totalWeight;
- quaternions = [];
- weights = [];
- quaternions.push(originalValue);
- weights.push(scale);
- }
- else {
- if (holder.animations.length === 2) {
- // Slerp as soon as we can
- Quaternion.SlerpToRef(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight, refQuaternion);
- if (holder.totalAdditiveWeight === 0) {
- return refQuaternion;
- }
- }
- quaternions = [];
- weights = [];
- normalizer = holder.totalWeight;
- }
- for (let animIndex = 0; animIndex < holder.animations.length; animIndex++) {
- const runtimeAnimation = holder.animations[animIndex];
- quaternions.push(runtimeAnimation.currentValue);
- weights.push(runtimeAnimation.weight / normalizer);
- }
- // https://gamedev.stackexchange.com/questions/62354/method-for-interpolation-between-3-quaternions
- let cumulativeAmount = 0;
- for (let index = 0; index < quaternions.length;) {
- if (!index) {
- Quaternion.SlerpToRef(quaternions[index], quaternions[index + 1], weights[index + 1] / (weights[index] + weights[index + 1]), refQuaternion);
- cumulativeQuaternion = refQuaternion;
- cumulativeAmount = weights[index] + weights[index + 1];
- index += 2;
- continue;
- }
- cumulativeAmount += weights[index];
- Quaternion.SlerpToRef(cumulativeQuaternion, quaternions[index], weights[index] / cumulativeAmount, cumulativeQuaternion);
- index++;
- }
- }
- // Add up the additive animations
- for (let animIndex = 0; animIndex < holder.additiveAnimations.length; animIndex++) {
- const runtimeAnimation = holder.additiveAnimations[animIndex];
- if (runtimeAnimation.weight === 0) {
- continue;
- }
- cumulativeQuaternion.multiplyToRef(runtimeAnimation.currentValue, TmpVectors.Quaternion[0]);
- Quaternion.SlerpToRef(cumulativeQuaternion, TmpVectors.Quaternion[0], runtimeAnimation.weight, cumulativeQuaternion);
- }
- return cumulativeQuaternion;
- };
- Scene.prototype._processLateAnimationBindings = function () {
- if (!this._registeredForLateAnimationBindings.length) {
- return;
- }
- for (let index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
- const target = this._registeredForLateAnimationBindings.data[index];
- for (const path in target._lateAnimationHolders) {
- const holder = target._lateAnimationHolders[path];
- const originalAnimation = holder.animations[0];
- const originalValue = holder.originalValue;
- if (originalValue === undefined || originalValue === null) {
- continue;
- }
- const matrixDecomposeMode = Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
- let finalValue = target[path];
- if (matrixDecomposeMode) {
- finalValue = this._processLateAnimationBindingsForMatrices(holder);
- }
- else {
- const quaternionMode = originalValue.w !== undefined;
- if (quaternionMode) {
- finalValue = this._processLateAnimationBindingsForQuaternions(holder, finalValue || Quaternion.Identity());
- }
- else {
- let startIndex = 0;
- let normalizer = 1.0;
- const originalAnimationIsLoopRelativeFromCurrent = originalAnimation && originalAnimation._animationState.loopMode === Animation.ANIMATIONLOOPMODE_RELATIVE_FROM_CURRENT;
- if (holder.totalWeight < 1.0) {
- // We need to mix the original value in
- if (originalAnimationIsLoopRelativeFromCurrent) {
- finalValue = originalValue.clone ? originalValue.clone() : originalValue;
- }
- else if (originalAnimation && originalValue.scale) {
- finalValue = originalValue.scale(1.0 - holder.totalWeight);
- }
- else if (originalAnimation) {
- finalValue = originalValue * (1.0 - holder.totalWeight);
- }
- else if (originalValue.clone) {
- finalValue = originalValue.clone();
- }
- else {
- finalValue = originalValue;
- }
- }
- else if (originalAnimation) {
- // We need to normalize the weights
- normalizer = holder.totalWeight;
- const scale = originalAnimation.weight / normalizer;
- if (scale !== 1) {
- if (originalAnimation.currentValue.scale) {
- finalValue = originalAnimation.currentValue.scale(scale);
- }
- else {
- finalValue = originalAnimation.currentValue * scale;
- }
- }
- else {
- finalValue = originalAnimation.currentValue;
- }
- if (originalAnimationIsLoopRelativeFromCurrent) {
- if (finalValue.addToRef) {
- finalValue.addToRef(originalValue, finalValue);
- }
- else {
- finalValue += originalValue;
- }
- }
- startIndex = 1;
- }
- // Add up the override animations
- for (let animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
- const runtimeAnimation = holder.animations[animIndex];
- const scale = runtimeAnimation.weight / normalizer;
- if (!scale) {
- continue;
- }
- else if (runtimeAnimation.currentValue.scaleAndAddToRef) {
- runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
- }
- else {
- finalValue += runtimeAnimation.currentValue * scale;
- }
- }
- // Add up the additive animations
- for (let animIndex = 0; animIndex < holder.additiveAnimations.length; animIndex++) {
- const runtimeAnimation = holder.additiveAnimations[animIndex];
- const scale = runtimeAnimation.weight;
- if (!scale) {
- continue;
- }
- else if (runtimeAnimation.currentValue.scaleAndAddToRef) {
- runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
- }
- else {
- finalValue += runtimeAnimation.currentValue * scale;
- }
- }
- }
- }
- target[path] = finalValue;
- }
- target._lateAnimationHolders = {};
- }
- this._registeredForLateAnimationBindings.reset();
- };
- Bone.prototype.copyAnimationRange = function (source, rangeName, frameOffset, rescaleAsRequired = false, skelDimensionsRatio = null) {
- // all animation may be coming from a library skeleton, so may need to create animation
- if (this.animations.length === 0) {
- this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
- this.animations[0].setKeys([]);
- }
- // get animation info / verify there is such a range from the source bone
- const sourceRange = source.animations[0].getRange(rangeName);
- if (!sourceRange) {
- return false;
- }
- const from = sourceRange.from;
- const to = sourceRange.to;
- const sourceKeys = source.animations[0].getKeys();
- // rescaling prep
- const sourceBoneLength = source.length;
- const sourceParent = source.getParent();
- const parent = this.getParent();
- const parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
- const parentRatio = parentScalingReqd && parent && sourceParent ? parent.length / sourceParent.length : 1;
- const dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);
- const destKeys = this.animations[0].getKeys();
- // loop vars declaration
- let orig;
- let origTranslation;
- let mat;
- for (let key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
- orig = sourceKeys[key];
- if (orig.frame >= from && orig.frame <= to) {
- if (rescaleAsRequired) {
- mat = orig.value.clone();
- // scale based on parent ratio, when bone has parent
- if (parentScalingReqd) {
- origTranslation = mat.getTranslation();
- mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
- // scale based on skeleton dimension ratio when root bone, and value is passed
- }
- else if (dimensionsScalingReqd && skelDimensionsRatio) {
- origTranslation = mat.getTranslation();
- mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));
- // use original when root bone, and no data for skelDimensionsRatio
- }
- else {
- mat = orig.value;
- }
- }
- else {
- mat = orig.value;
- }
- destKeys.push({ frame: orig.frame + frameOffset, value: mat });
- }
- }
- this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
- return true;
- };
- //# sourceMappingURL=animatable.js.map
|