Browse Source

Merge branch 'master' into bloomRefactor

Trevor Baron 7 năm trước cách đây
mục cha
commit
5c8a978076
50 tập tin đã thay đổi với 976367 bổ sung43295 xóa
  1. 13681 13529
      Playground/babylon.d.txt
  2. 930590 0
      Playground/scenes/dummy.babylon
  3. 14381 14228
      dist/preview release/babylon.d.ts
  4. 52 52
      dist/preview release/babylon.js
  5. 440 102
      dist/preview release/babylon.max.js
  6. 53 53
      dist/preview release/babylon.worker.js
  7. 14187 14034
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  8. 54 54
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  9. 440 102
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  10. 440 102
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  11. 440 102
      dist/preview release/es6.js
  12. 3 3
      dist/preview release/gui/babylon.gui.min.js
  13. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  15. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  16. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  17. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  18. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  19. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  20. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  21. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  22. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  23. 3 3
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  24. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  25. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  26. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  27. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  28. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  29. 2 119
      dist/preview release/typedocValidationBaseline.json
  30. 68 67
      dist/preview release/viewer/babylon.viewer.js
  31. 440 102
      dist/preview release/viewer/babylon.viewer.max.js
  32. 15 11
      dist/preview release/what's new.md
  33. 22 16
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  34. 8 7
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  35. 74 63
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  36. 3 3
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  37. 9 6
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  38. 24 2
      src/Animations/babylon.animatable.ts
  39. 111 22
      src/Animations/babylon.runtimeAnimation.ts
  40. 3 3
      src/Engine/babylon.engine.ts
  41. 15 15
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  42. 7 0
      src/Materials/PBR/babylon.pbrMaterial.ts
  43. 19 5
      src/Materials/babylon.material.ts
  44. 1 1
      src/Materials/babylon.materialHelper.ts
  45. 221 69
      src/Math/babylon.math.ts
  46. 15 0
      src/Mesh/babylon.subMesh.ts
  47. 386 379
      src/Shaders/pbr.fragment.fx
  48. 6 0
      src/Tools/babylon.tools.ts
  49. 122 15
      src/babylon.scene.ts
  50. 6 0
      tests/validation/validation.js

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 13681 - 13529
Playground/babylon.d.txt


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 930590 - 0
Playground/scenes/dummy.babylon


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 14381 - 14228
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 52 - 52
dist/preview release/babylon.js


+ 440 - 102
dist/preview release/babylon.max.js

@@ -1572,7 +1572,7 @@ var BABYLON;
         /**
          * Multiplies in place each rgb value by scale
          * @param scale defines the scaling factor
-         * @returns the updated Color3.
+         * @returns the updated Color3
          */
         Color3.prototype.scale = function (scale) {
             return new Color3(this.r * scale, this.g * scale, this.b * scale);
@@ -1581,7 +1581,7 @@ var BABYLON;
          * Multiplies the rgb values by scale and stores the result into "result"
          * @param scale defines the scaling factor
          * @param result defines the Color3 object where to store the result
-         * @returns the unmodified current Color3.
+         * @returns the unmodified current Color3
          */
         Color3.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1590,6 +1590,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color3 values by a factor and add the result to a given Color3
+         * @param scale defines the scale factor
+         * @param result defines color to store the result into
+         * @returns the unmodified current Color3
+         */
+        Color3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -1957,7 +1969,7 @@ var BABYLON;
          * Multiplies the current Color4 values by scale and stores the result in "result"
          * @param scale defines the scaling factor to apply
          * @param result defines the Color4 object where to store the result
-         * @returns the current Color4.
+         * @returns the current unmodified Color4
          */
         Color4.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1967,6 +1979,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color4 values by a factor and add the result to a given Color4
+         * @param scale defines the scale factor
+         * @param result defines the Color4 object where to store the result
+         * @returns the unmodified current Color4
+         */
+        Color4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            result.a += this.a * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -2410,7 +2435,31 @@ var BABYLON;
          * Returns a new Vector2 scaled by "scale" from the current Vector2.
          */
         Vector2.prototype.scale = function (scale) {
-            return new Vector2(this.x * scale, this.y * scale);
+            var result = new Vector2(0, 0);
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Vector2 values by a factor to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            return this;
+        };
+        /**
+         * Scale the current Vector2 values by a factor and add the result to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            return this;
         };
         /**
          * Boolean : True if the passed vector coordinates strictly equal the current Vector2 ones.
@@ -2850,6 +2899,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector3 values by a factor and add the result to a given Vector3
+         * @param scale defines the scale factor
+         * @param result defines the Vector3 object where to store the result
+         * @returns the unmodified current Vector3
+         */
+        Vector3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            return this;
+        };
+        /**
          * Returns true if the current Vector3 and the passed vector coordinates are strictly equal
          * @param otherVector defines the second operand
          * @returns true if both vectors are equals
@@ -3794,6 +3855,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector4 values by a factor and add the result to a given Vector4
+         * @param scale defines the scale factor
+         * @param result defines the Vector4 object where to store the result
+         * @returns the unmodified current Vector4
+         */
+        Vector4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Boolean : True if the current Vector4 coordinates are stricly equal to the passed ones.
          */
         Vector4.prototype.equals = function (otherVector) {
@@ -4320,6 +4394,32 @@ var BABYLON;
             return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
         };
         /**
+         * Scale the current Quaternion values by a factor to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            result.z = this.z * scale;
+            result.w = this.w * scale;
+            return this;
+        };
+        /**
+         * Scale the current Quaternion values by a factor and add the result to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Returns a new Quaternion set as the quaternion mulplication result of the current one with the passed one "q1".
          */
         Quaternion.prototype.multiply = function (q1) {
@@ -5066,6 +5166,99 @@ var BABYLON;
             return true;
         };
         /**
+         * Returns the index-th row of the current matrix as a new Vector4.
+         */
+        Matrix.prototype.getRow = function (index) {
+            if (index < 0 || index > 3) {
+                return null;
+            }
+            var i = index * 4;
+            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed Vector4 values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRow = function (index, row) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = row.x;
+            this.m[i + 1] = row.y;
+            this.m[i + 2] = row.z;
+            this.m[i + 3] = row.w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns a new Matrix.
+         */
+        Matrix.prototype.transpose = function () {
+            return Matrix.Transpose(this);
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns the current matrix.
+         */
+        Matrix.prototype.transposeToRef = function (result) {
+            Matrix.TransposeToRef(this, result);
+            return this;
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute a new Matrix set with the current Matrix values multiplied by scale (float)
+         * @param scale defines the scale factor
+         * @returns a new Matrix
+         */
+        Matrix.prototype.scale = function (scale) {
+            var result = new Matrix();
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Matrix values by a factor to a given result Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] = this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
+         * Scale the current Matrix values by a factor and add the result to a given Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleAndAddToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] += this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
          * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).
          * @param ref matrix to store the result
          */
@@ -5150,63 +5343,6 @@ var BABYLON;
             result.m[15] = initialM44;
             result._markAsUpdated();
         };
-        /**
-         * Returns the index-th row of the current matrix as a new Vector4.
-         */
-        Matrix.prototype.getRow = function (index) {
-            if (index < 0 || index > 3) {
-                return null;
-            }
-            var i = index * 4;
-            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed Vector4 values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRow = function (index, row) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = row.x;
-            this.m[i + 1] = row.y;
-            this.m[i + 2] = row.z;
-            this.m[i + 3] = row.w;
-            this._markAsUpdated();
-            return this;
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns a new Matrix.
-         */
-        Matrix.prototype.transpose = function () {
-            return Matrix.Transpose(this);
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns the current matrix.
-         */
-        Matrix.prototype.transposeToRef = function (result) {
-            Matrix.TransposeToRef(this, result);
-            return this;
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed 4 x float values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = x;
-            this.m[i + 1] = y;
-            this.m[i + 2] = z;
-            this.m[i + 3] = w;
-            this._markAsUpdated();
-            return this;
-        };
         Object.defineProperty(Matrix, "IdentityReadOnly", {
             /**
              * Static identity matrix to be used as readonly matrix
@@ -12499,7 +12635,7 @@ var BABYLON;
             this.applyStates();
             this._drawCalls.addCount(1, false);
             // Render
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
@@ -12513,7 +12649,7 @@ var BABYLON;
             // Apply states
             this.applyStates();
             this._drawCalls.addCount(1, false);
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             if (instancesCount) {
                 this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             }
@@ -12521,7 +12657,7 @@ var BABYLON;
                 this._gl.drawArrays(drawMode, verticesStart, verticesCount);
             }
         };
-        Engine.prototype.DrawMode = function (fillMode) {
+        Engine.prototype._drawMode = function (fillMode) {
             switch (fillMode) {
                 // Triangle views
                 case BABYLON.Material.TriangleFillMode:
@@ -21648,6 +21784,7 @@ var BABYLON;
             this.onRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
+            this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
              * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
              * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
@@ -23168,22 +23305,46 @@ var BABYLON;
         // Animations
         /**
          * Will start the animation sequence of a given target
-         * @param target - the target
-         * @param {number} from - from which frame should animation start
-         * @param {number} to - till which frame should animation run.
-         * @param {boolean} [loop] - should the animation loop
-         * @param {number} [speedRatio] - the speed in which to run the animation
-         * @param {Function} [onAnimationEnd] function to be executed when the animation ended.
-         * @param {BABYLON.Animatable} [animatable] an animatable object. If not provided a new one will be created from the given params.
-         * Returns {BABYLON.Animatable} the animatable object created for this animation
-         * See BABYLON.Animatable
-         */
-        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable) {
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginWeightedAnimation = function (target, from, to, weight, loop, speedRatio, onAnimationEnd, animatable) {
+            if (weight === void 0) { weight = 1.0; }
             if (speedRatio === void 0) { speedRatio = 1.0; }
+            var returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false);
+            returnedAnimatable.weight = weight;
+            return returnedAnimatable;
+        };
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent) {
+            if (speedRatio === void 0) { speedRatio = 1.0; }
+            if (stopCurrent === void 0) { stopCurrent = true; }
             if (from > to && speedRatio > 0) {
                 speedRatio *= -1;
             }
-            this.stopAnimation(target);
+            if (stopCurrent) {
+                this.stopAnimation(target);
+            }
             if (!animatable) {
                 animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd);
             }
@@ -23195,10 +23356,12 @@ var BABYLON;
             if (target.getAnimatables) {
                 var animatables = target.getAnimatables();
                 for (var index = 0; index < animatables.length; index++) {
-                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable);
+                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent);
                 }
             }
-            animatable.reset();
+            if (stopCurrent) {
+                animatable.reset();
+            }
             return animatable;
         };
         /**
@@ -23296,6 +23459,62 @@ var BABYLON;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 this._activeAnimatables[index]._animate(this._animationTime);
             }
+            // Late animation bindings
+            this._processLateAnimationBindings();
+        };
+        /** @ignore */
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+            var target = runtimeAnimation.target;
+            this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+            if (!target._lateAnimationHolders) {
+                target._lateAnimationHolders = {};
+            }
+            if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+                target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+                    totalWeight: 0,
+                    animations: []
+                };
+            }
+            target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+            target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+        };
+        Scene.prototype._processLateAnimationBindings = function () {
+            if (!this._registeredForLateAnimationBindings.length) {
+                return;
+            }
+            for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+                var target = this._registeredForLateAnimationBindings.data[index];
+                for (var path in target._lateAnimationHolders) {
+                    var holder = target._lateAnimationHolders[path];
+                    // Sanity check
+                    if (!holder.animations[0].originalValue.scaleAndAddToRef) {
+                        continue;
+                    }
+                    var normalizer = 1.0;
+                    var finalValue = void 0;
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        var originalValue = holder.animations[0].originalValue;
+                        finalValue = originalValue.scale(1.0 - holder.totalWeight);
+                    }
+                    else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                    }
+                    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];
+                        if (finalValue) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(runtimeAnimation.weight / normalizer, finalValue);
+                        }
+                        else {
+                            finalValue = runtimeAnimation.currentValue.scale(runtimeAnimation.weight / normalizer);
+                        }
+                    }
+                    runtimeAnimation.target[path] = finalValue;
+                }
+                target._lateAnimationHolders = {};
+            }
+            this._registeredForLateAnimationBindings.reset();
         };
         // Matrix
         Scene.prototype._switchToAlternateCameraConfiguration = function (active) {
@@ -24925,6 +25144,7 @@ var BABYLON;
             this._activeSkeletons.dispose();
             this._softwareSkinnedMeshes.dispose();
             this._renderTargets.dispose();
+            this._registeredForLateAnimationBindings.dispose();
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.dispose();
             }
@@ -47568,16 +47788,74 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     var RuntimeAnimation = /** @class */ (function () {
-        function RuntimeAnimation(target, animation) {
+        /**
+         * Create a new RuntimeAnimation object
+         * @param target defines the target of the animation
+         * @param animation defines the source {BABYLON.Animation} object
+         * @param scene defines the hosting scene
+         */
+        function RuntimeAnimation(target, animation, scene) {
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
             this._blendingFactor = 0;
+            this._weight = 1.0;
             this._ratioOffset = 0;
             this._animation = animation;
             this._target = target;
+            this._scene = scene;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "weight", {
+            /**
+             * Gets the weight of the runtime animation
+             */
+            get: function () {
+                return this._weight;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "originalValue", {
+            /**
+             * Gets the original value of the runtime animation
+             */
+            get: function () {
+                return this._originalValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "currentValue", {
+            /**
+             * Gets the current value of the runtime animation
+             */
+            get: function () {
+                return this._currentValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "targetPath", {
+            /**
+             * Gets the path where to store the animated value in the target
+             */
+            get: function () {
+                return this._targetPath;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "target", {
+            /**
+             * Gets the actual target of the runtime animation
+             */
+            get: function () {
+                return this._activeTarget;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "animation", {
             get: function () {
                 return this._animation;
@@ -47590,7 +47868,7 @@ var BABYLON;
             this._highLimitsCache = {};
             this.currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalBlendValue = null;
+            this._originalValue = null;
         };
         RuntimeAnimation.prototype.isStopped = function () {
             return this._stopped;
@@ -47718,8 +47996,13 @@ var BABYLON;
             }
             return this._getKeyValue(keys[keys.length - 1].value);
         };
-        RuntimeAnimation.prototype.setValue = function (currentValue, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Affect the interpolated value to the target
+         * @param currentValue defines the value computed by the animation
+         * @param weight defines the weight to apply to this value
+         */
+        RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
+            if (weight === void 0) { weight = 1.0; }
             // Set value
             var path;
             var destination;
@@ -47729,43 +48012,62 @@ var BABYLON;
                 for (var index = 1; index < targetPropertyPath.length - 1; index++) {
                     property = property[targetPropertyPath[index]];
                 }
-                path = targetPropertyPath[targetPropertyPath.length - 1];
+                path = [targetPropertyPath.length - 1];
                 destination = property;
             }
             else {
                 path = targetPropertyPath[0];
                 destination = this._target;
             }
+            this._targetPath = path;
+            this._activeTarget = destination;
+            this._weight = weight;
             // Blending
             var enableBlending = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
             var blendingSpeed = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.blendingSpeed : this._animation.blendingSpeed;
-            if (enableBlending && this._blendingFactor <= 1.0) {
-                if (!this._originalBlendValue) {
-                    if (destination[path].clone) {
-                        this._originalBlendValue = destination[path].clone();
+            if (enableBlending && this._blendingFactor <= 1.0 || weight !== -1.0) {
+                if (!this._originalValue) {
+                    var originalValue = void 0;
+                    if (destination.getRestPose) {
+                        originalValue = destination.getRestPose();
                     }
                     else {
-                        this._originalBlendValue = destination[path];
+                        originalValue = destination[path];
+                    }
+                    if (originalValue.clone) {
+                        this._originalValue = originalValue.clone();
+                    }
+                    else {
+                        this._originalValue = originalValue;
                     }
                 }
-                if (this._originalBlendValue.prototype) {
-                    if (this._originalBlendValue.prototype.Lerp) {
-                        destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
+            }
+            if (enableBlending && this._blendingFactor <= 1.0) {
+                if (this._originalValue.prototype) {
+                    if (this._originalValue.prototype.Lerp) {
+                        this._currentValue = this._originalValue.construtor.prototype.Lerp(currentValue, this._originalValue, this._blendingFactor);
                     }
                     else {
-                        destination[path] = currentValue;
+                        this._currentValue = currentValue;
                     }
                 }
-                else if (this._originalBlendValue.m) {
-                    destination[path] = BABYLON.Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
+                else if (this._originalValue.m) {
+                    this._currentValue = BABYLON.Matrix.Lerp(this._originalValue, currentValue, this._blendingFactor);
                 }
                 else {
-                    destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
+                    this._currentValue = this._originalValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
                 }
                 this._blendingFactor += blendingSpeed;
+                destination[path] = this._currentValue;
             }
             else {
-                destination[path] = currentValue;
+                this._currentValue = currentValue;
+                if (weight !== -1.0) {
+                    this._scene._registerTargetForLateAnimationBinding(this);
+                }
+                else {
+                    destination[path] = this._currentValue;
+                }
             }
             if (this._target.markAsDirty) {
                 this._target.markAsDirty(this._animation.targetProperty);
@@ -47777,6 +48079,10 @@ var BABYLON;
             }
             return this._animation.loopMode;
         };
+        /**
+         * Move the current animation to a given frame
+         * @param frame defines the frame to move to
+         */
         RuntimeAnimation.prototype.goToFrame = function (frame) {
             var keys = this._animation.getKeys();
             if (frame < keys[0].frame) {
@@ -47786,14 +48092,24 @@ var BABYLON;
                 frame = keys[keys.length - 1].frame;
             }
             var currentValue = this._interpolate(frame, 0, this._getCorrectLoopMode());
-            this.setValue(currentValue);
+            this.setValue(currentValue, -1);
         };
         RuntimeAnimation.prototype._prepareForSpeedRatioChange = function (newSpeedRatio) {
             var newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
             this._ratioOffset = this._previousRatio - newRatio;
         };
-        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Execute the current animation
+         * @param delay defines the delay to add to the current frame
+         * @param from defines the lower bound of the animation range
+         * @param to defines the upper bound of the animation range
+         * @param loop defines if the current animation must loop
+         * @param speedRatio defines the current speed ratio
+         * @param weight defines the weight of the animation (default is -1 so no weight)
+         * @returns a boolean indicating if the animation has ended
+         */
+        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, weight) {
+            if (weight === void 0) { weight = -1.0; }
             var targetPropertyPath = this._animation.targetPropertyPath;
             if (!targetPropertyPath || targetPropertyPath.length < 1) {
                 this._stopped = true;
@@ -47903,7 +48219,7 @@ var BABYLON;
             var currentFrame = returnValue ? from + ratio % range : to;
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
-            this.setValue(currentValue);
+            this.setValue(currentValue, weight);
             // Check events
             var events = this._animation.getEvents();
             for (var index = 0; index < events.length; index++) {
@@ -47957,6 +48273,7 @@ var BABYLON;
             this._runtimeAnimations = new Array();
             this._paused = false;
             this._speedRatio = 1;
+            this._weight = -1.0;
             this.animationStarted = false;
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -47965,7 +48282,28 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "weight", {
+            /**
+             * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+             */
+            get: function () {
+                return this._weight;
+            },
+            set: function (value) {
+                if (value === -1) {
+                    this._weight = -1;
+                    return;
+                }
+                // Else weight must be in [0, 1] range
+                this._weight = Math.min(Math.max(value, 0), 1.0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "speedRatio", {
+            /**
+             * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
+             */
             get: function () {
                 return this._speedRatio;
             },
@@ -47986,7 +48324,7 @@ var BABYLON;
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -48114,7 +48452,7 @@ var BABYLON;
             var index;
             for (index = 0; index < runtimeAnimations.length; index++) {
                 var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
                 running = running || isRunning;
             }
             this.animationStarted = running;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 53 - 53
dist/preview release/babylon.worker.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 14187 - 14034
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 54 - 54
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 440 - 102
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -1572,7 +1572,7 @@ var BABYLON;
         /**
          * Multiplies in place each rgb value by scale
          * @param scale defines the scaling factor
-         * @returns the updated Color3.
+         * @returns the updated Color3
          */
         Color3.prototype.scale = function (scale) {
             return new Color3(this.r * scale, this.g * scale, this.b * scale);
@@ -1581,7 +1581,7 @@ var BABYLON;
          * Multiplies the rgb values by scale and stores the result into "result"
          * @param scale defines the scaling factor
          * @param result defines the Color3 object where to store the result
-         * @returns the unmodified current Color3.
+         * @returns the unmodified current Color3
          */
         Color3.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1590,6 +1590,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color3 values by a factor and add the result to a given Color3
+         * @param scale defines the scale factor
+         * @param result defines color to store the result into
+         * @returns the unmodified current Color3
+         */
+        Color3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -1957,7 +1969,7 @@ var BABYLON;
          * Multiplies the current Color4 values by scale and stores the result in "result"
          * @param scale defines the scaling factor to apply
          * @param result defines the Color4 object where to store the result
-         * @returns the current Color4.
+         * @returns the current unmodified Color4
          */
         Color4.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1967,6 +1979,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color4 values by a factor and add the result to a given Color4
+         * @param scale defines the scale factor
+         * @param result defines the Color4 object where to store the result
+         * @returns the unmodified current Color4
+         */
+        Color4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            result.a += this.a * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -2410,7 +2435,31 @@ var BABYLON;
          * Returns a new Vector2 scaled by "scale" from the current Vector2.
          */
         Vector2.prototype.scale = function (scale) {
-            return new Vector2(this.x * scale, this.y * scale);
+            var result = new Vector2(0, 0);
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Vector2 values by a factor to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            return this;
+        };
+        /**
+         * Scale the current Vector2 values by a factor and add the result to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            return this;
         };
         /**
          * Boolean : True if the passed vector coordinates strictly equal the current Vector2 ones.
@@ -2850,6 +2899,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector3 values by a factor and add the result to a given Vector3
+         * @param scale defines the scale factor
+         * @param result defines the Vector3 object where to store the result
+         * @returns the unmodified current Vector3
+         */
+        Vector3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            return this;
+        };
+        /**
          * Returns true if the current Vector3 and the passed vector coordinates are strictly equal
          * @param otherVector defines the second operand
          * @returns true if both vectors are equals
@@ -3794,6 +3855,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector4 values by a factor and add the result to a given Vector4
+         * @param scale defines the scale factor
+         * @param result defines the Vector4 object where to store the result
+         * @returns the unmodified current Vector4
+         */
+        Vector4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Boolean : True if the current Vector4 coordinates are stricly equal to the passed ones.
          */
         Vector4.prototype.equals = function (otherVector) {
@@ -4320,6 +4394,32 @@ var BABYLON;
             return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
         };
         /**
+         * Scale the current Quaternion values by a factor to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            result.z = this.z * scale;
+            result.w = this.w * scale;
+            return this;
+        };
+        /**
+         * Scale the current Quaternion values by a factor and add the result to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Returns a new Quaternion set as the quaternion mulplication result of the current one with the passed one "q1".
          */
         Quaternion.prototype.multiply = function (q1) {
@@ -5066,6 +5166,99 @@ var BABYLON;
             return true;
         };
         /**
+         * Returns the index-th row of the current matrix as a new Vector4.
+         */
+        Matrix.prototype.getRow = function (index) {
+            if (index < 0 || index > 3) {
+                return null;
+            }
+            var i = index * 4;
+            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed Vector4 values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRow = function (index, row) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = row.x;
+            this.m[i + 1] = row.y;
+            this.m[i + 2] = row.z;
+            this.m[i + 3] = row.w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns a new Matrix.
+         */
+        Matrix.prototype.transpose = function () {
+            return Matrix.Transpose(this);
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns the current matrix.
+         */
+        Matrix.prototype.transposeToRef = function (result) {
+            Matrix.TransposeToRef(this, result);
+            return this;
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute a new Matrix set with the current Matrix values multiplied by scale (float)
+         * @param scale defines the scale factor
+         * @returns a new Matrix
+         */
+        Matrix.prototype.scale = function (scale) {
+            var result = new Matrix();
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Matrix values by a factor to a given result Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] = this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
+         * Scale the current Matrix values by a factor and add the result to a given Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleAndAddToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] += this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
          * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).
          * @param ref matrix to store the result
          */
@@ -5150,63 +5343,6 @@ var BABYLON;
             result.m[15] = initialM44;
             result._markAsUpdated();
         };
-        /**
-         * Returns the index-th row of the current matrix as a new Vector4.
-         */
-        Matrix.prototype.getRow = function (index) {
-            if (index < 0 || index > 3) {
-                return null;
-            }
-            var i = index * 4;
-            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed Vector4 values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRow = function (index, row) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = row.x;
-            this.m[i + 1] = row.y;
-            this.m[i + 2] = row.z;
-            this.m[i + 3] = row.w;
-            this._markAsUpdated();
-            return this;
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns a new Matrix.
-         */
-        Matrix.prototype.transpose = function () {
-            return Matrix.Transpose(this);
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns the current matrix.
-         */
-        Matrix.prototype.transposeToRef = function (result) {
-            Matrix.TransposeToRef(this, result);
-            return this;
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed 4 x float values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = x;
-            this.m[i + 1] = y;
-            this.m[i + 2] = z;
-            this.m[i + 3] = w;
-            this._markAsUpdated();
-            return this;
-        };
         Object.defineProperty(Matrix, "IdentityReadOnly", {
             /**
              * Static identity matrix to be used as readonly matrix
@@ -12499,7 +12635,7 @@ var BABYLON;
             this.applyStates();
             this._drawCalls.addCount(1, false);
             // Render
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
@@ -12513,7 +12649,7 @@ var BABYLON;
             // Apply states
             this.applyStates();
             this._drawCalls.addCount(1, false);
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             if (instancesCount) {
                 this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             }
@@ -12521,7 +12657,7 @@ var BABYLON;
                 this._gl.drawArrays(drawMode, verticesStart, verticesCount);
             }
         };
-        Engine.prototype.DrawMode = function (fillMode) {
+        Engine.prototype._drawMode = function (fillMode) {
             switch (fillMode) {
                 // Triangle views
                 case BABYLON.Material.TriangleFillMode:
@@ -21648,6 +21784,7 @@ var BABYLON;
             this.onRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
+            this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
              * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
              * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
@@ -23168,22 +23305,46 @@ var BABYLON;
         // Animations
         /**
          * Will start the animation sequence of a given target
-         * @param target - the target
-         * @param {number} from - from which frame should animation start
-         * @param {number} to - till which frame should animation run.
-         * @param {boolean} [loop] - should the animation loop
-         * @param {number} [speedRatio] - the speed in which to run the animation
-         * @param {Function} [onAnimationEnd] function to be executed when the animation ended.
-         * @param {BABYLON.Animatable} [animatable] an animatable object. If not provided a new one will be created from the given params.
-         * Returns {BABYLON.Animatable} the animatable object created for this animation
-         * See BABYLON.Animatable
-         */
-        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable) {
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginWeightedAnimation = function (target, from, to, weight, loop, speedRatio, onAnimationEnd, animatable) {
+            if (weight === void 0) { weight = 1.0; }
             if (speedRatio === void 0) { speedRatio = 1.0; }
+            var returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false);
+            returnedAnimatable.weight = weight;
+            return returnedAnimatable;
+        };
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent) {
+            if (speedRatio === void 0) { speedRatio = 1.0; }
+            if (stopCurrent === void 0) { stopCurrent = true; }
             if (from > to && speedRatio > 0) {
                 speedRatio *= -1;
             }
-            this.stopAnimation(target);
+            if (stopCurrent) {
+                this.stopAnimation(target);
+            }
             if (!animatable) {
                 animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd);
             }
@@ -23195,10 +23356,12 @@ var BABYLON;
             if (target.getAnimatables) {
                 var animatables = target.getAnimatables();
                 for (var index = 0; index < animatables.length; index++) {
-                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable);
+                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent);
                 }
             }
-            animatable.reset();
+            if (stopCurrent) {
+                animatable.reset();
+            }
             return animatable;
         };
         /**
@@ -23296,6 +23459,62 @@ var BABYLON;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 this._activeAnimatables[index]._animate(this._animationTime);
             }
+            // Late animation bindings
+            this._processLateAnimationBindings();
+        };
+        /** @ignore */
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+            var target = runtimeAnimation.target;
+            this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+            if (!target._lateAnimationHolders) {
+                target._lateAnimationHolders = {};
+            }
+            if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+                target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+                    totalWeight: 0,
+                    animations: []
+                };
+            }
+            target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+            target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+        };
+        Scene.prototype._processLateAnimationBindings = function () {
+            if (!this._registeredForLateAnimationBindings.length) {
+                return;
+            }
+            for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+                var target = this._registeredForLateAnimationBindings.data[index];
+                for (var path in target._lateAnimationHolders) {
+                    var holder = target._lateAnimationHolders[path];
+                    // Sanity check
+                    if (!holder.animations[0].originalValue.scaleAndAddToRef) {
+                        continue;
+                    }
+                    var normalizer = 1.0;
+                    var finalValue = void 0;
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        var originalValue = holder.animations[0].originalValue;
+                        finalValue = originalValue.scale(1.0 - holder.totalWeight);
+                    }
+                    else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                    }
+                    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];
+                        if (finalValue) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(runtimeAnimation.weight / normalizer, finalValue);
+                        }
+                        else {
+                            finalValue = runtimeAnimation.currentValue.scale(runtimeAnimation.weight / normalizer);
+                        }
+                    }
+                    runtimeAnimation.target[path] = finalValue;
+                }
+                target._lateAnimationHolders = {};
+            }
+            this._registeredForLateAnimationBindings.reset();
         };
         // Matrix
         Scene.prototype._switchToAlternateCameraConfiguration = function (active) {
@@ -24925,6 +25144,7 @@ var BABYLON;
             this._activeSkeletons.dispose();
             this._softwareSkinnedMeshes.dispose();
             this._renderTargets.dispose();
+            this._registeredForLateAnimationBindings.dispose();
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.dispose();
             }
@@ -47568,16 +47788,74 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     var RuntimeAnimation = /** @class */ (function () {
-        function RuntimeAnimation(target, animation) {
+        /**
+         * Create a new RuntimeAnimation object
+         * @param target defines the target of the animation
+         * @param animation defines the source {BABYLON.Animation} object
+         * @param scene defines the hosting scene
+         */
+        function RuntimeAnimation(target, animation, scene) {
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
             this._blendingFactor = 0;
+            this._weight = 1.0;
             this._ratioOffset = 0;
             this._animation = animation;
             this._target = target;
+            this._scene = scene;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "weight", {
+            /**
+             * Gets the weight of the runtime animation
+             */
+            get: function () {
+                return this._weight;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "originalValue", {
+            /**
+             * Gets the original value of the runtime animation
+             */
+            get: function () {
+                return this._originalValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "currentValue", {
+            /**
+             * Gets the current value of the runtime animation
+             */
+            get: function () {
+                return this._currentValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "targetPath", {
+            /**
+             * Gets the path where to store the animated value in the target
+             */
+            get: function () {
+                return this._targetPath;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "target", {
+            /**
+             * Gets the actual target of the runtime animation
+             */
+            get: function () {
+                return this._activeTarget;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "animation", {
             get: function () {
                 return this._animation;
@@ -47590,7 +47868,7 @@ var BABYLON;
             this._highLimitsCache = {};
             this.currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalBlendValue = null;
+            this._originalValue = null;
         };
         RuntimeAnimation.prototype.isStopped = function () {
             return this._stopped;
@@ -47718,8 +47996,13 @@ var BABYLON;
             }
             return this._getKeyValue(keys[keys.length - 1].value);
         };
-        RuntimeAnimation.prototype.setValue = function (currentValue, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Affect the interpolated value to the target
+         * @param currentValue defines the value computed by the animation
+         * @param weight defines the weight to apply to this value
+         */
+        RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
+            if (weight === void 0) { weight = 1.0; }
             // Set value
             var path;
             var destination;
@@ -47729,43 +48012,62 @@ var BABYLON;
                 for (var index = 1; index < targetPropertyPath.length - 1; index++) {
                     property = property[targetPropertyPath[index]];
                 }
-                path = targetPropertyPath[targetPropertyPath.length - 1];
+                path = [targetPropertyPath.length - 1];
                 destination = property;
             }
             else {
                 path = targetPropertyPath[0];
                 destination = this._target;
             }
+            this._targetPath = path;
+            this._activeTarget = destination;
+            this._weight = weight;
             // Blending
             var enableBlending = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
             var blendingSpeed = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.blendingSpeed : this._animation.blendingSpeed;
-            if (enableBlending && this._blendingFactor <= 1.0) {
-                if (!this._originalBlendValue) {
-                    if (destination[path].clone) {
-                        this._originalBlendValue = destination[path].clone();
+            if (enableBlending && this._blendingFactor <= 1.0 || weight !== -1.0) {
+                if (!this._originalValue) {
+                    var originalValue = void 0;
+                    if (destination.getRestPose) {
+                        originalValue = destination.getRestPose();
                     }
                     else {
-                        this._originalBlendValue = destination[path];
+                        originalValue = destination[path];
+                    }
+                    if (originalValue.clone) {
+                        this._originalValue = originalValue.clone();
+                    }
+                    else {
+                        this._originalValue = originalValue;
                     }
                 }
-                if (this._originalBlendValue.prototype) {
-                    if (this._originalBlendValue.prototype.Lerp) {
-                        destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
+            }
+            if (enableBlending && this._blendingFactor <= 1.0) {
+                if (this._originalValue.prototype) {
+                    if (this._originalValue.prototype.Lerp) {
+                        this._currentValue = this._originalValue.construtor.prototype.Lerp(currentValue, this._originalValue, this._blendingFactor);
                     }
                     else {
-                        destination[path] = currentValue;
+                        this._currentValue = currentValue;
                     }
                 }
-                else if (this._originalBlendValue.m) {
-                    destination[path] = BABYLON.Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
+                else if (this._originalValue.m) {
+                    this._currentValue = BABYLON.Matrix.Lerp(this._originalValue, currentValue, this._blendingFactor);
                 }
                 else {
-                    destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
+                    this._currentValue = this._originalValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
                 }
                 this._blendingFactor += blendingSpeed;
+                destination[path] = this._currentValue;
             }
             else {
-                destination[path] = currentValue;
+                this._currentValue = currentValue;
+                if (weight !== -1.0) {
+                    this._scene._registerTargetForLateAnimationBinding(this);
+                }
+                else {
+                    destination[path] = this._currentValue;
+                }
             }
             if (this._target.markAsDirty) {
                 this._target.markAsDirty(this._animation.targetProperty);
@@ -47777,6 +48079,10 @@ var BABYLON;
             }
             return this._animation.loopMode;
         };
+        /**
+         * Move the current animation to a given frame
+         * @param frame defines the frame to move to
+         */
         RuntimeAnimation.prototype.goToFrame = function (frame) {
             var keys = this._animation.getKeys();
             if (frame < keys[0].frame) {
@@ -47786,14 +48092,24 @@ var BABYLON;
                 frame = keys[keys.length - 1].frame;
             }
             var currentValue = this._interpolate(frame, 0, this._getCorrectLoopMode());
-            this.setValue(currentValue);
+            this.setValue(currentValue, -1);
         };
         RuntimeAnimation.prototype._prepareForSpeedRatioChange = function (newSpeedRatio) {
             var newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
             this._ratioOffset = this._previousRatio - newRatio;
         };
-        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Execute the current animation
+         * @param delay defines the delay to add to the current frame
+         * @param from defines the lower bound of the animation range
+         * @param to defines the upper bound of the animation range
+         * @param loop defines if the current animation must loop
+         * @param speedRatio defines the current speed ratio
+         * @param weight defines the weight of the animation (default is -1 so no weight)
+         * @returns a boolean indicating if the animation has ended
+         */
+        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, weight) {
+            if (weight === void 0) { weight = -1.0; }
             var targetPropertyPath = this._animation.targetPropertyPath;
             if (!targetPropertyPath || targetPropertyPath.length < 1) {
                 this._stopped = true;
@@ -47903,7 +48219,7 @@ var BABYLON;
             var currentFrame = returnValue ? from + ratio % range : to;
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
-            this.setValue(currentValue);
+            this.setValue(currentValue, weight);
             // Check events
             var events = this._animation.getEvents();
             for (var index = 0; index < events.length; index++) {
@@ -47957,6 +48273,7 @@ var BABYLON;
             this._runtimeAnimations = new Array();
             this._paused = false;
             this._speedRatio = 1;
+            this._weight = -1.0;
             this.animationStarted = false;
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -47965,7 +48282,28 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "weight", {
+            /**
+             * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+             */
+            get: function () {
+                return this._weight;
+            },
+            set: function (value) {
+                if (value === -1) {
+                    this._weight = -1;
+                    return;
+                }
+                // Else weight must be in [0, 1] range
+                this._weight = Math.min(Math.max(value, 0), 1.0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "speedRatio", {
+            /**
+             * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
+             */
             get: function () {
                 return this._speedRatio;
             },
@@ -47986,7 +48324,7 @@ var BABYLON;
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -48114,7 +48452,7 @@ var BABYLON;
             var index;
             for (index = 0; index < runtimeAnimations.length; index++) {
                 var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
                 running = running || isRunning;
             }
             this.animationStarted = running;

+ 440 - 102
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js

@@ -1545,7 +1545,7 @@ var BABYLON;
         /**
          * Multiplies in place each rgb value by scale
          * @param scale defines the scaling factor
-         * @returns the updated Color3.
+         * @returns the updated Color3
          */
         Color3.prototype.scale = function (scale) {
             return new Color3(this.r * scale, this.g * scale, this.b * scale);
@@ -1554,7 +1554,7 @@ var BABYLON;
          * Multiplies the rgb values by scale and stores the result into "result"
          * @param scale defines the scaling factor
          * @param result defines the Color3 object where to store the result
-         * @returns the unmodified current Color3.
+         * @returns the unmodified current Color3
          */
         Color3.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1563,6 +1563,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color3 values by a factor and add the result to a given Color3
+         * @param scale defines the scale factor
+         * @param result defines color to store the result into
+         * @returns the unmodified current Color3
+         */
+        Color3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -1930,7 +1942,7 @@ var BABYLON;
          * Multiplies the current Color4 values by scale and stores the result in "result"
          * @param scale defines the scaling factor to apply
          * @param result defines the Color4 object where to store the result
-         * @returns the current Color4.
+         * @returns the current unmodified Color4
          */
         Color4.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1940,6 +1952,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color4 values by a factor and add the result to a given Color4
+         * @param scale defines the scale factor
+         * @param result defines the Color4 object where to store the result
+         * @returns the unmodified current Color4
+         */
+        Color4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            result.a += this.a * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -2383,7 +2408,31 @@ var BABYLON;
          * Returns a new Vector2 scaled by "scale" from the current Vector2.
          */
         Vector2.prototype.scale = function (scale) {
-            return new Vector2(this.x * scale, this.y * scale);
+            var result = new Vector2(0, 0);
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Vector2 values by a factor to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            return this;
+        };
+        /**
+         * Scale the current Vector2 values by a factor and add the result to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            return this;
         };
         /**
          * Boolean : True if the passed vector coordinates strictly equal the current Vector2 ones.
@@ -2823,6 +2872,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector3 values by a factor and add the result to a given Vector3
+         * @param scale defines the scale factor
+         * @param result defines the Vector3 object where to store the result
+         * @returns the unmodified current Vector3
+         */
+        Vector3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            return this;
+        };
+        /**
          * Returns true if the current Vector3 and the passed vector coordinates are strictly equal
          * @param otherVector defines the second operand
          * @returns true if both vectors are equals
@@ -3767,6 +3828,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector4 values by a factor and add the result to a given Vector4
+         * @param scale defines the scale factor
+         * @param result defines the Vector4 object where to store the result
+         * @returns the unmodified current Vector4
+         */
+        Vector4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Boolean : True if the current Vector4 coordinates are stricly equal to the passed ones.
          */
         Vector4.prototype.equals = function (otherVector) {
@@ -4293,6 +4367,32 @@ var BABYLON;
             return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
         };
         /**
+         * Scale the current Quaternion values by a factor to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            result.z = this.z * scale;
+            result.w = this.w * scale;
+            return this;
+        };
+        /**
+         * Scale the current Quaternion values by a factor and add the result to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Returns a new Quaternion set as the quaternion mulplication result of the current one with the passed one "q1".
          */
         Quaternion.prototype.multiply = function (q1) {
@@ -5039,6 +5139,99 @@ var BABYLON;
             return true;
         };
         /**
+         * Returns the index-th row of the current matrix as a new Vector4.
+         */
+        Matrix.prototype.getRow = function (index) {
+            if (index < 0 || index > 3) {
+                return null;
+            }
+            var i = index * 4;
+            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed Vector4 values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRow = function (index, row) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = row.x;
+            this.m[i + 1] = row.y;
+            this.m[i + 2] = row.z;
+            this.m[i + 3] = row.w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns a new Matrix.
+         */
+        Matrix.prototype.transpose = function () {
+            return Matrix.Transpose(this);
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns the current matrix.
+         */
+        Matrix.prototype.transposeToRef = function (result) {
+            Matrix.TransposeToRef(this, result);
+            return this;
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute a new Matrix set with the current Matrix values multiplied by scale (float)
+         * @param scale defines the scale factor
+         * @returns a new Matrix
+         */
+        Matrix.prototype.scale = function (scale) {
+            var result = new Matrix();
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Matrix values by a factor to a given result Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] = this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
+         * Scale the current Matrix values by a factor and add the result to a given Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleAndAddToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] += this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
          * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).
          * @param ref matrix to store the result
          */
@@ -5123,63 +5316,6 @@ var BABYLON;
             result.m[15] = initialM44;
             result._markAsUpdated();
         };
-        /**
-         * Returns the index-th row of the current matrix as a new Vector4.
-         */
-        Matrix.prototype.getRow = function (index) {
-            if (index < 0 || index > 3) {
-                return null;
-            }
-            var i = index * 4;
-            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed Vector4 values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRow = function (index, row) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = row.x;
-            this.m[i + 1] = row.y;
-            this.m[i + 2] = row.z;
-            this.m[i + 3] = row.w;
-            this._markAsUpdated();
-            return this;
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns a new Matrix.
-         */
-        Matrix.prototype.transpose = function () {
-            return Matrix.Transpose(this);
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns the current matrix.
-         */
-        Matrix.prototype.transposeToRef = function (result) {
-            Matrix.TransposeToRef(this, result);
-            return this;
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed 4 x float values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = x;
-            this.m[i + 1] = y;
-            this.m[i + 2] = z;
-            this.m[i + 3] = w;
-            this._markAsUpdated();
-            return this;
-        };
         Object.defineProperty(Matrix, "IdentityReadOnly", {
             /**
              * Static identity matrix to be used as readonly matrix
@@ -12472,7 +12608,7 @@ var BABYLON;
             this.applyStates();
             this._drawCalls.addCount(1, false);
             // Render
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
@@ -12486,7 +12622,7 @@ var BABYLON;
             // Apply states
             this.applyStates();
             this._drawCalls.addCount(1, false);
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             if (instancesCount) {
                 this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             }
@@ -12494,7 +12630,7 @@ var BABYLON;
                 this._gl.drawArrays(drawMode, verticesStart, verticesCount);
             }
         };
-        Engine.prototype.DrawMode = function (fillMode) {
+        Engine.prototype._drawMode = function (fillMode) {
             switch (fillMode) {
                 // Triangle views
                 case BABYLON.Material.TriangleFillMode:
@@ -21621,6 +21757,7 @@ var BABYLON;
             this.onRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
+            this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
              * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
              * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
@@ -23141,22 +23278,46 @@ var BABYLON;
         // Animations
         /**
          * Will start the animation sequence of a given target
-         * @param target - the target
-         * @param {number} from - from which frame should animation start
-         * @param {number} to - till which frame should animation run.
-         * @param {boolean} [loop] - should the animation loop
-         * @param {number} [speedRatio] - the speed in which to run the animation
-         * @param {Function} [onAnimationEnd] function to be executed when the animation ended.
-         * @param {BABYLON.Animatable} [animatable] an animatable object. If not provided a new one will be created from the given params.
-         * Returns {BABYLON.Animatable} the animatable object created for this animation
-         * See BABYLON.Animatable
-         */
-        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable) {
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginWeightedAnimation = function (target, from, to, weight, loop, speedRatio, onAnimationEnd, animatable) {
+            if (weight === void 0) { weight = 1.0; }
             if (speedRatio === void 0) { speedRatio = 1.0; }
+            var returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false);
+            returnedAnimatable.weight = weight;
+            return returnedAnimatable;
+        };
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent) {
+            if (speedRatio === void 0) { speedRatio = 1.0; }
+            if (stopCurrent === void 0) { stopCurrent = true; }
             if (from > to && speedRatio > 0) {
                 speedRatio *= -1;
             }
-            this.stopAnimation(target);
+            if (stopCurrent) {
+                this.stopAnimation(target);
+            }
             if (!animatable) {
                 animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd);
             }
@@ -23168,10 +23329,12 @@ var BABYLON;
             if (target.getAnimatables) {
                 var animatables = target.getAnimatables();
                 for (var index = 0; index < animatables.length; index++) {
-                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable);
+                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent);
                 }
             }
-            animatable.reset();
+            if (stopCurrent) {
+                animatable.reset();
+            }
             return animatable;
         };
         /**
@@ -23269,6 +23432,62 @@ var BABYLON;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 this._activeAnimatables[index]._animate(this._animationTime);
             }
+            // Late animation bindings
+            this._processLateAnimationBindings();
+        };
+        /** @ignore */
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+            var target = runtimeAnimation.target;
+            this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+            if (!target._lateAnimationHolders) {
+                target._lateAnimationHolders = {};
+            }
+            if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+                target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+                    totalWeight: 0,
+                    animations: []
+                };
+            }
+            target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+            target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+        };
+        Scene.prototype._processLateAnimationBindings = function () {
+            if (!this._registeredForLateAnimationBindings.length) {
+                return;
+            }
+            for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+                var target = this._registeredForLateAnimationBindings.data[index];
+                for (var path in target._lateAnimationHolders) {
+                    var holder = target._lateAnimationHolders[path];
+                    // Sanity check
+                    if (!holder.animations[0].originalValue.scaleAndAddToRef) {
+                        continue;
+                    }
+                    var normalizer = 1.0;
+                    var finalValue = void 0;
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        var originalValue = holder.animations[0].originalValue;
+                        finalValue = originalValue.scale(1.0 - holder.totalWeight);
+                    }
+                    else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                    }
+                    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];
+                        if (finalValue) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(runtimeAnimation.weight / normalizer, finalValue);
+                        }
+                        else {
+                            finalValue = runtimeAnimation.currentValue.scale(runtimeAnimation.weight / normalizer);
+                        }
+                    }
+                    runtimeAnimation.target[path] = finalValue;
+                }
+                target._lateAnimationHolders = {};
+            }
+            this._registeredForLateAnimationBindings.reset();
         };
         // Matrix
         Scene.prototype._switchToAlternateCameraConfiguration = function (active) {
@@ -24898,6 +25117,7 @@ var BABYLON;
             this._activeSkeletons.dispose();
             this._softwareSkinnedMeshes.dispose();
             this._renderTargets.dispose();
+            this._registeredForLateAnimationBindings.dispose();
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.dispose();
             }
@@ -47541,16 +47761,74 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     var RuntimeAnimation = /** @class */ (function () {
-        function RuntimeAnimation(target, animation) {
+        /**
+         * Create a new RuntimeAnimation object
+         * @param target defines the target of the animation
+         * @param animation defines the source {BABYLON.Animation} object
+         * @param scene defines the hosting scene
+         */
+        function RuntimeAnimation(target, animation, scene) {
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
             this._blendingFactor = 0;
+            this._weight = 1.0;
             this._ratioOffset = 0;
             this._animation = animation;
             this._target = target;
+            this._scene = scene;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "weight", {
+            /**
+             * Gets the weight of the runtime animation
+             */
+            get: function () {
+                return this._weight;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "originalValue", {
+            /**
+             * Gets the original value of the runtime animation
+             */
+            get: function () {
+                return this._originalValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "currentValue", {
+            /**
+             * Gets the current value of the runtime animation
+             */
+            get: function () {
+                return this._currentValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "targetPath", {
+            /**
+             * Gets the path where to store the animated value in the target
+             */
+            get: function () {
+                return this._targetPath;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "target", {
+            /**
+             * Gets the actual target of the runtime animation
+             */
+            get: function () {
+                return this._activeTarget;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "animation", {
             get: function () {
                 return this._animation;
@@ -47563,7 +47841,7 @@ var BABYLON;
             this._highLimitsCache = {};
             this.currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalBlendValue = null;
+            this._originalValue = null;
         };
         RuntimeAnimation.prototype.isStopped = function () {
             return this._stopped;
@@ -47691,8 +47969,13 @@ var BABYLON;
             }
             return this._getKeyValue(keys[keys.length - 1].value);
         };
-        RuntimeAnimation.prototype.setValue = function (currentValue, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Affect the interpolated value to the target
+         * @param currentValue defines the value computed by the animation
+         * @param weight defines the weight to apply to this value
+         */
+        RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
+            if (weight === void 0) { weight = 1.0; }
             // Set value
             var path;
             var destination;
@@ -47702,43 +47985,62 @@ var BABYLON;
                 for (var index = 1; index < targetPropertyPath.length - 1; index++) {
                     property = property[targetPropertyPath[index]];
                 }
-                path = targetPropertyPath[targetPropertyPath.length - 1];
+                path = [targetPropertyPath.length - 1];
                 destination = property;
             }
             else {
                 path = targetPropertyPath[0];
                 destination = this._target;
             }
+            this._targetPath = path;
+            this._activeTarget = destination;
+            this._weight = weight;
             // Blending
             var enableBlending = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
             var blendingSpeed = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.blendingSpeed : this._animation.blendingSpeed;
-            if (enableBlending && this._blendingFactor <= 1.0) {
-                if (!this._originalBlendValue) {
-                    if (destination[path].clone) {
-                        this._originalBlendValue = destination[path].clone();
+            if (enableBlending && this._blendingFactor <= 1.0 || weight !== -1.0) {
+                if (!this._originalValue) {
+                    var originalValue = void 0;
+                    if (destination.getRestPose) {
+                        originalValue = destination.getRestPose();
                     }
                     else {
-                        this._originalBlendValue = destination[path];
+                        originalValue = destination[path];
+                    }
+                    if (originalValue.clone) {
+                        this._originalValue = originalValue.clone();
+                    }
+                    else {
+                        this._originalValue = originalValue;
                     }
                 }
-                if (this._originalBlendValue.prototype) {
-                    if (this._originalBlendValue.prototype.Lerp) {
-                        destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
+            }
+            if (enableBlending && this._blendingFactor <= 1.0) {
+                if (this._originalValue.prototype) {
+                    if (this._originalValue.prototype.Lerp) {
+                        this._currentValue = this._originalValue.construtor.prototype.Lerp(currentValue, this._originalValue, this._blendingFactor);
                     }
                     else {
-                        destination[path] = currentValue;
+                        this._currentValue = currentValue;
                     }
                 }
-                else if (this._originalBlendValue.m) {
-                    destination[path] = BABYLON.Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
+                else if (this._originalValue.m) {
+                    this._currentValue = BABYLON.Matrix.Lerp(this._originalValue, currentValue, this._blendingFactor);
                 }
                 else {
-                    destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
+                    this._currentValue = this._originalValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
                 }
                 this._blendingFactor += blendingSpeed;
+                destination[path] = this._currentValue;
             }
             else {
-                destination[path] = currentValue;
+                this._currentValue = currentValue;
+                if (weight !== -1.0) {
+                    this._scene._registerTargetForLateAnimationBinding(this);
+                }
+                else {
+                    destination[path] = this._currentValue;
+                }
             }
             if (this._target.markAsDirty) {
                 this._target.markAsDirty(this._animation.targetProperty);
@@ -47750,6 +48052,10 @@ var BABYLON;
             }
             return this._animation.loopMode;
         };
+        /**
+         * Move the current animation to a given frame
+         * @param frame defines the frame to move to
+         */
         RuntimeAnimation.prototype.goToFrame = function (frame) {
             var keys = this._animation.getKeys();
             if (frame < keys[0].frame) {
@@ -47759,14 +48065,24 @@ var BABYLON;
                 frame = keys[keys.length - 1].frame;
             }
             var currentValue = this._interpolate(frame, 0, this._getCorrectLoopMode());
-            this.setValue(currentValue);
+            this.setValue(currentValue, -1);
         };
         RuntimeAnimation.prototype._prepareForSpeedRatioChange = function (newSpeedRatio) {
             var newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
             this._ratioOffset = this._previousRatio - newRatio;
         };
-        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Execute the current animation
+         * @param delay defines the delay to add to the current frame
+         * @param from defines the lower bound of the animation range
+         * @param to defines the upper bound of the animation range
+         * @param loop defines if the current animation must loop
+         * @param speedRatio defines the current speed ratio
+         * @param weight defines the weight of the animation (default is -1 so no weight)
+         * @returns a boolean indicating if the animation has ended
+         */
+        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, weight) {
+            if (weight === void 0) { weight = -1.0; }
             var targetPropertyPath = this._animation.targetPropertyPath;
             if (!targetPropertyPath || targetPropertyPath.length < 1) {
                 this._stopped = true;
@@ -47876,7 +48192,7 @@ var BABYLON;
             var currentFrame = returnValue ? from + ratio % range : to;
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
-            this.setValue(currentValue);
+            this.setValue(currentValue, weight);
             // Check events
             var events = this._animation.getEvents();
             for (var index = 0; index < events.length; index++) {
@@ -47930,6 +48246,7 @@ var BABYLON;
             this._runtimeAnimations = new Array();
             this._paused = false;
             this._speedRatio = 1;
+            this._weight = -1.0;
             this.animationStarted = false;
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -47938,7 +48255,28 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "weight", {
+            /**
+             * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+             */
+            get: function () {
+                return this._weight;
+            },
+            set: function (value) {
+                if (value === -1) {
+                    this._weight = -1;
+                    return;
+                }
+                // Else weight must be in [0, 1] range
+                this._weight = Math.min(Math.max(value, 0), 1.0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "speedRatio", {
+            /**
+             * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
+             */
             get: function () {
                 return this._speedRatio;
             },
@@ -47959,7 +48297,7 @@ var BABYLON;
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -48087,7 +48425,7 @@ var BABYLON;
             var index;
             for (index = 0; index < runtimeAnimations.length; index++) {
                 var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
                 running = running || isRunning;
             }
             this.animationStarted = running;

+ 440 - 102
dist/preview release/es6.js

@@ -1545,7 +1545,7 @@ var BABYLON;
         /**
          * Multiplies in place each rgb value by scale
          * @param scale defines the scaling factor
-         * @returns the updated Color3.
+         * @returns the updated Color3
          */
         Color3.prototype.scale = function (scale) {
             return new Color3(this.r * scale, this.g * scale, this.b * scale);
@@ -1554,7 +1554,7 @@ var BABYLON;
          * Multiplies the rgb values by scale and stores the result into "result"
          * @param scale defines the scaling factor
          * @param result defines the Color3 object where to store the result
-         * @returns the unmodified current Color3.
+         * @returns the unmodified current Color3
          */
         Color3.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1563,6 +1563,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color3 values by a factor and add the result to a given Color3
+         * @param scale defines the scale factor
+         * @param result defines color to store the result into
+         * @returns the unmodified current Color3
+         */
+        Color3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -1930,7 +1942,7 @@ var BABYLON;
          * Multiplies the current Color4 values by scale and stores the result in "result"
          * @param scale defines the scaling factor to apply
          * @param result defines the Color4 object where to store the result
-         * @returns the current Color4.
+         * @returns the current unmodified Color4
          */
         Color4.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1940,6 +1952,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color4 values by a factor and add the result to a given Color4
+         * @param scale defines the scale factor
+         * @param result defines the Color4 object where to store the result
+         * @returns the unmodified current Color4
+         */
+        Color4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            result.a += this.a * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -2383,7 +2408,31 @@ var BABYLON;
          * Returns a new Vector2 scaled by "scale" from the current Vector2.
          */
         Vector2.prototype.scale = function (scale) {
-            return new Vector2(this.x * scale, this.y * scale);
+            var result = new Vector2(0, 0);
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Vector2 values by a factor to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            return this;
+        };
+        /**
+         * Scale the current Vector2 values by a factor and add the result to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            return this;
         };
         /**
          * Boolean : True if the passed vector coordinates strictly equal the current Vector2 ones.
@@ -2823,6 +2872,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector3 values by a factor and add the result to a given Vector3
+         * @param scale defines the scale factor
+         * @param result defines the Vector3 object where to store the result
+         * @returns the unmodified current Vector3
+         */
+        Vector3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            return this;
+        };
+        /**
          * Returns true if the current Vector3 and the passed vector coordinates are strictly equal
          * @param otherVector defines the second operand
          * @returns true if both vectors are equals
@@ -3767,6 +3828,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector4 values by a factor and add the result to a given Vector4
+         * @param scale defines the scale factor
+         * @param result defines the Vector4 object where to store the result
+         * @returns the unmodified current Vector4
+         */
+        Vector4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Boolean : True if the current Vector4 coordinates are stricly equal to the passed ones.
          */
         Vector4.prototype.equals = function (otherVector) {
@@ -4293,6 +4367,32 @@ var BABYLON;
             return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
         };
         /**
+         * Scale the current Quaternion values by a factor to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            result.z = this.z * scale;
+            result.w = this.w * scale;
+            return this;
+        };
+        /**
+         * Scale the current Quaternion values by a factor and add the result to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Returns a new Quaternion set as the quaternion mulplication result of the current one with the passed one "q1".
          */
         Quaternion.prototype.multiply = function (q1) {
@@ -5039,6 +5139,99 @@ var BABYLON;
             return true;
         };
         /**
+         * Returns the index-th row of the current matrix as a new Vector4.
+         */
+        Matrix.prototype.getRow = function (index) {
+            if (index < 0 || index > 3) {
+                return null;
+            }
+            var i = index * 4;
+            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed Vector4 values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRow = function (index, row) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = row.x;
+            this.m[i + 1] = row.y;
+            this.m[i + 2] = row.z;
+            this.m[i + 3] = row.w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns a new Matrix.
+         */
+        Matrix.prototype.transpose = function () {
+            return Matrix.Transpose(this);
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns the current matrix.
+         */
+        Matrix.prototype.transposeToRef = function (result) {
+            Matrix.TransposeToRef(this, result);
+            return this;
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute a new Matrix set with the current Matrix values multiplied by scale (float)
+         * @param scale defines the scale factor
+         * @returns a new Matrix
+         */
+        Matrix.prototype.scale = function (scale) {
+            var result = new Matrix();
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Matrix values by a factor to a given result Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] = this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
+         * Scale the current Matrix values by a factor and add the result to a given Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleAndAddToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] += this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
          * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).
          * @param ref matrix to store the result
          */
@@ -5123,63 +5316,6 @@ var BABYLON;
             result.m[15] = initialM44;
             result._markAsUpdated();
         };
-        /**
-         * Returns the index-th row of the current matrix as a new Vector4.
-         */
-        Matrix.prototype.getRow = function (index) {
-            if (index < 0 || index > 3) {
-                return null;
-            }
-            var i = index * 4;
-            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed Vector4 values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRow = function (index, row) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = row.x;
-            this.m[i + 1] = row.y;
-            this.m[i + 2] = row.z;
-            this.m[i + 3] = row.w;
-            this._markAsUpdated();
-            return this;
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns a new Matrix.
-         */
-        Matrix.prototype.transpose = function () {
-            return Matrix.Transpose(this);
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns the current matrix.
-         */
-        Matrix.prototype.transposeToRef = function (result) {
-            Matrix.TransposeToRef(this, result);
-            return this;
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed 4 x float values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = x;
-            this.m[i + 1] = y;
-            this.m[i + 2] = z;
-            this.m[i + 3] = w;
-            this._markAsUpdated();
-            return this;
-        };
         Object.defineProperty(Matrix, "IdentityReadOnly", {
             /**
              * Static identity matrix to be used as readonly matrix
@@ -12472,7 +12608,7 @@ var BABYLON;
             this.applyStates();
             this._drawCalls.addCount(1, false);
             // Render
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
@@ -12486,7 +12622,7 @@ var BABYLON;
             // Apply states
             this.applyStates();
             this._drawCalls.addCount(1, false);
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             if (instancesCount) {
                 this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             }
@@ -12494,7 +12630,7 @@ var BABYLON;
                 this._gl.drawArrays(drawMode, verticesStart, verticesCount);
             }
         };
-        Engine.prototype.DrawMode = function (fillMode) {
+        Engine.prototype._drawMode = function (fillMode) {
             switch (fillMode) {
                 // Triangle views
                 case BABYLON.Material.TriangleFillMode:
@@ -21621,6 +21757,7 @@ var BABYLON;
             this.onRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
+            this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
              * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
              * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
@@ -23141,22 +23278,46 @@ var BABYLON;
         // Animations
         /**
          * Will start the animation sequence of a given target
-         * @param target - the target
-         * @param {number} from - from which frame should animation start
-         * @param {number} to - till which frame should animation run.
-         * @param {boolean} [loop] - should the animation loop
-         * @param {number} [speedRatio] - the speed in which to run the animation
-         * @param {Function} [onAnimationEnd] function to be executed when the animation ended.
-         * @param {BABYLON.Animatable} [animatable] an animatable object. If not provided a new one will be created from the given params.
-         * Returns {BABYLON.Animatable} the animatable object created for this animation
-         * See BABYLON.Animatable
-         */
-        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable) {
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginWeightedAnimation = function (target, from, to, weight, loop, speedRatio, onAnimationEnd, animatable) {
+            if (weight === void 0) { weight = 1.0; }
             if (speedRatio === void 0) { speedRatio = 1.0; }
+            var returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false);
+            returnedAnimatable.weight = weight;
+            return returnedAnimatable;
+        };
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent) {
+            if (speedRatio === void 0) { speedRatio = 1.0; }
+            if (stopCurrent === void 0) { stopCurrent = true; }
             if (from > to && speedRatio > 0) {
                 speedRatio *= -1;
             }
-            this.stopAnimation(target);
+            if (stopCurrent) {
+                this.stopAnimation(target);
+            }
             if (!animatable) {
                 animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd);
             }
@@ -23168,10 +23329,12 @@ var BABYLON;
             if (target.getAnimatables) {
                 var animatables = target.getAnimatables();
                 for (var index = 0; index < animatables.length; index++) {
-                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable);
+                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent);
                 }
             }
-            animatable.reset();
+            if (stopCurrent) {
+                animatable.reset();
+            }
             return animatable;
         };
         /**
@@ -23269,6 +23432,62 @@ var BABYLON;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 this._activeAnimatables[index]._animate(this._animationTime);
             }
+            // Late animation bindings
+            this._processLateAnimationBindings();
+        };
+        /** @ignore */
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+            var target = runtimeAnimation.target;
+            this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+            if (!target._lateAnimationHolders) {
+                target._lateAnimationHolders = {};
+            }
+            if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+                target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+                    totalWeight: 0,
+                    animations: []
+                };
+            }
+            target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+            target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+        };
+        Scene.prototype._processLateAnimationBindings = function () {
+            if (!this._registeredForLateAnimationBindings.length) {
+                return;
+            }
+            for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+                var target = this._registeredForLateAnimationBindings.data[index];
+                for (var path in target._lateAnimationHolders) {
+                    var holder = target._lateAnimationHolders[path];
+                    // Sanity check
+                    if (!holder.animations[0].originalValue.scaleAndAddToRef) {
+                        continue;
+                    }
+                    var normalizer = 1.0;
+                    var finalValue = void 0;
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        var originalValue = holder.animations[0].originalValue;
+                        finalValue = originalValue.scale(1.0 - holder.totalWeight);
+                    }
+                    else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                    }
+                    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];
+                        if (finalValue) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(runtimeAnimation.weight / normalizer, finalValue);
+                        }
+                        else {
+                            finalValue = runtimeAnimation.currentValue.scale(runtimeAnimation.weight / normalizer);
+                        }
+                    }
+                    runtimeAnimation.target[path] = finalValue;
+                }
+                target._lateAnimationHolders = {};
+            }
+            this._registeredForLateAnimationBindings.reset();
         };
         // Matrix
         Scene.prototype._switchToAlternateCameraConfiguration = function (active) {
@@ -24898,6 +25117,7 @@ var BABYLON;
             this._activeSkeletons.dispose();
             this._softwareSkinnedMeshes.dispose();
             this._renderTargets.dispose();
+            this._registeredForLateAnimationBindings.dispose();
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.dispose();
             }
@@ -47541,16 +47761,74 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     var RuntimeAnimation = /** @class */ (function () {
-        function RuntimeAnimation(target, animation) {
+        /**
+         * Create a new RuntimeAnimation object
+         * @param target defines the target of the animation
+         * @param animation defines the source {BABYLON.Animation} object
+         * @param scene defines the hosting scene
+         */
+        function RuntimeAnimation(target, animation, scene) {
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
             this._blendingFactor = 0;
+            this._weight = 1.0;
             this._ratioOffset = 0;
             this._animation = animation;
             this._target = target;
+            this._scene = scene;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "weight", {
+            /**
+             * Gets the weight of the runtime animation
+             */
+            get: function () {
+                return this._weight;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "originalValue", {
+            /**
+             * Gets the original value of the runtime animation
+             */
+            get: function () {
+                return this._originalValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "currentValue", {
+            /**
+             * Gets the current value of the runtime animation
+             */
+            get: function () {
+                return this._currentValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "targetPath", {
+            /**
+             * Gets the path where to store the animated value in the target
+             */
+            get: function () {
+                return this._targetPath;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "target", {
+            /**
+             * Gets the actual target of the runtime animation
+             */
+            get: function () {
+                return this._activeTarget;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "animation", {
             get: function () {
                 return this._animation;
@@ -47563,7 +47841,7 @@ var BABYLON;
             this._highLimitsCache = {};
             this.currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalBlendValue = null;
+            this._originalValue = null;
         };
         RuntimeAnimation.prototype.isStopped = function () {
             return this._stopped;
@@ -47691,8 +47969,13 @@ var BABYLON;
             }
             return this._getKeyValue(keys[keys.length - 1].value);
         };
-        RuntimeAnimation.prototype.setValue = function (currentValue, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Affect the interpolated value to the target
+         * @param currentValue defines the value computed by the animation
+         * @param weight defines the weight to apply to this value
+         */
+        RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
+            if (weight === void 0) { weight = 1.0; }
             // Set value
             var path;
             var destination;
@@ -47702,43 +47985,62 @@ var BABYLON;
                 for (var index = 1; index < targetPropertyPath.length - 1; index++) {
                     property = property[targetPropertyPath[index]];
                 }
-                path = targetPropertyPath[targetPropertyPath.length - 1];
+                path = [targetPropertyPath.length - 1];
                 destination = property;
             }
             else {
                 path = targetPropertyPath[0];
                 destination = this._target;
             }
+            this._targetPath = path;
+            this._activeTarget = destination;
+            this._weight = weight;
             // Blending
             var enableBlending = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
             var blendingSpeed = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.blendingSpeed : this._animation.blendingSpeed;
-            if (enableBlending && this._blendingFactor <= 1.0) {
-                if (!this._originalBlendValue) {
-                    if (destination[path].clone) {
-                        this._originalBlendValue = destination[path].clone();
+            if (enableBlending && this._blendingFactor <= 1.0 || weight !== -1.0) {
+                if (!this._originalValue) {
+                    var originalValue = void 0;
+                    if (destination.getRestPose) {
+                        originalValue = destination.getRestPose();
                     }
                     else {
-                        this._originalBlendValue = destination[path];
+                        originalValue = destination[path];
+                    }
+                    if (originalValue.clone) {
+                        this._originalValue = originalValue.clone();
+                    }
+                    else {
+                        this._originalValue = originalValue;
                     }
                 }
-                if (this._originalBlendValue.prototype) {
-                    if (this._originalBlendValue.prototype.Lerp) {
-                        destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
+            }
+            if (enableBlending && this._blendingFactor <= 1.0) {
+                if (this._originalValue.prototype) {
+                    if (this._originalValue.prototype.Lerp) {
+                        this._currentValue = this._originalValue.construtor.prototype.Lerp(currentValue, this._originalValue, this._blendingFactor);
                     }
                     else {
-                        destination[path] = currentValue;
+                        this._currentValue = currentValue;
                     }
                 }
-                else if (this._originalBlendValue.m) {
-                    destination[path] = BABYLON.Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
+                else if (this._originalValue.m) {
+                    this._currentValue = BABYLON.Matrix.Lerp(this._originalValue, currentValue, this._blendingFactor);
                 }
                 else {
-                    destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
+                    this._currentValue = this._originalValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
                 }
                 this._blendingFactor += blendingSpeed;
+                destination[path] = this._currentValue;
             }
             else {
-                destination[path] = currentValue;
+                this._currentValue = currentValue;
+                if (weight !== -1.0) {
+                    this._scene._registerTargetForLateAnimationBinding(this);
+                }
+                else {
+                    destination[path] = this._currentValue;
+                }
             }
             if (this._target.markAsDirty) {
                 this._target.markAsDirty(this._animation.targetProperty);
@@ -47750,6 +48052,10 @@ var BABYLON;
             }
             return this._animation.loopMode;
         };
+        /**
+         * Move the current animation to a given frame
+         * @param frame defines the frame to move to
+         */
         RuntimeAnimation.prototype.goToFrame = function (frame) {
             var keys = this._animation.getKeys();
             if (frame < keys[0].frame) {
@@ -47759,14 +48065,24 @@ var BABYLON;
                 frame = keys[keys.length - 1].frame;
             }
             var currentValue = this._interpolate(frame, 0, this._getCorrectLoopMode());
-            this.setValue(currentValue);
+            this.setValue(currentValue, -1);
         };
         RuntimeAnimation.prototype._prepareForSpeedRatioChange = function (newSpeedRatio) {
             var newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
             this._ratioOffset = this._previousRatio - newRatio;
         };
-        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Execute the current animation
+         * @param delay defines the delay to add to the current frame
+         * @param from defines the lower bound of the animation range
+         * @param to defines the upper bound of the animation range
+         * @param loop defines if the current animation must loop
+         * @param speedRatio defines the current speed ratio
+         * @param weight defines the weight of the animation (default is -1 so no weight)
+         * @returns a boolean indicating if the animation has ended
+         */
+        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, weight) {
+            if (weight === void 0) { weight = -1.0; }
             var targetPropertyPath = this._animation.targetPropertyPath;
             if (!targetPropertyPath || targetPropertyPath.length < 1) {
                 this._stopped = true;
@@ -47876,7 +48192,7 @@ var BABYLON;
             var currentFrame = returnValue ? from + ratio % range : to;
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
-            this.setValue(currentValue);
+            this.setValue(currentValue, weight);
             // Check events
             var events = this._animation.getEvents();
             for (var index = 0; index < events.length; index++) {
@@ -47930,6 +48246,7 @@ var BABYLON;
             this._runtimeAnimations = new Array();
             this._paused = false;
             this._speedRatio = 1;
+            this._weight = -1.0;
             this.animationStarted = false;
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -47938,7 +48255,28 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "weight", {
+            /**
+             * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+             */
+            get: function () {
+                return this._weight;
+            },
+            set: function (value) {
+                if (value === -1) {
+                    this._weight = -1;
+                    return;
+                }
+                // Else weight must be in [0, 1] range
+                this._weight = Math.min(Math.max(value, 0), 1.0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "speedRatio", {
+            /**
+             * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
+             */
             get: function () {
                 return this._speedRatio;
             },
@@ -47959,7 +48297,7 @@ var BABYLON;
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -48087,7 +48425,7 @@ var BABYLON;
             var index;
             for (index = 0; index < runtimeAnimations.length; index++) {
                 var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
                 running = running || isRunning;
             }
             this.animationStarted = running;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


+ 2 - 119
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
-  "errors": 7175,
+  "errors": 7154,
   "babylon.typedoc.json": {
-    "errors": 7175,
+    "errors": 7154,
     "AnimationKeyInterpolation": {
       "Enumeration": {
         "Comments": {
@@ -2205,11 +2205,6 @@
             "MissingText": true
           }
         },
-        "speedRatio": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
         "target": {
           "Comments": {
             "MissingText": true
@@ -25731,25 +25726,6 @@
           "MissingText": true
         }
       },
-      "Constructor": {
-        "new RuntimeAnimation": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "target": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "animation": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      },
       "Property": {
         "animation": {
           "Comments": {
@@ -25775,60 +25751,11 @@
             }
           }
         },
-        "animate": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "delay": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "from": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "to": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "loop": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "speedRatio": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "blend": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "dispose": {
           "Comments": {
             "MissingText": true
           }
         },
-        "goToFrame": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "frame": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "isStopped": {
           "Comments": {
             "MissingText": true
@@ -25838,23 +25765,6 @@
           "Comments": {
             "MissingText": true
           }
-        },
-        "setValue": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "currentValue": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "blend": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
         }
       }
     },
@@ -27161,33 +27071,6 @@
             }
           }
         },
-        "beginAnimation": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "loop": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "speedRatio": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "onAnimationEnd": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "animatable": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "beginDirectAnimation": {
           "Parameter": {
             "target": {

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 68 - 67
dist/preview release/viewer/babylon.viewer.js


+ 440 - 102
dist/preview release/viewer/babylon.viewer.max.js

@@ -1655,7 +1655,7 @@ var BABYLON;
         /**
          * Multiplies in place each rgb value by scale
          * @param scale defines the scaling factor
-         * @returns the updated Color3.
+         * @returns the updated Color3
          */
         Color3.prototype.scale = function (scale) {
             return new Color3(this.r * scale, this.g * scale, this.b * scale);
@@ -1664,7 +1664,7 @@ var BABYLON;
          * Multiplies the rgb values by scale and stores the result into "result"
          * @param scale defines the scaling factor
          * @param result defines the Color3 object where to store the result
-         * @returns the unmodified current Color3.
+         * @returns the unmodified current Color3
          */
         Color3.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -1673,6 +1673,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color3 values by a factor and add the result to a given Color3
+         * @param scale defines the scale factor
+         * @param result defines color to store the result into
+         * @returns the unmodified current Color3
+         */
+        Color3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -2040,7 +2052,7 @@ var BABYLON;
          * Multiplies the current Color4 values by scale and stores the result in "result"
          * @param scale defines the scaling factor to apply
          * @param result defines the Color4 object where to store the result
-         * @returns the current Color4.
+         * @returns the current unmodified Color4
          */
         Color4.prototype.scaleToRef = function (scale, result) {
             result.r = this.r * scale;
@@ -2050,6 +2062,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Color4 values by a factor and add the result to a given Color4
+         * @param scale defines the scale factor
+         * @param result defines the Color4 object where to store the result
+         * @returns the unmodified current Color4
+         */
+        Color4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            result.a += this.a * scale;
+            return this;
+        };
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -2493,7 +2518,31 @@ var BABYLON;
          * Returns a new Vector2 scaled by "scale" from the current Vector2.
          */
         Vector2.prototype.scale = function (scale) {
-            return new Vector2(this.x * scale, this.y * scale);
+            var result = new Vector2(0, 0);
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Vector2 values by a factor to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            return this;
+        };
+        /**
+         * Scale the current Vector2 values by a factor and add the result to a given Vector2
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2
+         */
+        Vector2.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            return this;
         };
         /**
          * Boolean : True if the passed vector coordinates strictly equal the current Vector2 ones.
@@ -2933,6 +2982,18 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector3 values by a factor and add the result to a given Vector3
+         * @param scale defines the scale factor
+         * @param result defines the Vector3 object where to store the result
+         * @returns the unmodified current Vector3
+         */
+        Vector3.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            return this;
+        };
+        /**
          * Returns true if the current Vector3 and the passed vector coordinates are strictly equal
          * @param otherVector defines the second operand
          * @returns true if both vectors are equals
@@ -3877,6 +3938,19 @@ var BABYLON;
             return this;
         };
         /**
+         * Scale the current Vector4 values by a factor and add the result to a given Vector4
+         * @param scale defines the scale factor
+         * @param result defines the Vector4 object where to store the result
+         * @returns the unmodified current Vector4
+         */
+        Vector4.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Boolean : True if the current Vector4 coordinates are stricly equal to the passed ones.
          */
         Vector4.prototype.equals = function (otherVector) {
@@ -4403,6 +4477,32 @@ var BABYLON;
             return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
         };
         /**
+         * Scale the current Quaternion values by a factor to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleToRef = function (scale, result) {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            result.z = this.z * scale;
+            result.w = this.w * scale;
+            return this;
+        };
+        /**
+         * Scale the current Quaternion values by a factor and add the result to a given Quaternion
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion
+         */
+        Quaternion.prototype.scaleAndAddToRef = function (scale, result) {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        };
+        /**
          * Returns a new Quaternion set as the quaternion mulplication result of the current one with the passed one "q1".
          */
         Quaternion.prototype.multiply = function (q1) {
@@ -5149,6 +5249,99 @@ var BABYLON;
             return true;
         };
         /**
+         * Returns the index-th row of the current matrix as a new Vector4.
+         */
+        Matrix.prototype.getRow = function (index) {
+            if (index < 0 || index > 3) {
+                return null;
+            }
+            var i = index * 4;
+            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed Vector4 values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRow = function (index, row) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = row.x;
+            this.m[i + 1] = row.y;
+            this.m[i + 2] = row.z;
+            this.m[i + 3] = row.w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns a new Matrix.
+         */
+        Matrix.prototype.transpose = function () {
+            return Matrix.Transpose(this);
+        };
+        /**
+         * Compute the transpose of the matrix.
+         * Returns the current matrix.
+         */
+        Matrix.prototype.transposeToRef = function (result) {
+            Matrix.TransposeToRef(this, result);
+            return this;
+        };
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.
+         */
+        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+            this._markAsUpdated();
+            return this;
+        };
+        /**
+         * Compute a new Matrix set with the current Matrix values multiplied by scale (float)
+         * @param scale defines the scale factor
+         * @returns a new Matrix
+         */
+        Matrix.prototype.scale = function (scale) {
+            var result = new Matrix();
+            this.scaleToRef(scale, result);
+            return result;
+        };
+        /**
+         * Scale the current Matrix values by a factor to a given result Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] = this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
+         * Scale the current Matrix values by a factor and add the result to a given Matrix
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix
+         */
+        Matrix.prototype.scaleAndAddToRef = function (scale, result) {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] += this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        };
+        /**
          * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).
          * @param ref matrix to store the result
          */
@@ -5233,63 +5426,6 @@ var BABYLON;
             result.m[15] = initialM44;
             result._markAsUpdated();
         };
-        /**
-         * Returns the index-th row of the current matrix as a new Vector4.
-         */
-        Matrix.prototype.getRow = function (index) {
-            if (index < 0 || index > 3) {
-                return null;
-            }
-            var i = index * 4;
-            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed Vector4 values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRow = function (index, row) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = row.x;
-            this.m[i + 1] = row.y;
-            this.m[i + 2] = row.z;
-            this.m[i + 3] = row.w;
-            this._markAsUpdated();
-            return this;
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns a new Matrix.
-         */
-        Matrix.prototype.transpose = function () {
-            return Matrix.Transpose(this);
-        };
-        /**
-         * Compute the transpose of the matrix.
-         * Returns the current matrix.
-         */
-        Matrix.prototype.transposeToRef = function (result) {
-            Matrix.TransposeToRef(this, result);
-            return this;
-        };
-        /**
-         * Sets the index-th row of the current matrix with the passed 4 x float values.
-         * Returns the updated Matrix.
-         */
-        Matrix.prototype.setRowFromFloats = function (index, x, y, z, w) {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = x;
-            this.m[i + 1] = y;
-            this.m[i + 2] = z;
-            this.m[i + 3] = w;
-            this._markAsUpdated();
-            return this;
-        };
         Object.defineProperty(Matrix, "IdentityReadOnly", {
             /**
              * Static identity matrix to be used as readonly matrix
@@ -12582,7 +12718,7 @@ var BABYLON;
             this.applyStates();
             this._drawCalls.addCount(1, false);
             // Render
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
@@ -12596,7 +12732,7 @@ var BABYLON;
             // Apply states
             this.applyStates();
             this._drawCalls.addCount(1, false);
-            var drawMode = this.DrawMode(fillMode);
+            var drawMode = this._drawMode(fillMode);
             if (instancesCount) {
                 this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             }
@@ -12604,7 +12740,7 @@ var BABYLON;
                 this._gl.drawArrays(drawMode, verticesStart, verticesCount);
             }
         };
-        Engine.prototype.DrawMode = function (fillMode) {
+        Engine.prototype._drawMode = function (fillMode) {
             switch (fillMode) {
                 // Triangle views
                 case BABYLON.Material.TriangleFillMode:
@@ -21731,6 +21867,7 @@ var BABYLON;
             this.onRenderingGroupObservable = new BABYLON.Observable();
             // Animations
             this.animations = [];
+            this._registeredForLateAnimationBindings = new BABYLON.SmartArrayNoDuplicate(256);
             /**
              * This observable event is triggered when any ponter event is triggered. It is registered during Scene.attachControl() and it is called BEFORE the 3D engine process anything (mesh/sprite picking for instance).
              * You have the possibility to skip the process and the call to onPointerObservable by setting PointerInfoPre.skipOnPointerObservable to true
@@ -23251,22 +23388,46 @@ var BABYLON;
         // Animations
         /**
          * Will start the animation sequence of a given target
-         * @param target - the target
-         * @param {number} from - from which frame should animation start
-         * @param {number} to - till which frame should animation run.
-         * @param {boolean} [loop] - should the animation loop
-         * @param {number} [speedRatio] - the speed in which to run the animation
-         * @param {Function} [onAnimationEnd] function to be executed when the animation ended.
-         * @param {BABYLON.Animatable} [animatable] an animatable object. If not provided a new one will be created from the given params.
-         * Returns {BABYLON.Animatable} the animatable object created for this animation
-         * See BABYLON.Animatable
-         */
-        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable) {
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginWeightedAnimation = function (target, from, to, weight, loop, speedRatio, onAnimationEnd, animatable) {
+            if (weight === void 0) { weight = 1.0; }
             if (speedRatio === void 0) { speedRatio = 1.0; }
+            var returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false);
+            returnedAnimatable.weight = weight;
+            return returnedAnimatable;
+        };
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent) {
+            if (speedRatio === void 0) { speedRatio = 1.0; }
+            if (stopCurrent === void 0) { stopCurrent = true; }
             if (from > to && speedRatio > 0) {
                 speedRatio *= -1;
             }
-            this.stopAnimation(target);
+            if (stopCurrent) {
+                this.stopAnimation(target);
+            }
             if (!animatable) {
                 animatable = new BABYLON.Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd);
             }
@@ -23278,10 +23439,12 @@ var BABYLON;
             if (target.getAnimatables) {
                 var animatables = target.getAnimatables();
                 for (var index = 0; index < animatables.length; index++) {
-                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable);
+                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent);
                 }
             }
-            animatable.reset();
+            if (stopCurrent) {
+                animatable.reset();
+            }
             return animatable;
         };
         /**
@@ -23379,6 +23542,62 @@ var BABYLON;
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 this._activeAnimatables[index]._animate(this._animationTime);
             }
+            // Late animation bindings
+            this._processLateAnimationBindings();
+        };
+        /** @ignore */
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+            var target = runtimeAnimation.target;
+            this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+            if (!target._lateAnimationHolders) {
+                target._lateAnimationHolders = {};
+            }
+            if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+                target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+                    totalWeight: 0,
+                    animations: []
+                };
+            }
+            target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+            target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+        };
+        Scene.prototype._processLateAnimationBindings = function () {
+            if (!this._registeredForLateAnimationBindings.length) {
+                return;
+            }
+            for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+                var target = this._registeredForLateAnimationBindings.data[index];
+                for (var path in target._lateAnimationHolders) {
+                    var holder = target._lateAnimationHolders[path];
+                    // Sanity check
+                    if (!holder.animations[0].originalValue.scaleAndAddToRef) {
+                        continue;
+                    }
+                    var normalizer = 1.0;
+                    var finalValue = void 0;
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        var originalValue = holder.animations[0].originalValue;
+                        finalValue = originalValue.scale(1.0 - holder.totalWeight);
+                    }
+                    else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                    }
+                    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];
+                        if (finalValue) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(runtimeAnimation.weight / normalizer, finalValue);
+                        }
+                        else {
+                            finalValue = runtimeAnimation.currentValue.scale(runtimeAnimation.weight / normalizer);
+                        }
+                    }
+                    runtimeAnimation.target[path] = finalValue;
+                }
+                target._lateAnimationHolders = {};
+            }
+            this._registeredForLateAnimationBindings.reset();
         };
         // Matrix
         Scene.prototype._switchToAlternateCameraConfiguration = function (active) {
@@ -25008,6 +25227,7 @@ var BABYLON;
             this._activeSkeletons.dispose();
             this._softwareSkinnedMeshes.dispose();
             this._renderTargets.dispose();
+            this._registeredForLateAnimationBindings.dispose();
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.dispose();
             }
@@ -47651,16 +47871,74 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     var RuntimeAnimation = /** @class */ (function () {
-        function RuntimeAnimation(target, animation) {
+        /**
+         * Create a new RuntimeAnimation object
+         * @param target defines the target of the animation
+         * @param animation defines the source {BABYLON.Animation} object
+         * @param scene defines the hosting scene
+         */
+        function RuntimeAnimation(target, animation, scene) {
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
             this._blendingFactor = 0;
+            this._weight = 1.0;
             this._ratioOffset = 0;
             this._animation = animation;
             this._target = target;
+            this._scene = scene;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "weight", {
+            /**
+             * Gets the weight of the runtime animation
+             */
+            get: function () {
+                return this._weight;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "originalValue", {
+            /**
+             * Gets the original value of the runtime animation
+             */
+            get: function () {
+                return this._originalValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "currentValue", {
+            /**
+             * Gets the current value of the runtime animation
+             */
+            get: function () {
+                return this._currentValue;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "targetPath", {
+            /**
+             * Gets the path where to store the animated value in the target
+             */
+            get: function () {
+                return this._targetPath;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(RuntimeAnimation.prototype, "target", {
+            /**
+             * Gets the actual target of the runtime animation
+             */
+            get: function () {
+                return this._activeTarget;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "animation", {
             get: function () {
                 return this._animation;
@@ -47673,7 +47951,7 @@ var BABYLON;
             this._highLimitsCache = {};
             this.currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalBlendValue = null;
+            this._originalValue = null;
         };
         RuntimeAnimation.prototype.isStopped = function () {
             return this._stopped;
@@ -47801,8 +48079,13 @@ var BABYLON;
             }
             return this._getKeyValue(keys[keys.length - 1].value);
         };
-        RuntimeAnimation.prototype.setValue = function (currentValue, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Affect the interpolated value to the target
+         * @param currentValue defines the value computed by the animation
+         * @param weight defines the weight to apply to this value
+         */
+        RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
+            if (weight === void 0) { weight = 1.0; }
             // Set value
             var path;
             var destination;
@@ -47812,43 +48095,62 @@ var BABYLON;
                 for (var index = 1; index < targetPropertyPath.length - 1; index++) {
                     property = property[targetPropertyPath[index]];
                 }
-                path = targetPropertyPath[targetPropertyPath.length - 1];
+                path = [targetPropertyPath.length - 1];
                 destination = property;
             }
             else {
                 path = targetPropertyPath[0];
                 destination = this._target;
             }
+            this._targetPath = path;
+            this._activeTarget = destination;
+            this._weight = weight;
             // Blending
             var enableBlending = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
             var blendingSpeed = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.blendingSpeed : this._animation.blendingSpeed;
-            if (enableBlending && this._blendingFactor <= 1.0) {
-                if (!this._originalBlendValue) {
-                    if (destination[path].clone) {
-                        this._originalBlendValue = destination[path].clone();
+            if (enableBlending && this._blendingFactor <= 1.0 || weight !== -1.0) {
+                if (!this._originalValue) {
+                    var originalValue = void 0;
+                    if (destination.getRestPose) {
+                        originalValue = destination.getRestPose();
                     }
                     else {
-                        this._originalBlendValue = destination[path];
+                        originalValue = destination[path];
+                    }
+                    if (originalValue.clone) {
+                        this._originalValue = originalValue.clone();
+                    }
+                    else {
+                        this._originalValue = originalValue;
                     }
                 }
-                if (this._originalBlendValue.prototype) {
-                    if (this._originalBlendValue.prototype.Lerp) {
-                        destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
+            }
+            if (enableBlending && this._blendingFactor <= 1.0) {
+                if (this._originalValue.prototype) {
+                    if (this._originalValue.prototype.Lerp) {
+                        this._currentValue = this._originalValue.construtor.prototype.Lerp(currentValue, this._originalValue, this._blendingFactor);
                     }
                     else {
-                        destination[path] = currentValue;
+                        this._currentValue = currentValue;
                     }
                 }
-                else if (this._originalBlendValue.m) {
-                    destination[path] = BABYLON.Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
+                else if (this._originalValue.m) {
+                    this._currentValue = BABYLON.Matrix.Lerp(this._originalValue, currentValue, this._blendingFactor);
                 }
                 else {
-                    destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
+                    this._currentValue = this._originalValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
                 }
                 this._blendingFactor += blendingSpeed;
+                destination[path] = this._currentValue;
             }
             else {
-                destination[path] = currentValue;
+                this._currentValue = currentValue;
+                if (weight !== -1.0) {
+                    this._scene._registerTargetForLateAnimationBinding(this);
+                }
+                else {
+                    destination[path] = this._currentValue;
+                }
             }
             if (this._target.markAsDirty) {
                 this._target.markAsDirty(this._animation.targetProperty);
@@ -47860,6 +48162,10 @@ var BABYLON;
             }
             return this._animation.loopMode;
         };
+        /**
+         * Move the current animation to a given frame
+         * @param frame defines the frame to move to
+         */
         RuntimeAnimation.prototype.goToFrame = function (frame) {
             var keys = this._animation.getKeys();
             if (frame < keys[0].frame) {
@@ -47869,14 +48175,24 @@ var BABYLON;
                 frame = keys[keys.length - 1].frame;
             }
             var currentValue = this._interpolate(frame, 0, this._getCorrectLoopMode());
-            this.setValue(currentValue);
+            this.setValue(currentValue, -1);
         };
         RuntimeAnimation.prototype._prepareForSpeedRatioChange = function (newSpeedRatio) {
             var newRatio = this._previousDelay * (this._animation.framePerSecond * newSpeedRatio) / 1000.0;
             this._ratioOffset = this._previousRatio - newRatio;
         };
-        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, blend) {
-            if (blend === void 0) { blend = false; }
+        /**
+         * Execute the current animation
+         * @param delay defines the delay to add to the current frame
+         * @param from defines the lower bound of the animation range
+         * @param to defines the upper bound of the animation range
+         * @param loop defines if the current animation must loop
+         * @param speedRatio defines the current speed ratio
+         * @param weight defines the weight of the animation (default is -1 so no weight)
+         * @returns a boolean indicating if the animation has ended
+         */
+        RuntimeAnimation.prototype.animate = function (delay, from, to, loop, speedRatio, weight) {
+            if (weight === void 0) { weight = -1.0; }
             var targetPropertyPath = this._animation.targetPropertyPath;
             if (!targetPropertyPath || targetPropertyPath.length < 1) {
                 this._stopped = true;
@@ -47986,7 +48302,7 @@ var BABYLON;
             var currentFrame = returnValue ? from + ratio % range : to;
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
-            this.setValue(currentValue);
+            this.setValue(currentValue, weight);
             // Check events
             var events = this._animation.getEvents();
             for (var index = 0; index < events.length; index++) {
@@ -48040,6 +48356,7 @@ var BABYLON;
             this._runtimeAnimations = new Array();
             this._paused = false;
             this._speedRatio = 1;
+            this._weight = -1.0;
             this.animationStarted = false;
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -48048,7 +48365,28 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "weight", {
+            /**
+             * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+             */
+            get: function () {
+                return this._weight;
+            },
+            set: function (value) {
+                if (value === -1) {
+                    this._weight = -1;
+                    return;
+                }
+                // Else weight must be in [0, 1] range
+                this._weight = Math.min(Math.max(value, 0), 1.0);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "speedRatio", {
+            /**
+             * Gets or sets the speed ratio to apply to the animatable (1.0 by default)
+             */
             get: function () {
                 return this._speedRatio;
             },
@@ -48069,7 +48407,7 @@ var BABYLON;
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -48197,7 +48535,7 @@ var BABYLON;
             var index;
             for (index = 0; index < runtimeAnimations.length; index++) {
                 var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
                 running = running || isRunning;
             }
             this.animationStarted = running;

+ 15 - 11
dist/preview release/what's new.md

@@ -2,26 +2,28 @@
 
 ## Major updates
 
-- Support for [GPU particles](https://doc.babylonjs.com/babylon101/particles#gpu-particles) ([deltakosh](https://github.com/deltakosh))
+- Support for [GPU particles](https://doc.babylonjs.com/babylon101/particles#gpu-particles). Demo [here](https://www.babylonjs-playground.com/frame.html#PU4WYI#2) ([deltakosh](https://github.com/deltakosh))
 - Improved building process: We now run a full visual validation test for each pull request. Furthermore, code comments and what's new updates are now mandatory ([sebavan](https://github.com/sebavan))
+- Babylon.js now uses Promises in addition to callbacks. We created several `xxxAsync` functions all over the framework (`SceneLoader.AppendAsync` for instance, which returns a Promise). A polyfill is also integrated to support older browsers ([deltakosh](https://github.com/deltakosh))
 - Introduced texture binding atlas. This optimization allows the engine to reuse texture bindings instead of rebinding textures when they are not on constant sampler indexes ([deltakosh](https://github.com/deltakosh))
 - New [AnimationGroup class](http://doc.babylonjs.com/how_to/group) to control simultaneously multiple animations with different targets ([deltakosh](https://github.com/deltakosh))
-- `WebVRCamera` add basic support for Daydream and Gear VR ([brianzinn](https://github.com/brianzinn))
-- New glTF [serializer](https://github.com/BabylonJS/Babylon.js/tree/master/serializers/src/glTF/2.0). You can now export glTF or glb files directly from a Babylon scene ([kcoley](https://github.com/kcoley))
-- Babylon.js now uses Promises in addition to callbacks. We created several `xxxAsync` functions all over the framework (`SceneLoader.AppendAsync` for instance, which returns a Promise). A polyfill is also integrated to support older browsers ([deltakosh](https://github.com/deltakosh))
-- Introduced [Projection Texture on SpotLight](http://doc.babylonjs.com/babylon101/lights#projection-texture) ([lostink](https://github.com/lostink))
-- Introduced support for [local cubemaps](http://doc.babylonjs.com/how_to/reflect#using-local-cubemap-mode) ([deltakosh](https://github.com/deltakosh))
-- Added [VideoDome](http://doc.babylonjs.com/how_to/360videodome) class to easily support 360 videos ([DavidHGillen](https://github.com/DavidHGillen))
-- Added [GlowLayer](https://doc.babylonjs.com/how_to/glow_layer) to easily support glow from emissive materials ([sebavan](https://github.com/sebavan))
-- New [AssetContainer](http://doc.babylonjs.com/how_to/how_to_use_assetcontainer) Class and loading methods ([trevordev](https://github.com/trevordev))
-- Added depth of field, sharpening, MSAA, chromatic aberration and grain effect to the default pipeline ([trevordev](https://github.com/trevordev))
-- Particle System SubEmitters- Spawn new Sub emitter when particles dies. Cone/Sphere shapes emitters ([IbraheemOsama](https://github.com/IbraheemOsama))
+- `WebVRCamera`: added basic support for Daydream and Gear VR ([brianzinn](https://github.com/brianzinn))
+- Introduced [Projection Texture on SpotLight](http://doc.babylonjs.com/babylon101/lights#projection-texture). Demo [here](https://www.babylonjs-playground.com/frame.html#CQNGRK) ([lostink](https://github.com/lostink))
+- Introduced support for [local cubemaps](http://doc.babylonjs.com/how_to/reflect#using-local-cubemap-mode). Demo [here](https://www.babylonjs-playground.com/frame.html#RNASML#4) ([deltakosh](https://github.com/deltakosh))
+- Added [VideoDome](http://doc.babylonjs.com/how_to/360videodome) class to easily support 360 videos. Demo [here](https://www.babylonjs-playground.com/frame.html#1E9JQ8#7) ([DavidHGillen](https://github.com/DavidHGillen))
+- Added [GlowLayer](https://doc.babylonjs.com/how_to/glow_layer) to easily support glow from emissive materials. Demo [here](http://www.babylonjs.com/demos/GlowLayer/) ([sebavan](https://github.com/sebavan))
+- New [AssetContainer](http://doc.babylonjs.com/how_to/how_to_use_assetcontainer) class and loading methods ([trevordev](https://github.com/trevordev))
+- Added [depth of field](https://www.babylonjs-playground.com/frame.html#8F5HYV#5), sharpening, MSAA, chromatic aberration and grain effect to the default pipeline ([trevordev](https://github.com/trevordev))
+- Added support for [animation weights](http://doc.babylonjs.com/babylon101/animations#animation-weights). Demo [here](https://www.babylonjs-playground.com/#IQN716#3) ([deltakosh](https://github.com/deltakosh))
+- Added [sub emitters for particle system](http://doc.babylonjs.com/babylon101/particles#sub-emitters) which will spawn new particle systems when particles dies. Demo [here](https://www.babylonjs-playground.com/frame.html#9NHBCC#1) ([IbraheemOsama](https://github.com/IbraheemOsama))
 - New [Babylon.js](http://doc.babylonjs.com/resources/maya) and [glTF](http://doc.babylonjs.com/resources/maya_to_gltf) exporter for Autodesk Maya ([Noalak](https://github.com/Noalak))
+- New glTF [serializer](https://github.com/BabylonJS/Babylon.js/tree/master/serializers/src/glTF/2.0). You can now export glTF or glb files directly from a Babylon scene ([kcoley](https://github.com/kcoley))
 - New [glTF exporter](http://doc.babylonjs.com/resources/3dsmax_to_gltf) for Autodesk 3dsmax ([Noalak](https://github.com/Noalak))
 
 ## Updates
 
 - Tons of functions and classes received the code comments they deserved (All the community)
+- New [particle system emitter shapes](http://doc.babylonjs.com/babylon101/particles#particles-shapes): cone and sphere ([IbraheemOsama](https://github.com/IbraheemOsama))
 - Added support for 16bits TGA ([deltakosh](https://github.com/deltakosh))
 - New `AnimationPropertiesOverride` class used to simplify setting animation properties on child animations. [Documentation](http://doc.babylonjs.com/babylon101/animations#overriding-properties) ([deltakosh](https://github.com/deltakosh))
 - New `Texture.UseSerializedUrlIfAny` static property to let textures serialize complete URL instead of using side by side loading ([deltakosh](https://github.com/deltakosh))
@@ -94,6 +96,7 @@
 - onPointer* callbacks have now the event type as a 3rd variable ([RaananW](https://github.com/RaananW))
 - Lightmap texture in PBR material follow the gammaSpace Flag of the texture ([sebavan](https://github.com/sebavan))
 - Added setTextureFromPostProcessOutput to bind the output of a postprocess into an effect ([trevordev](https://github.com/trevordev))
+- Added support for primitive modes to glTF 2.0 loader. ([bghgary](https://github.com/bghgary)]
 
 ## Bug fixes
 
@@ -112,6 +115,7 @@
 - Fixed WebCam Texture on Firefox and Edge - [#3825](https://github.com/BabylonJS/Babylon.js/issues/3825) ([sebavan](https://github.com/sebavan))
 - Add onLoadObservable on VideoTexture - [#3845](https://github.com/BabylonJS/Babylon.js/issues/3845) ([sebavan](https://github.com/sebavan))
 - beforeRender is now triggered after the camera updated its state - [#3873](https://github.com/BabylonJS/Babylon.js/issues/3873) ([RaananW](https://github.com/RaananW))
+- Tools.DeepCopy no longer copying getter-only elements - [#3929](https://github.com/BabylonJS/Babylon.js/issues/3929) ([RaananW](https://github.com/RaananW))
 
 ## Breaking changes
 

+ 22 - 16
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -16,34 +16,40 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRMaterialsPbrSpecularGlossiness>(context, material, (context, extension) => {
-                material._babylonMeshes = material._babylonMeshes || [];
-                material._babylonMeshes.push(babylonMesh);
-
-                if (!material._loaded) {
+                material._babylonData = material._babylonData || {};
+                let babylonData = material._babylonData[babylonDrawMode];
+                if (!babylonData) {
                     const promises = new Array<Promise<void>>();
 
-                    const babylonMaterial = this._loader._createMaterial(material);
-                    material._babylonMaterial = babylonMaterial;
+                    const name = material.name || `materialSG_${material._index}`;
+                    const babylonMaterial = this._loader._createMaterial(PBRMaterial, name, babylonDrawMode);
 
-                    promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material));
-                    promises.push(this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                    promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
+                    promises.push(this._loadSpecularGlossinessPropertiesAsync(context, material, extension, babylonMaterial));
 
                     this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                    material._loaded = Promise.all(promises).then(() => {});
+
+                    babylonData = {
+                        material: babylonMaterial,
+                        meshes: [],
+                        loaded: Promise.all(promises).then(() => {})
+                    };
+
+                    material._babylonData[babylonDrawMode] = babylonData;
                 }
 
-                assign(material._babylonMaterial!);
-                return material._loaded;
+                babylonData.meshes.push(babylonMesh);
+
+                assign(babylonData.material);
+                return babylonData.loaded;
             });
         }
 
-        private _loadSpecularGlossinessPropertiesAsync(context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): Promise<void> {
+        private _loadSpecularGlossinessPropertiesAsync(context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness, babylonMaterial: PBRMaterial): Promise<void> {
             const promises = new Array<Promise<void>>();
 
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
             if (properties.diffuseFactor) {
                 babylonMaterial.albedoColor = Color3.FromArray(properties.diffuseFactor);
                 babylonMaterial.alpha = properties.diffuseFactor[3];
@@ -70,7 +76,7 @@ module BABYLON.GLTF2.Extensions {
                 babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
             }
 
-            this._loader._loadMaterialAlphaProperties(context, material);
+            this._loader._loadMaterialAlphaProperties(context, material, babylonMaterial);
 
             return Promise.all(promises).then(() => {});
         }

+ 8 - 7
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -71,7 +71,7 @@ module BABYLON.GLTF2.Extensions {
             });
         }
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             // Don't load material LODs if already loading a node LOD.
             if (this._loadingNodeLOD) {
                 return null;
@@ -92,14 +92,15 @@ module BABYLON.GLTF2.Extensions {
                         }
                     }
 
-                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, indexLOD === 0 ? assign : () => {}).then(() => {
+                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, babylonDrawMode, indexLOD === 0 ? assign : () => {}).then(() => {
                         if (indexLOD !== 0) {
-                            assign(materialLOD._babylonMaterial!);
+                            const babylonDataLOD = materialLOD._babylonData!;
+                            assign(babylonDataLOD[babylonDrawMode].material);
 
-                            const previousMaterialLOD = materialLODs[indexLOD - 1];
-                            if (previousMaterialLOD._babylonMaterial) {
-                                previousMaterialLOD._babylonMaterial.dispose();
-                                delete previousMaterialLOD._babylonMaterial;
+                            const previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData!;
+                            if (previousBabylonDataLOD[babylonDrawMode]) {
+                                previousBabylonDataLOD[babylonDrawMode].material.dispose();
+                                delete previousBabylonDataLOD[babylonDrawMode];
                             }
                         }
 

+ 74 - 63
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -16,6 +16,11 @@ module BABYLON.GLTF2 {
         readonly BYTES_PER_ELEMENT: number;
     }
 
+    export interface MaterialConstructor<T extends Material> {
+        readonly prototype: T;
+        new(name: string, scene: Scene): T;
+    }
+
     export class GLTFLoader implements IGLTFLoader {
         public _gltf: ILoaderGLTF;
         public _babylonScene: Scene;
@@ -27,6 +32,7 @@ module BABYLON.GLTF2 {
         private _rootUrl: string;
         private _rootBabylonMesh: Mesh;
         private _defaultSampler = {} as ILoaderSampler;
+        private _defaultBabylonMaterials: { [drawMode: number]: PBRMaterial } = {};
         private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
         private _requests = new Array<IFileRequestInfo>();
 
@@ -315,7 +321,7 @@ module BABYLON.GLTF2 {
             }
             else {
                 callback(node._babylonMesh!);
-        }
+            }
         }
 
         private _getMeshes(): Mesh[] {
@@ -329,12 +335,12 @@ module BABYLON.GLTF2 {
                 for (const node of nodes) {
                     if (node._babylonMesh) {
                         meshes.push(node._babylonMesh);
-                }
+                    }
 
                     if (node._primitiveBabylonMeshes) {
                         for (const babylonMesh of node._primitiveBabylonMeshes) {
                             meshes.push(babylonMesh);
-            }
+                        }
                     }
                 }
             }
@@ -470,12 +476,13 @@ module BABYLON.GLTF2 {
                 return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
             }));
 
+            const babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
             if (primitive.material == undefined) {
-                babylonMesh.material = this._getDefaultMaterial();
+                babylonMesh.material = this._getDefaultMaterial(babylonDrawMode);
             }
             else {
                 const material = GLTFLoader._GetProperty(`${context}/material}`, this._gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonMaterial => {
+                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonDrawMode, babylonMaterial => {
                     babylonMesh.material = babylonMaterial;
                 }));
             }
@@ -494,11 +501,6 @@ module BABYLON.GLTF2 {
                 throw new Error(`${context}: Attributes are missing`);
             }
 
-            if (primitive.mode != undefined && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
-                // TODO: handle other primitive modes
-                throw new Error(`${context}: Mode (${primitive.mode}) is not currently supported`);
-            }
-
             const promises = new Array<Promise<void>>();
 
             const babylonVertexData = new VertexData();
@@ -944,19 +946,9 @@ module BABYLON.GLTF2 {
                     }
                 }
 
-                let keys: Array<IAnimationKey>;
-                if (data.input.length === 1) {
-                    let key = getNextKey!(0);
-                    keys = [
-                        { frame: key.frame, value: key.value },
-                        { frame: key.frame + 1, value: key.value }
-                    ];
-                }
-                else {
-                    keys = new Array(data.input.length);
-                    for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) {
-                        keys[frameIndex] = getNextKey!(frameIndex);
-                    }
+                const keys = new Array(data.input.length);
+                for (let frameIndex = 0; frameIndex < data.input.length; frameIndex++) {
+                    keys[frameIndex] = getNextKey!(frameIndex);
                 }
 
                 if (targetPath === "influence") {
@@ -1141,13 +1133,11 @@ module BABYLON.GLTF2 {
             return targetBuffer;
         }
 
-        private _getDefaultMaterial(): Material {
-            const id = "__gltf_default";
-            let babylonMaterial = <PBRMaterial>this._babylonScene.getMaterialByName(id);
+        private _getDefaultMaterial(drawMode: number): Material {
+            let babylonMaterial = this._defaultBabylonMaterials[drawMode];
             if (!babylonMaterial) {
-                babylonMaterial = new PBRMaterial(id, this._babylonScene);
+                babylonMaterial = this._createMaterial(PBRMaterial, "__gltf_default", drawMode);
                 babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE;
-                babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
                 babylonMaterial.metallic = 1;
                 babylonMaterial.roughness = 1;
                 this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
@@ -1156,11 +1146,9 @@ module BABYLON.GLTF2 {
             return babylonMaterial;
         }
 
-        private _loadMaterialMetallicRoughnessPropertiesAsync(context: string, material: ILoaderMaterial): Promise<void> {
+        private _loadMaterialMetallicRoughnessPropertiesAsync(context: string, material: ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void> {
             const promises = new Array<Promise<void>>();
 
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
             // Ensure metallic workflow
             babylonMaterial.metallic = 1;
             babylonMaterial.roughness = 1;
@@ -1195,48 +1183,55 @@ module BABYLON.GLTF2 {
                 }
             }
 
-            this._loadMaterialAlphaProperties(context, material);
+            this._loadMaterialAlphaProperties(context, material, babylonMaterial);
 
             return Promise.all(promises).then(() => {});
         }
 
-        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
+        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Promise<void> {
+            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, babylonDrawMode, assign);
             if (promise) {
                 return promise;
             }
 
-            material._babylonMeshes = material._babylonMeshes || [];
-            material._babylonMeshes.push(babylonMesh);
-
-            if (!material._loaded) {
+            material._babylonData = material._babylonData || {};
+            let babylonData = material._babylonData[babylonDrawMode];
+            if (!babylonData) {
                 const promises = new Array<Promise<void>>();
 
-                const babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
+                const name = material.name || `materialSG_${material._index}`;
+                const babylonMaterial = this._createMaterial(PBRMaterial, name, babylonDrawMode);
 
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
+                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material, babylonMaterial));
 
                 this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                material._loaded = Promise.all(promises).then(() => {});
+
+                babylonData = {
+                    material: babylonMaterial,
+                    meshes: [],
+                    loaded: Promise.all(promises).then(() => {})
+                };
+
+                material._babylonData[babylonDrawMode] = babylonData;
             }
 
-            assign(material._babylonMaterial!);
-            return material._loaded;
+            babylonData.meshes.push(babylonMesh);
+
+            assign(babylonData.material);
+            return babylonData.loaded;
         }
 
-        public _createMaterial(material: ILoaderMaterial): PBRMaterial {
-            const babylonMaterial = new PBRMaterial(material.name || `material${material._index}`, this._babylonScene);
+        public _createMaterial<T extends Material>(type: MaterialConstructor<T>, name: string, drawMode: number): T {
+            const babylonMaterial = new type(name, this._babylonScene);
             babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
+            babylonMaterial.fillMode = drawMode;
             return babylonMaterial;
         }
 
-        public _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void> {
+        public _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void> {
             const promises = new Array<Promise<void>>();
 
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
             babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0);
             if (material.doubleSided) {
                 babylonMaterial.backFaceCulling = false;
@@ -1275,9 +1270,7 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
         }
 
-        public _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void {
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
+        public _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial, babylonMaterial: PBRMaterial): void {
             const alphaMode = material.alphaMode || MaterialAlphaMode.OPAQUE;
             switch (alphaMode) {
                 case MaterialAlphaMode.OPAQUE: {
@@ -1532,21 +1525,39 @@ module BABYLON.GLTF2 {
             return (Tools.IsBase64(uri) || uri.indexOf("..") === -1);
         }
 
+        private static _GetDrawMode(context: string, mode: number | undefined): number {
+            mode = mode || MeshPrimitiveMode.TRIANGLES;
+
+            switch (mode) {
+                case MeshPrimitiveMode.POINTS: return Material.PointListDrawMode;
+                case MeshPrimitiveMode.LINES: return Material.LineListDrawMode;
+                case MeshPrimitiveMode.LINE_LOOP: return Material.LineLoopDrawMode;
+                case MeshPrimitiveMode.LINE_STRIP: return Material.LineStripDrawMode;
+                case MeshPrimitiveMode.TRIANGLES: return Material.TriangleFillMode;
+                case MeshPrimitiveMode.TRIANGLE_STRIP: return Material.TriangleStripDrawMode;
+                case MeshPrimitiveMode.TRIANGLE_FAN: return Material.TriangleFanDrawMode;
+            }
+
+            throw new Error(`${context}: Invalid mesh primitive mode (${mode})`);
+        }
+
         private _compileMaterialsAsync(): Promise<void> {
             const promises = new Array<Promise<void>>();
 
             if (this._gltf.materials) {
                 for (const material of this._gltf.materials) {
-                    const babylonMaterial = material._babylonMaterial;
-                    const babylonMeshes = material._babylonMeshes;
-                    if (babylonMaterial && babylonMeshes) {
-                        for (const babylonMesh of babylonMeshes) {
-                            // Ensure nonUniformScaling is set if necessary.
-                            babylonMesh.computeWorldMatrix(true);
-
-                            promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
-                            if (this.useClipPlane) {
-                                promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
+                    if (material._babylonData) {
+                        for (const babylonDrawMode in material._babylonData) {
+                            const babylonData = material._babylonData[babylonDrawMode];
+                            for (const babylonMesh of babylonData.meshes) {
+                                // Ensure nonUniformScaling is set if necessary.
+                                babylonMesh.computeWorldMatrix(true);
+
+                                const babylonMaterial = babylonData.material;
+                                promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
+                                if (this.useClipPlane) {
+                                    promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
+                                }
                             }
                         }
                     }

+ 3 - 3
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -27,7 +27,7 @@ module BABYLON.GLTF2 {
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> { return null; }
 
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
 
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> { return null; }
@@ -75,8 +75,8 @@ module BABYLON.GLTF2 {
         }
 
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, assign));
+        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
         }
 
         /** Helper method called by the loader to allow extensions to override loading uris. */

+ 9 - 6
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -7,7 +7,6 @@ module BABYLON.GLTF2 {
     }
 
     export interface ILoaderAnimationChannel extends IAnimationChannel, IArrayItem {
-        _babylonAnimationGroup: AnimationGroup;
     }
 
     export interface ILoaderAnimationSamplerData {
@@ -24,7 +23,7 @@ module BABYLON.GLTF2 {
         channels: ILoaderAnimationChannel[];
         samplers: ILoaderAnimationSampler[];
 
-        _babylonAnimationGroup: Nullable<AnimationGroup>;
+        _babylonAnimationGroup?: AnimationGroup;
     }
 
     export interface ILoaderBuffer extends IBuffer, IArrayItem {
@@ -43,9 +42,13 @@ module BABYLON.GLTF2 {
     }
 
     export interface ILoaderMaterial extends IMaterial, IArrayItem {
-        _babylonMaterial?: Material;
-        _babylonMeshes?: AbstractMesh[];
-        _loaded?: Promise<void>;
+        _babylonData?: {
+            [drawMode: number]: {
+                material: Material;
+                meshes: AbstractMesh[];
+                loaded: Promise<void>;
+            }
+        };
     }
 
     export interface ILoaderMesh extends IMesh, IArrayItem {
@@ -78,7 +81,7 @@ module BABYLON.GLTF2 {
     }
 
     export interface ILoaderSkin extends ISkin, IArrayItem {
-        _babylonSkeleton: Nullable<Skeleton>;
+        _babylonSkeleton?: Skeleton;
         _loaded?: Promise<void>;
     }
 

+ 24 - 2
src/Animations/babylon.animatable.ts

@@ -6,9 +6,30 @@
         private _paused = false;
         private _scene: Scene;
         private _speedRatio = 1;
+        private _weight = -1.0;
 
         public animationStarted = false;
 
+        /**
+         * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
+         */
+        public get weight(): number {
+            return this._weight;
+        }
+
+        public set weight(value: number) {
+            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)
+         */
         public get speedRatio(): number {
             return this._speedRatio;
         }
@@ -22,6 +43,7 @@
             this._speedRatio = value;
         }
 
+
         constructor(scene: Scene, public target: any, public fromFrame: number = 0, public toFrame: number = 100, public loopAnimation: boolean = false, speedRatio: number = 1.0, public onAnimationEnd?: Nullable<() => void>, animations?: any) {
             if (animations) {
                 this.appendAnimations(target, animations);
@@ -41,7 +63,7 @@
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
 
-                this._runtimeAnimations.push(new RuntimeAnimation(target, animation));
+                this._runtimeAnimations.push(new RuntimeAnimation(target, animation, this._scene));
             }
         }
 
@@ -205,7 +227,7 @@
 
             for (index = 0; index < runtimeAnimations.length; index++) {
                 var animation = runtimeAnimations[index];
-                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio);
+                var isRunning = animation.animate(delay - this._localDelayOffset, this.fromFrame, this.toFrame, this.loopAnimation, this._speedRatio, this._weight);
                 running = running || isRunning;
             }
 

+ 111 - 22
src/Animations/babylon.runtimeAnimation.ts

@@ -5,15 +5,63 @@
         private _animation: Animation;
         private _target: any;
 
-        private _originalBlendValue: any;
+        private _originalValue: any;
         private _offsetsCache: {[key: string]: any} = {};
         private _highLimitsCache: {[key: string]: any} = {};
         private _stopped = false;
         private _blendingFactor = 0;
-        
-        public constructor(target: any, animation: Animation) {
+        private _scene: Scene;
+
+        private _currentValue: any;
+        private _activeTarget: any;
+        private _targetPath: string;
+        private _weight = 1.0
+
+        /**
+         * Gets the weight of the runtime animation
+         */
+        public get weight(): number {
+            return this._weight;
+        }           
+
+        /**
+         * Gets the original value of the runtime animation
+         */
+        public get originalValue(): any {
+            return this._originalValue;
+        }        
+
+        /**
+         * Gets the current value of the runtime animation
+         */
+        public get currentValue(): any {
+            return this._currentValue;
+        }
+
+        /**
+         * Gets the path where to store the animated value in the target
+         */
+        public get targetPath(): string {
+            return this._targetPath;
+        }
+
+        /**
+         * Gets the actual target of the runtime animation
+         */
+        public get target(): any {
+            return this._activeTarget;
+        }
+
+        /**
+         * Create a new RuntimeAnimation object
+         * @param target defines the target of the animation
+         * @param animation defines the source {BABYLON.Animation} object
+         * @param scene defines the hosting scene
+         */
+        public constructor(target: any, animation: Animation, scene: Scene) {
             this._animation = animation;
             this._target = target;
+            this._scene = scene;
 
             animation._runtimeAnimations.push(this);
         }
@@ -27,7 +75,7 @@
             this._highLimitsCache = {};
             this.currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalBlendValue = null;
+            this._originalValue = null;
         }
 
         public isStopped(): boolean {
@@ -175,7 +223,12 @@
             return this._getKeyValue(keys[keys.length - 1].value);
         }
 
-        public setValue(currentValue: any, blend: boolean = false): void {
+        /**
+         * Affect the interpolated value to the target
+         * @param currentValue defines the value computed by the animation
+         * @param weight defines the weight to apply to this value
+         */
+        public setValue(currentValue: any, weight = 1.0): void {
             // Set value
             var path: any;
             var destination: any;
@@ -189,42 +242,63 @@
                     property = property[targetPropertyPath[index]];
                 }
 
-                path = targetPropertyPath[targetPropertyPath.length - 1];
+                path =  [targetPropertyPath.length - 1];
                 destination = property;
             } else {
                 path = targetPropertyPath[0];
                 destination = this._target;
             }
 
+            this._targetPath = path;
+            this._activeTarget = destination;
+            this._weight = weight;
+
             // Blending
             let enableBlending = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.enableBlending : this._animation.enableBlending;
             let blendingSpeed = this._target && this._target.animationPropertiesOverride ? this._target.animationPropertiesOverride.blendingSpeed : this._animation.blendingSpeed;
             
-            if (enableBlending && this._blendingFactor <= 1.0) {
-                if (!this._originalBlendValue) {
-                    if (destination[path].clone) {
-                        this._originalBlendValue = destination[path].clone();
+            if (enableBlending && this._blendingFactor <= 1.0 || weight !== -1.0) {
+                if (!this._originalValue) {
+                    let originalValue: any;
+
+                    if (destination.getRestPose) { // For bones
+                        originalValue = destination.getRestPose();
+                    } else {
+                        originalValue = destination[path]
+                    }
+
+                    if (originalValue.clone) {
+                        this._originalValue = originalValue.clone();
                     } else {
-                        this._originalBlendValue = destination[path];
+                        this._originalValue = originalValue;
                     }
                 }
+            }
 
-                if (this._originalBlendValue.prototype) { // Complex value
+            if (enableBlending && this._blendingFactor <= 1.0) {
+                if (this._originalValue.prototype) { // Complex value
                     
-                    if (this._originalBlendValue.prototype.Lerp) { // Lerp supported
-                        destination[path] = this._originalBlendValue.construtor.prototype.Lerp(currentValue, this._originalBlendValue, this._blendingFactor);
+                    if (this._originalValue.prototype.Lerp) { // Lerp supported
+                        this._currentValue = this._originalValue.construtor.prototype.Lerp(currentValue, this._originalValue, this._blendingFactor);
                     } else { // Blending not supported
-                        destination[path] = currentValue;
+                        this._currentValue = currentValue;
                     }
 
-                } else if (this._originalBlendValue.m) { // Matrix
-                    destination[path] = Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
+                } else if (this._originalValue.m) { // Matrix
+                    this._currentValue = Matrix.Lerp(this._originalValue, currentValue, this._blendingFactor);
                 } else { // Direct value
-                    destination[path] = this._originalBlendValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
+                    this._currentValue = this._originalValue * (1.0 - this._blendingFactor) + this._blendingFactor * currentValue;
                 }
                 this._blendingFactor += blendingSpeed;
+                
+                destination[path] = this._currentValue;
             } else {
-                destination[path] = currentValue;
+                this._currentValue = currentValue;
+                if (weight !== -1.0) {
+                    this._scene._registerTargetForLateAnimationBinding(this);
+                } else {
+                    destination[path] = this._currentValue;
+                }
             }
 
             if (this._target.markAsDirty) {
@@ -240,6 +314,10 @@
             return this._animation.loopMode;
         }
 
+        /**
+         * Move the current animation to a given frame
+         * @param frame defines the frame to move to
+         */
         public goToFrame(frame: number): void {
             let keys = this._animation.getKeys();
 
@@ -251,7 +329,7 @@
 
             var currentValue = this._interpolate(frame, 0, this._getCorrectLoopMode());
 
-            this.setValue(currentValue);
+            this.setValue(currentValue, -1);
         }
 
         public _prepareForSpeedRatioChange(newSpeedRatio: number): void {
@@ -264,7 +342,17 @@
         private _previousDelay: number;
         private _previousRatio: number;
 
-        public animate(delay: number, from: number, to: number, loop: boolean, speedRatio: number, blend: boolean = false): boolean {
+        /**
+         * Execute the current animation
+         * @param delay defines the delay to add to the current frame
+         * @param from defines the lower bound of the animation range
+         * @param to defines the upper bound of the animation range
+         * @param loop defines if the current animation must loop
+         * @param speedRatio defines the current speed ratio
+         * @param weight defines the weight of the animation (default is -1 so no weight)
+         * @returns a boolean indicating if the animation has ended
+         */
+        public animate(delay: number, from: number, to: number, loop: boolean, speedRatio: number, weight = -1.0): boolean {
             let targetPropertyPath = this._animation.targetPropertyPath
             if (!targetPropertyPath || targetPropertyPath.length < 1) {
                 this._stopped = true;
@@ -385,7 +473,8 @@
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
 
             // Set value
-            this.setValue(currentValue);
+            this.setValue(currentValue, weight);
+
             // Check events
             let events = this._animation.getEvents();
             for (var index = 0; index < events.length; index++) {

+ 3 - 3
src/Engine/babylon.engine.ts

@@ -2634,7 +2634,7 @@
             this._drawCalls.addCount(1, false);
             // Render
 
-            const drawMode = this.DrawMode(fillMode);
+            const drawMode = this._drawMode(fillMode);
             var indexFormat = this._uintIndicesCurrentlySet ? this._gl.UNSIGNED_INT : this._gl.UNSIGNED_SHORT;
             var mult = this._uintIndicesCurrentlySet ? 4 : 2;
             if (instancesCount) {
@@ -2649,7 +2649,7 @@
             this.applyStates();
             this._drawCalls.addCount(1, false);
 
-            const drawMode = this.DrawMode(fillMode);
+            const drawMode = this._drawMode(fillMode);
             if (instancesCount) {
                 this._gl.drawArraysInstanced(drawMode, verticesStart, verticesCount, instancesCount);
             } else {
@@ -2657,7 +2657,7 @@
             }
         }
 
-        private DrawMode(fillMode: number): number {
+        private _drawMode(fillMode: number): number {
             switch (fillMode) {
                 // Triangle views
                 case Material.TriangleFillMode:

+ 15 - 15
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -132,6 +132,8 @@
 
         public FORCENORMALFORWARD = false;
 
+        public UNLIT = false;
+
         /**
          * Initializes the PBR Material defines.
          */
@@ -533,6 +535,11 @@
         private _useLogarithmicDepth: boolean;
 
         /**
+         * If set to true, no lighting calculations will be applied.
+         */
+        private _unlit = false;
+
+        /**
          * Instantiates a new PBRMaterial instance.
          * 
          * @param name The material name
@@ -787,20 +794,10 @@
                 }
             }
 
-            if (!engine.getCaps().standardDerivatives) {
-                let bufferMesh = null;
-                if (mesh.getClassName() === "InstancedMesh") {
-                    bufferMesh = (mesh as InstancedMesh).sourceMesh;
-                }
-                else if (mesh.getClassName() === "Mesh") {
-                    bufferMesh = mesh as Mesh;
-                }
-
-                if (bufferMesh && bufferMesh.geometry && bufferMesh.geometry.isReady() && !bufferMesh.geometry.isVerticesDataPresent(VertexBuffer.NormalKind)) {
-                    bufferMesh.createNormals(true);
-                    Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + bufferMesh.name);
+            if (!engine.getCaps().standardDerivatives && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
+                mesh.createNormals(true);
+                Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + mesh.name);
                 }
-            }
 
             const effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
             if (effect) {
@@ -999,7 +996,7 @@
 
             // Textures
             defines.METALLICWORKFLOW = this.isMetallicWorkflow();
-            if (defines._areTexturesDirty) {     
+            if (defines._areTexturesDirty) {
                 defines._needUVs = false;
                 if (scene.texturesEnabled) {
                     if (scene.getEngine().getCaps().textureLOD) {
@@ -1217,7 +1214,10 @@
             defines.HORIZONOCCLUSION = this._useHorizonOcclusion;
 
             // Misc.
-            MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh) || this._forceAlphaTest, defines);
+            if (defines._areMiscDirty) {
+                MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh) || this._forceAlphaTest, defines);
+                defines.UNLIT = this._unlit || ((this.pointsCloud || this.wireframe) && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind));
+            }
 
             // Values that need to be evaluated on every frame
             MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false, useClipPlane);

+ 7 - 0
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -475,6 +475,13 @@
         public useRadianceOcclusion = true;
 
         /**
+         * If set to true, no lighting calculations will be applied.
+         */
+        @serialize()
+        @expandToProperty("_markAllSubMeshesAsLightsDirty")
+        public unlit = false;
+
+        /**
          * Gets the image processing configuration used either in this material.
          */
         public get imageProcessingConfiguration(): ImageProcessingConfiguration {

+ 19 - 5
src/Materials/babylon.material.ts

@@ -638,14 +638,22 @@
          */
         @serialize()
         public get wireframe(): boolean {
-            return this._fillMode === Material.WireFrameFillMode;
+            switch (this._fillMode) {
+                case Material.WireFrameFillMode:
+                case Material.LineListDrawMode:
+                case Material.LineLoopDrawMode:
+                case Material.LineStripDrawMode:
+                    return true;
+            }
+
+            return this._scene.forceWireframe;
         }
 
         /**
          * Sets the state of wireframe mode.
          */
         public set wireframe(value: boolean) {
-            this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
+            this.fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
         }
 
         /**
@@ -653,14 +661,20 @@
          */
         @serialize()
         public get pointsCloud(): boolean {
-            return this._fillMode === Material.PointFillMode;
+            switch (this._fillMode) {
+                case Material.PointFillMode:
+                case Material.PointListDrawMode:
+                    return true;
+            }
+
+            return this._scene.forcePointsCloud;
         }
 
         /**
          * Sets the state of point cloud mode.
          */
         public set pointsCloud(value: boolean) {
-            this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
+            this.fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
         }
 
         /**
@@ -1202,7 +1216,7 @@
                 defines.markAsTexturesDirty();
                 defines.markAsMiscDirty();
             });
-        }        
+        }
 
         /**
          * Disposes the material.

+ 1 - 1
src/Materials/babylon.materialHelper.ts

@@ -71,7 +71,7 @@ module BABYLON {
         public static PrepareDefinesForMisc(mesh: AbstractMesh, scene: Scene, useLogarithmicDepth: boolean, pointsCloud: boolean, fogEnabled: boolean, alphaTest: boolean, defines: any): void {
             if (defines._areMiscDirty) {
                 defines["LOGARITHMICDEPTH"] = useLogarithmicDepth;
-                defines["POINTSIZE"] = (pointsCloud || scene.forcePointsCloud);
+                defines["POINTSIZE"] = pointsCloud;
                 defines["FOG"] = (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && fogEnabled);
                 defines["NONUNIFORMSCALING"] = mesh.nonUniformScaling;
                 defines["ALPHATEST"] = alphaTest;

+ 221 - 69
src/Math/babylon.math.ts

@@ -148,7 +148,7 @@
         /**
          * Multiplies in place each rgb value by scale 
          * @param scale defines the scaling factor
-         * @returns the updated Color3.  
+         * @returns the updated Color3 
          */
         public scale(scale: number): Color3 {
             return new Color3(this.r * scale, this.g * scale, this.b * scale);
@@ -158,7 +158,7 @@
          * Multiplies the rgb values by scale and stores the result into "result"
          * @param scale defines the scaling factor 
          * @param result defines the Color3 object where to store the result
-         * @returns the unmodified current Color3.  
+         * @returns the unmodified current Color3 
          */
         public scaleToRef(scale: number, result: Color3): Color3 {
             result.r = this.r * scale;
@@ -168,6 +168,19 @@
         }
 
         /**
+         * Scale the current Color3 values by a factor and add the result to a given Color3  
+         * @param scale defines the scale factor
+         * @param result defines color to store the result into
+         * @returns the unmodified current Color3 
+         */
+        public scaleAndAddToRef(scale: number, result: Color3): Color3 {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            return this;
+        }          
+
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -553,7 +566,7 @@
          * Multiplies the current Color4 values by scale and stores the result in "result"
          * @param scale defines the scaling factor to apply
          * @param result defines the Color4 object where to store the result
-         * @returns the current Color4.  
+         * @returns the current unmodified Color4
          */
         public scaleToRef(scale: number, result: Color4): Color4 {
             result.r = this.r * scale;
@@ -564,6 +577,20 @@
         }
 
         /**
+         * Scale the current Color4 values by a factor and add the result to a given Color4  
+         * @param scale defines the scale factor
+         * @param result defines the Color4 object where to store the result
+         * @returns the unmodified current Color4 
+         */
+        public scaleAndAddToRef(scale: number, result: Color4): Color4 {
+            result.r += this.r * scale;
+            result.g += this.g * scale;
+            result.b += this.b * scale;
+            result.a += this.a * scale;
+            return this;
+        }          
+
+        /**
          * Clamps the rgb values by the min and max values and stores the result into "result"
          * @param min defines minimum clamping value (default is 0)
          * @param max defines maximum clamping value (default is 1)
@@ -1026,12 +1053,40 @@
             this.y *= scale;
             return this;
         }
+
         /**
          * Returns a new Vector2 scaled by "scale" from the current Vector2.  
          */
         public scale(scale: number): Vector2 {
-            return new Vector2(this.x * scale, this.y * scale);
+            let result = new Vector2(0, 0);
+            this.scaleToRef(scale, result);
+            return result;
         }
+
+        /**
+         * Scale the current Vector2 values by a factor to a given Vector2  
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2 
+         */
+        public scaleToRef(scale: number, result: Vector2): Vector2 {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            return this;
+        }         
+
+        /**
+         * Scale the current Vector2 values by a factor and add the result to a given Vector2  
+         * @param scale defines the scale factor
+         * @param result defines the Vector2 object where to store the result
+         * @returns the unmodified current Vector2 
+         */
+        public scaleAndAddToRef(scale: number, result: Vector2): Vector2 {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            return this;
+        } 
+
         /**
          * Boolean : True if the passed vector coordinates strictly equal the current Vector2 ones.  
          */
@@ -1515,6 +1570,19 @@
         }
 
         /**
+         * Scale the current Vector3 values by a factor and add the result to a given Vector3  
+         * @param scale defines the scale factor
+         * @param result defines the Vector3 object where to store the result
+         * @returns the unmodified current Vector3 
+         */
+        public scaleAndAddToRef(scale: number, result: Vector3): Vector3 {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            return this;
+        }         
+
+        /**
          * Returns true if the current Vector3 and the passed vector coordinates are strictly equal
          * @param otherVector defines the second operand
          * @returns true if both vectors are equals
@@ -2554,6 +2622,20 @@
         }
 
         /**
+         * Scale the current Vector4 values by a factor and add the result to a given Vector4  
+         * @param scale defines the scale factor
+         * @param result defines the Vector4 object where to store the result
+         * @returns the unmodified current Vector4 
+         */
+        public scaleAndAddToRef(scale: number, result: Vector4): Vector4 {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        }         
+
+        /**
          * Boolean : True if the current Vector4 coordinates are stricly equal to the passed ones.  
          */
         public equals(otherVector: Vector4): boolean {
@@ -3088,6 +3170,35 @@
         public scale(value: number): Quaternion {
             return new Quaternion(this.x * value, this.y * value, this.z * value, this.w * value);
         }
+
+        /**
+         * Scale the current Quaternion values by a factor to a given Quaternion  
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion 
+         */
+        public scaleToRef(scale: number, result: Quaternion): Quaternion {
+            result.x = this.x * scale;
+            result.y = this.y * scale;
+            result.z = this.z * scale;
+            result.w = this.w * scale;
+            return this;
+        }         
+
+        /**
+         * Scale the current Quaternion values by a factor and add the result to a given Quaternion  
+         * @param scale defines the scale factor
+         * @param result defines the Quaternion object where to store the result
+         * @returns the unmodified current Quaternion 
+         */
+        public scaleAndAddToRef(scale: number, result: Quaternion): Quaternion {
+            result.x += this.x * scale;
+            result.y += this.y * scale;
+            result.z += this.z * scale;
+            result.w += this.w * scale;
+            return this;
+        }            
+
         /**
          * Returns a new Quaternion set as the quaternion mulplication result of the current one with the passed one "q1".  
          */
@@ -3899,6 +4010,111 @@
         }
 
         /**
+         * Returns the index-th row of the current matrix as a new Vector4.  
+         */
+        public getRow(index: number): Nullable<Vector4> {
+            if (index < 0 || index > 3) {
+                return null;
+            }
+            var i = index * 4;
+            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
+        }
+
+        /**
+         * Sets the index-th row of the current matrix with the passed Vector4 values.
+         * Returns the updated Matrix.    
+         */
+        public setRow(index: number, row: Vector4): Matrix {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = row.x;
+            this.m[i + 1] = row.y;
+            this.m[i + 2] = row.z;
+            this.m[i + 3] = row.w;
+
+            this._markAsUpdated();
+
+            return this;
+        }
+
+        /**
+         * Compute the transpose of the matrix.  
+         * Returns a new Matrix.  
+         */
+        public transpose(): Matrix {
+            return Matrix.Transpose(this);
+        }
+
+        /**
+         * Compute the transpose of the matrix.  
+         * Returns the current matrix.  
+         */
+        public transposeToRef(result: Matrix): Matrix {
+            Matrix.TransposeToRef(this, result);
+
+            return this;
+        }
+
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.    
+         */
+        public setRowFromFloats(index: number, x: number, y: number, z: number, w: number): Matrix {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+
+            this._markAsUpdated();
+            return this;
+        }
+
+        /**
+         * Compute a new Matrix set with the current Matrix values multiplied by scale (float)
+         * @param scale defines the scale factor
+         * @returns a new Matrix
+         */
+        public scale(scale: number): Matrix {
+            var result = new Matrix();
+            this.scaleToRef(scale, result);
+            return result;
+        }
+
+        /**
+         * Scale the current Matrix values by a factor to a given result Matrix  
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix  
+         */
+        public scaleToRef(scale: number, result: Matrix): Matrix {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] = this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        }          
+        
+        /**
+         * Scale the current Matrix values by a factor and add the result to a given Matrix  
+         * @param scale defines the scale factor
+         * @param result defines the Matrix to store the result
+         * @returns the current Matrix  
+         */
+        public scaleAndAddToRef(scale: number, result: Matrix): Matrix {
+            for (var index = 0; index < 16; index++) {
+                result.m[index] += this.m[index] * scale;
+            }
+            result._markAsUpdated();
+            return this;
+        }          
+
+        /**
          * Writes to the given matrix a normal matrix, computed from this one (using values from identity matrix for fourth row and column).  
          * @param ref matrix to store the result
          */
@@ -4004,71 +4220,7 @@
             result.m[15] = initialM44;
 
             result._markAsUpdated();
-        }
-        /**
-         * Returns the index-th row of the current matrix as a new Vector4.  
-         */
-        public getRow(index: number): Nullable<Vector4> {
-            if (index < 0 || index > 3) {
-                return null;
-            }
-            var i = index * 4;
-            return new Vector4(this.m[i + 0], this.m[i + 1], this.m[i + 2], this.m[i + 3]);
-        }
-        /**
-         * Sets the index-th row of the current matrix with the passed Vector4 values.
-         * Returns the updated Matrix.    
-         */
-        public setRow(index: number, row: Vector4): Matrix {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = row.x;
-            this.m[i + 1] = row.y;
-            this.m[i + 2] = row.z;
-            this.m[i + 3] = row.w;
-
-            this._markAsUpdated();
-
-            return this;
-        }
-
-        /**
-         * Compute the transpose of the matrix.  
-         * Returns a new Matrix.  
-         */
-        public transpose(): Matrix {
-            return Matrix.Transpose(this);
-        }
-
-        /**
-         * Compute the transpose of the matrix.  
-         * Returns the current matrix.  
-         */
-        public transposeToRef(result: Matrix): Matrix {
-            Matrix.TransposeToRef(this, result);
-
-            return this;
-        }
-
-        /**
-         * Sets the index-th row of the current matrix with the passed 4 x float values.
-         * Returns the updated Matrix.    
-         */
-        public setRowFromFloats(index: number, x: number, y: number, z: number, w: number): Matrix {
-            if (index < 0 || index > 3) {
-                return this;
-            }
-            var i = index * 4;
-            this.m[i + 0] = x;
-            this.m[i + 1] = y;
-            this.m[i + 2] = z;
-            this.m[i + 3] = w;
-
-            this._markAsUpdated();
-            return this;
-        }
+        }    
 
         /**
          * Static identity matrix to be used as readonly matrix

+ 15 - 0
src/Mesh/babylon.subMesh.ts

@@ -247,6 +247,21 @@
         public intersects(ray: Ray, positions: Vector3[], indices: IndicesArray, fastCheck?: boolean): Nullable<IntersectionInfo> {
             var intersectInfo: Nullable<IntersectionInfo> = null;
 
+            const material = this.getMaterial();
+            if (!material) {
+                return null;
+            }
+
+            switch (material.fillMode) {
+                case Material.PointListDrawMode:
+                case Material.LineListDrawMode:
+                case Material.LineLoopDrawMode:
+                case Material.LineStripDrawMode:
+                case Material.TriangleFanDrawMode:
+                case Material.TriangleStripDrawMode:
+                    return null;
+            }
+
             // LineMesh first as it's also a Mesh...
             if (LinesMesh && this._mesh instanceof LinesMesh) {
                 var lineMesh = <LinesMesh>this._mesh;

+ 386 - 379
src/Shaders/pbr.fragment.fx

@@ -304,431 +304,451 @@ void main(void) {
 	ambientOcclusionColor = mix(ambientOcclusionColor, ambientOcclusionColorMap, vAmbientInfos.z);
 #endif
 
-// _____________________________ Reflectivity Info _______________________________
-	float microSurface = vReflectivityColor.a;
-	vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
+#ifdef UNLIT
+	vec3 diffuseBase = vec3(1., 1., 1.);
+#else
+	// _____________________________ Reflectivity Info _______________________________
+		float microSurface = vReflectivityColor.a;
+		vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
 
-#ifdef METALLICWORKFLOW
-	vec2 metallicRoughness = surfaceReflectivityColor.rg;
+	#ifdef METALLICWORKFLOW
+		vec2 metallicRoughness = surfaceReflectivityColor.rg;
 
-	#ifdef REFLECTIVITY
-		vec4 surfaceMetallicColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
+		#ifdef REFLECTIVITY
+			vec4 surfaceMetallicColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
 
-		#ifdef AOSTOREINMETALMAPRED
-			vec3 aoStoreInMetalMap = vec3(surfaceMetallicColorMap.r, surfaceMetallicColorMap.r, surfaceMetallicColorMap.r);
-			ambientOcclusionColor = mix(ambientOcclusionColor, aoStoreInMetalMap, vReflectivityInfos.z);
-		#endif
+			#ifdef AOSTOREINMETALMAPRED
+				vec3 aoStoreInMetalMap = vec3(surfaceMetallicColorMap.r, surfaceMetallicColorMap.r, surfaceMetallicColorMap.r);
+				ambientOcclusionColor = mix(ambientOcclusionColor, aoStoreInMetalMap, vReflectivityInfos.z);
+			#endif
 
-		#ifdef METALLNESSSTOREINMETALMAPBLUE
-			metallicRoughness.r *= surfaceMetallicColorMap.b;
-		#else
-			metallicRoughness.r *= surfaceMetallicColorMap.r;
-		#endif
+			#ifdef METALLNESSSTOREINMETALMAPBLUE
+				metallicRoughness.r *= surfaceMetallicColorMap.b;
+			#else
+				metallicRoughness.r *= surfaceMetallicColorMap.r;
+			#endif
 
-		#ifdef ROUGHNESSSTOREINMETALMAPALPHA
-			metallicRoughness.g *= surfaceMetallicColorMap.a;
-		#else
-			#ifdef ROUGHNESSSTOREINMETALMAPGREEN
-				metallicRoughness.g *= surfaceMetallicColorMap.g;
+			#ifdef ROUGHNESSSTOREINMETALMAPALPHA
+				metallicRoughness.g *= surfaceMetallicColorMap.a;
+			#else
+				#ifdef ROUGHNESSSTOREINMETALMAPGREEN
+					metallicRoughness.g *= surfaceMetallicColorMap.g;
+				#endif
 			#endif
 		#endif
-	#endif
 
-	#ifdef MICROSURFACEMAP
-		vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
-		metallicRoughness.g *= microSurfaceTexel.r;
-	#endif
-
-	// Compute microsurface form roughness.
-	microSurface = 1.0 - metallicRoughness.g;
+		#ifdef MICROSURFACEMAP
+			vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
+			metallicRoughness.g *= microSurfaceTexel.r;
+		#endif
 
-	// Diffuse is used as the base of the reflectivity.
-	vec3 baseColor = surfaceAlbedo;
+		// Compute microsurface form roughness.
+		microSurface = 1.0 - metallicRoughness.g;
 
-	// Default specular reflectance at normal incidence.
-	// 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
-	const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
+		// Diffuse is used as the base of the reflectivity.
+		vec3 baseColor = surfaceAlbedo;
 
-	// Compute the converted diffuse.
-	surfaceAlbedo = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
+		// Default specular reflectance at normal incidence.
+		// 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
+		const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
 
-	// Compute the converted reflectivity.
-	surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);
-#else
-	#ifdef REFLECTIVITY
-		vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
-		surfaceReflectivityColor *= toLinearSpace(surfaceReflectivityColorMap.rgb);
-		surfaceReflectivityColor *= vReflectivityInfos.y;
-
-		#ifdef MICROSURFACEFROMREFLECTIVITYMAP
-			microSurface *= surfaceReflectivityColorMap.a;
-			microSurface *= vReflectivityInfos.z;
-		#else
-			#ifdef MICROSURFACEAUTOMATIC
-				microSurface *= computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
-			#endif
+		// Compute the converted diffuse.
+		surfaceAlbedo = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
 
-			#ifdef MICROSURFACEMAP
-				vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
-				microSurface *= microSurfaceTexel.r;
+		// Compute the converted reflectivity.
+		surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);
+	#else
+		#ifdef REFLECTIVITY
+			vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
+			surfaceReflectivityColor *= toLinearSpace(surfaceReflectivityColorMap.rgb);
+			surfaceReflectivityColor *= vReflectivityInfos.y;
+
+			#ifdef MICROSURFACEFROMREFLECTIVITYMAP
+				microSurface *= surfaceReflectivityColorMap.a;
+				microSurface *= vReflectivityInfos.z;
+			#else
+				#ifdef MICROSURFACEAUTOMATIC
+					microSurface *= computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
+				#endif
+
+				#ifdef MICROSURFACEMAP
+					vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
+					microSurface *= microSurfaceTexel.r;
+				#endif
 			#endif
 		#endif
 	#endif
-#endif
 
-	// Adapt microSurface.
-	microSurface = clamp(microSurface, 0., 1.);
-	// Compute roughness.
-	float roughness = 1. - microSurface;
-
-// _____________________________ Alpha Fresnel ___________________________________
-#ifdef ALPHAFRESNEL
-	#if defined(ALPHATEST) || defined(ALPHABLEND)
-		// Convert approximate perceptual opacity (gamma-encoded opacity) to linear opacity (absorptance, or inverse transmission)
-		// for use with the linear HDR render target. The final composition will be converted back to gamma encoded values for eventual display.
-		// Uses power 2.0 rather than 2.2 for simplicity/efficiency, and because the mapping does not need to map the gamma applied to RGB.
-		float opacityPerceptual = alpha;
-
-		#ifdef LINEARALPHAFRESNEL
-			float opacity0 = opacityPerceptual;
-		#else
-			float opacity0 = opacityPerceptual * opacityPerceptual;
-		#endif
-		float opacity90 = fresnelGrazingReflectance(opacity0);
+		// Adapt microSurface.
+		microSurface = clamp(microSurface, 0., 1.);
+		// Compute roughness.
+		float roughness = 1. - microSurface;
 
-		vec3 normalForward = faceforward(normalW, -viewDirectionW, normalW);
+	// _____________________________ Alpha Fresnel ___________________________________
+	#ifdef ALPHAFRESNEL
+		#if defined(ALPHATEST) || defined(ALPHABLEND)
+			// Convert approximate perceptual opacity (gamma-encoded opacity) to linear opacity (absorptance, or inverse transmission)
+			// for use with the linear HDR render target. The final composition will be converted back to gamma encoded values for eventual display.
+			// Uses power 2.0 rather than 2.2 for simplicity/efficiency, and because the mapping does not need to map the gamma applied to RGB.
+			float opacityPerceptual = alpha;
 
-		// Calculate the appropriate linear opacity for the current viewing angle (formally, this quantity is the "directional absorptance").
-		alpha = fresnelSchlickEnvironmentGGX(clamp(dot(viewDirectionW, normalForward), 0.0, 1.0), vec3(opacity0), vec3(opacity90), sqrt(microSurface)).x;
-		
-		#ifdef ALPHATEST
-			if (alpha <= ALPHATESTVALUE)
-				discard;
+			#ifdef LINEARALPHAFRESNEL
+				float opacity0 = opacityPerceptual;
+			#else
+				float opacity0 = opacityPerceptual * opacityPerceptual;
+			#endif
+			float opacity90 = fresnelGrazingReflectance(opacity0);
 
-			#ifndef ALPHABLEND
-				// Prevent to blend with the canvas.
-				alpha = 1.0;
+			vec3 normalForward = faceforward(normalW, -viewDirectionW, normalW);
+
+			// Calculate the appropriate linear opacity for the current viewing angle (formally, this quantity is the "directional absorptance").
+			alpha = fresnelSchlickEnvironmentGGX(clamp(dot(viewDirectionW, normalForward), 0.0, 1.0), vec3(opacity0), vec3(opacity90), sqrt(microSurface)).x;
+
+			#ifdef ALPHATEST
+				if (alpha <= ALPHATESTVALUE)
+					discard;
+
+				#ifndef ALPHABLEND
+					// Prevent to blend with the canvas.
+					alpha = 1.0;
+				#endif
 			#endif
 		#endif
 	#endif
-#endif
 
-// _____________________________ Compute LODs Fetch ____________________________________
-	// Compute N dot V.
-	float NdotVUnclamped = dot(normalW, viewDirectionW);
-	float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
-	float alphaG = convertRoughnessToAverageSlope(roughness);
+	// _____________________________ Compute LODs Fetch ____________________________________
+		// Compute N dot V.
+		float NdotVUnclamped = dot(normalW, viewDirectionW);
+		float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
+		float alphaG = convertRoughnessToAverageSlope(roughness);
 
-// _____________________________ Refraction Info _______________________________________
-#ifdef REFRACTION
-	vec3 environmentRefraction = vec3(0., 0., 0.);
-	
-	vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
-	#ifdef REFRACTIONMAP_OPPOSITEZ
-		refractionVector.z *= -1.0;
-	#endif
+	// _____________________________ Refraction Info _______________________________________
+	#ifdef REFRACTION
+		vec3 environmentRefraction = vec3(0., 0., 0.);
 
-	// _____________________________ 2D vs 3D Maps ________________________________
-	#ifdef REFRACTIONMAP_3D
-		refractionVector.y = refractionVector.y * vRefractionInfos.w;
-		vec3 refractionCoords = refractionVector;
-		refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
-	#else
-		vec3 vRefractionUVW = vec3(refractionMatrix * (view * vec4(vPositionW + refractionVector * vRefractionInfos.z, 1.0)));
-		vec2 refractionCoords = vRefractionUVW.xy / vRefractionUVW.z;
-		refractionCoords.y = 1.0 - refractionCoords.y;
-	#endif
+		vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
+		#ifdef REFRACTIONMAP_OPPOSITEZ
+			refractionVector.z *= -1.0;
+		#endif
 
-	#ifdef LODINREFRACTIONALPHA
-		float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
-	#else
-		float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, 1.0);
-	#endif
-	
-	#ifdef LODBASEDMICROSFURACE
-		// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
-		refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
+		// _____________________________ 2D vs 3D Maps ________________________________
+		#ifdef REFRACTIONMAP_3D
+			refractionVector.y = refractionVector.y * vRefractionInfos.w;
+			vec3 refractionCoords = refractionVector;
+			refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
+		#else
+			vec3 vRefractionUVW = vec3(refractionMatrix * (view * vec4(vPositionW + refractionVector * vRefractionInfos.z, 1.0)));
+			vec2 refractionCoords = vRefractionUVW.xy / vRefractionUVW.z;
+			refractionCoords.y = 1.0 - refractionCoords.y;
+		#endif
 
 		#ifdef LODINREFRACTIONALPHA
-			// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection 
-			// is constrained to appropriate LOD levels in order to prevent aliasing.
-			// The environment map is first sampled without custom LOD selection to determine
-			// the hardware-selected LOD, and this is then used to constrain the final LOD selection
-			// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry 
-			// where the normal is varying rapidly).
-
-			// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
-			// manual calculation via derivatives is also possible, but for simplicity we use the 
-			// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
-			float automaticRefractionLOD = UNPACK_LOD(sampleRefraction(refractionSampler, refractionCoords).a);
-			float requestedRefractionLOD = max(automaticRefractionLOD, refractionLOD);
+			float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
 		#else
-			float requestedRefractionLOD = refractionLOD;
+			float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, 1.0);
 		#endif
 
-		environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD).rgb;
-	#else
-		float lodRefractionNormalized = clamp(refractionLOD / log2(vRefractionMicrosurfaceInfos.x), 0., 1.);
-		float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
-
-		vec3 environmentRefractionMid = sampleRefraction(refractionSampler, refractionCoords).rgb;
-		if(lodRefractionNormalizedDoubled < 1.0){
-			environmentRefraction = mix(
-				sampleRefraction(refractionSamplerHigh, refractionCoords).rgb,
-				environmentRefractionMid,
-				lodRefractionNormalizedDoubled
-			);
-		}else{
-			environmentRefraction = mix(
-				environmentRefractionMid,
-				sampleRefraction(refractionSamplerLow, refractionCoords).rgb,
-				lodRefractionNormalizedDoubled - 1.0
-			);
-		}
-	#endif
-
-	#ifdef GAMMAREFRACTION
-		environmentRefraction = toLinearSpace(environmentRefraction.rgb);
-	#endif
-
-	// _____________________________ Levels _____________________________________
-	environmentRefraction *= vRefractionInfos.x;
-#endif
+		#ifdef LODBASEDMICROSFURACE
+			// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+			refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
+
+			#ifdef LODINREFRACTIONALPHA
+				// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection
+				// is constrained to appropriate LOD levels in order to prevent aliasing.
+				// The environment map is first sampled without custom LOD selection to determine
+				// the hardware-selected LOD, and this is then used to constrain the final LOD selection
+				// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry
+				// where the normal is varying rapidly).
+
+				// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
+				// manual calculation via derivatives is also possible, but for simplicity we use the 
+				// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
+				float automaticRefractionLOD = UNPACK_LOD(sampleRefraction(refractionSampler, refractionCoords).a);
+				float requestedRefractionLOD = max(automaticRefractionLOD, refractionLOD);
+			#else
+				float requestedRefractionLOD = refractionLOD;
+			#endif
 
-// _____________________________ Reflection Info _______________________________________
-#ifdef REFLECTION
-	vec3 environmentRadiance = vec3(0., 0., 0.);
-	vec3 environmentIrradiance = vec3(0., 0., 0.);
+			environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD).rgb;
+		#else
+			float lodRefractionNormalized = clamp(refractionLOD / log2(vRefractionMicrosurfaceInfos.x), 0., 1.);
+			float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
+
+			vec3 environmentRefractionMid = sampleRefraction(refractionSampler, refractionCoords).rgb;
+			if(lodRefractionNormalizedDoubled < 1.0){
+				environmentRefraction = mix(
+					sampleRefraction(refractionSamplerHigh, refractionCoords).rgb,
+					environmentRefractionMid,
+					lodRefractionNormalizedDoubled
+				);
+			}else{
+				environmentRefraction = mix(
+					environmentRefractionMid,
+					sampleRefraction(refractionSamplerLow, refractionCoords).rgb,
+					lodRefractionNormalizedDoubled - 1.0
+				);
+			}
+		#endif
+
+		#ifdef GAMMAREFRACTION
+			environmentRefraction = toLinearSpace(environmentRefraction.rgb);
+		#endif
 
-	vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
-	#ifdef REFLECTIONMAP_OPPOSITEZ
-		reflectionVector.z *= -1.0;
+		// _____________________________ Levels _____________________________________
+		environmentRefraction *= vRefractionInfos.x;
 	#endif
 
-	// _____________________________ 2D vs 3D Maps ________________________________
-	#ifdef REFLECTIONMAP_3D
-		vec3 reflectionCoords = reflectionVector;
-	#else
-		vec2 reflectionCoords = reflectionVector.xy;
-		#ifdef REFLECTIONMAP_PROJECTION
-			reflectionCoords /= reflectionVector.z;
+	// _____________________________ Reflection Info _______________________________________
+	#ifdef REFLECTION
+		vec3 environmentRadiance = vec3(0., 0., 0.);
+		vec3 environmentIrradiance = vec3(0., 0., 0.);
+
+		vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
+		#ifdef REFLECTIONMAP_OPPOSITEZ
+			reflectionVector.z *= -1.0;
 		#endif
-		reflectionCoords.y = 1.0 - reflectionCoords.y;
-	#endif
-	
-	#if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX)
-		float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
-	#else
-		float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, 1.);
-	#endif
-
-	#ifdef LODBASEDMICROSFURACE
-		// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
-		reflectionLOD = reflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
-
-		#ifdef LODINREFLECTIONALPHA
-			// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection 
-			// is constrained to appropriate LOD levels in order to prevent aliasing.
-			// The environment map is first sampled without custom LOD selection to determine
-			// the hardware-selected LOD, and this is then used to constrain the final LOD selection
-			// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry 
-			// where the normal is varying rapidly).
-
-			// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
-			// manual calculation via derivatives is also possible, but for simplicity we use the 
-			// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
-			float automaticReflectionLOD = UNPACK_LOD(sampleReflection(reflectionSampler, reflectionCoords).a);
-			float requestedReflectionLOD = max(automaticReflectionLOD, reflectionLOD);
+
+		// _____________________________ 2D vs 3D Maps ________________________________
+		#ifdef REFLECTIONMAP_3D
+			vec3 reflectionCoords = reflectionVector;
+		#else
+			vec2 reflectionCoords = reflectionVector.xy;
+			#ifdef REFLECTIONMAP_PROJECTION
+				reflectionCoords /= reflectionVector.z;
+			#endif
+			reflectionCoords.y = 1.0 - reflectionCoords.y;
+		#endif
+		
+		#if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX)
+			float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
 		#else
-			float requestedReflectionLOD = reflectionLOD;
+			float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, 1.);
 		#endif
 
-		environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD).rgb;
-	#else
-		float lodReflectionNormalized = clamp(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
-		float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
-
-		vec3 environmentSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
-		if(lodReflectionNormalizedDoubled < 1.0){
-			environmentRadiance = mix(
-				sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
-				environmentSpecularMid,
-				lodReflectionNormalizedDoubled
-			);
-		}else{
-			environmentRadiance = mix(
-				environmentSpecularMid,
-				sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
-				lodReflectionNormalizedDoubled - 1.0
-			);
-		}
-	#endif
-
-	#ifdef GAMMAREFLECTION
-		environmentRadiance = toLinearSpace(environmentRadiance.rgb);
-	#endif
-
-	// _____________________________ Irradiance ________________________________
-	#ifdef USESPHERICALFROMREFLECTIONMAP
-		#if defined(NORMAL) && defined(USESPHERICALINVERTEX)
-			environmentIrradiance = vEnvironmentIrradiance;
+		#ifdef LODBASEDMICROSFURACE
+			// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+			reflectionLOD = reflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
+
+			#ifdef LODINREFLECTIONALPHA
+				// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection
+				// is constrained to appropriate LOD levels in order to prevent aliasing.
+				// The environment map is first sampled without custom LOD selection to determine
+				// the hardware-selected LOD, and this is then used to constrain the final LOD selection
+				// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry
+				// where the normal is varying rapidly).
+
+				// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
+				// manual calculation via derivatives is also possible, but for simplicity we use the
+				// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
+				float automaticReflectionLOD = UNPACK_LOD(sampleReflection(reflectionSampler, reflectionCoords).a);
+				float requestedReflectionLOD = max(automaticReflectionLOD, reflectionLOD);
+			#else
+				float requestedReflectionLOD = reflectionLOD;
+			#endif
+
+			environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD).rgb;
 		#else
-			vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
-			#ifdef REFLECTIONMAP_OPPOSITEZ
-				irradianceVector.z *= -1.0;
+			float lodReflectionNormalized = clamp(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
+			float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
+
+			vec3 environmentSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
+			if(lodReflectionNormalizedDoubled < 1.0){
+				environmentRadiance = mix(
+					sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
+					environmentSpecularMid,
+					lodReflectionNormalizedDoubled
+				);
+			}else{
+				environmentRadiance = mix(
+					environmentSpecularMid,
+					sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
+					lodReflectionNormalizedDoubled - 1.0
+				);
+			}
+		#endif
+
+		#ifdef GAMMAREFLECTION
+			environmentRadiance = toLinearSpace(environmentRadiance.rgb);
+		#endif
+
+		// _____________________________ Irradiance ________________________________
+		#ifdef USESPHERICALFROMREFLECTIONMAP
+			#if defined(NORMAL) && defined(USESPHERICALINVERTEX)
+				environmentIrradiance = vEnvironmentIrradiance;
+			#else
+				vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
+				#ifdef REFLECTIONMAP_OPPOSITEZ
+					irradianceVector.z *= -1.0;
+				#endif
+				environmentIrradiance = environmentIrradianceJones(irradianceVector);
 			#endif
-			environmentIrradiance = environmentIrradianceJones(irradianceVector);
 		#endif
+
+		// _____________________________ Levels _____________________________________
+		environmentRadiance *= vReflectionInfos.x;
+		environmentRadiance *= vReflectionColor.rgb;
+		environmentIrradiance *= vReflectionColor.rgb;
 	#endif
 
-	// _____________________________ Levels _____________________________________
-	environmentRadiance *= vReflectionInfos.x;
-	environmentRadiance *= vReflectionColor.rgb;
-	environmentIrradiance *= vReflectionColor.rgb;
-#endif
+	// ____________________________________________________________________________________
+	// _____________________________ Direct Lighting Param ________________________________
+		// Compute reflectance.
+		float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
+		float reflectance90 = fresnelGrazingReflectance(reflectance);
+		vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
+		vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
-// ____________________________________________________________________________________
-// _____________________________ Direct Lighting Param ________________________________
-	// Compute reflectance.
-	float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
-	float reflectance90 = fresnelGrazingReflectance(reflectance);
-	vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
-	vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
-
-// _____________________________ Direct Lighting Info __________________________________
-	vec3 diffuseBase = vec3(0., 0., 0.);
-#ifdef SPECULARTERM
-	vec3 specularBase = vec3(0., 0., 0.);
-#endif
+	// _____________________________ Direct Lighting Info __________________________________
+		vec3 diffuseBase = vec3(0., 0., 0.);
+	#ifdef SPECULARTERM
+		vec3 specularBase = vec3(0., 0., 0.);
+	#endif
 
-#ifdef LIGHTMAP
-	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb);
-	#ifdef GAMMALIGHTMAP
-		lightmapColor = toLinearSpace(lightmapColor);
+	#ifdef LIGHTMAP
+		vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb);
+		#ifdef GAMMALIGHTMAP
+			lightmapColor = toLinearSpace(lightmapColor);
+		#endif
+		lightmapColor *= vLightmapInfos.y
 	#endif
-	lightmapColor *= vLightmapInfos.y
-#endif
 
-	lightingInfo info;
-	float shadow = 1.; // 1 - shadowLevel
-	float NdotL = -1.;
+		lightingInfo info;
+		float shadow = 1.; // 1 - shadowLevel
+		float NdotL = -1.;
 
-#include<lightFragment>[0..maxSimultaneousLights]
+	#include<lightFragment>[0..maxSimultaneousLights]
 
-// _________________________ Specular Environment Oclusion __________________________
-#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
-	// Indexed on cos(theta) and roughness
-	vec2 brdfSamplerUV = vec2(NdotV, roughness);
-	
-	// We can find the scale and offset to apply to the specular value.
-	vec4 environmentBrdf = texture2D(environmentBrdfSampler, brdfSamplerUV);
+	// _________________________ Specular Environment Oclusion __________________________
+	#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
+		// Indexed on cos(theta) and roughness
+		vec2 brdfSamplerUV = vec2(NdotV, roughness);
+		
+		// We can find the scale and offset to apply to the specular value.
+		vec4 environmentBrdf = texture2D(environmentBrdfSampler, brdfSamplerUV);
 
-	vec3 specularEnvironmentReflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
+		vec3 specularEnvironmentReflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
 
-	#ifdef RADIANCEOCCLUSION
-		#ifdef AMBIENTINGRAYSCALE
-			float ambientMonochrome = ambientOcclusionColor.r;
-		#else
-			float ambientMonochrome = getLuminance(ambientOcclusionColor);
-		#endif
+		#ifdef RADIANCEOCCLUSION
+			#ifdef AMBIENTINGRAYSCALE
+				float ambientMonochrome = ambientOcclusionColor.r;
+			#else
+				float ambientMonochrome = getLuminance(ambientOcclusionColor);
+			#endif
 
-		float seo = environmentRadianceOcclusion(ambientMonochrome, NdotVUnclamped);
-		specularEnvironmentReflectance *= seo;
-	#endif
+			float seo = environmentRadianceOcclusion(ambientMonochrome, NdotVUnclamped);
+			specularEnvironmentReflectance *= seo;
+		#endif
 
-	#ifdef HORIZONOCCLUSION
-		#ifdef BUMP
-			#ifdef REFLECTIONMAP_3D
-				float eho = environmentHorizonOcclusion(reflectionCoords, normalW);
-				specularEnvironmentReflectance *= eho;
+		#ifdef HORIZONOCCLUSION
+			#ifdef BUMP
+				#ifdef REFLECTIONMAP_3D
+					float eho = environmentHorizonOcclusion(reflectionCoords, normalW);
+					specularEnvironmentReflectance *= eho;
+				#endif
 			#endif
 		#endif
+	#else
+		// Jones implementation of a well balanced fast analytical solution.
+		vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
 	#endif
-#else
-	// Jones implementation of a well balanced fast analytical solution.
-	vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
-#endif
 
-// _____________________________ Refractance+Tint ________________________________
-#ifdef REFRACTION
-	vec3 refractance = vec3(0.0, 0.0, 0.0);
-	vec3 transmission = vec3(1.0, 1.0, 1.0);
-	#ifdef LINKREFRACTIONTOTRANSPARENCY
-		// Transmission based on alpha.
-		transmission *= (1.0 - alpha);
+	// _____________________________ Refractance+Tint ________________________________
+	#ifdef REFRACTION
+		vec3 refractance = vec3(0.0, 0.0, 0.0);
+		vec3 transmission = vec3(1.0, 1.0, 1.0);
+		#ifdef LINKREFRACTIONTOTRANSPARENCY
+			// Transmission based on alpha.
+			transmission *= (1.0 - alpha);
 
-		// Tint the material with albedo.
-		// TODO. PBR Tinting.
-		vec3 mixedAlbedo = surfaceAlbedo;
-		float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
-		vec3 tint = clamp(maxChannel * mixedAlbedo, 0.0, 1.0);
+			// Tint the material with albedo.
+			// TODO. PBR Tinting.
+			vec3 mixedAlbedo = surfaceAlbedo;
+			float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
+			vec3 tint = clamp(maxChannel * mixedAlbedo, 0.0, 1.0);
 
-		// Decrease Albedo Contribution
-		surfaceAlbedo *= alpha;
+			// Decrease Albedo Contribution
+			surfaceAlbedo *= alpha;
 
-		// Decrease irradiance Contribution
-		environmentIrradiance *= alpha;
+			// Decrease irradiance Contribution
+			environmentIrradiance *= alpha;
 
-		// Tint reflectance
-		environmentRefraction *= tint;
+			// Tint reflectance
+			environmentRefraction *= tint;
 
-		// Put alpha back to 1;
-		alpha = 1.0;
-	#endif
+			// Put alpha back to 1;
+			alpha = 1.0;
+		#endif
 
-	// Add Multiple internal bounces.
-	vec3 bounceSpecularEnvironmentReflectance = (2.0 * specularEnvironmentReflectance) / (1.0 + specularEnvironmentReflectance);
-	specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, alpha);
+		// Add Multiple internal bounces.
+		vec3 bounceSpecularEnvironmentReflectance = (2.0 * specularEnvironmentReflectance) / (1.0 + specularEnvironmentReflectance);
+		specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, alpha);
 
-	// In theory T = 1 - R.
-	transmission *= 1.0 - specularEnvironmentReflectance;
+		// In theory T = 1 - R.
+		transmission *= 1.0 - specularEnvironmentReflectance;
 
-	// Should baked in diffuse.
-	refractance = transmission;
-#endif
+		// Should baked in diffuse.
+		refractance = transmission;
+	#endif
 
-// ______________________________________________________________________________
-// _____________________________ Energy Conservation  ___________________________
+	// ______________________________________________________________________________
+	// _____________________________ Energy Conservation  ___________________________
 	// Apply Energy Conservation taking in account the environment level only if 
 	// the environment is present.
 	surfaceAlbedo.rgb = (1. - reflectance) * surfaceAlbedo.rgb;
 
-// _____________________________ Diffuse ________________________________________
-	vec3 finalDiffuse = diffuseBase;
-	finalDiffuse.rgb += vAmbientColor;
-	finalDiffuse *= surfaceAlbedo.rgb;
-	finalDiffuse = max(finalDiffuse, 0.0);
+	// _____________________________ Irradiance ______________________________________
+	#ifdef REFLECTION
+		vec3 finalIrradiance = environmentIrradiance;
+		finalIrradiance *= surfaceAlbedo.rgb;
+	#endif
 
-// _____________________________ Irradiance ______________________________________
-#ifdef REFLECTION
-	vec3 finalIrradiance = environmentIrradiance;
-	finalIrradiance *= surfaceAlbedo.rgb;
-#endif
+	// _____________________________ Specular ________________________________________
+	#ifdef SPECULARTERM
+		vec3 finalSpecular = specularBase;
+		finalSpecular = max(finalSpecular, 0.0);
 
-// _____________________________ Specular ________________________________________
-#ifdef SPECULARTERM
-	vec3 finalSpecular = specularBase;
-	finalSpecular = max(finalSpecular, 0.0);
+		// Full value needed for alpha.
+		vec3 finalSpecularScaled = finalSpecular * vLightingIntensity.x * vLightingIntensity.w;
+	#endif
 
-	// Full value needed for alpha.
-	vec3 finalSpecularScaled = finalSpecular * vLightingIntensity.x * vLightingIntensity.w;
-#endif
+	// _____________________________ Radiance ________________________________________
+	#ifdef REFLECTION
+		vec3 finalRadiance = environmentRadiance;
+		finalRadiance *= specularEnvironmentReflectance;
 
-// _____________________________ Radiance_________________________________________
-#ifdef REFLECTION
-	vec3 finalRadiance = environmentRadiance;
-	finalRadiance *= specularEnvironmentReflectance;
+		// Full value needed for alpha. 
+		vec3 finalRadianceScaled = finalRadiance * vLightingIntensity.z;
+	#endif
 
-	// Full value needed for alpha. 
-	vec3 finalRadianceScaled = finalRadiance * vLightingIntensity.z;
-#endif
+	// _____________________________ Refraction ______________________________________
+	#ifdef REFRACTION
+		vec3 finalRefraction = environmentRefraction;
+		finalRefraction *= refractance;
+	#endif
 
-// _____________________________ Refraction ______________________________________
-#ifdef REFRACTION
-	vec3 finalRefraction = environmentRefraction;
-	finalRefraction *= refractance;
+	// _____________________________ Highlights on Alpha _____________________________
+	#ifdef ALPHABLEND
+		float luminanceOverAlpha = 0.0;
+		#if	defined(REFLECTION) && defined(RADIANCEOVERALPHA)
+			luminanceOverAlpha += getLuminance(finalRadianceScaled);
+		#endif
+
+		#if defined(SPECULARTERM) && defined(SPECULAROVERALPHA)
+			luminanceOverAlpha += getLuminance(finalSpecularScaled);
+		#endif
+
+		#if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
+			alpha = clamp(alpha + luminanceOverAlpha * luminanceOverAlpha, 0., 1.);
+		#endif
+	#endif
 #endif
 
+// _____________________________ Diffuse ________________________________________
+	vec3 finalDiffuse = diffuseBase;
+	finalDiffuse.rgb += vAmbientColor;
+	finalDiffuse *= surfaceAlbedo.rgb;
+	finalDiffuse = max(finalDiffuse, 0.0);
+
 // _____________________________ Emissive ________________________________________
 	vec3 finalEmissive = vEmissiveColor;
 #ifdef EMISSIVE
@@ -737,44 +757,31 @@ void main(void) {
 	finalEmissive *=  vEmissiveInfos.y;
 #endif
 
-// _____________________________ Highlights on Alpha _____________________________
-#ifdef ALPHABLEND
-	float luminanceOverAlpha = 0.0;
-	#if	defined(REFLECTION) && defined(RADIANCEOVERALPHA)
-		luminanceOverAlpha += getLuminance(finalRadianceScaled);
-	#endif
-
-	#if defined(SPECULARTERM) && defined(SPECULAROVERALPHA)
-		luminanceOverAlpha += getLuminance(finalSpecularScaled);
-	#endif
-
-	#if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
-		alpha = clamp(alpha + luminanceOverAlpha * luminanceOverAlpha, 0., 1.);
-	#endif
-#endif
-
 // _______________________________________________________________________________
 // _____________________________ Composition _____________________________________
 	// Reflection already includes the environment intensity.
-	vec4 finalColor = vec4(finalDiffuse			* ambientOcclusionColor * vLightingIntensity.x +
-#ifdef REFLECTION
-						finalIrradiance			* ambientOcclusionColor * vLightingIntensity.z +
-#endif
-#ifdef SPECULARTERM
-// Computed in the previous step to help with alpha luminance.
-//						finalSpecular			* vLightingIntensity.x * vLightingIntensity.w +
-						finalSpecularScaled +
-#endif
-#ifdef REFLECTION
-// Comupted in the previous step to help with alpha luminance.
-//						finalRadiance			* vLightingIntensity.z +
-						finalRadianceScaled +
-#endif
-#ifdef REFRACTION
-						finalRefraction			* vLightingIntensity.z +
+	vec4 finalColor = vec4(
+		finalDiffuse			* ambientOcclusionColor * vLightingIntensity.x +
+#ifndef UNLIT
+	#ifdef REFLECTION
+		finalIrradiance			* ambientOcclusionColor * vLightingIntensity.z +
+	#endif
+	#ifdef SPECULARTERM
+	// Computed in the previous step to help with alpha luminance.
+	//	finalSpecular			* vLightingIntensity.x * vLightingIntensity.w +
+		finalSpecularScaled +
+	#endif
+	#ifdef REFLECTION
+	// Comupted in the previous step to help with alpha luminance.
+	//	finalRadiance			* vLightingIntensity.z +
+		finalRadianceScaled +
+	#endif
+	#ifdef REFRACTION
+		finalRefraction			* vLightingIntensity.z +
+	#endif
 #endif
-						finalEmissive			* vLightingIntensity.y,
-						alpha);
+		finalEmissive			* vLightingIntensity.y,
+		alpha);
 
 // _____________________________ LightMappping _____________________________________
 #ifdef LIGHTMAP

+ 6 - 0
src/Tools/babylon.tools.ts

@@ -814,6 +814,12 @@
                 if (doNotCopyList && doNotCopyList.indexOf(prop) !== -1) {
                     continue;
                 }
+
+                let descriptor = Object.getOwnPropertyDescriptor(source, prop);
+                //descriptor should exist, should be writable, and should not be a getter.
+                if (!descriptor || !descriptor.writable || descriptor.get) {
+                    continue;
+                }
                 var sourceValue = source[prop];
                 var typeOfSourceValue = typeof sourceValue;
 

+ 122 - 15
src/babylon.scene.ts

@@ -170,7 +170,18 @@
             return this._imageProcessingConfiguration;
         }
 
-        public forceWireframe = false;
+        private _forceWireframe = false;
+        public set forceWireframe(value: boolean) {
+            if (this._forceWireframe === value) {
+                return;
+            }
+            this._forceWireframe = value;
+            this.markAllMaterialsAsDirty(Material.MiscDirtyFlag);
+        }
+        public get forceWireframe(): boolean {
+            return this._forceWireframe;
+        }
+
         private _forcePointsCloud = false;
         public set forcePointsCloud(value: boolean) {
             if (this._forcePointsCloud === value) {
@@ -467,6 +478,7 @@
 
         // Animations
         public animations: Animation[] = [];
+        private _registeredForLateAnimationBindings = new SmartArrayNoDuplicate<any>(256);
 
         // Pointers
         public pointerDownPredicate: (Mesh: AbstractMesh) => boolean;
@@ -2066,25 +2078,49 @@
         }
 
         // Animations
+
         /**
          * Will start the animation sequence of a given target
-         * @param target - the target
-         * @param {number} from - from which frame should animation start
-         * @param {number} to - till which frame should animation run.
-         * @param {boolean} [loop] - should the animation loop
-         * @param {number} [speedRatio] - the speed in which to run the animation
-         * @param {Function} [onAnimationEnd] function to be executed when the animation ended.
-         * @param {BABYLON.Animatable} [animatable] an animatable object. If not provided a new one will be created from the given params.
-         * Returns {BABYLON.Animatable} the animatable object created for this animation
-         * See BABYLON.Animatable
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
          */
-        public beginAnimation(target: any, from: number, to: number, loop?: boolean, speedRatio: number = 1.0, onAnimationEnd?: () => void, animatable?: Animatable): Animatable {
+        public beginWeightedAnimation(target: any, from: number, to: number, weight = 1.0, loop?: boolean, speedRatio: number = 1.0, onAnimationEnd?: () => void, animatable?: Animatable): Animatable {
+            let returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false);
+            returnedAnimatable.weight = weight;
+
+            return returnedAnimatable;
+        }
+
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @returns the animatable object created for this animation
+         * @see BABYLON.Animatable
+         */
+        public beginAnimation(target: any, from: number, to: number, loop?: boolean, speedRatio: number = 1.0, onAnimationEnd?: () => void, animatable?: Animatable, stopCurrent = true): Animatable {
 
             if (from > to && speedRatio > 0) {
                 speedRatio *= -1;
             }
 
-            this.stopAnimation(target);
+            if (stopCurrent) {
+                this.stopAnimation(target);
+            }
 
             if (!animatable) {
                 animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd);
@@ -2099,11 +2135,13 @@
             if (target.getAnimatables) {
                 var animatables = target.getAnimatables();
                 for (var index = 0; index < animatables.length; index++) {
-                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable);
+                    this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent);
                 }
             }
 
-            animatable.reset();
+            if (stopCurrent) {
+                animatable.reset();
+            }
 
             return animatable;
         }
@@ -2195,7 +2233,7 @@
             if (!this.animationsEnabled || this._activeAnimatables.length === 0) {
                 return;
             }
-
+           
             // Getting time
             var now = Tools.Now;
             if (!this._animationTimeLast) {
@@ -2210,6 +2248,74 @@
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 this._activeAnimatables[index]._animate(this._animationTime);
             }
+
+            // Late animation bindings
+            this._processLateAnimationBindings();
+        }
+
+        /** @ignore */
+        public _registerTargetForLateAnimationBinding(runtimeAnimation: RuntimeAnimation): void {
+            let target = runtimeAnimation.target;
+            this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+
+            if (!target._lateAnimationHolders) {
+                target._lateAnimationHolders = {};               
+            }
+
+            if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+                target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+                    totalWeight: 0,
+                    animations: []
+                }
+            }
+
+            target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+            target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+        }
+
+        private _processLateAnimationBindings(): void {
+            if (!this._registeredForLateAnimationBindings.length) {
+                return;
+            }
+            for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+                var target = this._registeredForLateAnimationBindings.data[index];
+
+                for (var path in target._lateAnimationHolders) {
+                    var holder = target._lateAnimationHolders[path];       
+                    
+                    // Sanity check
+                    if (!holder.animations[0].originalValue.scaleAndAddToRef) {
+                        continue;
+                    }
+
+                    let normalizer = 1.0;
+                    let finalValue: any;
+
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        let originalValue = holder.animations[0].originalValue;                       
+
+                        finalValue = originalValue.scale(1.0 - holder.totalWeight)
+                    } else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                    }
+
+                    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];    
+                        if (finalValue) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(runtimeAnimation.weight / normalizer, finalValue);
+                        } else {
+                            finalValue = runtimeAnimation.currentValue.scale(runtimeAnimation.weight / normalizer);
+                        }
+                    }
+
+                    runtimeAnimation.target[path] = finalValue;
+                }
+
+                target._lateAnimationHolders = {};
+            }
+            this._registeredForLateAnimationBindings.reset();
         }
 
         // Matrix
@@ -4149,6 +4255,7 @@
             this._activeSkeletons.dispose();
             this._softwareSkinnedMeshes.dispose();
             this._renderTargets.dispose();
+            this._registeredForLateAnimationBindings.dispose();
 
             if (this._boundingBoxRenderer) {
                 this._boundingBoxRenderer.dispose();

+ 6 - 0
tests/validation/validation.js

@@ -217,6 +217,12 @@ function runTest(index, done) {
             });
     }
     else if (test.playgroundId) {
+        if (test.playgroundId[0] !== "#" || test.playgroundId.indexOf("#", 1) === -1) {
+            console.error("Invalid playground id");
+            done(false);
+            return;
+        }
+
         var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var pgRoot = "/Playground"
         var xmlHttp = new XMLHttpRequest();