浏览代码

Merge pull request #4110 from kcoley/moveInterpolationToAnimation

Moved interpolation function from runtimeAnimation to animation
David Catuhe 7 年之前
父节点
当前提交
fe0c68c5d2
共有 3 个文件被更改,包括 697 次插入1128 次删除
  1. 35 948
      dist/preview release/typedocValidationBaseline.json
  2. 544 40
      src/Animations/babylon.animation.ts
  3. 118 140
      src/Animations/babylon.runtimeAnimation.ts

文件差异内容过多而无法显示
+ 35 - 948
dist/preview release/typedocValidationBaseline.json


+ 544 - 40
src/Animations/babylon.animation.ts

@@ -1,8 +1,27 @@
 module BABYLON {
+    /**
+     * Represents the range of an animation
+     */
     export class AnimationRange {
-        constructor(public name: string, public from: number, public to: number) {
+        /**
+         * Initializes the range of an animation
+         * @param name The name of the animation range
+         * @param from The starting frame of the animation
+         * @param to The ending frame of the animation
+         */
+        constructor(
+            /**The name of the animation range**/
+            public name: string, 
+            /**The starting frame of the animation */
+            public from: number, 
+            /**The ending frame of the animation*/
+            public to: number) {
         }
 
+        /**
+         * Makes a copy of the animation range
+         * @returns A copy of the animation range
+         */
         public clone(): AnimationRange {
             return new AnimationRange(this.name, this.from, this.to);
         }
@@ -12,37 +31,90 @@
      * Composed of a frame, and an action function
      */
     export class AnimationEvent {
+        /**
+         * Specifies if the animation event is done
+         */
         public isDone: boolean = false;
-        constructor(public frame: number, public action: () => void, public onlyOnce?: boolean) {
+
+        /**
+         * Initializes the animation event
+         * @param frame The frame for which the event is triggered
+         * @param action The event to perform when triggered
+         * @param onlyOnce Specifies if the event should be triggered only once
+         */
+        constructor(
+            /** The frame for which the event is triggered **/
+            public frame: number,
+            /** The event to perform when triggered **/
+            public action: () => void , 
+            /** Specifies if the event should be triggered only once**/
+            public onlyOnce?: boolean ) {
         }
     }
 
+    /**
+     * A cursor which tracks a point on a path
+     */
     export class PathCursor {
+        /**
+         * Stores path cursor callbacks for when an onchange event is triggered
+         */
         private _onchange = new Array<(cursor: PathCursor) => void>();
 
+        /**
+         * The value of the path cursor
+         */
         value: number = 0;
+
+        /**
+         * The animation array of the path cursor
+         */
         animations = new Array<Animation>();
 
+        /**
+         * Initializes the path cursor
+         * @param path The path to track
+         */
         constructor(private path: Path2) {
         }
 
+        /**
+         * Gets the cursor point on the path
+         * @returns A point on the path cursor at the cursor location
+         */
         public getPoint(): Vector3 {
             var point = this.path.getPointAtLengthPosition(this.value);
             return new Vector3(point.x, 0, point.y);
         }
 
+        /**
+         * Moves the cursor ahead by the step amount
+         * @param step The amount to move the cursor forward
+         * @returns This path cursor
+         */
         public moveAhead(step: number = 0.002): PathCursor {
             this.move(step);
 
             return this;
         }
 
+        /**
+         * Moves the cursor behind by the step amount
+         * @param step The amount to move the cursor back
+         * @returns This path cursor
+         */
         public moveBack(step: number = 0.002): PathCursor {
             this.move(-step);
 
             return this;
         }
 
+        /**
+         * Moves the cursor by the step amount
+         * If the step amount is greater than one, an exception is thrown
+         * @param step The amount to move the cursor
+         * @returns This path cursor
+         */
         public move(step: number): PathCursor {
 
             if (Math.abs(step) > 1) {
@@ -56,6 +128,10 @@
             return this;
         }
 
+        /**
+         * Ensures that the value is limited between zero and one
+         * @returns This path cursor
+         */
         private ensureLimits(): PathCursor {
             while (this.value > 1) {
                 this.value -= 1;
@@ -67,13 +143,21 @@
             return this;
         }
 
-        // used by animation engine
+        /**
+         * Runs onchange callbacks on change (used by the animation engine)
+         * @returns This path cursor
+         */
         private raiseOnChange(): PathCursor {
             this._onchange.forEach(f => f(this));
 
             return this;
         }
 
+        /**
+         * Executes a function on change
+         * @param f A path cursor onchange callback
+         * @returns This path cursor
+         */
         public onchange(f: (cursor: PathCursor) => void): PathCursor {
             this._onchange.push(f);
 
@@ -81,17 +165,38 @@
         }
     }
 
+    /**
+     * Defines an interface which represents an animation key frame
+     */
     export interface IAnimationKey {
+        /**
+         * Frame of the key frame
+         */
         frame: number;
+        /**
+         * Value at the specifies key frame
+         */
         value: any;
+        /**
+         * The input tangent for the cubic hermite spline
+         */
         inTangent?: any;
+        /**
+         * The output tangent for the cubic hermite spline
+         */
         outTangent?: any;
+        /**
+         * The animation interpolation type
+         */
         interpolation?: AnimationKeyInterpolation;
     }
 
+    /**
+     * Enum for the animation key frame interpolation type
+     */
     export enum AnimationKeyInterpolation {
         /**
-         * Do not interpolate between keys and use the start key value only. Tangents are ignored.
+         * Do not interpolate between keys and use the start key value only. Tangents are ignored
          */
         STEP = 1
     }
@@ -110,21 +215,45 @@
          */
         public static AllowMatrixDecomposeForInterpolation = true;
 
+        /**
+         * Stores the key frames of the animation
+         */
         private _keys: Array<IAnimationKey>;
+
+        /**
+         * Stores the easing function of the animation
+         */
         private _easingFunction: IEasingFunction;
 
+        /**
+         * @ignore Internal use only
+         */
         public _runtimeAnimations = new Array<RuntimeAnimation>();
 
-        // The set of event that will be linked to this animation
+        /**
+         * The set of event that will be linked to this animation
+         */
         private _events = new Array<AnimationEvent>();
 
+        /**
+         * Stores an array of target property paths
+         */
         public targetPropertyPath: string[];
 
+        /**
+         * Stores the blending speed of the animation
+         */
         public blendingSpeed = 0.01;
 
+        /**
+         * Stores the animation ranges for the animation
+         */
         private _ranges: { [name: string]: Nullable<AnimationRange> } = {};
 
-        static _PrepareAnimation(name: string, targetProperty: string, framePerSecond: number, totalFrame: number,
+        /**
+         * @ignore Internal use
+         */
+        public static _PrepareAnimation(name: string, targetProperty: string, framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction): Nullable<Animation> {
             var dataType = undefined;
 
@@ -159,10 +288,11 @@
         }
 
         /**
-		 * Sets up an animation.
-		 * @param property the property to animate
-		 * @param animationType the animation type to apply
-		 * @param easingFunction the easing function used in the animation
+		 * Sets up an animation
+		 * @param property The property to animate
+		 * @param animationType The animation type to apply
+         * @param framePerSecond The frames per second of the animation
+		 * @param easingFunction The easing function used in the animation
 		 * @returns The created animation
 		 */
         public static CreateAnimation(property: string, animationType: number, framePerSecond: number, easingFunction: EasingFunction): Animation {
@@ -179,15 +309,15 @@
 
         /**
          * Create and start an animation on a node
-         * @param {string} name defines the name of the global animation that will be run on all nodes
-         * @param {BABYLON.Node} node defines the root node where the animation will take place
-         * @param {string} targetProperty defines property to animate
-         * @param {number} framePerSecond defines the number of frame per second yo use
-         * @param {number} totalFrame defines the number of frames in total
-         * @param {any} from defines the initial value
-         * @param {any} to defines the final value
-         * @param {number} loopMode defines which loop mode you want to use (off by default)
-         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param name defines the name of the global animation that will be run on all nodes
+         * @param node defines the root node where the animation will take place
+         * @param targetProperty defines property to animate
+         * @param framePerSecond defines the number of frame per second yo use
+         * @param totalFrame defines the number of frames in total
+         * @param from defines the initial value
+         * @param to defines the final value
+         * @param loopMode defines which loop mode you want to use (off by default)
+         * @param easingFunction defines the easing function to use (linear by default)
          * @param onAnimationEnd defines the callback to call when animation end
          * @returns the animatable created for this animation
          */
@@ -206,16 +336,16 @@
 
         /**
          * Create and start an animation on a node and its descendants
-         * @param {string} name defines the name of the global animation that will be run on all nodes
-         * @param {BABYLON.Node} node defines the root node where the animation will take place
-         * @param {boolean} 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.
-         * @param {string} targetProperty defines property to animate
-         * @param {number} framePerSecond defines the number of frame per second yo use
-         * @param {number} totalFrame defines the number of frames in total
-         * @param {any} from defines the initial value
-         * @param {any} to defines the final value
-         * @param {number} loopMode defines which loop mode you want to use (off by default)
-         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param name defines the name of the global animation that will be run on all nodes
+         * @param node defines the root node where the animation will take place
+         * @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
+         * @param targetProperty defines property to animate
+         * @param framePerSecond defines the number of frame per second to use
+         * @param totalFrame defines the number of frames in total
+         * @param from defines the initial value
+         * @param to defines the final value
+         * @param loopMode defines which loop mode you want to use (off by default)
+         * @param easingFunction defines the easing function to use (linear by default)
          * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
          * @returns the list of animatables created for all nodes
          * @example https://www.babylonjs-playground.com/#MH0VLI
@@ -234,6 +364,20 @@
             return scene.beginDirectHierarchyAnimation(node, directDescendantsOnly, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
         }
 
+        /**
+         * Creates a new animation, merges it with the existing animations and starts it
+         * @param name Name of the animation
+         * @param node Node which contains the scene that begins the animations
+         * @param targetProperty Specifies which property to animate
+         * @param framePerSecond The frames per second of the animation
+         * @param totalFrame The total number of frames
+         * @param from The frame at the beginning of the animation
+         * @param to The frame at the end of the animation
+         * @param loopMode Specifies the loop mode of the animation
+         * @param easingFunction (Optional) The easing function of the animation, which allow custom mathematical formulas for animations
+         * @param onAnimationEnd Callback to run once the animation is complete
+         * @returns Nullable animation
+         */
         public static CreateMergeAndStartAnimation(name: string, node: Node, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {
@@ -250,7 +394,7 @@
         }
 
         /**
-		 * Transition property of the Camera to the target Value.
+		 * Transition property of the Camera to the target Value
 		 * @param property The property to transition
 		 * @param targetValue The target Value of the property
          * @param host The object where the property to animate belongs
@@ -258,10 +402,10 @@
          * @param frameRate Framerate (in frame/s) to use
 		 * @param transition The transition type we want to use
 		 * @param duration The duration of the animation, in milliseconds
-		 * @param onAnimationEnd Call back trigger at the end of the animation.
+		 * @param onAnimationEnd Callback trigger at the end of the animation
+         * @returns Nullable animation
 		 */
         public static TransitionTo(property: string, targetValue: any, host: any, scene: Scene, frameRate: number, transition: Animation, duration: number, onAnimationEnd: Nullable<() => void> = null): Nullable<Animatable> {
-
             if (duration <= 0) {
                 host[property] = targetValue;
                 if (onAnimationEnd) {
@@ -299,6 +443,9 @@
             return this._runtimeAnimations;
         }
 
+        /**
+         * Specifies if any of the runtime animations are currently running
+         */
         public get hasRunningRuntimeAnimations(): boolean {
             for (var runtimeAnimation of this._runtimeAnimations) {
                 if (!runtimeAnimation.isStopped) {
@@ -309,7 +456,28 @@
             return false;
         }
 
-        constructor(public name: string, public targetProperty: string, public framePerSecond: number, public dataType: number, public loopMode?: number, public enableBlending?: boolean) {
+        /**
+         * Initializes the animation
+         * @param name Name of the animation
+         * @param targetProperty Property to animate
+         * @param framePerSecond The frames per second of the animation
+         * @param dataType The data type of the animation
+         * @param loopMode The loop mode of the animation
+         * @param enableBlendings Specifies if blending should be enabled
+         */
+        constructor(
+            /**Name of the animation */
+            public name: string, 
+            /**Property to animate */
+            public targetProperty: string, 
+            /**The frames per second of the animation */
+            public framePerSecond: number, 
+            /**The data type of the animation */
+            public dataType: number,
+            /**The loop mode of the animation */ 
+            public loopMode?: number, 
+            /**Specifies if blending should be enabled */
+            public enableBlending?: boolean) {
             this.targetPropertyPath = targetProperty.split(".");
             this.dataType = dataType;
             this.loopMode = loopMode === undefined ? Animation.ANIMATIONLOOPMODE_CYCLE : loopMode;
@@ -317,7 +485,9 @@
 
         // Methods
         /**
-         * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
+         * Converts the animation to a string
+         * @param fullDetails support for multiple levels of logging within scene loading
+         * @returns String form of the animation
          */
         public toString(fullDetails?: boolean): string {
             var ret = "Name: " + this.name + ", property: " + this.targetProperty;
@@ -340,7 +510,8 @@
         }
 
         /**
-         * Add an event to this animation.
+         * Add an event to this animation
+         * @param event Event to add
          */
         public addEvent(event: AnimationEvent): void {
             this._events.push(event);
@@ -348,7 +519,7 @@
 
         /**
          * Remove all events found at the given frame
-         * @param frame
+         * @param frame The frame to remove events from
          */
         public removeEvents(frame: number): void {
             for (var index = 0; index < this._events.length; index++) {
@@ -359,10 +530,20 @@
             }
         }
 
+        /**
+         * Retrieves all the events from the animation
+         * @returns Events from the animation
+         */
         public getEvents(): AnimationEvent[] {
             return this._events;
         }
 
+        /**
+         * Creates an animation range
+         * @param name Name of the animation range
+         * @param from Starting frame of the animation range
+         * @param to Ending frame of the animation
+         */
         public createRange(name: string, from: number, to: number): void {
             // check name not already in use; could happen for bones after serialized
             if (!this._ranges[name]) {
@@ -370,6 +551,11 @@
             }
         }
 
+        /**
+         * Deletes an animation range by name
+         * @param name Name of the animation range to delete
+         * @param deleteFrames Specifies if the key frames for the range should also be deleted (true) or not (false)
+         */
         public deleteRange(name: string, deleteFrames = true): void {
             let range = this._ranges[name];
             if (!range) {
@@ -391,15 +577,27 @@
 
         }
 
+        /**
+         * Gets the animation range by name, or null if not defined
+         * @param name Name of the animation range
+         * @returns Nullable animation range
+         */
         public getRange(name: string): Nullable<AnimationRange> {
             return this._ranges[name];
         }
 
-
+        /**
+         * Gets the key frames from the animation
+         * @returns The key frames of the animation
+         */
         public getKeys(): Array<IAnimationKey> {
             return this._keys;
         }
 
+        /**
+         * Gets the highest frame rate of the animation
+         * @returns Highest frame rate of the animation
+         */
         public getHighestFrame(): number {
             var ret = 0;
 
@@ -411,55 +609,279 @@
             return ret;
         }
 
-        public getEasingFunction() {
+        /**
+         * Gets the easing function of the animation
+         * @returns Easing function of the animation
+         */
+        public getEasingFunction(): IEasingFunction {
             return this._easingFunction;
         }
 
-        public setEasingFunction(easingFunction: EasingFunction) {
+        /**
+         * Sets the easing function of the animation
+         * @param easingFunction A custom mathematical formula for animation
+         */
+        public setEasingFunction(easingFunction: EasingFunction): void {
             this._easingFunction = easingFunction;
         }
 
+        /**
+         * Interpolates a scalar linearly
+         * @param startValue Start value of the animation curve
+         * @param endValue End value of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated scalar value
+         */
         public floatInterpolateFunction(startValue: number, endValue: number, gradient: number): number {
             return Scalar.Lerp(startValue, endValue, gradient);
         }
 
+        /**
+         * Interpolates a scalar cubically
+         * @param startValue Start value of the animation curve
+         * @param outTangent End tangent of the animation
+         * @param endValue End value of the animation curve
+         * @param inTangent Start tangent of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated scalar value
+         */
         public floatInterpolateFunctionWithTangents(startValue: number, outTangent: number, endValue: number, inTangent: number, gradient: number): number {
             return Scalar.Hermite(startValue, outTangent, endValue, inTangent, gradient);
         }
 
+        /**
+         * Interpolates a quaternion using a spherical linear interpolation
+         * @param startValue Start value of the animation curve
+         * @param endValue End value of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated quaternion value
+         */
         public quaternionInterpolateFunction(startValue: Quaternion, endValue: Quaternion, gradient: number): Quaternion {
             return Quaternion.Slerp(startValue, endValue, gradient);
         }
 
+        /**
+         * Interpolates a quaternion cubically
+         * @param startValue Start value of the animation curve
+         * @param outTangent End tangent of the animation curve
+         * @param endValue End value of the animation curve
+         * @param inTangent Start tangent of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated quaternion value
+         */
         public quaternionInterpolateFunctionWithTangents(startValue: Quaternion, outTangent: Quaternion, endValue: Quaternion, inTangent: Quaternion, gradient: number): Quaternion {
             return Quaternion.Hermite(startValue, outTangent, endValue, inTangent, gradient).normalize();
         }
 
+        /**
+         * Interpolates a Vector3 linearl
+         * @param startValue Start value of the animation curve
+         * @param endValue End value of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated scalar value
+         */
         public vector3InterpolateFunction(startValue: Vector3, endValue: Vector3, gradient: number): Vector3 {
             return Vector3.Lerp(startValue, endValue, gradient);
         }
 
+        /**
+         * Interpolates a Vector3 cubically
+         * @param startValue Start value of the animation curve
+         * @param outTangent End tangent of the animation
+         * @param endValue End value of the animation curve
+         * @param inTangent Start tangent of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns InterpolatedVector3 value
+         */
         public vector3InterpolateFunctionWithTangents(startValue: Vector3, outTangent: Vector3, endValue: Vector3, inTangent: Vector3, gradient: number): Vector3 {
             return Vector3.Hermite(startValue, outTangent, endValue, inTangent, gradient);
         }
 
+        /**
+         * Interpolates a Vector2 linearly
+         * @param startValue Start value of the animation curve
+         * @param endValue End value of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated Vector2 value
+         */
         public vector2InterpolateFunction(startValue: Vector2, endValue: Vector2, gradient: number): Vector2 {
             return Vector2.Lerp(startValue, endValue, gradient);
         }
 
+        /**
+         * Interpolates a Vector2 cubically
+         * @param startValue Start value of the animation curve
+         * @param outTangent End tangent of the animation
+         * @param endValue End value of the animation curve
+         * @param inTangent Start tangent of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated Vector2 value
+         */
         public vector2InterpolateFunctionWithTangents(startValue: Vector2, outTangent: Vector2, endValue: Vector2, inTangent: Vector2, gradient: number): Vector2 {
             return Vector2.Hermite(startValue, outTangent, endValue, inTangent, gradient);
         }
 
+        /**
+         * Interpolates a size linearly
+         * @param startValue Start value of the animation curve
+         * @param endValue End value of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated Size value
+         */
         public sizeInterpolateFunction(startValue: Size, endValue: Size, gradient: number): Size {
             return Size.Lerp(startValue, endValue, gradient);
         }
 
+        /**
+         * Interpolates a Color3 linearly
+         * @param startValue Start value of the animation curve
+         * @param endValue End value of the animation curve
+         * @param gradient Scalar amount to interpolate
+         * @returns Interpolated Color3 value
+         */
         public color3InterpolateFunction(startValue: Color3, endValue: Color3, gradient: number): Color3 {
             return Color3.Lerp(startValue, endValue, gradient);
         }
 
         /**
+         * @ignore Internal use only
+         */
+        public _getKeyValue(value: any): any {
+            if (typeof value === "function") {
+                return value();
+            }
+
+            return value;
+        } 
+
+        /**
+         * @ignore Internal use only
+         */
+        public _interpolate(currentFrame: number, repeatCount: number, workValue?: any, loopMode?: number, offsetValue?: any, highLimitValue?: any): any {
+            if (loopMode === Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
+                return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
+            }
+
+            let keys = this.getKeys();
+
+            // Try to get a hash to find the right key
+            var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
+
+            if (keys[startKeyIndex].frame >= currentFrame) {
+                while (startKeyIndex - 1 >= 0 && keys[startKeyIndex].frame >= currentFrame) {
+                    startKeyIndex--;
+                }
+            }
+
+            for (var key = startKeyIndex; key < keys.length; key++) {
+                var endKey = keys[key + 1];
+
+                if (endKey.frame >= currentFrame) {
+
+                    var startKey = keys[key];
+                    var startValue = this._getKeyValue(startKey.value);
+                    if (startKey.interpolation === AnimationKeyInterpolation.STEP) {
+                        return startValue;
+                    }
+
+                    var endValue = this._getKeyValue(endKey.value);
+
+                    var useTangent = startKey.outTangent !== undefined && endKey.inTangent !== undefined;
+                    var frameDelta = endKey.frame - startKey.frame;
+
+                    // gradient : percent of currentFrame between the frame inf and the frame sup
+                    var gradient = (currentFrame - startKey.frame) / frameDelta;
+
+                    // check for easingFunction and correction of gradient
+                    let easingFunction = this.getEasingFunction();
+                    if (easingFunction != null) {
+                        gradient = easingFunction.ease(gradient);
+                    }
+
+                    switch (this.dataType) {
+                        // Float
+                        case Animation.ANIMATIONTYPE_FLOAT:
+                            var floatValue = useTangent ? this.floatInterpolateFunctionWithTangents(startValue, startKey.outTangent * frameDelta, endValue, endKey.inTangent * frameDelta, gradient) : this.floatInterpolateFunction(startValue, endValue, gradient);
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return floatValue;
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return offsetValue * repeatCount + floatValue;
+                            }
+                            break;
+                        // Quaternion
+                        case Animation.ANIMATIONTYPE_QUATERNION:
+                            var quatValue = useTangent ? this.quaternionInterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this.quaternionInterpolateFunction(startValue, endValue, gradient);
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return quatValue;
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return quatValue.addInPlace(offsetValue.scale(repeatCount));
+                            }
+
+                            return quatValue;
+                        // Vector3
+                        case Animation.ANIMATIONTYPE_VECTOR3:
+                            var vec3Value = useTangent ? this.vector3InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this.vector3InterpolateFunction(startValue, endValue, gradient);
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return vec3Value;
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return vec3Value.add(offsetValue.scale(repeatCount));
+                            }
+                        // Vector2
+                        case Animation.ANIMATIONTYPE_VECTOR2:
+                            var vec2Value = useTangent ? this.vector2InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this.vector2InterpolateFunction(startValue, endValue, gradient);
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return vec2Value;
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return vec2Value.add(offsetValue.scale(repeatCount));
+                            }
+                        // Size
+                        case Animation.ANIMATIONTYPE_SIZE:
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient);
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return this.sizeInterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                            }
+                        // Color3
+                        case Animation.ANIMATIONTYPE_COLOR3:
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    return this.color3InterpolateFunction(startValue, endValue, gradient);
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return this.color3InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
+                            }
+                        // Matrix
+                        case Animation.ANIMATIONTYPE_MATRIX:
+                            switch (loopMode) {
+                                case Animation.ANIMATIONLOOPMODE_CYCLE:
+                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
+                                    if (Animation.AllowMatricesInterpolation) {
+                                        workValue = this.matrixInterpolateFunction(startValue, endValue, gradient, workValue);
+                                        return workValue;
+                                    }
+                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
+                                    return startValue;
+                            }
+                        default:
+                            break;
+                    }
+                    break;
+                }
+            }
+            return this._getKeyValue(keys[keys.length - 1].value);
+        }
+
+        /**
          * Defines the function to use to interpolate matrices
          * @param startValue defines the start matrix
          * @param endValue defines the end matrix
@@ -483,6 +905,10 @@
             return Matrix.Lerp(startValue, endValue, gradient);
         }
 
+        /**
+         * Makes a copy of the animation
+         * @returns Cloned animation
+         */
         public clone(): Animation {
             var clone = new Animation(this.name, this.targetPropertyPath.join("."), this.framePerSecond, this.dataType, this.loopMode);
 
@@ -507,10 +933,18 @@
             return clone;
         }
 
+        /**
+         * Sets the key frames of the animation
+         * @param values The animation key frames to set
+         */
         public setKeys(values: Array<IAnimationKey>): void {
             this._keys = values.slice(0);
         }
 
+        /**
+         * Serializes the animation to an object
+         * @returns Serialized object
+         */
         public serialize(): any {
             var serializationObject: any = {};
 
@@ -564,57 +998,122 @@
         }
 
         // Statics
+        /**
+         * Float animation type
+         */
         private static _ANIMATIONTYPE_FLOAT = 0;
+        /**
+         * Vector3 animation type
+         */
         private static _ANIMATIONTYPE_VECTOR3 = 1;
+        /**
+         * Quaternion animation type
+         */
         private static _ANIMATIONTYPE_QUATERNION = 2;
+        /**
+         * Matrix animation type
+         */
         private static _ANIMATIONTYPE_MATRIX = 3;
+        /**
+         * Color3 animation type
+         */
         private static _ANIMATIONTYPE_COLOR3 = 4;
+        /**
+         * Vector2 animation type
+         */
         private static _ANIMATIONTYPE_VECTOR2 = 5;
+        /**
+         * Size animation type
+         */
         private static _ANIMATIONTYPE_SIZE = 6;
+        /**
+         * Relative Loop Mode
+         */
         private static _ANIMATIONLOOPMODE_RELATIVE = 0;
+        /**
+         * Cycle Loop Mode
+         */
         private static _ANIMATIONLOOPMODE_CYCLE = 1;
+        /**
+         * Constant Loop Mode
+         */
         private static _ANIMATIONLOOPMODE_CONSTANT = 2;
 
+        /**
+         * Get the float animation type
+         */
         public static get ANIMATIONTYPE_FLOAT(): number {
             return Animation._ANIMATIONTYPE_FLOAT;
         }
 
+        /**
+         * Get the Vector3 animation type
+         */
         public static get ANIMATIONTYPE_VECTOR3(): number {
             return Animation._ANIMATIONTYPE_VECTOR3;
         }
 
+        /**
+         * Get the Vectpr2 animation type
+         */
         public static get ANIMATIONTYPE_VECTOR2(): number {
             return Animation._ANIMATIONTYPE_VECTOR2;
         }
 
+        /**
+         * Get the Size animation type
+         */
         public static get ANIMATIONTYPE_SIZE(): number {
             return Animation._ANIMATIONTYPE_SIZE;
         }
 
+        /**
+         * Get the Quaternion animation type
+         */
         public static get ANIMATIONTYPE_QUATERNION(): number {
             return Animation._ANIMATIONTYPE_QUATERNION;
         }
 
+        /**
+         * Get the Matrix animation type
+         */
         public static get ANIMATIONTYPE_MATRIX(): number {
             return Animation._ANIMATIONTYPE_MATRIX;
         }
 
+        /**
+         * Get the Color3 animation type
+         */
         public static get ANIMATIONTYPE_COLOR3(): number {
             return Animation._ANIMATIONTYPE_COLOR3;
         }
 
+        /**
+         * Get the Relative Loop Mode
+         */
         public static get ANIMATIONLOOPMODE_RELATIVE(): number {
             return Animation._ANIMATIONLOOPMODE_RELATIVE;
         }
 
+        /**
+         * Get the Cycle Loop Mode
+         */
         public static get ANIMATIONLOOPMODE_CYCLE(): number {
             return Animation._ANIMATIONLOOPMODE_CYCLE;
         }
 
+        /**
+         * Get the Constant Loop Mode
+         */
         public static get ANIMATIONLOOPMODE_CONSTANT(): number {
             return Animation._ANIMATIONLOOPMODE_CONSTANT;
         }
 
+        /**
+         * Parses an animation object and creates an animation
+         * @param parsedAnimation Parsed animation object
+         * @returns Animation object
+         */
         public static Parse(parsedAnimation: any): Animation {
             var animation = new Animation(parsedAnimation.name, parsedAnimation.property, parsedAnimation.framePerSecond, parsedAnimation.dataType, parsedAnimation.loopBehavior);
 
@@ -698,7 +1197,12 @@
             return animation;
         }
 
-        public static AppendSerializedAnimations(source: IAnimatable, destination: any): any {
+        /**
+         * Appends the serialized animations from the source animations
+         * @param source Source containing the animations
+         * @param destination Target to store the animations
+         */
+        public static AppendSerializedAnimations(source: IAnimatable, destination: any): void {
             if (source.animations) {
                 destination.animations = [];
                 for (var animationIndex = 0; animationIndex < source.animations.length; animationIndex++) {

+ 118 - 140
src/Animations/babylon.runtimeAnimation.ts

@@ -1,28 +1,104 @@
 module BABYLON {
 
+    /**
+     * Defines a runtime animation
+     */
     export class RuntimeAnimation {
+        /**
+         * The current frame of the runtime animation
+         */
         private _currentFrame: number = 0;
+
+        /**
+         * The animation used by the runtime animation
+         */
         private _animation: Animation;
+        
+        /**
+         * The target of the runtime animation
+         */
         private _target: any;
-        private _host: Animatable;
 
+        /**
+         * The initiating animatable
+         */
+        private _host: Animatable;
+        
+        /**
+         * The original value of the runtime animation
+         */
         private _originalValue: any;
+        
+        /**
+         * The original blend value of the runtime animation
+         */
         private _originalBlendValue: any;
+        
+        /**
+         * The offsets cache of the runtime animation
+         */
         private _offsetsCache: {[key: string]: any} = {};
+        
+        /**
+         * The high limits cache of the runtime animation
+         */
         private _highLimitsCache: {[key: string]: any} = {};
+        
+        /**
+         * Specifies if the runtime animation has been stopped
+         */
         private _stopped = false;
+        
+        /**
+         * The blending factor of the runtime animation
+         */
         private _blendingFactor = 0;
+        
+        /**
+         * The BabylonJS scene
+         */
         private _scene: Scene;
 
+        /**
+         * The current value of the runtime animation
+         */
         private _currentValue: any;
+        
         /** @ignore */
         public _workValue: any;
+        
+        /**
+         * The active target of the runtime animation
+         */
         private _activeTarget: any;
+        
+        /**
+         * The target path of the runtime animation
+         */
         private _targetPath: string = "";
+        
+        /**
+         * The weight of the runtime animation
+         */
         private _weight = 1.0;
 
         /**
-         * Gets the current frame
+         * The ratio offset of the runtime animation
+         */
+        private _ratioOffset = 0;
+
+        /**
+         * The previous delay of the runtime animation
+         */
+        private _previousDelay: number = 0;
+        
+        /**
+         * The previous ratio of the runtime animation
+         */
+        private _previousRatio: number = 0;
+
+        /**
+         * Gets the current frame of the runtime animation
          */
         public get currentFrame(): number {
             return this._currentFrame;
@@ -50,7 +126,7 @@
         }
 
         /**
-         * Gets the path where to store the animated value in the target
+         * Gets the target path of the runtime animation
          */
         public get targetPath(): string {
             return this._targetPath;
@@ -66,7 +142,7 @@
         /**
          * Create a new RuntimeAnimation object
          * @param target defines the target of the animation
-         * @param animation defines the source {BABYLON.Animation} object
+         * @param animation defines the source animation object
          * @param scene defines the hosting scene
          * @param host defines the initiating Animatable
          */
@@ -79,10 +155,16 @@
             animation._runtimeAnimations.push(this);
         }
 
+        /**
+         * Gets the animation from the runtime animation
+         */
         public get animation(): Animation {
             return this._animation;
         }
 
+        /**
+         * Resets the runtime animation to the beginning
+         */
         public reset(): void {
             this._offsetsCache = {};
             this._highLimitsCache = {};
@@ -91,10 +173,17 @@
             this._originalValue = null;
         }
 
+        /**
+         * Specifies if the runtime animation is stopped
+         * @returns Boolean specifying if the runtime animation is stopped
+         */
         public isStopped(): boolean {
             return this._stopped;
         }        
 
+        /**
+         * Disposes of the runtime animation
+         */
         public dispose(): void {
             let index = this._animation.runtimeAnimations.indexOf(this);
 
@@ -102,139 +191,19 @@
                 this._animation.runtimeAnimations.splice(index, 1);
             }
         }
-
-        private _getKeyValue(value: any): any {
-            if (typeof value === "function") {
-                return value();
-            }
-
-            return value;
-        }      
         
-        private _interpolate(currentFrame: number, repeatCount: number, loopMode?: number, offsetValue?: any, highLimitValue?: any) {
-            if (loopMode === Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
-                return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
-            }
-
+        /**
+         * Interpolates the animation from the current frame
+         * @param currentFrame The frame to interpolate the animation to
+         * @param repeatCount The number of times that the animation should loop
+         * @param loopMode The type of looping mode to use
+         * @param offsetValue Animation offset value
+         * @param highLimitValue The high limit value
+         * @returns The interpolated value
+         */
+        private _interpolate(currentFrame: number, repeatCount: number, loopMode?: number, offsetValue?: any, highLimitValue?: any): any {
             this._currentFrame = currentFrame;
-
-            let keys = this._animation.getKeys();
-
-            // Try to get a hash to find the right key
-            var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
-
-            if (keys[startKeyIndex].frame >= currentFrame) {
-                while (startKeyIndex - 1 >= 0 && keys[startKeyIndex].frame >= currentFrame) {
-                    startKeyIndex--;
-                }
-            }
-
-            for (var key = startKeyIndex; key < keys.length; key++) {
-                var endKey = keys[key + 1];
-
-                if (endKey.frame >= currentFrame) {
-
-                    var startKey = keys[key];
-                    var startValue = this._getKeyValue(startKey.value);
-                    if (startKey.interpolation === AnimationKeyInterpolation.STEP) {
-                        return startValue;
-                    }
-
-                    var endValue = this._getKeyValue(endKey.value);
-
-                    var useTangent = startKey.outTangent !== undefined && endKey.inTangent !== undefined;
-                    var frameDelta = endKey.frame - startKey.frame;
-
-                    // gradient : percent of currentFrame between the frame inf and the frame sup
-                    var gradient = (currentFrame - startKey.frame) / frameDelta;
-
-                    // check for easingFunction and correction of gradient
-                    let easingFunction = this._animation.getEasingFunction();
-                    if (easingFunction != null) {
-                        gradient = easingFunction.ease(gradient);
-                    }
-
-                    switch (this._animation.dataType) {
-                        // Float
-                        case Animation.ANIMATIONTYPE_FLOAT:
-                            var floatValue = useTangent ? this._animation.floatInterpolateFunctionWithTangents(startValue, startKey.outTangent * frameDelta, endValue, endKey.inTangent * frameDelta, gradient) : this._animation.floatInterpolateFunction(startValue, endValue, gradient);
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    return floatValue;
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return offsetValue * repeatCount + floatValue;
-                            }
-                            break;
-                        // Quaternion
-                        case Animation.ANIMATIONTYPE_QUATERNION:
-                            var quatValue = useTangent ? this._animation.quaternionInterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this._animation.quaternionInterpolateFunction(startValue, endValue, gradient);
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    return quatValue;
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return quatValue.addInPlace(offsetValue.scale(repeatCount));
-                            }
-
-                            return quatValue;
-                        // Vector3
-                        case Animation.ANIMATIONTYPE_VECTOR3:
-                            var vec3Value = useTangent ? this._animation.vector3InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this._animation.vector3InterpolateFunction(startValue, endValue, gradient);
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    return vec3Value;
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return vec3Value.add(offsetValue.scale(repeatCount));
-                            }
-                        // Vector2
-                        case Animation.ANIMATIONTYPE_VECTOR2:
-                            var vec2Value = useTangent ? this._animation.vector2InterpolateFunctionWithTangents(startValue, startKey.outTangent.scale(frameDelta), endValue, endKey.inTangent.scale(frameDelta), gradient) : this._animation.vector2InterpolateFunction(startValue, endValue, gradient);
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    return vec2Value;
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return vec2Value.add(offsetValue.scale(repeatCount));
-                            }
-                        // Size
-                        case Animation.ANIMATIONTYPE_SIZE:
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    return this._animation.sizeInterpolateFunction(startValue, endValue, gradient);
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return this._animation.sizeInterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
-                            }
-                        // Color3
-                        case Animation.ANIMATIONTYPE_COLOR3:
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    return this._animation.color3InterpolateFunction(startValue, endValue, gradient);
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return this._animation.color3InterpolateFunction(startValue, endValue, gradient).add(offsetValue.scale(repeatCount));
-                            }
-                        // Matrix
-                        case Animation.ANIMATIONTYPE_MATRIX:
-                            switch (loopMode) {
-                                case Animation.ANIMATIONLOOPMODE_CYCLE:
-                                case Animation.ANIMATIONLOOPMODE_CONSTANT:
-                                    if (Animation.AllowMatricesInterpolation) {
-                                        this._workValue = this._animation.matrixInterpolateFunction(startValue, endValue, gradient, this._workValue);
-                                        return this._workValue;
-                                    }
-                                case Animation.ANIMATIONLOOPMODE_RELATIVE:
-                                    return startValue;
-                            }
-                        default:
-                            break;
-                    }
-                    break;
-                }
-            }
-            return this._getKeyValue(keys[keys.length - 1].value);
+            return this._animation._interpolate(currentFrame, repeatCount, this._workValue, loopMode, offsetValue, highLimitValue);
         }
 
         /**
@@ -253,6 +222,12 @@
             }
         }
 
+        /**
+         * Sets the value of the runtime animation
+         * @param target The target property of the runtime animation
+         * @param currentValue The current value to use for the runtime animation
+         * @param weight The weight to use for the runtime animation (Defaults to 1.0)
+         */
         private _setValue(target: any, currentValue: any, weight = 1.0): void {
             // Set value
             var path: any;
@@ -355,6 +330,10 @@
             }
         }
 
+        /**
+         * Gets the loop pmode of the runtime animation
+         * @returns Loop Mode
+         */
         private _getCorrectLoopMode(): number | undefined {
             if ( this._target && this._target.animationPropertiesOverride) {
                 return this._target.animationPropertiesOverride.loopMode;
@@ -381,16 +360,15 @@
             this.setValue(currentValue, -1);
         }
 
+        /**
+         * @ignore Internal use only
+         */
         public _prepareForSpeedRatioChange(newSpeedRatio: number): void {
             let newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
 
             this._ratioOffset = this._previousRatio - newRatio;
         }
 
-        private _ratioOffset = 0;
-        private _previousDelay: number = 0;
-        private _previousRatio: number = 0;
-
         /**
          * Execute the current animation
          * @param delay defines the delay to add to the current frame
@@ -445,7 +423,7 @@
 
             if (((to > from && ratio > range) || (from > to && ratio < range)) && !loop) { // If we are out of range and not looping get back to caller
                 returnValue = false;
-                highLimitValue = this._getKeyValue(keys[keys.length - 1].value);
+                highLimitValue = this._animation._getKeyValue(keys[keys.length - 1].value);
             } else {
                 // Get max value if required