Forráskód Böngészése

Fixing quaternion interpolation issue

David Catuhe 7 éve
szülő
commit
f61c739245

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1147 - 1135
Playground/babylon.d.txt


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1148 - 1136
dist/preview release/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 33 - 33
dist/preview release/babylon.js


+ 136 - 56
dist/preview release/babylon.max.js

@@ -4783,6 +4783,25 @@ var BABYLON;
             }
         };
         /**
+         * Returns the dot product (float) between the quaternions "left" and "right"
+         * @param left defines the left operand
+         * @param right defines the right operand
+         * @returns the dot product
+         */
+        Quaternion.Dot = function (left, right) {
+            return (left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w);
+        };
+        /**
+         * Checks if the two quaternions are close to each other
+         * @param quat0 defines the first quaternion to check
+         * @param quat1 defines the second quaternion to check
+         * @returns true if the two quaternions are close to each other
+         */
+        Quaternion.AreClose = function (quat0, quat1) {
+            var dot = Quaternion.Dot(quat0, quat1);
+            return dot >= 0;
+        };
+        /**
          * Creates an empty quaternion
          * @returns a new quaternion set to (0.0, 0.0, 0.0)
          */
@@ -23849,6 +23868,7 @@ var BABYLON;
             this._previousStartingPointerPosition = new BABYLON.Vector2(0, 0);
             this._startingPointerTime = 0;
             this._previousStartingPointerTime = 0;
+            this._pointerCaptures = {};
             // Deterministic lockstep
             this._timeAccumulator = 0;
             this._currentStepId = 0;
@@ -25229,7 +25249,7 @@ var BABYLON;
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
                 // PreObservable support
-                if (_this.onPrePointerObservable.hasObservers()) {
+                if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                     var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
                     var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
@@ -25268,6 +25288,7 @@ var BABYLON;
                 if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                     return;
                 }
+                _this._pointerCaptures[evt.pointerId] = true;
                 _this._startingPointerPosition.x = _this._pointerX;
                 _this._startingPointerPosition.y = _this._pointerY;
                 _this._startingPointerTime = Date.now();
@@ -25315,7 +25336,7 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 _this._initClickEvent(_this.onPrePointerObservable, _this.onPointerObservable, evt, function (clickInfo, pickResult) {
                     // PreObservable support
-                    if (_this.onPrePointerObservable.hasObservers()) {
+                    if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
                                 if (clickInfo.singleClick && _this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
@@ -25348,6 +25369,7 @@ var BABYLON;
                     if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                         return;
                     }
+                    _this._pointerCaptures[evt.pointerId] = false;
                     if (!_this.pointerUpPredicate) {
                         _this.pointerUpPredicate = function (mesh) {
                             return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.isEnabled();
@@ -25869,7 +25891,7 @@ var BABYLON;
             this._processLateAnimationBindings();
         };
         /** @hidden */
-        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation, originalValue) {
             var target = runtimeAnimation.target;
             this._registeredForLateAnimationBindings.pushNoDuplicate(target);
             if (!target._lateAnimationHolders) {
@@ -25878,19 +25900,21 @@ var BABYLON;
             if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
                 target._lateAnimationHolders[runtimeAnimation.targetPath] = {
                     totalWeight: 0,
-                    animations: []
+                    animations: [],
+                    originalValue: originalValue
                 };
             }
             target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
             target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
         };
-        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder, originalValue) {
+        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder) {
             var normalizer = 1.0;
             var finalPosition = BABYLON.Tmp.Vector3[0];
             var finalScaling = BABYLON.Tmp.Vector3[1];
             var finalQuaternion = BABYLON.Tmp.Quaternion[0];
             var startIndex = 0;
             var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
             var scale = 1;
             if (holder.totalWeight < 1.0) {
                 // We need to mix the original value in                     
@@ -25924,6 +25948,46 @@ var BABYLON;
             BABYLON.Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, originalAnimation._workValue);
             return originalAnimation._workValue;
         };
+        Scene.prototype._processLateAnimationBindingsForQuaternions = function (holder) {
+            var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
+            if (holder.animations.length === 1) {
+                return BABYLON.Quaternion.Slerp(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight));
+            }
+            var normalizer = 1.0;
+            var cumulativeQuaternion = null;
+            if (holder.totalWeight < 1.0) {
+                // We need to mix the original value in                     
+                //originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
+                var scale = 1.0 - holder.totalWeight;
+                cumulativeQuaternion = originalValue.scaleInPlace(scale);
+                cumulativeQuaternion.normalize();
+            }
+            else {
+                if (holder.animations.length === 2) { // Slerp as soon as we can
+                    return BABYLON.Quaternion.Slerp(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight);
+                }
+                normalizer = holder.totalWeight;
+            }
+            // There is no simple way to cumulate and weight quaternions so doing approximations here
+            for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                var runtimeAnimation = holder.animations[animIndex];
+                var scale = runtimeAnimation.weight / normalizer;
+                var current = runtimeAnimation.currentValue;
+                current.scaleInPlace(scale);
+                current.normalize();
+                if (!cumulativeQuaternion) {
+                    cumulativeQuaternion = current;
+                    continue;
+                }
+                if (!BABYLON.Quaternion.AreClose(current, cumulativeQuaternion)) {
+                    current.conjugateInPlace();
+                }
+                cumulativeQuaternion.addInPlace(current);
+                cumulativeQuaternion.normalize();
+            }
+            return cumulativeQuaternion;
+        };
         Scene.prototype._processLateAnimationBindings = function () {
             if (!this._registeredForLateAnimationBindings.length) {
                 return;
@@ -25933,54 +25997,59 @@ var BABYLON;
                 for (var path in target._lateAnimationHolders) {
                     var holder = target._lateAnimationHolders[path];
                     var originalAnimation = holder.animations[0];
-                    var originalValue = originalAnimation.originalValue;
-                    var finalTarget = originalAnimation.target;
+                    var originalValue = holder.originalValue;
                     var matrixDecomposeMode = BABYLON.Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
                     var finalValue = void 0;
                     if (matrixDecomposeMode) {
-                        finalValue = this._processLateAnimationBindingsForMatrices(holder, originalValue);
+                        finalValue = this._processLateAnimationBindingsForMatrices(holder);
                     }
                     else {
-                        var startIndex = 0;
-                        var normalizer = 1.0;
-                        if (holder.totalWeight < 1.0) {
-                            // We need to mix the original value in     
-                            if (originalValue.scale) {
-                                finalValue = originalValue.scale(1.0 - holder.totalWeight);
-                            }
-                            else {
-                                finalValue = originalValue * (1.0 - holder.totalWeight);
-                            }
+                        var quaternionMode = originalValue.w !== undefined;
+                        if (quaternionMode) {
+                            finalValue = this._processLateAnimationBindingsForQuaternions(holder);
                         }
                         else {
-                            // We need to normalize the weights
-                            normalizer = holder.totalWeight;
-                            var scale_1 = originalAnimation.weight / normalizer;
-                            if (scale_1 !== 1) {
-                                if (originalAnimation.currentValue.scale) {
-                                    finalValue = originalAnimation.currentValue.scale(scale_1);
+                            var startIndex = 0;
+                            var normalizer = 1.0;
+                            if (holder.totalWeight < 1.0) {
+                                // We need to mix the original value in     
+                                if (originalValue.scale) {
+                                    finalValue = originalValue.scale(1.0 - holder.totalWeight);
                                 }
                                 else {
-                                    finalValue = originalAnimation.currentValue * scale_1;
+                                    finalValue = originalValue * (1.0 - holder.totalWeight);
                                 }
                             }
                             else {
-                                finalValue = originalAnimation.currentValue;
-                            }
-                            startIndex = 1;
-                        }
-                        for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
-                            var runtimeAnimation = holder.animations[animIndex];
-                            var scale = runtimeAnimation.weight / normalizer;
-                            if (runtimeAnimation.currentValue.scaleAndAddToRef) {
-                                runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                // We need to normalize the weights
+                                normalizer = holder.totalWeight;
+                                var scale_1 = originalAnimation.weight / normalizer;
+                                if (scale_1 !== 1) {
+                                    if (originalAnimation.currentValue.scale) {
+                                        finalValue = originalAnimation.currentValue.scale(scale_1);
+                                    }
+                                    else {
+                                        finalValue = originalAnimation.currentValue * scale_1;
+                                    }
+                                }
+                                else {
+                                    finalValue = originalAnimation.currentValue;
+                                }
+                                startIndex = 1;
                             }
-                            else {
-                                finalValue += runtimeAnimation.currentValue * scale;
+                            for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+                                var runtimeAnimation = holder.animations[animIndex];
+                                var scale = runtimeAnimation.weight / normalizer;
+                                if (runtimeAnimation.currentValue.scaleAndAddToRef) {
+                                    runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                }
+                                else {
+                                    finalValue += runtimeAnimation.currentValue * scale;
+                                }
                             }
                         }
                     }
-                    finalTarget[path] = finalValue;
+                    target[path] = finalValue;
                 }
                 target._lateAnimationHolders = {};
             }
@@ -51755,6 +51824,10 @@ var BABYLON;
              */
             this._currentFrame = 0;
             /**
+             * The original value of the runtime animation
+             */
+            this._originalValue = new Array();
+            /**
              * The offsets cache of the runtime animation
              */
             this._offsetsCache = {};
@@ -51816,16 +51889,6 @@ var BABYLON;
             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
@@ -51872,14 +51935,28 @@ var BABYLON;
          */
         RuntimeAnimation.prototype.reset = function (restoreOriginal) {
             if (restoreOriginal === void 0) { restoreOriginal = false; }
-            if (restoreOriginal && this._originalValue != null) {
-                this.setValue(this._originalValue, -1);
+            if (restoreOriginal) {
+                if (this._target instanceof Array) {
+                    var index = 0;
+                    for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
+                        var target = _a[_i];
+                        if (this._originalValue[index] !== undefined) {
+                            this._setValue(target, this._originalValue[index], -1);
+                        }
+                        index++;
+                    }
+                }
+                else {
+                    if (this._originalValue[0] !== undefined) {
+                        this._setValue(this._target, this._originalValue[0], -1);
+                    }
+                }
             }
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalValue = null;
+            this._originalValue = new Array();
         };
         /**
          * Specifies if the runtime animation is stopped
@@ -51921,16 +51998,19 @@ var BABYLON;
         RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
             if (weight === void 0) { weight = 1.0; }
             if (this._target instanceof Array) {
+                var index = 0;
                 for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
                     var target = _a[_i];
-                    this._setValue(target, currentValue, weight);
+                    this._setValue(target, currentValue, weight, index);
+                    index++;
                 }
             }
             else {
                 this._setValue(this._target, currentValue, weight);
             }
         };
-        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight) {
+        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight, targetIndex) {
+            if (targetIndex === void 0) { targetIndex = 0; }
             // Set value
             var path;
             var destination;
@@ -51950,7 +52030,7 @@ var BABYLON;
             this._targetPath = path;
             this._activeTarget = destination;
             this._weight = weight;
-            if (!this._originalValue) {
+            if (this._originalValue[targetIndex] === undefined) {
                 var originalValue = void 0;
                 if (destination.getRestPose && path === "_matrix") { // For bones
                     originalValue = destination.getRestPose();
@@ -51959,10 +52039,10 @@ var BABYLON;
                     originalValue = destination[path];
                 }
                 if (originalValue && originalValue.clone) {
-                    this._originalValue = originalValue.clone();
+                    this._originalValue[targetIndex] = originalValue.clone();
                 }
                 else {
-                    this._originalValue = originalValue;
+                    this._originalValue[targetIndex] = originalValue;
                 }
             }
             // Blending
@@ -52017,7 +52097,7 @@ var BABYLON;
                 this._currentValue = currentValue;
             }
             if (weight !== -1.0) {
-                this._scene._registerTargetForLateAnimationBinding(this);
+                this._scene._registerTargetForLateAnimationBinding(this, this._originalValue[targetIndex]);
             }
             else {
                 destination[path] = this._currentValue;

+ 136 - 56
dist/preview release/babylon.no-module.max.js

@@ -4750,6 +4750,25 @@ var BABYLON;
             }
         };
         /**
+         * Returns the dot product (float) between the quaternions "left" and "right"
+         * @param left defines the left operand
+         * @param right defines the right operand
+         * @returns the dot product
+         */
+        Quaternion.Dot = function (left, right) {
+            return (left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w);
+        };
+        /**
+         * Checks if the two quaternions are close to each other
+         * @param quat0 defines the first quaternion to check
+         * @param quat1 defines the second quaternion to check
+         * @returns true if the two quaternions are close to each other
+         */
+        Quaternion.AreClose = function (quat0, quat1) {
+            var dot = Quaternion.Dot(quat0, quat1);
+            return dot >= 0;
+        };
+        /**
          * Creates an empty quaternion
          * @returns a new quaternion set to (0.0, 0.0, 0.0)
          */
@@ -23816,6 +23835,7 @@ var BABYLON;
             this._previousStartingPointerPosition = new BABYLON.Vector2(0, 0);
             this._startingPointerTime = 0;
             this._previousStartingPointerTime = 0;
+            this._pointerCaptures = {};
             // Deterministic lockstep
             this._timeAccumulator = 0;
             this._currentStepId = 0;
@@ -25196,7 +25216,7 @@ var BABYLON;
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
                 // PreObservable support
-                if (_this.onPrePointerObservable.hasObservers()) {
+                if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                     var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
                     var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
@@ -25235,6 +25255,7 @@ var BABYLON;
                 if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                     return;
                 }
+                _this._pointerCaptures[evt.pointerId] = true;
                 _this._startingPointerPosition.x = _this._pointerX;
                 _this._startingPointerPosition.y = _this._pointerY;
                 _this._startingPointerTime = Date.now();
@@ -25282,7 +25303,7 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 _this._initClickEvent(_this.onPrePointerObservable, _this.onPointerObservable, evt, function (clickInfo, pickResult) {
                     // PreObservable support
-                    if (_this.onPrePointerObservable.hasObservers()) {
+                    if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
                                 if (clickInfo.singleClick && _this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
@@ -25315,6 +25336,7 @@ var BABYLON;
                     if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                         return;
                     }
+                    _this._pointerCaptures[evt.pointerId] = false;
                     if (!_this.pointerUpPredicate) {
                         _this.pointerUpPredicate = function (mesh) {
                             return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.isEnabled();
@@ -25836,7 +25858,7 @@ var BABYLON;
             this._processLateAnimationBindings();
         };
         /** @hidden */
-        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation, originalValue) {
             var target = runtimeAnimation.target;
             this._registeredForLateAnimationBindings.pushNoDuplicate(target);
             if (!target._lateAnimationHolders) {
@@ -25845,19 +25867,21 @@ var BABYLON;
             if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
                 target._lateAnimationHolders[runtimeAnimation.targetPath] = {
                     totalWeight: 0,
-                    animations: []
+                    animations: [],
+                    originalValue: originalValue
                 };
             }
             target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
             target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
         };
-        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder, originalValue) {
+        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder) {
             var normalizer = 1.0;
             var finalPosition = BABYLON.Tmp.Vector3[0];
             var finalScaling = BABYLON.Tmp.Vector3[1];
             var finalQuaternion = BABYLON.Tmp.Quaternion[0];
             var startIndex = 0;
             var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
             var scale = 1;
             if (holder.totalWeight < 1.0) {
                 // We need to mix the original value in                     
@@ -25891,6 +25915,46 @@ var BABYLON;
             BABYLON.Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, originalAnimation._workValue);
             return originalAnimation._workValue;
         };
+        Scene.prototype._processLateAnimationBindingsForQuaternions = function (holder) {
+            var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
+            if (holder.animations.length === 1) {
+                return BABYLON.Quaternion.Slerp(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight));
+            }
+            var normalizer = 1.0;
+            var cumulativeQuaternion = null;
+            if (holder.totalWeight < 1.0) {
+                // We need to mix the original value in                     
+                //originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
+                var scale = 1.0 - holder.totalWeight;
+                cumulativeQuaternion = originalValue.scaleInPlace(scale);
+                cumulativeQuaternion.normalize();
+            }
+            else {
+                if (holder.animations.length === 2) { // Slerp as soon as we can
+                    return BABYLON.Quaternion.Slerp(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight);
+                }
+                normalizer = holder.totalWeight;
+            }
+            // There is no simple way to cumulate and weight quaternions so doing approximations here
+            for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                var runtimeAnimation = holder.animations[animIndex];
+                var scale = runtimeAnimation.weight / normalizer;
+                var current = runtimeAnimation.currentValue;
+                current.scaleInPlace(scale);
+                current.normalize();
+                if (!cumulativeQuaternion) {
+                    cumulativeQuaternion = current;
+                    continue;
+                }
+                if (!BABYLON.Quaternion.AreClose(current, cumulativeQuaternion)) {
+                    current.conjugateInPlace();
+                }
+                cumulativeQuaternion.addInPlace(current);
+                cumulativeQuaternion.normalize();
+            }
+            return cumulativeQuaternion;
+        };
         Scene.prototype._processLateAnimationBindings = function () {
             if (!this._registeredForLateAnimationBindings.length) {
                 return;
@@ -25900,54 +25964,59 @@ var BABYLON;
                 for (var path in target._lateAnimationHolders) {
                     var holder = target._lateAnimationHolders[path];
                     var originalAnimation = holder.animations[0];
-                    var originalValue = originalAnimation.originalValue;
-                    var finalTarget = originalAnimation.target;
+                    var originalValue = holder.originalValue;
                     var matrixDecomposeMode = BABYLON.Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
                     var finalValue = void 0;
                     if (matrixDecomposeMode) {
-                        finalValue = this._processLateAnimationBindingsForMatrices(holder, originalValue);
+                        finalValue = this._processLateAnimationBindingsForMatrices(holder);
                     }
                     else {
-                        var startIndex = 0;
-                        var normalizer = 1.0;
-                        if (holder.totalWeight < 1.0) {
-                            // We need to mix the original value in     
-                            if (originalValue.scale) {
-                                finalValue = originalValue.scale(1.0 - holder.totalWeight);
-                            }
-                            else {
-                                finalValue = originalValue * (1.0 - holder.totalWeight);
-                            }
+                        var quaternionMode = originalValue.w !== undefined;
+                        if (quaternionMode) {
+                            finalValue = this._processLateAnimationBindingsForQuaternions(holder);
                         }
                         else {
-                            // We need to normalize the weights
-                            normalizer = holder.totalWeight;
-                            var scale_1 = originalAnimation.weight / normalizer;
-                            if (scale_1 !== 1) {
-                                if (originalAnimation.currentValue.scale) {
-                                    finalValue = originalAnimation.currentValue.scale(scale_1);
+                            var startIndex = 0;
+                            var normalizer = 1.0;
+                            if (holder.totalWeight < 1.0) {
+                                // We need to mix the original value in     
+                                if (originalValue.scale) {
+                                    finalValue = originalValue.scale(1.0 - holder.totalWeight);
                                 }
                                 else {
-                                    finalValue = originalAnimation.currentValue * scale_1;
+                                    finalValue = originalValue * (1.0 - holder.totalWeight);
                                 }
                             }
                             else {
-                                finalValue = originalAnimation.currentValue;
-                            }
-                            startIndex = 1;
-                        }
-                        for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
-                            var runtimeAnimation = holder.animations[animIndex];
-                            var scale = runtimeAnimation.weight / normalizer;
-                            if (runtimeAnimation.currentValue.scaleAndAddToRef) {
-                                runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                // We need to normalize the weights
+                                normalizer = holder.totalWeight;
+                                var scale_1 = originalAnimation.weight / normalizer;
+                                if (scale_1 !== 1) {
+                                    if (originalAnimation.currentValue.scale) {
+                                        finalValue = originalAnimation.currentValue.scale(scale_1);
+                                    }
+                                    else {
+                                        finalValue = originalAnimation.currentValue * scale_1;
+                                    }
+                                }
+                                else {
+                                    finalValue = originalAnimation.currentValue;
+                                }
+                                startIndex = 1;
                             }
-                            else {
-                                finalValue += runtimeAnimation.currentValue * scale;
+                            for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+                                var runtimeAnimation = holder.animations[animIndex];
+                                var scale = runtimeAnimation.weight / normalizer;
+                                if (runtimeAnimation.currentValue.scaleAndAddToRef) {
+                                    runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                }
+                                else {
+                                    finalValue += runtimeAnimation.currentValue * scale;
+                                }
                             }
                         }
                     }
-                    finalTarget[path] = finalValue;
+                    target[path] = finalValue;
                 }
                 target._lateAnimationHolders = {};
             }
@@ -51722,6 +51791,10 @@ var BABYLON;
              */
             this._currentFrame = 0;
             /**
+             * The original value of the runtime animation
+             */
+            this._originalValue = new Array();
+            /**
              * The offsets cache of the runtime animation
              */
             this._offsetsCache = {};
@@ -51783,16 +51856,6 @@ var BABYLON;
             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
@@ -51839,14 +51902,28 @@ var BABYLON;
          */
         RuntimeAnimation.prototype.reset = function (restoreOriginal) {
             if (restoreOriginal === void 0) { restoreOriginal = false; }
-            if (restoreOriginal && this._originalValue != null) {
-                this.setValue(this._originalValue, -1);
+            if (restoreOriginal) {
+                if (this._target instanceof Array) {
+                    var index = 0;
+                    for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
+                        var target = _a[_i];
+                        if (this._originalValue[index] !== undefined) {
+                            this._setValue(target, this._originalValue[index], -1);
+                        }
+                        index++;
+                    }
+                }
+                else {
+                    if (this._originalValue[0] !== undefined) {
+                        this._setValue(this._target, this._originalValue[0], -1);
+                    }
+                }
             }
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalValue = null;
+            this._originalValue = new Array();
         };
         /**
          * Specifies if the runtime animation is stopped
@@ -51888,16 +51965,19 @@ var BABYLON;
         RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
             if (weight === void 0) { weight = 1.0; }
             if (this._target instanceof Array) {
+                var index = 0;
                 for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
                     var target = _a[_i];
-                    this._setValue(target, currentValue, weight);
+                    this._setValue(target, currentValue, weight, index);
+                    index++;
                 }
             }
             else {
                 this._setValue(this._target, currentValue, weight);
             }
         };
-        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight) {
+        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight, targetIndex) {
+            if (targetIndex === void 0) { targetIndex = 0; }
             // Set value
             var path;
             var destination;
@@ -51917,7 +51997,7 @@ var BABYLON;
             this._targetPath = path;
             this._activeTarget = destination;
             this._weight = weight;
-            if (!this._originalValue) {
+            if (this._originalValue[targetIndex] === undefined) {
                 var originalValue = void 0;
                 if (destination.getRestPose && path === "_matrix") { // For bones
                     originalValue = destination.getRestPose();
@@ -51926,10 +52006,10 @@ var BABYLON;
                     originalValue = destination[path];
                 }
                 if (originalValue && originalValue.clone) {
-                    this._originalValue = originalValue.clone();
+                    this._originalValue[targetIndex] = originalValue.clone();
                 }
                 else {
-                    this._originalValue = originalValue;
+                    this._originalValue[targetIndex] = originalValue;
                 }
             }
             // Blending
@@ -51984,7 +52064,7 @@ var BABYLON;
                 this._currentValue = currentValue;
             }
             if (weight !== -1.0) {
-                this._scene._registerTargetForLateAnimationBinding(this);
+                this._scene._registerTargetForLateAnimationBinding(this, this._originalValue[targetIndex]);
             }
             else {
                 destination[path] = this._currentValue;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 34 - 34
dist/preview release/babylon.worker.js


+ 136 - 56
dist/preview release/es6.js

@@ -4750,6 +4750,25 @@ var BABYLON;
             }
         };
         /**
+         * Returns the dot product (float) between the quaternions "left" and "right"
+         * @param left defines the left operand
+         * @param right defines the right operand
+         * @returns the dot product
+         */
+        Quaternion.Dot = function (left, right) {
+            return (left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w);
+        };
+        /**
+         * Checks if the two quaternions are close to each other
+         * @param quat0 defines the first quaternion to check
+         * @param quat1 defines the second quaternion to check
+         * @returns true if the two quaternions are close to each other
+         */
+        Quaternion.AreClose = function (quat0, quat1) {
+            var dot = Quaternion.Dot(quat0, quat1);
+            return dot >= 0;
+        };
+        /**
          * Creates an empty quaternion
          * @returns a new quaternion set to (0.0, 0.0, 0.0)
          */
@@ -23816,6 +23835,7 @@ var BABYLON;
             this._previousStartingPointerPosition = new BABYLON.Vector2(0, 0);
             this._startingPointerTime = 0;
             this._previousStartingPointerTime = 0;
+            this._pointerCaptures = {};
             // Deterministic lockstep
             this._timeAccumulator = 0;
             this._currentStepId = 0;
@@ -25196,7 +25216,7 @@ var BABYLON;
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
                 // PreObservable support
-                if (_this.onPrePointerObservable.hasObservers()) {
+                if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                     var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
                     var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
@@ -25235,6 +25255,7 @@ var BABYLON;
                 if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                     return;
                 }
+                _this._pointerCaptures[evt.pointerId] = true;
                 _this._startingPointerPosition.x = _this._pointerX;
                 _this._startingPointerPosition.y = _this._pointerY;
                 _this._startingPointerTime = Date.now();
@@ -25282,7 +25303,7 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 _this._initClickEvent(_this.onPrePointerObservable, _this.onPointerObservable, evt, function (clickInfo, pickResult) {
                     // PreObservable support
-                    if (_this.onPrePointerObservable.hasObservers()) {
+                    if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
                                 if (clickInfo.singleClick && _this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
@@ -25315,6 +25336,7 @@ var BABYLON;
                     if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                         return;
                     }
+                    _this._pointerCaptures[evt.pointerId] = false;
                     if (!_this.pointerUpPredicate) {
                         _this.pointerUpPredicate = function (mesh) {
                             return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.isEnabled();
@@ -25836,7 +25858,7 @@ var BABYLON;
             this._processLateAnimationBindings();
         };
         /** @hidden */
-        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation, originalValue) {
             var target = runtimeAnimation.target;
             this._registeredForLateAnimationBindings.pushNoDuplicate(target);
             if (!target._lateAnimationHolders) {
@@ -25845,19 +25867,21 @@ var BABYLON;
             if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
                 target._lateAnimationHolders[runtimeAnimation.targetPath] = {
                     totalWeight: 0,
-                    animations: []
+                    animations: [],
+                    originalValue: originalValue
                 };
             }
             target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
             target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
         };
-        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder, originalValue) {
+        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder) {
             var normalizer = 1.0;
             var finalPosition = BABYLON.Tmp.Vector3[0];
             var finalScaling = BABYLON.Tmp.Vector3[1];
             var finalQuaternion = BABYLON.Tmp.Quaternion[0];
             var startIndex = 0;
             var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
             var scale = 1;
             if (holder.totalWeight < 1.0) {
                 // We need to mix the original value in                     
@@ -25891,6 +25915,46 @@ var BABYLON;
             BABYLON.Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, originalAnimation._workValue);
             return originalAnimation._workValue;
         };
+        Scene.prototype._processLateAnimationBindingsForQuaternions = function (holder) {
+            var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
+            if (holder.animations.length === 1) {
+                return BABYLON.Quaternion.Slerp(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight));
+            }
+            var normalizer = 1.0;
+            var cumulativeQuaternion = null;
+            if (holder.totalWeight < 1.0) {
+                // We need to mix the original value in                     
+                //originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
+                var scale = 1.0 - holder.totalWeight;
+                cumulativeQuaternion = originalValue.scaleInPlace(scale);
+                cumulativeQuaternion.normalize();
+            }
+            else {
+                if (holder.animations.length === 2) { // Slerp as soon as we can
+                    return BABYLON.Quaternion.Slerp(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight);
+                }
+                normalizer = holder.totalWeight;
+            }
+            // There is no simple way to cumulate and weight quaternions so doing approximations here
+            for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                var runtimeAnimation = holder.animations[animIndex];
+                var scale = runtimeAnimation.weight / normalizer;
+                var current = runtimeAnimation.currentValue;
+                current.scaleInPlace(scale);
+                current.normalize();
+                if (!cumulativeQuaternion) {
+                    cumulativeQuaternion = current;
+                    continue;
+                }
+                if (!BABYLON.Quaternion.AreClose(current, cumulativeQuaternion)) {
+                    current.conjugateInPlace();
+                }
+                cumulativeQuaternion.addInPlace(current);
+                cumulativeQuaternion.normalize();
+            }
+            return cumulativeQuaternion;
+        };
         Scene.prototype._processLateAnimationBindings = function () {
             if (!this._registeredForLateAnimationBindings.length) {
                 return;
@@ -25900,54 +25964,59 @@ var BABYLON;
                 for (var path in target._lateAnimationHolders) {
                     var holder = target._lateAnimationHolders[path];
                     var originalAnimation = holder.animations[0];
-                    var originalValue = originalAnimation.originalValue;
-                    var finalTarget = originalAnimation.target;
+                    var originalValue = holder.originalValue;
                     var matrixDecomposeMode = BABYLON.Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
                     var finalValue = void 0;
                     if (matrixDecomposeMode) {
-                        finalValue = this._processLateAnimationBindingsForMatrices(holder, originalValue);
+                        finalValue = this._processLateAnimationBindingsForMatrices(holder);
                     }
                     else {
-                        var startIndex = 0;
-                        var normalizer = 1.0;
-                        if (holder.totalWeight < 1.0) {
-                            // We need to mix the original value in     
-                            if (originalValue.scale) {
-                                finalValue = originalValue.scale(1.0 - holder.totalWeight);
-                            }
-                            else {
-                                finalValue = originalValue * (1.0 - holder.totalWeight);
-                            }
+                        var quaternionMode = originalValue.w !== undefined;
+                        if (quaternionMode) {
+                            finalValue = this._processLateAnimationBindingsForQuaternions(holder);
                         }
                         else {
-                            // We need to normalize the weights
-                            normalizer = holder.totalWeight;
-                            var scale_1 = originalAnimation.weight / normalizer;
-                            if (scale_1 !== 1) {
-                                if (originalAnimation.currentValue.scale) {
-                                    finalValue = originalAnimation.currentValue.scale(scale_1);
+                            var startIndex = 0;
+                            var normalizer = 1.0;
+                            if (holder.totalWeight < 1.0) {
+                                // We need to mix the original value in     
+                                if (originalValue.scale) {
+                                    finalValue = originalValue.scale(1.0 - holder.totalWeight);
                                 }
                                 else {
-                                    finalValue = originalAnimation.currentValue * scale_1;
+                                    finalValue = originalValue * (1.0 - holder.totalWeight);
                                 }
                             }
                             else {
-                                finalValue = originalAnimation.currentValue;
-                            }
-                            startIndex = 1;
-                        }
-                        for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
-                            var runtimeAnimation = holder.animations[animIndex];
-                            var scale = runtimeAnimation.weight / normalizer;
-                            if (runtimeAnimation.currentValue.scaleAndAddToRef) {
-                                runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                // We need to normalize the weights
+                                normalizer = holder.totalWeight;
+                                var scale_1 = originalAnimation.weight / normalizer;
+                                if (scale_1 !== 1) {
+                                    if (originalAnimation.currentValue.scale) {
+                                        finalValue = originalAnimation.currentValue.scale(scale_1);
+                                    }
+                                    else {
+                                        finalValue = originalAnimation.currentValue * scale_1;
+                                    }
+                                }
+                                else {
+                                    finalValue = originalAnimation.currentValue;
+                                }
+                                startIndex = 1;
                             }
-                            else {
-                                finalValue += runtimeAnimation.currentValue * scale;
+                            for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+                                var runtimeAnimation = holder.animations[animIndex];
+                                var scale = runtimeAnimation.weight / normalizer;
+                                if (runtimeAnimation.currentValue.scaleAndAddToRef) {
+                                    runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                }
+                                else {
+                                    finalValue += runtimeAnimation.currentValue * scale;
+                                }
                             }
                         }
                     }
-                    finalTarget[path] = finalValue;
+                    target[path] = finalValue;
                 }
                 target._lateAnimationHolders = {};
             }
@@ -51722,6 +51791,10 @@ var BABYLON;
              */
             this._currentFrame = 0;
             /**
+             * The original value of the runtime animation
+             */
+            this._originalValue = new Array();
+            /**
              * The offsets cache of the runtime animation
              */
             this._offsetsCache = {};
@@ -51783,16 +51856,6 @@ var BABYLON;
             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
@@ -51839,14 +51902,28 @@ var BABYLON;
          */
         RuntimeAnimation.prototype.reset = function (restoreOriginal) {
             if (restoreOriginal === void 0) { restoreOriginal = false; }
-            if (restoreOriginal && this._originalValue != null) {
-                this.setValue(this._originalValue, -1);
+            if (restoreOriginal) {
+                if (this._target instanceof Array) {
+                    var index = 0;
+                    for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
+                        var target = _a[_i];
+                        if (this._originalValue[index] !== undefined) {
+                            this._setValue(target, this._originalValue[index], -1);
+                        }
+                        index++;
+                    }
+                }
+                else {
+                    if (this._originalValue[0] !== undefined) {
+                        this._setValue(this._target, this._originalValue[0], -1);
+                    }
+                }
             }
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalValue = null;
+            this._originalValue = new Array();
         };
         /**
          * Specifies if the runtime animation is stopped
@@ -51888,16 +51965,19 @@ var BABYLON;
         RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
             if (weight === void 0) { weight = 1.0; }
             if (this._target instanceof Array) {
+                var index = 0;
                 for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
                     var target = _a[_i];
-                    this._setValue(target, currentValue, weight);
+                    this._setValue(target, currentValue, weight, index);
+                    index++;
                 }
             }
             else {
                 this._setValue(this._target, currentValue, weight);
             }
         };
-        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight) {
+        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight, targetIndex) {
+            if (targetIndex === void 0) { targetIndex = 0; }
             // Set value
             var path;
             var destination;
@@ -51917,7 +51997,7 @@ var BABYLON;
             this._targetPath = path;
             this._activeTarget = destination;
             this._weight = weight;
-            if (!this._originalValue) {
+            if (this._originalValue[targetIndex] === undefined) {
                 var originalValue = void 0;
                 if (destination.getRestPose && path === "_matrix") { // For bones
                     originalValue = destination.getRestPose();
@@ -51926,10 +52006,10 @@ var BABYLON;
                     originalValue = destination[path];
                 }
                 if (originalValue && originalValue.clone) {
-                    this._originalValue = originalValue.clone();
+                    this._originalValue[targetIndex] = originalValue.clone();
                 }
                 else {
-                    this._originalValue = originalValue;
+                    this._originalValue[targetIndex] = originalValue;
                 }
             }
             // Blending
@@ -51984,7 +52064,7 @@ var BABYLON;
                 this._currentValue = currentValue;
             }
             if (weight !== -1.0) {
-                this._scene._registerTargetForLateAnimationBinding(this);
+                this._scene._registerTargetForLateAnimationBinding(this, this._originalValue[targetIndex]);
             }
             else {
                 destination[path] = this._currentValue;

+ 1 - 0
dist/preview release/materialsLibrary/babylon.furMaterial.d.ts

@@ -14,6 +14,7 @@ declare module BABYLON {
         furGravity: Vector3;
         furSpeed: number;
         furDensity: number;
+        furOcclusion: number;
         furTexture: DynamicTexture;
         private _disableLighting;
         disableLighting: boolean;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 7 - 2
dist/preview release/materialsLibrary/babylon.furMaterial.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.furMaterial.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.d.ts

@@ -292,6 +292,7 @@ declare module BABYLON {
         furGravity: Vector3;
         furSpeed: number;
         furDensity: number;
+        furOcclusion: number;
         furTexture: DynamicTexture;
         private _disableLighting;
         disableLighting: boolean;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 7 - 2
dist/preview release/materialsLibrary/babylonjs.materials.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -297,6 +297,7 @@ declare module BABYLON {
         furGravity: Vector3;
         furSpeed: number;
         furDensity: number;
+        furOcclusion: number;
         furTexture: DynamicTexture;
         private _disableLighting;
         disableLighting: boolean;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 42 - 42
dist/preview release/viewer/babylon.viewer.js


+ 144 - 57
dist/preview release/viewer/babylon.viewer.max.js

@@ -4871,6 +4871,25 @@ var BABYLON;
             }
         };
         /**
+         * Returns the dot product (float) between the quaternions "left" and "right"
+         * @param left defines the left operand
+         * @param right defines the right operand
+         * @returns the dot product
+         */
+        Quaternion.Dot = function (left, right) {
+            return (left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w);
+        };
+        /**
+         * Checks if the two quaternions are close to each other
+         * @param quat0 defines the first quaternion to check
+         * @param quat1 defines the second quaternion to check
+         * @returns true if the two quaternions are close to each other
+         */
+        Quaternion.AreClose = function (quat0, quat1) {
+            var dot = Quaternion.Dot(quat0, quat1);
+            return dot >= 0;
+        };
+        /**
          * Creates an empty quaternion
          * @returns a new quaternion set to (0.0, 0.0, 0.0)
          */
@@ -23937,6 +23956,7 @@ var BABYLON;
             this._previousStartingPointerPosition = new BABYLON.Vector2(0, 0);
             this._startingPointerTime = 0;
             this._previousStartingPointerTime = 0;
+            this._pointerCaptures = {};
             // Deterministic lockstep
             this._timeAccumulator = 0;
             this._currentStepId = 0;
@@ -25317,7 +25337,7 @@ var BABYLON;
             this._onPointerMove = function (evt) {
                 _this._updatePointerPosition(evt);
                 // PreObservable support
-                if (_this.onPrePointerObservable.hasObservers()) {
+                if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                     var type = evt.type === "mousewheel" || evt.type === "DOMMouseScroll" ? BABYLON.PointerEventTypes.POINTERWHEEL : BABYLON.PointerEventTypes.POINTERMOVE;
                     var pi = new BABYLON.PointerInfoPre(type, evt, _this._unTranslatedPointerX, _this._unTranslatedPointerY);
                     _this.onPrePointerObservable.notifyObservers(pi, type);
@@ -25356,6 +25376,7 @@ var BABYLON;
                 if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                     return;
                 }
+                _this._pointerCaptures[evt.pointerId] = true;
                 _this._startingPointerPosition.x = _this._pointerX;
                 _this._startingPointerPosition.y = _this._pointerY;
                 _this._startingPointerTime = Date.now();
@@ -25403,7 +25424,7 @@ var BABYLON;
                 _this._updatePointerPosition(evt);
                 _this._initClickEvent(_this.onPrePointerObservable, _this.onPointerObservable, evt, function (clickInfo, pickResult) {
                     // PreObservable support
-                    if (_this.onPrePointerObservable.hasObservers()) {
+                    if (_this.onPrePointerObservable.hasObservers() && !_this._pointerCaptures[evt.pointerId]) {
                         if (!clickInfo.ignore) {
                             if (!clickInfo.hasSwiped) {
                                 if (clickInfo.singleClick && _this.onPrePointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
@@ -25436,6 +25457,7 @@ var BABYLON;
                     if (!_this.cameraToUseForPointers && !_this.activeCamera) {
                         return;
                     }
+                    _this._pointerCaptures[evt.pointerId] = false;
                     if (!_this.pointerUpPredicate) {
                         _this.pointerUpPredicate = function (mesh) {
                             return mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.isEnabled();
@@ -25957,7 +25979,7 @@ var BABYLON;
             this._processLateAnimationBindings();
         };
         /** @hidden */
-        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation) {
+        Scene.prototype._registerTargetForLateAnimationBinding = function (runtimeAnimation, originalValue) {
             var target = runtimeAnimation.target;
             this._registeredForLateAnimationBindings.pushNoDuplicate(target);
             if (!target._lateAnimationHolders) {
@@ -25966,19 +25988,21 @@ var BABYLON;
             if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
                 target._lateAnimationHolders[runtimeAnimation.targetPath] = {
                     totalWeight: 0,
-                    animations: []
+                    animations: [],
+                    originalValue: originalValue
                 };
             }
             target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
             target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
         };
-        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder, originalValue) {
+        Scene.prototype._processLateAnimationBindingsForMatrices = function (holder) {
             var normalizer = 1.0;
             var finalPosition = BABYLON.Tmp.Vector3[0];
             var finalScaling = BABYLON.Tmp.Vector3[1];
             var finalQuaternion = BABYLON.Tmp.Quaternion[0];
             var startIndex = 0;
             var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
             var scale = 1;
             if (holder.totalWeight < 1.0) {
                 // We need to mix the original value in                     
@@ -26012,6 +26036,46 @@ var BABYLON;
             BABYLON.Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, originalAnimation._workValue);
             return originalAnimation._workValue;
         };
+        Scene.prototype._processLateAnimationBindingsForQuaternions = function (holder) {
+            var originalAnimation = holder.animations[0];
+            var originalValue = holder.originalValue;
+            if (holder.animations.length === 1) {
+                return BABYLON.Quaternion.Slerp(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight));
+            }
+            var normalizer = 1.0;
+            var cumulativeQuaternion = null;
+            if (holder.totalWeight < 1.0) {
+                // We need to mix the original value in                     
+                //originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
+                var scale = 1.0 - holder.totalWeight;
+                cumulativeQuaternion = originalValue.scaleInPlace(scale);
+                cumulativeQuaternion.normalize();
+            }
+            else {
+                if (holder.animations.length === 2) { // Slerp as soon as we can
+                    return BABYLON.Quaternion.Slerp(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight);
+                }
+                normalizer = holder.totalWeight;
+            }
+            // There is no simple way to cumulate and weight quaternions so doing approximations here
+            for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                var runtimeAnimation = holder.animations[animIndex];
+                var scale = runtimeAnimation.weight / normalizer;
+                var current = runtimeAnimation.currentValue;
+                current.scaleInPlace(scale);
+                current.normalize();
+                if (!cumulativeQuaternion) {
+                    cumulativeQuaternion = current;
+                    continue;
+                }
+                if (!BABYLON.Quaternion.AreClose(current, cumulativeQuaternion)) {
+                    current.conjugateInPlace();
+                }
+                cumulativeQuaternion.addInPlace(current);
+                cumulativeQuaternion.normalize();
+            }
+            return cumulativeQuaternion;
+        };
         Scene.prototype._processLateAnimationBindings = function () {
             if (!this._registeredForLateAnimationBindings.length) {
                 return;
@@ -26021,54 +26085,59 @@ var BABYLON;
                 for (var path in target._lateAnimationHolders) {
                     var holder = target._lateAnimationHolders[path];
                     var originalAnimation = holder.animations[0];
-                    var originalValue = originalAnimation.originalValue;
-                    var finalTarget = originalAnimation.target;
+                    var originalValue = holder.originalValue;
                     var matrixDecomposeMode = BABYLON.Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
                     var finalValue = void 0;
                     if (matrixDecomposeMode) {
-                        finalValue = this._processLateAnimationBindingsForMatrices(holder, originalValue);
+                        finalValue = this._processLateAnimationBindingsForMatrices(holder);
                     }
                     else {
-                        var startIndex = 0;
-                        var normalizer = 1.0;
-                        if (holder.totalWeight < 1.0) {
-                            // We need to mix the original value in     
-                            if (originalValue.scale) {
-                                finalValue = originalValue.scale(1.0 - holder.totalWeight);
-                            }
-                            else {
-                                finalValue = originalValue * (1.0 - holder.totalWeight);
-                            }
+                        var quaternionMode = originalValue.w !== undefined;
+                        if (quaternionMode) {
+                            finalValue = this._processLateAnimationBindingsForQuaternions(holder);
                         }
                         else {
-                            // We need to normalize the weights
-                            normalizer = holder.totalWeight;
-                            var scale_1 = originalAnimation.weight / normalizer;
-                            if (scale_1 !== 1) {
-                                if (originalAnimation.currentValue.scale) {
-                                    finalValue = originalAnimation.currentValue.scale(scale_1);
+                            var startIndex = 0;
+                            var normalizer = 1.0;
+                            if (holder.totalWeight < 1.0) {
+                                // We need to mix the original value in     
+                                if (originalValue.scale) {
+                                    finalValue = originalValue.scale(1.0 - holder.totalWeight);
                                 }
                                 else {
-                                    finalValue = originalAnimation.currentValue * scale_1;
+                                    finalValue = originalValue * (1.0 - holder.totalWeight);
                                 }
                             }
                             else {
-                                finalValue = originalAnimation.currentValue;
-                            }
-                            startIndex = 1;
-                        }
-                        for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
-                            var runtimeAnimation = holder.animations[animIndex];
-                            var scale = runtimeAnimation.weight / normalizer;
-                            if (runtimeAnimation.currentValue.scaleAndAddToRef) {
-                                runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                // We need to normalize the weights
+                                normalizer = holder.totalWeight;
+                                var scale_1 = originalAnimation.weight / normalizer;
+                                if (scale_1 !== 1) {
+                                    if (originalAnimation.currentValue.scale) {
+                                        finalValue = originalAnimation.currentValue.scale(scale_1);
+                                    }
+                                    else {
+                                        finalValue = originalAnimation.currentValue * scale_1;
+                                    }
+                                }
+                                else {
+                                    finalValue = originalAnimation.currentValue;
+                                }
+                                startIndex = 1;
                             }
-                            else {
-                                finalValue += runtimeAnimation.currentValue * scale;
+                            for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+                                var runtimeAnimation = holder.animations[animIndex];
+                                var scale = runtimeAnimation.weight / normalizer;
+                                if (runtimeAnimation.currentValue.scaleAndAddToRef) {
+                                    runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                }
+                                else {
+                                    finalValue += runtimeAnimation.currentValue * scale;
+                                }
                             }
                         }
                     }
-                    finalTarget[path] = finalValue;
+                    target[path] = finalValue;
                 }
                 target._lateAnimationHolders = {};
             }
@@ -51843,6 +51912,10 @@ var BABYLON;
              */
             this._currentFrame = 0;
             /**
+             * The original value of the runtime animation
+             */
+            this._originalValue = new Array();
+            /**
              * The offsets cache of the runtime animation
              */
             this._offsetsCache = {};
@@ -51904,16 +51977,6 @@ var BABYLON;
             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
@@ -51960,14 +52023,28 @@ var BABYLON;
          */
         RuntimeAnimation.prototype.reset = function (restoreOriginal) {
             if (restoreOriginal === void 0) { restoreOriginal = false; }
-            if (restoreOriginal && this._originalValue != null) {
-                this.setValue(this._originalValue, -1);
+            if (restoreOriginal) {
+                if (this._target instanceof Array) {
+                    var index = 0;
+                    for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
+                        var target = _a[_i];
+                        if (this._originalValue[index] !== undefined) {
+                            this._setValue(target, this._originalValue[index], -1);
+                        }
+                        index++;
+                    }
+                }
+                else {
+                    if (this._originalValue[0] !== undefined) {
+                        this._setValue(this._target, this._originalValue[0], -1);
+                    }
+                }
             }
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalValue = null;
+            this._originalValue = new Array();
         };
         /**
          * Specifies if the runtime animation is stopped
@@ -52009,16 +52086,19 @@ var BABYLON;
         RuntimeAnimation.prototype.setValue = function (currentValue, weight) {
             if (weight === void 0) { weight = 1.0; }
             if (this._target instanceof Array) {
+                var index = 0;
                 for (var _i = 0, _a = this._target; _i < _a.length; _i++) {
                     var target = _a[_i];
-                    this._setValue(target, currentValue, weight);
+                    this._setValue(target, currentValue, weight, index);
+                    index++;
                 }
             }
             else {
                 this._setValue(this._target, currentValue, weight);
             }
         };
-        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight) {
+        RuntimeAnimation.prototype._setValue = function (target, currentValue, weight, targetIndex) {
+            if (targetIndex === void 0) { targetIndex = 0; }
             // Set value
             var path;
             var destination;
@@ -52038,7 +52118,7 @@ var BABYLON;
             this._targetPath = path;
             this._activeTarget = destination;
             this._weight = weight;
-            if (!this._originalValue) {
+            if (this._originalValue[targetIndex] === undefined) {
                 var originalValue = void 0;
                 if (destination.getRestPose && path === "_matrix") { // For bones
                     originalValue = destination.getRestPose();
@@ -52047,10 +52127,10 @@ var BABYLON;
                     originalValue = destination[path];
                 }
                 if (originalValue && originalValue.clone) {
-                    this._originalValue = originalValue.clone();
+                    this._originalValue[targetIndex] = originalValue.clone();
                 }
                 else {
-                    this._originalValue = originalValue;
+                    this._originalValue[targetIndex] = originalValue;
                 }
             }
             // Blending
@@ -52105,7 +52185,7 @@ var BABYLON;
                 this._currentValue = currentValue;
             }
             if (weight !== -1.0) {
-                this._scene._registerTargetForLateAnimationBinding(this);
+                this._scene._registerTargetForLateAnimationBinding(this, this._originalValue[targetIndex]);
             }
             else {
                 destination[path] = this._currentValue;
@@ -102248,6 +102328,8 @@ var BABYLON;
              * If true, no extra effects are applied to transparent pixels.
              */
             this.transparencyAsCoverage = false;
+            /** @hidden */
+            this._normalizeAnimationGroupsToBeginAtZero = true;
             /**
              * Function called before loading a url referenced by the asset.
              */
@@ -102560,6 +102642,7 @@ var BABYLON;
             loader.useClipPlane = this.useClipPlane;
             loader.compileShadowGenerators = this.compileShadowGenerators;
             loader.transparencyAsCoverage = this.transparencyAsCoverage;
+            loader._normalizeAnimationGroupsToBeginAtZero = this._normalizeAnimationGroupsToBeginAtZero;
             loader.preprocessUrlAsync = this.preprocessUrlAsync;
             loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
@@ -104139,6 +104222,7 @@ var BABYLON;
                 this.useClipPlane = false;
                 this.compileShadowGenerators = false;
                 this.transparencyAsCoverage = false;
+                this._normalizeAnimationGroupsToBeginAtZero = true;
                 this.preprocessUrlAsync = function (url) { return Promise.resolve(url); };
                 this.onMeshLoadedObservable = new BABYLON.Observable();
                 this.onTextureLoadedObservable = new BABYLON.Observable();
@@ -105020,6 +105104,8 @@ var BABYLON;
                  * If true, no extra effects are applied to transparent pixels.
                  */
                 this.transparencyAsCoverage = false;
+                /** @hidden */
+                this._normalizeAnimationGroupsToBeginAtZero = true;
                 /**
                  * Function called before loading a url referenced by the asset.
                  */
@@ -105752,6 +105838,7 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadAnimationAsync = function (context, animation) {
+                var _this = this;
                 var babylonAnimationGroup = new BABYLON.AnimationGroup(animation.name || "animation" + animation._index, this._babylonScene);
                 animation._babylonAnimationGroup = babylonAnimationGroup;
                 var promises = new Array();
@@ -105762,7 +105849,7 @@ var BABYLON;
                     promises.push(this._loadAnimationChannelAsync(context + "/channels/" + channel._index, context, animation, channel, babylonAnimationGroup));
                 }
                 return Promise.all(promises).then(function () {
-                    babylonAnimationGroup.normalize();
+                    babylonAnimationGroup.normalize(_this._normalizeAnimationGroupsToBeginAtZero ? 0 : null);
                 });
             };
             GLTFLoader.prototype._loadAnimationChannelAsync = function (context, animationContext, animation, channel, babylonAnimationGroup) {

+ 26 - 18
src/Animations/babylon.runtimeAnimation.ts

@@ -27,7 +27,7 @@
         /**
          * The original value of the runtime animation
          */
-        private _originalValue: any;
+        private _originalValue = new Array<any>();
         
         /**
          * The original blend value of the runtime animation
@@ -109,14 +109,7 @@
          */
         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
@@ -167,15 +160,28 @@
          * @param restoreOriginal defines whether to restore the target property to the original value
          */
         public reset(restoreOriginal = false): void {
-            if (restoreOriginal && this._originalValue != null) {
-                this.setValue(this._originalValue, -1);
+            if (restoreOriginal) {
+                if (this._target instanceof Array) {
+                    var index = 0;
+                    for (const target of this._target) {
+                        if (this._originalValue[index] !== undefined) {
+                            this._setValue(target, this._originalValue[index], -1);
+                        }
+                        index++;
+                    }
+                }
+                else {
+                    if (this._originalValue[0] !== undefined) {
+                        this._setValue(this._target, this._originalValue[0], -1);
+                    }
+                }
             }
 
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._currentFrame = 0;
             this._blendingFactor = 0;
-            this._originalValue = null;
+            this._originalValue = new Array<any>();
         }
 
         /**
@@ -223,8 +229,10 @@
          */
         public setValue(currentValue: any, weight = 1.0): void {
             if (this._target instanceof Array) {
+                var index = 0;
                 for (const target of this._target) {
-                    this._setValue(target, currentValue, weight);
+                    this._setValue(target, currentValue, weight, index);
+                    index++;
                 }
             }
             else {
@@ -232,7 +240,7 @@
             }
         }
 
-        private _setValue(target: any, currentValue: any, weight: number): void {
+        private _setValue(target: any, currentValue: any, weight: number, targetIndex = 0): void {
             // Set value
             var path: any;
             var destination: any;
@@ -257,7 +265,7 @@
             this._activeTarget = destination;
             this._weight = weight;
 
-            if (!this._originalValue) {
+            if (this._originalValue[targetIndex] === undefined) {
                 let originalValue: any;
 
                 if (destination.getRestPose && path === "_matrix") { // For bones
@@ -267,9 +275,9 @@
                 }
 
                 if (originalValue && originalValue.clone) {
-                    this._originalValue = originalValue.clone();
+                    this._originalValue[targetIndex] = originalValue.clone();
                 } else {
-                    this._originalValue = originalValue;
+                    this._originalValue[targetIndex] = originalValue;
                 }
             }
 
@@ -320,7 +328,7 @@
             }
 
             if (weight !== -1.0) {
-                this._scene._registerTargetForLateAnimationBinding(this);
+                this._scene._registerTargetForLateAnimationBinding(this, this._originalValue[targetIndex]);
             } else {
                 destination[path] = this._currentValue;
             }

+ 22 - 0
src/Math/babylon.math.ts

@@ -3654,6 +3654,28 @@
         }
 
         /**
+         * Returns the dot product (float) between the quaternions "left" and "right"
+         * @param left defines the left operand
+         * @param right defines the right operand
+         * @returns the dot product
+         */
+        public static Dot(left: Quaternion, right: Quaternion): number {
+            return (left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w);
+        }        
+
+        /**
+         * Checks if the two quaternions are close to each other
+         * @param quat0 defines the first quaternion to check
+         * @param quat1 defines the second quaternion to check
+         * @returns true if the two quaternions are close to each other
+         */
+        public static AreClose(quat0: Quaternion, quat1: Quaternion): boolean {
+            let dot = Quaternion.Dot(quat0, quat1);
+ 
+            return dot >= 0;					
+        }    
+        
+        /**
          * Creates an empty quaternion
          * @returns a new quaternion set to (0.0, 0.0, 0.0)
          */

+ 95 - 38
src/babylon.scene.ts

@@ -2687,7 +2687,7 @@
         }
 
         /** @hidden */
-        public _registerTargetForLateAnimationBinding(runtimeAnimation: RuntimeAnimation): void {
+        public _registerTargetForLateAnimationBinding(runtimeAnimation: RuntimeAnimation, originalValue: any): void {
             let target = runtimeAnimation.target;
             this._registeredForLateAnimationBindings.pushNoDuplicate(target);
 
@@ -2698,7 +2698,8 @@
             if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
                 target._lateAnimationHolders[runtimeAnimation.targetPath] = {
                     totalWeight: 0,
-                    animations: []
+                    animations: [],
+                    originalValue: originalValue
                 }
             }
 
@@ -2708,14 +2709,16 @@
 
         private _processLateAnimationBindingsForMatrices(holder: {
             totalWeight: number,
-            animations: RuntimeAnimation[]
-        }, originalValue: Matrix): any {
+            animations: RuntimeAnimation[],
+            originalValue: Matrix
+        }): any {
             let normalizer = 1.0;
             let finalPosition = Tmp.Vector3[0];
             let finalScaling = Tmp.Vector3[1];
             let finalQuaternion = Tmp.Quaternion[0];
             let startIndex = 0;            
             let originalAnimation = holder.animations[0];
+            let originalValue = holder.originalValue;
 
             var scale = 1;
             if (holder.totalWeight < 1.0) {
@@ -2754,6 +2757,56 @@
             return originalAnimation._workValue;
         }
 
+        private _processLateAnimationBindingsForQuaternions(holder: {
+            totalWeight: number,
+            animations: RuntimeAnimation[],
+            originalValue: Quaternion
+        }): Quaternion {
+            let originalAnimation = holder.animations[0];
+            let originalValue = holder.originalValue;
+
+            if (holder.animations.length === 1) {
+                return Quaternion.Slerp(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight));
+            }
+
+            let normalizer = 1.0;
+            let cumulativeQuaternion: Nullable<Quaternion> = null;
+            
+            if (holder.totalWeight < 1.0) {
+                // We need to mix the original value in                     
+                //originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
+                let scale = 1.0 - holder.totalWeight;
+                cumulativeQuaternion = originalValue.scaleInPlace(scale);
+                cumulativeQuaternion.normalize();
+            } else {
+                if (holder.animations.length === 2) { // Slerp as soon as we can
+                    return Quaternion.Slerp(holder.animations[0].currentValue,  holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight);
+                }
+                normalizer = holder.totalWeight;
+            }
+
+            // There is no simple way to cumulate and weight quaternions so doing approximations here
+            for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+                let runtimeAnimation = holder.animations[animIndex];   
+                let scale = runtimeAnimation.weight / normalizer;
+                let current: Quaternion = runtimeAnimation.currentValue;
+                current.scaleInPlace(scale);
+                current.normalize();
+                if (!cumulativeQuaternion) {
+                    cumulativeQuaternion = current;
+                    continue;
+                }
+
+                if (!Quaternion.AreClose(current, cumulativeQuaternion)) {
+                    current.conjugateInPlace();
+                }
+                cumulativeQuaternion.addInPlace(current);
+                cumulativeQuaternion.normalize();
+            }
+
+            return cumulativeQuaternion!;
+        }
+
         private _processLateAnimationBindings(): void {
             if (!this._registeredForLateAnimationBindings.length) {
                 return;
@@ -2763,55 +2816,59 @@
 
                 for (var path in target._lateAnimationHolders) {
                     var holder = target._lateAnimationHolders[path];                     
-                    let originalAnimation = holder.animations[0];
-                    let originalValue = originalAnimation.originalValue;
-                    let finalTarget = originalAnimation.target;   
+                    let originalAnimation: RuntimeAnimation = holder.animations[0];
+                    let originalValue = holder.originalValue;
                     
                     let matrixDecomposeMode = Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
 
                     let finalValue: any;
                     if (matrixDecomposeMode) {
-                        finalValue = this._processLateAnimationBindingsForMatrices(holder, originalValue);
+                        finalValue = this._processLateAnimationBindingsForMatrices(holder);
                     } else {
-                        let startIndex = 0;
-                        let normalizer = 1.0;
-
-                        if (holder.totalWeight < 1.0) {
-                            // We need to mix the original value in     
-                            if (originalValue.scale) {
-                                finalValue = originalValue.scale(1.0 - holder.totalWeight);
-                            } else {
-                                finalValue = originalValue * (1.0 - holder.totalWeight);
-                            }
+                        let quaternionMode = originalValue.w !== undefined;
+                        if (quaternionMode) {
+                            finalValue = this._processLateAnimationBindingsForQuaternions(holder);
                         } else {
-                            // We need to normalize the weights
-                            normalizer = holder.totalWeight;
-                            let scale = originalAnimation.weight / normalizer;
-                            if (scale !== 1) {
-                                if (originalAnimation.currentValue.scale) {
-                                    finalValue = originalAnimation.currentValue.scale(scale);
+
+                            let startIndex = 0;
+                            let normalizer = 1.0;
+
+                            if (holder.totalWeight < 1.0) {
+                                // We need to mix the original value in     
+                                if (originalValue.scale) {
+                                    finalValue = originalValue.scale(1.0 - holder.totalWeight);
                                 } else {
-                                    finalValue = originalAnimation.currentValue * scale;
+                                    finalValue = originalValue * (1.0 - holder.totalWeight);
                                 }
                             } else {
-                                finalValue = originalAnimation.currentValue;
-                            }
+                                // We need to normalize the weights
+                                normalizer = holder.totalWeight;
+                                let scale = originalAnimation.weight / normalizer;
+                                if (scale !== 1) {
+                                    if (originalAnimation.currentValue.scale) {                  
+                                        finalValue = originalAnimation.currentValue.scale(scale);
+                                    } else {
+                                        finalValue = originalAnimation.currentValue * scale;
+                                    }
+                                } else {
+                                    finalValue = originalAnimation.currentValue;
+                                }
 
-                            startIndex = 1;
-                        }
+                                startIndex = 1;
+                            }
 
-                        for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
-                            var runtimeAnimation = holder.animations[animIndex];   
-                            var scale = runtimeAnimation.weight / normalizer;
-                            if (runtimeAnimation.currentValue.scaleAndAddToRef) {
-                                runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
-                            } else {
-                                finalValue += runtimeAnimation.currentValue * scale;
+                            for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+                                var runtimeAnimation = holder.animations[animIndex];   
+                                var scale = runtimeAnimation.weight / normalizer;
+                                if (runtimeAnimation.currentValue.scaleAndAddToRef) {
+                                    runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                                } else {
+                                    finalValue += runtimeAnimation.currentValue * scale;
+                                }
                             }
                         }
                     }
-
-                    finalTarget[path] = finalValue;
+                    target[path] = finalValue;
                 }
 
                 target._lateAnimationHolders = {};