Raanan Weber 7 роки тому
батько
коміт
5d9917b9b6
32 змінених файлів з 10823 додано та 10677 видалено
  1. 630 611
      Playground/babylon.d.txt
  2. 3 2
      Viewer/src/index.ts
  3. 886 867
      dist/preview release/babylon.d.ts
  4. 27 27
      dist/preview release/babylon.js
  5. 176 160
      dist/preview release/babylon.max.js
  6. 27 27
      dist/preview release/babylon.worker.js
  7. 7934 7915
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  8. 30 30
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  9. 178 161
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  10. 178 161
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  11. 176 160
      dist/preview release/es6.js
  12. 1 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  13. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  14. 1 0
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  15. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  16. 1 0
      dist/preview release/loaders/babylon.glTFFileLoader.js
  17. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  18. 1 0
      dist/preview release/loaders/babylonjs.loaders.js
  19. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  20. 2 7
      dist/preview release/typedocValidationBaseline.json
  21. 372 351
      dist/preview release/viewer/babylon.viewer.max.js
  22. 3 1
      dist/preview release/what's new.md
  23. 1 2
      inspector/src/tsconfig.json
  24. 1 2
      loaders/src/tsconfig.json
  25. 1 2
      materialsLibrary/src/tsconfig.json
  26. 1 2
      postProcessLibrary/src/tsconfig.json
  27. 1 2
      proceduralTexturesLibrary/src/tsconfig.json
  28. 1 2
      serializers/src/tsconfig.json
  29. 42 1
      src/Animations/babylon.animatable.ts
  30. 23 5
      src/Animations/babylon.runtimeAnimation.ts
  31. 115 168
      src/Materials/babylon.effect.ts
  32. 1 1
      src/Mesh/Compression/babylon.dracoCompression.ts

Різницю між файлами не показано, бо вона завелика
+ 630 - 611
Playground/babylon.d.txt


+ 3 - 2
Viewer/src/index.ts

@@ -7,7 +7,8 @@ import { viewerManager } from './viewer/viewerManager';
 import { DefaultViewer } from './viewer/defaultViewer';
 import { AbstractViewer } from './viewer/viewer';
 import { ModelLoader } from './model/modelLoader';
-import { ViewerModel } from './model/viewerModel';
+import { ViewerModel, ModelState } from './model/viewerModel';
+import { AnimationPlayMode, AnimationState } from './model/modelAnimation';
 
 /**
  * BabylonJS Viewer
@@ -41,4 +42,4 @@ function disposeAll() {
 }
 
 // public API for initialization
-export { InitTags, DefaultViewer, AbstractViewer, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel };
+export { InitTags, DefaultViewer, AbstractViewer, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel, AnimationPlayMode, AnimationState, ModelState };

Різницю між файлами не показано, бо вона завелика
+ 886 - 867
dist/preview release/babylon.d.ts


Різницю між файлами не показано, бо вона завелика
+ 27 - 27
dist/preview release/babylon.js


+ 176 - 160
dist/preview release/babylon.max.js

@@ -221,10 +221,6 @@ var BABYLON;
                 this._fallbacks = fallbacks;
             }
             this.uniqueId = Effect._uniqueIdSeed++;
-            if (this._getFromCache(baseName)) {
-                this._prepareEffect();
-                return;
-            }
             var vertexSource;
             var fragmentSource;
             if (baseName.vertexElement) {
@@ -245,71 +241,30 @@ var BABYLON;
             else {
                 fragmentSource = baseName.fragment || baseName;
             }
-            var finalVertexCode;
-            this._loadVertexShaderAsync(vertexSource)
-                .then(function (vertexCode) {
-                return _this._processIncludesAsync(vertexCode);
-            })
-                .then(function (vertexCodeWithIncludes) {
-                finalVertexCode = _this._processShaderConversion(vertexCodeWithIncludes, false);
-                return _this._loadFragmentShaderAsync(fragmentSource);
-            })
-                .then(function (fragmentCode) {
-                return _this._processIncludesAsync(fragmentCode);
-            })
-                .then(function (fragmentCodeWithIncludes) {
-                var migratedFragmentCode = _this._processShaderConversion(fragmentCodeWithIncludes, true);
-                if (baseName) {
-                    var vertex = baseName.vertexElement || baseName.vertex || baseName;
-                    var fragment = baseName.fragmentElement || baseName.fragment || baseName;
-                    _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + finalVertexCode;
-                    _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
-                }
-                else {
-                    _this._vertexSourceCode = finalVertexCode;
-                    _this._fragmentSourceCode = migratedFragmentCode;
-                }
-                _this._setInCache(baseName);
-                _this._prepareEffect();
+            this._loadVertexShader(vertexSource, function (vertexCode) {
+                _this._processIncludes(vertexCode, function (vertexCodeWithIncludes) {
+                    _this._processShaderConversion(vertexCodeWithIncludes, false, function (migratedVertexCode) {
+                        _this._loadFragmentShader(fragmentSource, function (fragmentCode) {
+                            _this._processIncludes(fragmentCode, function (fragmentCodeWithIncludes) {
+                                _this._processShaderConversion(fragmentCodeWithIncludes, true, function (migratedFragmentCode) {
+                                    if (baseName) {
+                                        var vertex = baseName.vertexElement || baseName.vertex || baseName;
+                                        var fragment = baseName.fragmentElement || baseName.fragment || baseName;
+                                        _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
+                                        _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
+                                    }
+                                    else {
+                                        _this._vertexSourceCode = migratedVertexCode;
+                                        _this._fragmentSourceCode = migratedFragmentCode;
+                                    }
+                                    _this._prepareEffect();
+                                });
+                            });
+                        });
+                    });
+                });
             });
         }
-        Effect.prototype._getSourceCacheKey = function (baseName) {
-            var cacheKey = baseName;
-            if (this._indexParameters) {
-                for (var key in this._indexParameters) {
-                    if (this._indexParameters.hasOwnProperty(key)) {
-                        cacheKey += "|";
-                        cacheKey += key;
-                        cacheKey += "_";
-                        cacheKey += this._indexParameters[key];
-                    }
-                }
-            }
-            return cacheKey;
-        };
-        Effect.prototype._getFromCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return false;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            var sources = Effect._sourceCache[cacheKey];
-            if (!sources) {
-                return false;
-            }
-            this._vertexSourceCode = sources.vertex;
-            this._fragmentSourceCode = sources.fragment;
-            return true;
-        };
-        Effect.prototype._setInCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            Effect._sourceCache[cacheKey] = {
-                vertex: this._vertexSourceCode,
-                fragment: this._fragmentSourceCode
-            };
-        };
         Object.defineProperty(Effect.prototype, "key", {
             /**
              * Unique key for this effect
@@ -416,22 +371,25 @@ var BABYLON;
             });
         };
         /** @ignore */
-        Effect.prototype._loadVertexShaderAsync = function (vertex) {
+        Effect.prototype._loadVertexShader = function (vertex, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (vertex instanceof HTMLElement) {
                     var vertexCode = BABYLON.Tools.GetDOMTextContent(vertex);
-                    return Promise.resolve(vertexCode);
+                    callback(vertexCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (vertex.substr(0, 7) === "base64:") {
                 var vertexBinary = window.atob(vertex.substr(7));
-                return Promise.resolve(vertexBinary);
+                callback(vertexBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
-                return Promise.resolve(Effect.ShadersStore[vertex + "VertexShader"]);
+                callback(Effect.ShadersStore[vertex + "VertexShader"]);
+                return;
             }
             var vertexShaderUrl;
             if (vertex[0] === "." || vertex[0] === "/" || vertex.indexOf("http") > -1) {
@@ -441,28 +399,32 @@ var BABYLON;
                 vertexShaderUrl = BABYLON.Engine.ShadersRepository + vertex;
             }
             // Vertex shader
-            return this._engine._loadFileAsync(vertexShaderUrl + ".vertex.fx");
+            this._engine._loadFile(vertexShaderUrl + ".vertex.fx", callback);
         };
         /** @ignore */
-        Effect.prototype._loadFragmentShaderAsync = function (fragment) {
+        Effect.prototype._loadFragmentShader = function (fragment, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (fragment instanceof HTMLElement) {
                     var fragmentCode = BABYLON.Tools.GetDOMTextContent(fragment);
-                    return Promise.resolve(fragmentCode);
+                    callback(fragmentCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (fragment.substr(0, 7) === "base64:") {
                 var fragmentBinary = window.atob(fragment.substr(7));
-                return Promise.resolve(fragmentBinary);
+                callback(fragmentBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "PixelShader"]);
+                callback(Effect.ShadersStore[fragment + "PixelShader"]);
+                return;
             }
             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "FragmentShader"]);
+                callback(Effect.ShadersStore[fragment + "FragmentShader"]);
+                return;
             }
             var fragmentShaderUrl;
             if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
@@ -472,7 +434,7 @@ var BABYLON;
                 fragmentShaderUrl = BABYLON.Engine.ShadersRepository + fragment;
             }
             // Fragment shader
-            return this._engine._loadFileAsync(fragmentShaderUrl + ".fragment.fx");
+            this._engine._loadFile(fragmentShaderUrl + ".fragment.fx", callback);
         };
         Effect.prototype._dumpShadersSource = function (vertexCode, fragmentCode, defines) {
             // Rebuild shaders source code
@@ -501,14 +463,16 @@ var BABYLON;
             }
         };
         ;
-        Effect.prototype._processShaderConversion = function (sourceCode, isFragment) {
+        Effect.prototype._processShaderConversion = function (sourceCode, isFragment, callback) {
             var preparedSourceCode = this._processPrecision(sourceCode);
             if (this._engine.webGLVersion == 1) {
-                return preparedSourceCode;
+                callback(preparedSourceCode);
+                return;
             }
             // Already converted
             if (preparedSourceCode.indexOf("#version 3") !== -1) {
-                return preparedSourceCode.replace("#version 300 es", "");
+                callback(preparedSourceCode.replace("#version 300 es", ""));
+                return;
             }
             var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
             // Remove extensions 
@@ -532,86 +496,80 @@ var BABYLON;
                 result = result.replace(/gl_FragData/g, "glFragData");
                 result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
             }
-            return result;
+            callback(result);
         };
-        Effect.prototype._processIncludesAsync = function (sourceCode) {
+        Effect.prototype._processIncludes = function (sourceCode, callback) {
             var _this = this;
-            return new Promise(function (resolve, reject) {
-                var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
-                var match = regex.exec(sourceCode);
-                var returnValue = sourceCode;
-                while (match != null) {
-                    var includeFile = match[1];
-                    // Uniform declaration
-                    if (includeFile.indexOf("__decl__") !== -1) {
-                        includeFile = includeFile.replace(/__decl__/, "");
-                        if (_this._engine.supportsUniformBuffers) {
-                            includeFile = includeFile.replace(/Vertex/, "Ubo");
-                            includeFile = includeFile.replace(/Fragment/, "Ubo");
-                        }
-                        includeFile = includeFile + "Declaration";
-                    }
-                    if (Effect.IncludesShadersStore[includeFile]) {
-                        // Substitution
-                        var includeContent = Effect.IncludesShadersStore[includeFile];
-                        if (match[2]) {
-                            var splits = match[3].split(",");
-                            for (var index = 0; index < splits.length; index += 2) {
-                                var source = new RegExp(splits[index], "g");
-                                var dest = splits[index + 1];
-                                includeContent = includeContent.replace(source, dest);
-                            }
+            var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
+            var match = regex.exec(sourceCode);
+            var returnValue = new String(sourceCode);
+            while (match != null) {
+                var includeFile = match[1];
+                // Uniform declaration
+                if (includeFile.indexOf("__decl__") !== -1) {
+                    includeFile = includeFile.replace(/__decl__/, "");
+                    if (this._engine.supportsUniformBuffers) {
+                        includeFile = includeFile.replace(/Vertex/, "Ubo");
+                        includeFile = includeFile.replace(/Fragment/, "Ubo");
+                    }
+                    includeFile = includeFile + "Declaration";
+                }
+                if (Effect.IncludesShadersStore[includeFile]) {
+                    // Substitution
+                    var includeContent = Effect.IncludesShadersStore[includeFile];
+                    if (match[2]) {
+                        var splits = match[3].split(",");
+                        for (var index = 0; index < splits.length; index += 2) {
+                            var source = new RegExp(splits[index], "g");
+                            var dest = splits[index + 1];
+                            includeContent = includeContent.replace(source, dest);
                         }
-                        if (match[4]) {
-                            var indexString = match[5];
-                            if (indexString.indexOf("..") !== -1) {
-                                var indexSplits = indexString.split("..");
-                                var minIndex = parseInt(indexSplits[0]);
-                                var maxIndex = parseInt(indexSplits[1]);
-                                var sourceIncludeContent = includeContent.slice(0);
-                                includeContent = "";
-                                if (isNaN(maxIndex)) {
-                                    maxIndex = _this._indexParameters[indexSplits[1]];
-                                }
-                                for (var i = minIndex; i < maxIndex; i++) {
-                                    if (!_this._engine.supportsUniformBuffers) {
-                                        // Ubo replacement
-                                        sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
-                                            return p1 + "{X}";
-                                        });
-                                    }
-                                    includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
-                                }
+                    }
+                    if (match[4]) {
+                        var indexString = match[5];
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
                             }
-                            else {
-                                if (!_this._engine.supportsUniformBuffers) {
+                            for (var i = minIndex; i < maxIndex; i++) {
+                                if (!this._engine.supportsUniformBuffers) {
                                     // Ubo replacement
-                                    includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
                                         return p1 + "{X}";
                                     });
                                 }
-                                includeContent = includeContent.replace(/\{X\}/g, indexString);
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
                             }
                         }
-                        // Replace
-                        returnValue = returnValue.replace(match[0], includeContent);
-                    }
-                    else {
-                        var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
-                        _this._engine._loadFileAsync(includeShaderUrl)
-                            .then(function (fileContent) {
-                            Effect.IncludesShadersStore[includeFile] = fileContent;
-                            return _this._processIncludesAsync(returnValue);
-                        })
-                            .then(function (returnValue) {
-                            resolve(returnValue);
-                        });
-                        return;
+                        else {
+                            if (!this._engine.supportsUniformBuffers) {
+                                // Ubo replacement
+                                includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    return p1 + "{X}";
+                                });
+                            }
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
-                    match = regex.exec(sourceCode);
+                    // Replace
+                    returnValue = returnValue.replace(match[0], includeContent);
                 }
-                resolve(returnValue);
-            });
+                else {
+                    var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
+                    this._engine._loadFile(includeShaderUrl, function (fileContent) {
+                        Effect.IncludesShadersStore[includeFile] = fileContent;
+                        _this._processIncludes(returnValue, callback);
+                    });
+                    return;
+                }
+                match = regex.exec(sourceCode);
+            }
+            callback(returnValue);
         };
         Effect.prototype._processPrecision = function (source) {
             if (source.indexOf("precision highp float") === -1) {
@@ -803,8 +761,7 @@ var BABYLON;
             this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         };
         /**
-         * (Warning! setTextureFromPostProcessOutput may be desired instead)
-         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
+         * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the input texture from.
          */
@@ -812,7 +769,8 @@ var BABYLON;
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
         };
         /**
-         * Sets the output texture of the passed in post process to be input of this effect.
+         * (Warning! setTextureFromPostProcessOutput may be desired instead)
+         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the output texture from.
          */
@@ -1264,8 +1222,6 @@ var BABYLON;
         };
         Effect._uniqueIdSeed = 0;
         Effect._baseCache = {};
-        Effect._sourceCache = {};
-        // Statics
         /**
          * Store of each shader (The can be looked up using effect.key)
          */
@@ -47993,9 +47949,10 @@ var BABYLON;
          * @param target defines the target of the animation
          * @param animation defines the source {BABYLON.Animation} object
          * @param scene defines the hosting scene
+         * @param host defines the initiating Animatable
          */
-        function RuntimeAnimation(target, animation, scene) {
-            this.currentFrame = 0;
+        function RuntimeAnimation(target, animation, scene, host) {
+            this._currentFrame = 0;
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
@@ -48008,8 +47965,19 @@ var BABYLON;
             this._animation = animation;
             this._target = target;
             this._scene = scene;
+            this._host = host;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "currentFrame", {
+            /**
+             * Gets the current frame
+             */
+            get: function () {
+                return this._currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "weight", {
             /**
              * Gets the weight of the runtime animation
@@ -48070,7 +48038,7 @@ var BABYLON;
         RuntimeAnimation.prototype.reset = function () {
             this._offsetsCache = {};
             this._highLimitsCache = {};
-            this.currentFrame = 0;
+            this._currentFrame = 0;
             this._blendingFactor = 0;
             this._originalValue = null;
         };
@@ -48093,7 +48061,7 @@ var BABYLON;
             if (loopMode === BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
                 return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
             }
-            this.currentFrame = currentFrame;
+            this._currentFrame = currentFrame;
             var keys = this._animation.getKeys();
             // Try to get a hash to find the right key
             var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
@@ -48431,6 +48399,12 @@ var BABYLON;
             // Compute value
             var repeatCount = (ratio / range) >> 0;
             var currentFrame = returnValue ? from + ratio % range : to;
+            // Need to normalize?
+            if (this._host && this._host.syncRoot) {
+                var syncRoot = this._host.syncRoot;
+                var hostNormalizedFrame = (syncRoot.masterFrame - syncRoot.fromFrame) / (syncRoot.toFrame - syncRoot.fromFrame);
+                currentFrame = from + (to - from) * hostNormalizedFrame;
+            }
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
             this.setValue(currentValue, weight);
@@ -48497,6 +48471,30 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "syncRoot", {
+            /**
+             * Gets the root Animatable used to synchronize and normalize animations
+             */
+            get: function () {
+                return this._syncRoot;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Animatable.prototype, "masterFrame", {
+            /**
+             * Gets the current frame of the first RuntimeAnimation
+             * Used to synchronize Animatables
+             */
+            get: function () {
+                if (this._runtimeAnimations.length === 0) {
+                    return 0;
+                }
+                return this._runtimeAnimations[0].currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "weight", {
             /**
              * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
@@ -48533,13 +48531,31 @@ var BABYLON;
             configurable: true
         });
         // Methods
+        /**
+         * Synchronize and normalize current Animatable with a source Animatable.
+         * This is useful when using animation weights and when animations are not of the same length
+         * @param root defines the root Animatable to synchronize with
+         * @returns the current Animatable
+         */
+        Animatable.prototype.syncWith = function (root) {
+            this._syncRoot = root;
+            if (root) {
+                // Make sure this animatable will animate after the root
+                var index = this._scene._activeAnimatables.indexOf(this);
+                if (index > -1) {
+                    this._scene._activeAnimatables.splice(index, 1);
+                    this._scene._activeAnimatables.push(this);
+                }
+            }
+            return this;
+        };
         Animatable.prototype.getAnimations = function () {
             return this._runtimeAnimations;
         };
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene, this));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -57997,7 +58013,7 @@ var BABYLON;
             };
         };
         DracoCompression._GetDefaultDecoderUrl = function () {
-            if (!BABYLON.Tools.IsWindowObjectExist) {
+            if (!BABYLON.Tools.IsWindowObjectExist()) {
                 return null;
             }
             for (var i = 0; i < document.scripts.length; i++) {

Різницю між файлами не показано, бо вона завелика
+ 27 - 27
dist/preview release/babylon.worker.js


Різницю між файлами не показано, бо вона завелика
+ 7934 - 7915
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Різницю між файлами не показано, бо вона завелика
+ 30 - 30
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 178 - 161
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -221,10 +221,6 @@ var BABYLON;
                 this._fallbacks = fallbacks;
             }
             this.uniqueId = Effect._uniqueIdSeed++;
-            if (this._getFromCache(baseName)) {
-                this._prepareEffect();
-                return;
-            }
             var vertexSource;
             var fragmentSource;
             if (baseName.vertexElement) {
@@ -245,71 +241,30 @@ var BABYLON;
             else {
                 fragmentSource = baseName.fragment || baseName;
             }
-            var finalVertexCode;
-            this._loadVertexShaderAsync(vertexSource)
-                .then(function (vertexCode) {
-                return _this._processIncludesAsync(vertexCode);
-            })
-                .then(function (vertexCodeWithIncludes) {
-                finalVertexCode = _this._processShaderConversion(vertexCodeWithIncludes, false);
-                return _this._loadFragmentShaderAsync(fragmentSource);
-            })
-                .then(function (fragmentCode) {
-                return _this._processIncludesAsync(fragmentCode);
-            })
-                .then(function (fragmentCodeWithIncludes) {
-                var migratedFragmentCode = _this._processShaderConversion(fragmentCodeWithIncludes, true);
-                if (baseName) {
-                    var vertex = baseName.vertexElement || baseName.vertex || baseName;
-                    var fragment = baseName.fragmentElement || baseName.fragment || baseName;
-                    _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + finalVertexCode;
-                    _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
-                }
-                else {
-                    _this._vertexSourceCode = finalVertexCode;
-                    _this._fragmentSourceCode = migratedFragmentCode;
-                }
-                _this._setInCache(baseName);
-                _this._prepareEffect();
+            this._loadVertexShader(vertexSource, function (vertexCode) {
+                _this._processIncludes(vertexCode, function (vertexCodeWithIncludes) {
+                    _this._processShaderConversion(vertexCodeWithIncludes, false, function (migratedVertexCode) {
+                        _this._loadFragmentShader(fragmentSource, function (fragmentCode) {
+                            _this._processIncludes(fragmentCode, function (fragmentCodeWithIncludes) {
+                                _this._processShaderConversion(fragmentCodeWithIncludes, true, function (migratedFragmentCode) {
+                                    if (baseName) {
+                                        var vertex = baseName.vertexElement || baseName.vertex || baseName;
+                                        var fragment = baseName.fragmentElement || baseName.fragment || baseName;
+                                        _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
+                                        _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
+                                    }
+                                    else {
+                                        _this._vertexSourceCode = migratedVertexCode;
+                                        _this._fragmentSourceCode = migratedFragmentCode;
+                                    }
+                                    _this._prepareEffect();
+                                });
+                            });
+                        });
+                    });
+                });
             });
         }
-        Effect.prototype._getSourceCacheKey = function (baseName) {
-            var cacheKey = baseName;
-            if (this._indexParameters) {
-                for (var key in this._indexParameters) {
-                    if (this._indexParameters.hasOwnProperty(key)) {
-                        cacheKey += "|";
-                        cacheKey += key;
-                        cacheKey += "_";
-                        cacheKey += this._indexParameters[key];
-                    }
-                }
-            }
-            return cacheKey;
-        };
-        Effect.prototype._getFromCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return false;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            var sources = Effect._sourceCache[cacheKey];
-            if (!sources) {
-                return false;
-            }
-            this._vertexSourceCode = sources.vertex;
-            this._fragmentSourceCode = sources.fragment;
-            return true;
-        };
-        Effect.prototype._setInCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            Effect._sourceCache[cacheKey] = {
-                vertex: this._vertexSourceCode,
-                fragment: this._fragmentSourceCode
-            };
-        };
         Object.defineProperty(Effect.prototype, "key", {
             /**
              * Unique key for this effect
@@ -416,22 +371,25 @@ var BABYLON;
             });
         };
         /** @ignore */
-        Effect.prototype._loadVertexShaderAsync = function (vertex) {
+        Effect.prototype._loadVertexShader = function (vertex, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (vertex instanceof HTMLElement) {
                     var vertexCode = BABYLON.Tools.GetDOMTextContent(vertex);
-                    return Promise.resolve(vertexCode);
+                    callback(vertexCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (vertex.substr(0, 7) === "base64:") {
                 var vertexBinary = window.atob(vertex.substr(7));
-                return Promise.resolve(vertexBinary);
+                callback(vertexBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
-                return Promise.resolve(Effect.ShadersStore[vertex + "VertexShader"]);
+                callback(Effect.ShadersStore[vertex + "VertexShader"]);
+                return;
             }
             var vertexShaderUrl;
             if (vertex[0] === "." || vertex[0] === "/" || vertex.indexOf("http") > -1) {
@@ -441,28 +399,32 @@ var BABYLON;
                 vertexShaderUrl = BABYLON.Engine.ShadersRepository + vertex;
             }
             // Vertex shader
-            return this._engine._loadFileAsync(vertexShaderUrl + ".vertex.fx");
+            this._engine._loadFile(vertexShaderUrl + ".vertex.fx", callback);
         };
         /** @ignore */
-        Effect.prototype._loadFragmentShaderAsync = function (fragment) {
+        Effect.prototype._loadFragmentShader = function (fragment, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (fragment instanceof HTMLElement) {
                     var fragmentCode = BABYLON.Tools.GetDOMTextContent(fragment);
-                    return Promise.resolve(fragmentCode);
+                    callback(fragmentCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (fragment.substr(0, 7) === "base64:") {
                 var fragmentBinary = window.atob(fragment.substr(7));
-                return Promise.resolve(fragmentBinary);
+                callback(fragmentBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "PixelShader"]);
+                callback(Effect.ShadersStore[fragment + "PixelShader"]);
+                return;
             }
             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "FragmentShader"]);
+                callback(Effect.ShadersStore[fragment + "FragmentShader"]);
+                return;
             }
             var fragmentShaderUrl;
             if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
@@ -472,7 +434,7 @@ var BABYLON;
                 fragmentShaderUrl = BABYLON.Engine.ShadersRepository + fragment;
             }
             // Fragment shader
-            return this._engine._loadFileAsync(fragmentShaderUrl + ".fragment.fx");
+            this._engine._loadFile(fragmentShaderUrl + ".fragment.fx", callback);
         };
         Effect.prototype._dumpShadersSource = function (vertexCode, fragmentCode, defines) {
             // Rebuild shaders source code
@@ -501,14 +463,16 @@ var BABYLON;
             }
         };
         ;
-        Effect.prototype._processShaderConversion = function (sourceCode, isFragment) {
+        Effect.prototype._processShaderConversion = function (sourceCode, isFragment, callback) {
             var preparedSourceCode = this._processPrecision(sourceCode);
             if (this._engine.webGLVersion == 1) {
-                return preparedSourceCode;
+                callback(preparedSourceCode);
+                return;
             }
             // Already converted
             if (preparedSourceCode.indexOf("#version 3") !== -1) {
-                return preparedSourceCode.replace("#version 300 es", "");
+                callback(preparedSourceCode.replace("#version 300 es", ""));
+                return;
             }
             var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
             // Remove extensions 
@@ -532,86 +496,80 @@ var BABYLON;
                 result = result.replace(/gl_FragData/g, "glFragData");
                 result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
             }
-            return result;
+            callback(result);
         };
-        Effect.prototype._processIncludesAsync = function (sourceCode) {
+        Effect.prototype._processIncludes = function (sourceCode, callback) {
             var _this = this;
-            return new Promise(function (resolve, reject) {
-                var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
-                var match = regex.exec(sourceCode);
-                var returnValue = sourceCode;
-                while (match != null) {
-                    var includeFile = match[1];
-                    // Uniform declaration
-                    if (includeFile.indexOf("__decl__") !== -1) {
-                        includeFile = includeFile.replace(/__decl__/, "");
-                        if (_this._engine.supportsUniformBuffers) {
-                            includeFile = includeFile.replace(/Vertex/, "Ubo");
-                            includeFile = includeFile.replace(/Fragment/, "Ubo");
-                        }
-                        includeFile = includeFile + "Declaration";
-                    }
-                    if (Effect.IncludesShadersStore[includeFile]) {
-                        // Substitution
-                        var includeContent = Effect.IncludesShadersStore[includeFile];
-                        if (match[2]) {
-                            var splits = match[3].split(",");
-                            for (var index = 0; index < splits.length; index += 2) {
-                                var source = new RegExp(splits[index], "g");
-                                var dest = splits[index + 1];
-                                includeContent = includeContent.replace(source, dest);
-                            }
-                        }
-                        if (match[4]) {
-                            var indexString = match[5];
-                            if (indexString.indexOf("..") !== -1) {
-                                var indexSplits = indexString.split("..");
-                                var minIndex = parseInt(indexSplits[0]);
-                                var maxIndex = parseInt(indexSplits[1]);
-                                var sourceIncludeContent = includeContent.slice(0);
-                                includeContent = "";
-                                if (isNaN(maxIndex)) {
-                                    maxIndex = _this._indexParameters[indexSplits[1]];
-                                }
-                                for (var i = minIndex; i < maxIndex; i++) {
-                                    if (!_this._engine.supportsUniformBuffers) {
-                                        // Ubo replacement
-                                        sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
-                                            return p1 + "{X}";
-                                        });
-                                    }
-                                    includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
-                                }
+            var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
+            var match = regex.exec(sourceCode);
+            var returnValue = new String(sourceCode);
+            while (match != null) {
+                var includeFile = match[1];
+                // Uniform declaration
+                if (includeFile.indexOf("__decl__") !== -1) {
+                    includeFile = includeFile.replace(/__decl__/, "");
+                    if (this._engine.supportsUniformBuffers) {
+                        includeFile = includeFile.replace(/Vertex/, "Ubo");
+                        includeFile = includeFile.replace(/Fragment/, "Ubo");
+                    }
+                    includeFile = includeFile + "Declaration";
+                }
+                if (Effect.IncludesShadersStore[includeFile]) {
+                    // Substitution
+                    var includeContent = Effect.IncludesShadersStore[includeFile];
+                    if (match[2]) {
+                        var splits = match[3].split(",");
+                        for (var index = 0; index < splits.length; index += 2) {
+                            var source = new RegExp(splits[index], "g");
+                            var dest = splits[index + 1];
+                            includeContent = includeContent.replace(source, dest);
+                        }
+                    }
+                    if (match[4]) {
+                        var indexString = match[5];
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
                             }
-                            else {
-                                if (!_this._engine.supportsUniformBuffers) {
+                            for (var i = minIndex; i < maxIndex; i++) {
+                                if (!this._engine.supportsUniformBuffers) {
                                     // Ubo replacement
-                                    includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
                                         return p1 + "{X}";
                                     });
                                 }
-                                includeContent = includeContent.replace(/\{X\}/g, indexString);
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
                             }
                         }
-                        // Replace
-                        returnValue = returnValue.replace(match[0], includeContent);
-                    }
-                    else {
-                        var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
-                        _this._engine._loadFileAsync(includeShaderUrl)
-                            .then(function (fileContent) {
-                            Effect.IncludesShadersStore[includeFile] = fileContent;
-                            return _this._processIncludesAsync(returnValue);
-                        })
-                            .then(function (returnValue) {
-                            resolve(returnValue);
-                        });
-                        return;
+                        else {
+                            if (!this._engine.supportsUniformBuffers) {
+                                // Ubo replacement
+                                includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    return p1 + "{X}";
+                                });
+                            }
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
-                    match = regex.exec(sourceCode);
+                    // Replace
+                    returnValue = returnValue.replace(match[0], includeContent);
                 }
-                resolve(returnValue);
-            });
+                else {
+                    var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
+                    this._engine._loadFile(includeShaderUrl, function (fileContent) {
+                        Effect.IncludesShadersStore[includeFile] = fileContent;
+                        _this._processIncludes(returnValue, callback);
+                    });
+                    return;
+                }
+                match = regex.exec(sourceCode);
+            }
+            callback(returnValue);
         };
         Effect.prototype._processPrecision = function (source) {
             if (source.indexOf("precision highp float") === -1) {
@@ -803,8 +761,7 @@ var BABYLON;
             this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         };
         /**
-         * (Warning! setTextureFromPostProcessOutput may be desired instead)
-         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
+         * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the input texture from.
          */
@@ -812,7 +769,8 @@ var BABYLON;
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
         };
         /**
-         * Sets the output texture of the passed in post process to be input of this effect.
+         * (Warning! setTextureFromPostProcessOutput may be desired instead)
+         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the output texture from.
          */
@@ -1264,8 +1222,6 @@ var BABYLON;
         };
         Effect._uniqueIdSeed = 0;
         Effect._baseCache = {};
-        Effect._sourceCache = {};
-        // Statics
         /**
          * Store of each shader (The can be looked up using effect.key)
          */
@@ -47993,9 +47949,10 @@ var BABYLON;
          * @param target defines the target of the animation
          * @param animation defines the source {BABYLON.Animation} object
          * @param scene defines the hosting scene
+         * @param host defines the initiating Animatable
          */
-        function RuntimeAnimation(target, animation, scene) {
-            this.currentFrame = 0;
+        function RuntimeAnimation(target, animation, scene, host) {
+            this._currentFrame = 0;
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
@@ -48008,8 +47965,19 @@ var BABYLON;
             this._animation = animation;
             this._target = target;
             this._scene = scene;
+            this._host = host;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "currentFrame", {
+            /**
+             * Gets the current frame
+             */
+            get: function () {
+                return this._currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "weight", {
             /**
              * Gets the weight of the runtime animation
@@ -48070,7 +48038,7 @@ var BABYLON;
         RuntimeAnimation.prototype.reset = function () {
             this._offsetsCache = {};
             this._highLimitsCache = {};
-            this.currentFrame = 0;
+            this._currentFrame = 0;
             this._blendingFactor = 0;
             this._originalValue = null;
         };
@@ -48093,7 +48061,7 @@ var BABYLON;
             if (loopMode === BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
                 return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
             }
-            this.currentFrame = currentFrame;
+            this._currentFrame = currentFrame;
             var keys = this._animation.getKeys();
             // Try to get a hash to find the right key
             var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
@@ -48431,6 +48399,12 @@ var BABYLON;
             // Compute value
             var repeatCount = (ratio / range) >> 0;
             var currentFrame = returnValue ? from + ratio % range : to;
+            // Need to normalize?
+            if (this._host && this._host.syncRoot) {
+                var syncRoot = this._host.syncRoot;
+                var hostNormalizedFrame = (syncRoot.masterFrame - syncRoot.fromFrame) / (syncRoot.toFrame - syncRoot.fromFrame);
+                currentFrame = from + (to - from) * hostNormalizedFrame;
+            }
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
             this.setValue(currentValue, weight);
@@ -48497,6 +48471,30 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "syncRoot", {
+            /**
+             * Gets the root Animatable used to synchronize and normalize animations
+             */
+            get: function () {
+                return this._syncRoot;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Animatable.prototype, "masterFrame", {
+            /**
+             * Gets the current frame of the first RuntimeAnimation
+             * Used to synchronize Animatables
+             */
+            get: function () {
+                if (this._runtimeAnimations.length === 0) {
+                    return 0;
+                }
+                return this._runtimeAnimations[0].currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "weight", {
             /**
              * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
@@ -48533,13 +48531,31 @@ var BABYLON;
             configurable: true
         });
         // Methods
+        /**
+         * Synchronize and normalize current Animatable with a source Animatable.
+         * This is useful when using animation weights and when animations are not of the same length
+         * @param root defines the root Animatable to synchronize with
+         * @returns the current Animatable
+         */
+        Animatable.prototype.syncWith = function (root) {
+            this._syncRoot = root;
+            if (root) {
+                // Make sure this animatable will animate after the root
+                var index = this._scene._activeAnimatables.indexOf(this);
+                if (index > -1) {
+                    this._scene._activeAnimatables.splice(index, 1);
+                    this._scene._activeAnimatables.push(this);
+                }
+            }
+            return this;
+        };
         Animatable.prototype.getAnimations = function () {
             return this._runtimeAnimations;
         };
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene, this));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -57282,7 +57298,7 @@ var BABYLON;
             };
         };
         DracoCompression._GetDefaultDecoderUrl = function () {
-            if (!BABYLON.Tools.IsWindowObjectExist) {
+            if (!BABYLON.Tools.IsWindowObjectExist()) {
                 return null;
             }
             for (var i = 0; i < document.scripts.length; i++) {
@@ -92444,6 +92460,7 @@ var BABYLON;
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
             loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onAnimationGroupLoadedObservable.add(function (animationGroup) { return _this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup); });
             loader.onCompleteObservable.add(function () {
                 _this.onMeshLoadedObservable.clear();
                 _this.onTextureLoadedObservable.clear();

+ 178 - 161
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js

@@ -193,10 +193,6 @@ var BABYLON;
                 this._fallbacks = fallbacks;
             }
             this.uniqueId = Effect._uniqueIdSeed++;
-            if (this._getFromCache(baseName)) {
-                this._prepareEffect();
-                return;
-            }
             var vertexSource;
             var fragmentSource;
             if (baseName.vertexElement) {
@@ -217,71 +213,30 @@ var BABYLON;
             else {
                 fragmentSource = baseName.fragment || baseName;
             }
-            var finalVertexCode;
-            this._loadVertexShaderAsync(vertexSource)
-                .then(function (vertexCode) {
-                return _this._processIncludesAsync(vertexCode);
-            })
-                .then(function (vertexCodeWithIncludes) {
-                finalVertexCode = _this._processShaderConversion(vertexCodeWithIncludes, false);
-                return _this._loadFragmentShaderAsync(fragmentSource);
-            })
-                .then(function (fragmentCode) {
-                return _this._processIncludesAsync(fragmentCode);
-            })
-                .then(function (fragmentCodeWithIncludes) {
-                var migratedFragmentCode = _this._processShaderConversion(fragmentCodeWithIncludes, true);
-                if (baseName) {
-                    var vertex = baseName.vertexElement || baseName.vertex || baseName;
-                    var fragment = baseName.fragmentElement || baseName.fragment || baseName;
-                    _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + finalVertexCode;
-                    _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
-                }
-                else {
-                    _this._vertexSourceCode = finalVertexCode;
-                    _this._fragmentSourceCode = migratedFragmentCode;
-                }
-                _this._setInCache(baseName);
-                _this._prepareEffect();
+            this._loadVertexShader(vertexSource, function (vertexCode) {
+                _this._processIncludes(vertexCode, function (vertexCodeWithIncludes) {
+                    _this._processShaderConversion(vertexCodeWithIncludes, false, function (migratedVertexCode) {
+                        _this._loadFragmentShader(fragmentSource, function (fragmentCode) {
+                            _this._processIncludes(fragmentCode, function (fragmentCodeWithIncludes) {
+                                _this._processShaderConversion(fragmentCodeWithIncludes, true, function (migratedFragmentCode) {
+                                    if (baseName) {
+                                        var vertex = baseName.vertexElement || baseName.vertex || baseName;
+                                        var fragment = baseName.fragmentElement || baseName.fragment || baseName;
+                                        _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
+                                        _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
+                                    }
+                                    else {
+                                        _this._vertexSourceCode = migratedVertexCode;
+                                        _this._fragmentSourceCode = migratedFragmentCode;
+                                    }
+                                    _this._prepareEffect();
+                                });
+                            });
+                        });
+                    });
+                });
             });
         }
-        Effect.prototype._getSourceCacheKey = function (baseName) {
-            var cacheKey = baseName;
-            if (this._indexParameters) {
-                for (var key in this._indexParameters) {
-                    if (this._indexParameters.hasOwnProperty(key)) {
-                        cacheKey += "|";
-                        cacheKey += key;
-                        cacheKey += "_";
-                        cacheKey += this._indexParameters[key];
-                    }
-                }
-            }
-            return cacheKey;
-        };
-        Effect.prototype._getFromCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return false;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            var sources = Effect._sourceCache[cacheKey];
-            if (!sources) {
-                return false;
-            }
-            this._vertexSourceCode = sources.vertex;
-            this._fragmentSourceCode = sources.fragment;
-            return true;
-        };
-        Effect.prototype._setInCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            Effect._sourceCache[cacheKey] = {
-                vertex: this._vertexSourceCode,
-                fragment: this._fragmentSourceCode
-            };
-        };
         Object.defineProperty(Effect.prototype, "key", {
             /**
              * Unique key for this effect
@@ -388,22 +343,25 @@ var BABYLON;
             });
         };
         /** @ignore */
-        Effect.prototype._loadVertexShaderAsync = function (vertex) {
+        Effect.prototype._loadVertexShader = function (vertex, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (vertex instanceof HTMLElement) {
                     var vertexCode = BABYLON.Tools.GetDOMTextContent(vertex);
-                    return Promise.resolve(vertexCode);
+                    callback(vertexCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (vertex.substr(0, 7) === "base64:") {
                 var vertexBinary = window.atob(vertex.substr(7));
-                return Promise.resolve(vertexBinary);
+                callback(vertexBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
-                return Promise.resolve(Effect.ShadersStore[vertex + "VertexShader"]);
+                callback(Effect.ShadersStore[vertex + "VertexShader"]);
+                return;
             }
             var vertexShaderUrl;
             if (vertex[0] === "." || vertex[0] === "/" || vertex.indexOf("http") > -1) {
@@ -413,28 +371,32 @@ var BABYLON;
                 vertexShaderUrl = BABYLON.Engine.ShadersRepository + vertex;
             }
             // Vertex shader
-            return this._engine._loadFileAsync(vertexShaderUrl + ".vertex.fx");
+            this._engine._loadFile(vertexShaderUrl + ".vertex.fx", callback);
         };
         /** @ignore */
-        Effect.prototype._loadFragmentShaderAsync = function (fragment) {
+        Effect.prototype._loadFragmentShader = function (fragment, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (fragment instanceof HTMLElement) {
                     var fragmentCode = BABYLON.Tools.GetDOMTextContent(fragment);
-                    return Promise.resolve(fragmentCode);
+                    callback(fragmentCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (fragment.substr(0, 7) === "base64:") {
                 var fragmentBinary = window.atob(fragment.substr(7));
-                return Promise.resolve(fragmentBinary);
+                callback(fragmentBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "PixelShader"]);
+                callback(Effect.ShadersStore[fragment + "PixelShader"]);
+                return;
             }
             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "FragmentShader"]);
+                callback(Effect.ShadersStore[fragment + "FragmentShader"]);
+                return;
             }
             var fragmentShaderUrl;
             if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
@@ -444,7 +406,7 @@ var BABYLON;
                 fragmentShaderUrl = BABYLON.Engine.ShadersRepository + fragment;
             }
             // Fragment shader
-            return this._engine._loadFileAsync(fragmentShaderUrl + ".fragment.fx");
+            this._engine._loadFile(fragmentShaderUrl + ".fragment.fx", callback);
         };
         Effect.prototype._dumpShadersSource = function (vertexCode, fragmentCode, defines) {
             // Rebuild shaders source code
@@ -473,14 +435,16 @@ var BABYLON;
             }
         };
         ;
-        Effect.prototype._processShaderConversion = function (sourceCode, isFragment) {
+        Effect.prototype._processShaderConversion = function (sourceCode, isFragment, callback) {
             var preparedSourceCode = this._processPrecision(sourceCode);
             if (this._engine.webGLVersion == 1) {
-                return preparedSourceCode;
+                callback(preparedSourceCode);
+                return;
             }
             // Already converted
             if (preparedSourceCode.indexOf("#version 3") !== -1) {
-                return preparedSourceCode.replace("#version 300 es", "");
+                callback(preparedSourceCode.replace("#version 300 es", ""));
+                return;
             }
             var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
             // Remove extensions 
@@ -504,86 +468,80 @@ var BABYLON;
                 result = result.replace(/gl_FragData/g, "glFragData");
                 result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
             }
-            return result;
+            callback(result);
         };
-        Effect.prototype._processIncludesAsync = function (sourceCode) {
+        Effect.prototype._processIncludes = function (sourceCode, callback) {
             var _this = this;
-            return new Promise(function (resolve, reject) {
-                var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
-                var match = regex.exec(sourceCode);
-                var returnValue = sourceCode;
-                while (match != null) {
-                    var includeFile = match[1];
-                    // Uniform declaration
-                    if (includeFile.indexOf("__decl__") !== -1) {
-                        includeFile = includeFile.replace(/__decl__/, "");
-                        if (_this._engine.supportsUniformBuffers) {
-                            includeFile = includeFile.replace(/Vertex/, "Ubo");
-                            includeFile = includeFile.replace(/Fragment/, "Ubo");
-                        }
-                        includeFile = includeFile + "Declaration";
-                    }
-                    if (Effect.IncludesShadersStore[includeFile]) {
-                        // Substitution
-                        var includeContent = Effect.IncludesShadersStore[includeFile];
-                        if (match[2]) {
-                            var splits = match[3].split(",");
-                            for (var index = 0; index < splits.length; index += 2) {
-                                var source = new RegExp(splits[index], "g");
-                                var dest = splits[index + 1];
-                                includeContent = includeContent.replace(source, dest);
-                            }
-                        }
-                        if (match[4]) {
-                            var indexString = match[5];
-                            if (indexString.indexOf("..") !== -1) {
-                                var indexSplits = indexString.split("..");
-                                var minIndex = parseInt(indexSplits[0]);
-                                var maxIndex = parseInt(indexSplits[1]);
-                                var sourceIncludeContent = includeContent.slice(0);
-                                includeContent = "";
-                                if (isNaN(maxIndex)) {
-                                    maxIndex = _this._indexParameters[indexSplits[1]];
-                                }
-                                for (var i = minIndex; i < maxIndex; i++) {
-                                    if (!_this._engine.supportsUniformBuffers) {
-                                        // Ubo replacement
-                                        sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
-                                            return p1 + "{X}";
-                                        });
-                                    }
-                                    includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
-                                }
+            var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
+            var match = regex.exec(sourceCode);
+            var returnValue = new String(sourceCode);
+            while (match != null) {
+                var includeFile = match[1];
+                // Uniform declaration
+                if (includeFile.indexOf("__decl__") !== -1) {
+                    includeFile = includeFile.replace(/__decl__/, "");
+                    if (this._engine.supportsUniformBuffers) {
+                        includeFile = includeFile.replace(/Vertex/, "Ubo");
+                        includeFile = includeFile.replace(/Fragment/, "Ubo");
+                    }
+                    includeFile = includeFile + "Declaration";
+                }
+                if (Effect.IncludesShadersStore[includeFile]) {
+                    // Substitution
+                    var includeContent = Effect.IncludesShadersStore[includeFile];
+                    if (match[2]) {
+                        var splits = match[3].split(",");
+                        for (var index = 0; index < splits.length; index += 2) {
+                            var source = new RegExp(splits[index], "g");
+                            var dest = splits[index + 1];
+                            includeContent = includeContent.replace(source, dest);
+                        }
+                    }
+                    if (match[4]) {
+                        var indexString = match[5];
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
                             }
-                            else {
-                                if (!_this._engine.supportsUniformBuffers) {
+                            for (var i = minIndex; i < maxIndex; i++) {
+                                if (!this._engine.supportsUniformBuffers) {
                                     // Ubo replacement
-                                    includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
                                         return p1 + "{X}";
                                     });
                                 }
-                                includeContent = includeContent.replace(/\{X\}/g, indexString);
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
                             }
                         }
-                        // Replace
-                        returnValue = returnValue.replace(match[0], includeContent);
-                    }
-                    else {
-                        var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
-                        _this._engine._loadFileAsync(includeShaderUrl)
-                            .then(function (fileContent) {
-                            Effect.IncludesShadersStore[includeFile] = fileContent;
-                            return _this._processIncludesAsync(returnValue);
-                        })
-                            .then(function (returnValue) {
-                            resolve(returnValue);
-                        });
-                        return;
+                        else {
+                            if (!this._engine.supportsUniformBuffers) {
+                                // Ubo replacement
+                                includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    return p1 + "{X}";
+                                });
+                            }
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
-                    match = regex.exec(sourceCode);
+                    // Replace
+                    returnValue = returnValue.replace(match[0], includeContent);
                 }
-                resolve(returnValue);
-            });
+                else {
+                    var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
+                    this._engine._loadFile(includeShaderUrl, function (fileContent) {
+                        Effect.IncludesShadersStore[includeFile] = fileContent;
+                        _this._processIncludes(returnValue, callback);
+                    });
+                    return;
+                }
+                match = regex.exec(sourceCode);
+            }
+            callback(returnValue);
         };
         Effect.prototype._processPrecision = function (source) {
             if (source.indexOf("precision highp float") === -1) {
@@ -775,8 +733,7 @@ var BABYLON;
             this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         };
         /**
-         * (Warning! setTextureFromPostProcessOutput may be desired instead)
-         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
+         * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the input texture from.
          */
@@ -784,7 +741,8 @@ var BABYLON;
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
         };
         /**
-         * Sets the output texture of the passed in post process to be input of this effect.
+         * (Warning! setTextureFromPostProcessOutput may be desired instead)
+         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the output texture from.
          */
@@ -1236,8 +1194,6 @@ var BABYLON;
         };
         Effect._uniqueIdSeed = 0;
         Effect._baseCache = {};
-        Effect._sourceCache = {};
-        // Statics
         /**
          * Store of each shader (The can be looked up using effect.key)
          */
@@ -47965,9 +47921,10 @@ var BABYLON;
          * @param target defines the target of the animation
          * @param animation defines the source {BABYLON.Animation} object
          * @param scene defines the hosting scene
+         * @param host defines the initiating Animatable
          */
-        function RuntimeAnimation(target, animation, scene) {
-            this.currentFrame = 0;
+        function RuntimeAnimation(target, animation, scene, host) {
+            this._currentFrame = 0;
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
@@ -47980,8 +47937,19 @@ var BABYLON;
             this._animation = animation;
             this._target = target;
             this._scene = scene;
+            this._host = host;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "currentFrame", {
+            /**
+             * Gets the current frame
+             */
+            get: function () {
+                return this._currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "weight", {
             /**
              * Gets the weight of the runtime animation
@@ -48042,7 +48010,7 @@ var BABYLON;
         RuntimeAnimation.prototype.reset = function () {
             this._offsetsCache = {};
             this._highLimitsCache = {};
-            this.currentFrame = 0;
+            this._currentFrame = 0;
             this._blendingFactor = 0;
             this._originalValue = null;
         };
@@ -48065,7 +48033,7 @@ var BABYLON;
             if (loopMode === BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
                 return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
             }
-            this.currentFrame = currentFrame;
+            this._currentFrame = currentFrame;
             var keys = this._animation.getKeys();
             // Try to get a hash to find the right key
             var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
@@ -48403,6 +48371,12 @@ var BABYLON;
             // Compute value
             var repeatCount = (ratio / range) >> 0;
             var currentFrame = returnValue ? from + ratio % range : to;
+            // Need to normalize?
+            if (this._host && this._host.syncRoot) {
+                var syncRoot = this._host.syncRoot;
+                var hostNormalizedFrame = (syncRoot.masterFrame - syncRoot.fromFrame) / (syncRoot.toFrame - syncRoot.fromFrame);
+                currentFrame = from + (to - from) * hostNormalizedFrame;
+            }
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
             this.setValue(currentValue, weight);
@@ -48469,6 +48443,30 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "syncRoot", {
+            /**
+             * Gets the root Animatable used to synchronize and normalize animations
+             */
+            get: function () {
+                return this._syncRoot;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Animatable.prototype, "masterFrame", {
+            /**
+             * Gets the current frame of the first RuntimeAnimation
+             * Used to synchronize Animatables
+             */
+            get: function () {
+                if (this._runtimeAnimations.length === 0) {
+                    return 0;
+                }
+                return this._runtimeAnimations[0].currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "weight", {
             /**
              * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
@@ -48505,13 +48503,31 @@ var BABYLON;
             configurable: true
         });
         // Methods
+        /**
+         * Synchronize and normalize current Animatable with a source Animatable.
+         * This is useful when using animation weights and when animations are not of the same length
+         * @param root defines the root Animatable to synchronize with
+         * @returns the current Animatable
+         */
+        Animatable.prototype.syncWith = function (root) {
+            this._syncRoot = root;
+            if (root) {
+                // Make sure this animatable will animate after the root
+                var index = this._scene._activeAnimatables.indexOf(this);
+                if (index > -1) {
+                    this._scene._activeAnimatables.splice(index, 1);
+                    this._scene._activeAnimatables.push(this);
+                }
+            }
+            return this;
+        };
         Animatable.prototype.getAnimations = function () {
             return this._runtimeAnimations;
         };
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene, this));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -57254,7 +57270,7 @@ var BABYLON;
             };
         };
         DracoCompression._GetDefaultDecoderUrl = function () {
-            if (!BABYLON.Tools.IsWindowObjectExist) {
+            if (!BABYLON.Tools.IsWindowObjectExist()) {
                 return null;
             }
             for (var i = 0; i < document.scripts.length; i++) {
@@ -92416,6 +92432,7 @@ var BABYLON;
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
             loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onAnimationGroupLoadedObservable.add(function (animationGroup) { return _this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup); });
             loader.onCompleteObservable.add(function () {
                 _this.onMeshLoadedObservable.clear();
                 _this.onTextureLoadedObservable.clear();

+ 176 - 160
dist/preview release/es6.js

@@ -193,10 +193,6 @@ var BABYLON;
                 this._fallbacks = fallbacks;
             }
             this.uniqueId = Effect._uniqueIdSeed++;
-            if (this._getFromCache(baseName)) {
-                this._prepareEffect();
-                return;
-            }
             var vertexSource;
             var fragmentSource;
             if (baseName.vertexElement) {
@@ -217,71 +213,30 @@ var BABYLON;
             else {
                 fragmentSource = baseName.fragment || baseName;
             }
-            var finalVertexCode;
-            this._loadVertexShaderAsync(vertexSource)
-                .then(function (vertexCode) {
-                return _this._processIncludesAsync(vertexCode);
-            })
-                .then(function (vertexCodeWithIncludes) {
-                finalVertexCode = _this._processShaderConversion(vertexCodeWithIncludes, false);
-                return _this._loadFragmentShaderAsync(fragmentSource);
-            })
-                .then(function (fragmentCode) {
-                return _this._processIncludesAsync(fragmentCode);
-            })
-                .then(function (fragmentCodeWithIncludes) {
-                var migratedFragmentCode = _this._processShaderConversion(fragmentCodeWithIncludes, true);
-                if (baseName) {
-                    var vertex = baseName.vertexElement || baseName.vertex || baseName;
-                    var fragment = baseName.fragmentElement || baseName.fragment || baseName;
-                    _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + finalVertexCode;
-                    _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
-                }
-                else {
-                    _this._vertexSourceCode = finalVertexCode;
-                    _this._fragmentSourceCode = migratedFragmentCode;
-                }
-                _this._setInCache(baseName);
-                _this._prepareEffect();
+            this._loadVertexShader(vertexSource, function (vertexCode) {
+                _this._processIncludes(vertexCode, function (vertexCodeWithIncludes) {
+                    _this._processShaderConversion(vertexCodeWithIncludes, false, function (migratedVertexCode) {
+                        _this._loadFragmentShader(fragmentSource, function (fragmentCode) {
+                            _this._processIncludes(fragmentCode, function (fragmentCodeWithIncludes) {
+                                _this._processShaderConversion(fragmentCodeWithIncludes, true, function (migratedFragmentCode) {
+                                    if (baseName) {
+                                        var vertex = baseName.vertexElement || baseName.vertex || baseName;
+                                        var fragment = baseName.fragmentElement || baseName.fragment || baseName;
+                                        _this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
+                                        _this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
+                                    }
+                                    else {
+                                        _this._vertexSourceCode = migratedVertexCode;
+                                        _this._fragmentSourceCode = migratedFragmentCode;
+                                    }
+                                    _this._prepareEffect();
+                                });
+                            });
+                        });
+                    });
+                });
             });
         }
-        Effect.prototype._getSourceCacheKey = function (baseName) {
-            var cacheKey = baseName;
-            if (this._indexParameters) {
-                for (var key in this._indexParameters) {
-                    if (this._indexParameters.hasOwnProperty(key)) {
-                        cacheKey += "|";
-                        cacheKey += key;
-                        cacheKey += "_";
-                        cacheKey += this._indexParameters[key];
-                    }
-                }
-            }
-            return cacheKey;
-        };
-        Effect.prototype._getFromCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return false;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            var sources = Effect._sourceCache[cacheKey];
-            if (!sources) {
-                return false;
-            }
-            this._vertexSourceCode = sources.vertex;
-            this._fragmentSourceCode = sources.fragment;
-            return true;
-        };
-        Effect.prototype._setInCache = function (baseName) {
-            if (typeof baseName !== "string") {
-                return;
-            }
-            var cacheKey = this._getSourceCacheKey(baseName);
-            Effect._sourceCache[cacheKey] = {
-                vertex: this._vertexSourceCode,
-                fragment: this._fragmentSourceCode
-            };
-        };
         Object.defineProperty(Effect.prototype, "key", {
             /**
              * Unique key for this effect
@@ -388,22 +343,25 @@ var BABYLON;
             });
         };
         /** @ignore */
-        Effect.prototype._loadVertexShaderAsync = function (vertex) {
+        Effect.prototype._loadVertexShader = function (vertex, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (vertex instanceof HTMLElement) {
                     var vertexCode = BABYLON.Tools.GetDOMTextContent(vertex);
-                    return Promise.resolve(vertexCode);
+                    callback(vertexCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (vertex.substr(0, 7) === "base64:") {
                 var vertexBinary = window.atob(vertex.substr(7));
-                return Promise.resolve(vertexBinary);
+                callback(vertexBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
-                return Promise.resolve(Effect.ShadersStore[vertex + "VertexShader"]);
+                callback(Effect.ShadersStore[vertex + "VertexShader"]);
+                return;
             }
             var vertexShaderUrl;
             if (vertex[0] === "." || vertex[0] === "/" || vertex.indexOf("http") > -1) {
@@ -413,28 +371,32 @@ var BABYLON;
                 vertexShaderUrl = BABYLON.Engine.ShadersRepository + vertex;
             }
             // Vertex shader
-            return this._engine._loadFileAsync(vertexShaderUrl + ".vertex.fx");
+            this._engine._loadFile(vertexShaderUrl + ".vertex.fx", callback);
         };
         /** @ignore */
-        Effect.prototype._loadFragmentShaderAsync = function (fragment) {
+        Effect.prototype._loadFragmentShader = function (fragment, callback) {
             if (BABYLON.Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (fragment instanceof HTMLElement) {
                     var fragmentCode = BABYLON.Tools.GetDOMTextContent(fragment);
-                    return Promise.resolve(fragmentCode);
+                    callback(fragmentCode);
+                    return;
                 }
             }
             // Base64 encoded ?
             if (fragment.substr(0, 7) === "base64:") {
                 var fragmentBinary = window.atob(fragment.substr(7));
-                return Promise.resolve(fragmentBinary);
+                callback(fragmentBinary);
+                return;
             }
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "PixelShader"]);
+                callback(Effect.ShadersStore[fragment + "PixelShader"]);
+                return;
             }
             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "FragmentShader"]);
+                callback(Effect.ShadersStore[fragment + "FragmentShader"]);
+                return;
             }
             var fragmentShaderUrl;
             if (fragment[0] === "." || fragment[0] === "/" || fragment.indexOf("http") > -1) {
@@ -444,7 +406,7 @@ var BABYLON;
                 fragmentShaderUrl = BABYLON.Engine.ShadersRepository + fragment;
             }
             // Fragment shader
-            return this._engine._loadFileAsync(fragmentShaderUrl + ".fragment.fx");
+            this._engine._loadFile(fragmentShaderUrl + ".fragment.fx", callback);
         };
         Effect.prototype._dumpShadersSource = function (vertexCode, fragmentCode, defines) {
             // Rebuild shaders source code
@@ -473,14 +435,16 @@ var BABYLON;
             }
         };
         ;
-        Effect.prototype._processShaderConversion = function (sourceCode, isFragment) {
+        Effect.prototype._processShaderConversion = function (sourceCode, isFragment, callback) {
             var preparedSourceCode = this._processPrecision(sourceCode);
             if (this._engine.webGLVersion == 1) {
-                return preparedSourceCode;
+                callback(preparedSourceCode);
+                return;
             }
             // Already converted
             if (preparedSourceCode.indexOf("#version 3") !== -1) {
-                return preparedSourceCode.replace("#version 300 es", "");
+                callback(preparedSourceCode.replace("#version 300 es", ""));
+                return;
             }
             var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
             // Remove extensions 
@@ -504,86 +468,80 @@ var BABYLON;
                 result = result.replace(/gl_FragData/g, "glFragData");
                 result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
             }
-            return result;
+            callback(result);
         };
-        Effect.prototype._processIncludesAsync = function (sourceCode) {
+        Effect.prototype._processIncludes = function (sourceCode, callback) {
             var _this = this;
-            return new Promise(function (resolve, reject) {
-                var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
-                var match = regex.exec(sourceCode);
-                var returnValue = sourceCode;
-                while (match != null) {
-                    var includeFile = match[1];
-                    // Uniform declaration
-                    if (includeFile.indexOf("__decl__") !== -1) {
-                        includeFile = includeFile.replace(/__decl__/, "");
-                        if (_this._engine.supportsUniformBuffers) {
-                            includeFile = includeFile.replace(/Vertex/, "Ubo");
-                            includeFile = includeFile.replace(/Fragment/, "Ubo");
-                        }
-                        includeFile = includeFile + "Declaration";
-                    }
-                    if (Effect.IncludesShadersStore[includeFile]) {
-                        // Substitution
-                        var includeContent = Effect.IncludesShadersStore[includeFile];
-                        if (match[2]) {
-                            var splits = match[3].split(",");
-                            for (var index = 0; index < splits.length; index += 2) {
-                                var source = new RegExp(splits[index], "g");
-                                var dest = splits[index + 1];
-                                includeContent = includeContent.replace(source, dest);
-                            }
+            var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
+            var match = regex.exec(sourceCode);
+            var returnValue = new String(sourceCode);
+            while (match != null) {
+                var includeFile = match[1];
+                // Uniform declaration
+                if (includeFile.indexOf("__decl__") !== -1) {
+                    includeFile = includeFile.replace(/__decl__/, "");
+                    if (this._engine.supportsUniformBuffers) {
+                        includeFile = includeFile.replace(/Vertex/, "Ubo");
+                        includeFile = includeFile.replace(/Fragment/, "Ubo");
+                    }
+                    includeFile = includeFile + "Declaration";
+                }
+                if (Effect.IncludesShadersStore[includeFile]) {
+                    // Substitution
+                    var includeContent = Effect.IncludesShadersStore[includeFile];
+                    if (match[2]) {
+                        var splits = match[3].split(",");
+                        for (var index = 0; index < splits.length; index += 2) {
+                            var source = new RegExp(splits[index], "g");
+                            var dest = splits[index + 1];
+                            includeContent = includeContent.replace(source, dest);
                         }
-                        if (match[4]) {
-                            var indexString = match[5];
-                            if (indexString.indexOf("..") !== -1) {
-                                var indexSplits = indexString.split("..");
-                                var minIndex = parseInt(indexSplits[0]);
-                                var maxIndex = parseInt(indexSplits[1]);
-                                var sourceIncludeContent = includeContent.slice(0);
-                                includeContent = "";
-                                if (isNaN(maxIndex)) {
-                                    maxIndex = _this._indexParameters[indexSplits[1]];
-                                }
-                                for (var i = minIndex; i < maxIndex; i++) {
-                                    if (!_this._engine.supportsUniformBuffers) {
-                                        // Ubo replacement
-                                        sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
-                                            return p1 + "{X}";
-                                        });
-                                    }
-                                    includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
-                                }
+                    }
+                    if (match[4]) {
+                        var indexString = match[5];
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
                             }
-                            else {
-                                if (!_this._engine.supportsUniformBuffers) {
+                            for (var i = minIndex; i < maxIndex; i++) {
+                                if (!this._engine.supportsUniformBuffers) {
                                     // Ubo replacement
-                                    includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
                                         return p1 + "{X}";
                                     });
                                 }
-                                includeContent = includeContent.replace(/\{X\}/g, indexString);
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
                             }
                         }
-                        // Replace
-                        returnValue = returnValue.replace(match[0], includeContent);
-                    }
-                    else {
-                        var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
-                        _this._engine._loadFileAsync(includeShaderUrl)
-                            .then(function (fileContent) {
-                            Effect.IncludesShadersStore[includeFile] = fileContent;
-                            return _this._processIncludesAsync(returnValue);
-                        })
-                            .then(function (returnValue) {
-                            resolve(returnValue);
-                        });
-                        return;
+                        else {
+                            if (!this._engine.supportsUniformBuffers) {
+                                // Ubo replacement
+                                includeContent = includeContent.replace(/light\{X\}.(\w*)/g, function (str, p1) {
+                                    return p1 + "{X}";
+                                });
+                            }
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
-                    match = regex.exec(sourceCode);
+                    // Replace
+                    returnValue = returnValue.replace(match[0], includeContent);
                 }
-                resolve(returnValue);
-            });
+                else {
+                    var includeShaderUrl = BABYLON.Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
+                    this._engine._loadFile(includeShaderUrl, function (fileContent) {
+                        Effect.IncludesShadersStore[includeFile] = fileContent;
+                        _this._processIncludes(returnValue, callback);
+                    });
+                    return;
+                }
+                match = regex.exec(sourceCode);
+            }
+            callback(returnValue);
         };
         Effect.prototype._processPrecision = function (source) {
             if (source.indexOf("precision highp float") === -1) {
@@ -775,8 +733,7 @@ var BABYLON;
             this._engine.setTextureArray(this._samplers.indexOf(channel), this.getUniform(channel), textures);
         };
         /**
-         * (Warning! setTextureFromPostProcessOutput may be desired instead)
-         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
+         * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the input texture from.
          */
@@ -784,7 +741,8 @@ var BABYLON;
             this._engine.setTextureFromPostProcess(this._samplers.indexOf(channel), postProcess);
         };
         /**
-         * Sets the output texture of the passed in post process to be input of this effect.
+         * (Warning! setTextureFromPostProcessOutput may be desired instead)
+         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the output texture from.
          */
@@ -1236,8 +1194,6 @@ var BABYLON;
         };
         Effect._uniqueIdSeed = 0;
         Effect._baseCache = {};
-        Effect._sourceCache = {};
-        // Statics
         /**
          * Store of each shader (The can be looked up using effect.key)
          */
@@ -47965,9 +47921,10 @@ var BABYLON;
          * @param target defines the target of the animation
          * @param animation defines the source {BABYLON.Animation} object
          * @param scene defines the hosting scene
+         * @param host defines the initiating Animatable
          */
-        function RuntimeAnimation(target, animation, scene) {
-            this.currentFrame = 0;
+        function RuntimeAnimation(target, animation, scene, host) {
+            this._currentFrame = 0;
             this._offsetsCache = {};
             this._highLimitsCache = {};
             this._stopped = false;
@@ -47980,8 +47937,19 @@ var BABYLON;
             this._animation = animation;
             this._target = target;
             this._scene = scene;
+            this._host = host;
             animation._runtimeAnimations.push(this);
         }
+        Object.defineProperty(RuntimeAnimation.prototype, "currentFrame", {
+            /**
+             * Gets the current frame
+             */
+            get: function () {
+                return this._currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(RuntimeAnimation.prototype, "weight", {
             /**
              * Gets the weight of the runtime animation
@@ -48042,7 +48010,7 @@ var BABYLON;
         RuntimeAnimation.prototype.reset = function () {
             this._offsetsCache = {};
             this._highLimitsCache = {};
-            this.currentFrame = 0;
+            this._currentFrame = 0;
             this._blendingFactor = 0;
             this._originalValue = null;
         };
@@ -48065,7 +48033,7 @@ var BABYLON;
             if (loopMode === BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT && repeatCount > 0) {
                 return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
             }
-            this.currentFrame = currentFrame;
+            this._currentFrame = currentFrame;
             var keys = this._animation.getKeys();
             // Try to get a hash to find the right key
             var startKeyIndex = Math.max(0, Math.min(keys.length - 1, Math.floor(keys.length * (currentFrame - keys[0].frame) / (keys[keys.length - 1].frame - keys[0].frame)) - 1));
@@ -48403,6 +48371,12 @@ var BABYLON;
             // Compute value
             var repeatCount = (ratio / range) >> 0;
             var currentFrame = returnValue ? from + ratio % range : to;
+            // Need to normalize?
+            if (this._host && this._host.syncRoot) {
+                var syncRoot = this._host.syncRoot;
+                var hostNormalizedFrame = (syncRoot.masterFrame - syncRoot.fromFrame) / (syncRoot.toFrame - syncRoot.fromFrame);
+                currentFrame = from + (to - from) * hostNormalizedFrame;
+            }
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
             // Set value
             this.setValue(currentValue, weight);
@@ -48469,6 +48443,30 @@ var BABYLON;
             this._scene = scene;
             scene._activeAnimatables.push(this);
         }
+        Object.defineProperty(Animatable.prototype, "syncRoot", {
+            /**
+             * Gets the root Animatable used to synchronize and normalize animations
+             */
+            get: function () {
+                return this._syncRoot;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Animatable.prototype, "masterFrame", {
+            /**
+             * Gets the current frame of the first RuntimeAnimation
+             * Used to synchronize Animatables
+             */
+            get: function () {
+                if (this._runtimeAnimations.length === 0) {
+                    return 0;
+                }
+                return this._runtimeAnimations[0].currentFrame;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Animatable.prototype, "weight", {
             /**
              * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
@@ -48505,13 +48503,31 @@ var BABYLON;
             configurable: true
         });
         // Methods
+        /**
+         * Synchronize and normalize current Animatable with a source Animatable.
+         * This is useful when using animation weights and when animations are not of the same length
+         * @param root defines the root Animatable to synchronize with
+         * @returns the current Animatable
+         */
+        Animatable.prototype.syncWith = function (root) {
+            this._syncRoot = root;
+            if (root) {
+                // Make sure this animatable will animate after the root
+                var index = this._scene._activeAnimatables.indexOf(this);
+                if (index > -1) {
+                    this._scene._activeAnimatables.splice(index, 1);
+                    this._scene._activeAnimatables.push(this);
+                }
+            }
+            return this;
+        };
         Animatable.prototype.getAnimations = function () {
             return this._runtimeAnimations;
         };
         Animatable.prototype.appendAnimations = function (target, animations) {
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
-                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene));
+                this._runtimeAnimations.push(new BABYLON.RuntimeAnimation(target, animation, this._scene, this));
             }
         };
         Animatable.prototype.getAnimationByTargetProperty = function (property) {
@@ -57969,7 +57985,7 @@ var BABYLON;
             };
         };
         DracoCompression._GetDefaultDecoderUrl = function () {
-            if (!BABYLON.Tools.IsWindowObjectExist) {
+            if (!BABYLON.Tools.IsWindowObjectExist()) {
                 return null;
             }
             for (var i = 0; i < document.scripts.length; i++) {

+ 1 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -307,6 +307,7 @@ var BABYLON;
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
             loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onAnimationGroupLoadedObservable.add(function (animationGroup) { return _this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup); });
             loader.onCompleteObservable.add(function () {
                 _this.onMeshLoadedObservable.clear();
                 _this.onTextureLoadedObservable.clear();

Різницю між файлами не показано, бо вона завелика
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -307,6 +307,7 @@ var BABYLON;
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
             loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onAnimationGroupLoadedObservable.add(function (animationGroup) { return _this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup); });
             loader.onCompleteObservable.add(function () {
                 _this.onMeshLoadedObservable.clear();
                 _this.onTextureLoadedObservable.clear();

Різницю між файлами не показано, бо вона завелика
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -307,6 +307,7 @@ var BABYLON;
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
             loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onAnimationGroupLoadedObservable.add(function (animationGroup) { return _this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup); });
             loader.onCompleteObservable.add(function () {
                 _this.onMeshLoadedObservable.clear();
                 _this.onTextureLoadedObservable.clear();

Різницю між файлами не показано, бо вона завелика
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylonjs.loaders.js

@@ -1304,6 +1304,7 @@ var BABYLON;
             loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
             loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
             loader.onExtensionLoadedObservable.add(function (extension) { return _this.onExtensionLoadedObservable.notifyObservers(extension); });
+            loader.onAnimationGroupLoadedObservable.add(function (animationGroup) { return _this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup); });
             loader.onCompleteObservable.add(function () {
                 _this.onMeshLoadedObservable.clear();
                 _this.onTextureLoadedObservable.clear();

Різницю між файлами не показано, бо вона завелика
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


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

@@ -1,7 +1,7 @@
 {
-  "errors": 7153,
+  "errors": 7152,
   "babylon.typedoc.json": {
-    "errors": 7153,
+    "errors": 7152,
     "AnimationKeyInterpolation": {
       "Enumeration": {
         "Comments": {
@@ -25726,11 +25726,6 @@
           "Comments": {
             "MissingText": true
           }
-        },
-        "currentFrame": {
-          "Comments": {
-            "MissingText": true
-          }
         }
       },
       "Method": {

Різницю між файлами не показано, бо вона завелика
+ 372 - 351
dist/preview release/viewer/babylon.viewer.max.js


+ 3 - 1
dist/preview release/what's new.md

@@ -14,12 +14,13 @@
 - Added [GlowLayer](https://doc.babylonjs.com/how_to/glow_layer) to easily support glow from emissive materials. Demo [here](http://www.babylonjs.com/demos/GlowLayer/) ([sebavan](https://github.com/sebavan))
 - New [AssetContainer](http://doc.babylonjs.com/how_to/how_to_use_assetcontainer) class and loading methods ([trevordev](https://github.com/trevordev))
 - Added [depth of field](https://www.babylonjs-playground.com/frame.html#8F5HYV#5), sharpening, MSAA, chromatic aberration and grain effect to the default pipeline ([trevordev](https://github.com/trevordev))
-- Added support for [animation weights](http://doc.babylonjs.com/babylon101/animations#animation-weights). Demo [here](https://www.babylonjs-playground.com/#IQN716#8) ([deltakosh](https://github.com/deltakosh))
+- Added support for [animation weights](http://doc.babylonjs.com/babylon101/animations#animation-weights). Demo [here](https://www.babylonjs-playground.com/#IQN716#9) ([deltakosh](https://github.com/deltakosh))
 - Added [sub emitters for particle system](http://doc.babylonjs.com/babylon101/particles#sub-emitters) which will spawn new particle systems when particles dies. Demo [here](https://www.babylonjs-playground.com/frame.html#9NHBCC#1) ([IbraheemOsama](https://github.com/IbraheemOsama))
 - New [Babylon.js](http://doc.babylonjs.com/resources/maya) and [glTF](http://doc.babylonjs.com/resources/maya_to_gltf) exporter for Autodesk Maya ([Noalak](https://github.com/Noalak))
 - New glTF [serializer](https://github.com/BabylonJS/Babylon.js/tree/master/serializers/src/glTF/2.0). You can now export glTF or glb files directly from a Babylon scene ([kcoley](https://github.com/kcoley))
 - New [glTF exporter](http://doc.babylonjs.com/resources/3dsmax_to_gltf) for Autodesk 3dsmax ([Noalak](https://github.com/Noalak))
 - Physics - Latest production version of Oimo.js is being used - 1.0.9 ([RaananW](https://github.com/RaananW))
+- Shadows - Introduces [PCF](https://doc.babylonjs.com/babylon101/shadows#percentage-closer-filtering-webgl2-only) and [PCSS](https://doc.babylonjs.com/babylon101/shadows#contact-hardening-shadow-webgl2-only) support in Webgl 2 ([sebavan](https://github.com/sebavan)))
 
 ## Updates
 
@@ -99,6 +100,7 @@
 - Added setTextureFromPostProcessOutput to bind the output of a postprocess into an effect ([trevordev](https://github.com/trevordev))
 - Added support for primitive modes to glTF 2.0 loader. ([bghgary](https://github.com/bghgary)]
 - Cannon and Oimo are optional dependencies ([RaananW](https://github.com/RaananW))
+- Shadows - Introduces [Normal Bias](https://doc.babylonjs.com/babylon101/shadows#normal-bias-since-32) ([sebavan](https://github.com/sebavan)))
 
 ## Bug fixes
 

+ 1 - 2
inspector/src/tsconfig.json

@@ -7,7 +7,6 @@
         "noImplicitReturns": true,
         "noImplicitThis": true,
         "noUnusedLocals": true,    
-        "strictNullChecks": true,
-        "strict": true
+        "strictNullChecks": true
     }
 }

+ 1 - 2
loaders/src/tsconfig.json

@@ -10,7 +10,6 @@
     "noImplicitReturns": true,
     "noImplicitThis": true,
     "noUnusedLocals": true,    
-    "strictNullChecks": true,
-    "strict": true
+    "strictNullChecks": true
   }
 }

+ 1 - 2
materialsLibrary/src/tsconfig.json

@@ -7,7 +7,6 @@
     "noImplicitReturns": true,
     "noImplicitThis": true,
     "noUnusedLocals": true,
-    "strictNullChecks": true,
-    "strict": true
+    "strictNullChecks": true
   }
 }

+ 1 - 2
postProcessLibrary/src/tsconfig.json

@@ -7,7 +7,6 @@
     "noImplicitReturns": true,
     "noImplicitThis": true,
     "noUnusedLocals": true,    
-    "strictNullChecks": true,
-    "strict": true
+    "strictNullChecks": true
   }
 }

+ 1 - 2
proceduralTexturesLibrary/src/tsconfig.json

@@ -7,7 +7,6 @@
     "noImplicitReturns": true,
     "noImplicitThis": true,
     "noUnusedLocals": true,    
-    "strictNullChecks": true,
-    "strict": true
+    "strictNullChecks": true
   }
 }

+ 1 - 2
serializers/src/tsconfig.json

@@ -7,7 +7,6 @@
     "noImplicitReturns": true,
     "noImplicitThis": true,
     "noUnusedLocals": true,    
-    "strictNullChecks": true,
-    "strict": true
+    "strictNullChecks": true
   }
 }

+ 42 - 1
src/Animations/babylon.animatable.ts

@@ -7,10 +7,30 @@
         private _scene: Scene;
         private _speedRatio = 1;
         private _weight = -1.0;
+        private _syncRoot: Animatable;
 
         public animationStarted = false;
 
         /**
+         * Gets the root Animatable used to synchronize and normalize animations
+         */
+        public get syncRoot(): Animatable {
+            return this._syncRoot;
+        }
+
+        /**
+         * Gets the current frame of the first RuntimeAnimation
+         * Used to synchronize Animatables
+         */
+        public get masterFrame(): number {
+            if (this._runtimeAnimations.length === 0) {
+                return 0;
+            }
+
+            return this._runtimeAnimations[0].currentFrame;
+        }
+
+        /**
          * Gets or sets the animatable weight (-1.0 by default meaning not weighted)
          */
         public get weight(): number {
@@ -55,6 +75,27 @@
         }
 
         // Methods
+        /**
+         * Synchronize and normalize current Animatable with a source Animatable.
+         * This is useful when using animation weights and when animations are not of the same length
+         * @param root defines the root Animatable to synchronize with
+         * @returns the current Animatable
+         */
+        public syncWith(root: Animatable): Animatable {
+            this._syncRoot = root;
+
+            if (root) {
+                // Make sure this animatable will animate after the root
+                let index = this._scene._activeAnimatables.indexOf(this);
+                if (index > -1) {
+                    this._scene._activeAnimatables.splice(index, 1);
+                    this._scene._activeAnimatables.push(this);
+                }
+            }
+
+            return this;
+        }
+
         public getAnimations(): RuntimeAnimation[] {
             return this._runtimeAnimations;
         }
@@ -63,7 +104,7 @@
             for (var index = 0; index < animations.length; index++) {
                 var animation = animations[index];
 
-                this._runtimeAnimations.push(new RuntimeAnimation(target, animation, this._scene));
+                this._runtimeAnimations.push(new RuntimeAnimation(target, animation, this._scene, this));
             }
         }
 

+ 23 - 5
src/Animations/babylon.runtimeAnimation.ts

@@ -1,9 +1,10 @@
 module BABYLON {
 
     export class RuntimeAnimation {
-        public currentFrame: number = 0;
+        private _currentFrame: number = 0;
         private _animation: Animation;
         private _target: any;
+        private _host: Animatable;
 
         private _originalValue: any;
         private _originalBlendValue: any;
@@ -16,7 +17,14 @@
         private _currentValue: any;
         private _activeTarget: any;
         private _targetPath: string = "";
-        private _weight = 1.0
+        private _weight = 1.0;
+
+        /**
+         * Gets the current frame
+         */
+        public get currentFrame(): number {
+            return this._currentFrame;
+        }
 
         /**
          * Gets the weight of the runtime animation
@@ -58,11 +66,13 @@
          * @param target defines the target of the animation
          * @param animation defines the source {BABYLON.Animation} object
          * @param scene defines the hosting scene
+         * @param host defines the initiating Animatable
          */
-        public constructor(target: any, animation: Animation, scene: Scene) {
+        public constructor(target: any, animation: Animation, scene: Scene, host: Animatable) {
             this._animation = animation;
             this._target = target;
             this._scene = scene;
+            this._host = host;
 
             animation._runtimeAnimations.push(this);
         }
@@ -74,7 +84,7 @@
         public reset(): void {
             this._offsetsCache = {};
             this._highLimitsCache = {};
-            this.currentFrame = 0;
+            this._currentFrame = 0;
             this._blendingFactor = 0;
             this._originalValue = null;
         }
@@ -104,7 +114,7 @@
                 return highLimitValue.clone ? highLimitValue.clone() : highLimitValue;
             }
 
-            this.currentFrame = currentFrame;
+            this._currentFrame = currentFrame;
 
             let keys = this._animation.getKeys();
 
@@ -482,6 +492,14 @@
             // Compute value
             var repeatCount = (ratio / range) >> 0;
             var currentFrame = returnValue ? from + ratio % range : to;
+
+            // Need to normalize?
+            if (this._host && this._host.syncRoot) {
+                let syncRoot = this._host.syncRoot;
+                let hostNormalizedFrame = (syncRoot.masterFrame - syncRoot.fromFrame) / (syncRoot.toFrame - syncRoot.fromFrame);
+                currentFrame = from + (to - from) * hostNormalizedFrame;
+            }
+
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
 
             // Set value

+ 115 - 168
src/Materials/babylon.effect.ts

@@ -1,6 +1,6 @@
 module BABYLON {
     /**
-	 * EffectFallbacks can be used to add fallbacks (properties to disable) to certain properties when desired to improve performance.
+     * EffectFallbacks can be used to add fallbacks (properties to disable) to certain properties when desired to improve performance.
      * (Eg. Start at high quality with reflection and fog, if fps is low, remove reflection, if still low remove fog)
      */
     export class EffectFallbacks {
@@ -116,7 +116,7 @@
     }
 
     /**
-	 * Options to be used when creating an effect.
+     * Options to be used when creating an effect.
      */
     export class EffectCreationOptions {
         /**
@@ -227,7 +227,6 @@
         private _vertexSourceCodeOverride: string;
         private _fragmentSourceCodeOverride: string;
         private _transformFeedbackVaryings: Nullable<string[]>;
-
         /**
          * Compiled shader to webGL program.
          */
@@ -288,11 +287,6 @@
 
             this.uniqueId = Effect._uniqueIdSeed++;
 
-            if (this._getFromCache(baseName)) {
-                this._prepareEffect();
-                return;
-            }
-
             var vertexSource: any;
             var fragmentSource: any;
 
@@ -316,83 +310,31 @@
                 fragmentSource = baseName.fragment || baseName;
             }
 
-            let finalVertexCode: string;
-
-            this._loadVertexShaderAsync(vertexSource)
-            .then((vertexCode) => {
-                return this._processIncludesAsync(vertexCode);
-            })
-            .then((vertexCodeWithIncludes) => {
-                finalVertexCode = this._processShaderConversion(vertexCodeWithIncludes, false);
-                return this._loadFragmentShaderAsync(fragmentSource);
-            })
-            .then((fragmentCode) => {
-                return this._processIncludesAsync(fragmentCode);
-            })
-            .then((fragmentCodeWithIncludes) => {
-                let migratedFragmentCode = this._processShaderConversion(fragmentCodeWithIncludes, true);
-                if (baseName) {
-                    var vertex = baseName.vertexElement || baseName.vertex || baseName;
-                    var fragment = baseName.fragmentElement || baseName.fragment || baseName;
-
-                    this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + finalVertexCode;
-                    this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
-                } else {
-                    this._vertexSourceCode = finalVertexCode;
-                    this._fragmentSourceCode = migratedFragmentCode;
-                }
-
-                this._setInCache(baseName);
-                this._prepareEffect(); 
+            this._loadVertexShader(vertexSource, vertexCode => {
+                this._processIncludes(vertexCode, vertexCodeWithIncludes => {
+                    this._processShaderConversion(vertexCodeWithIncludes, false, migratedVertexCode => {
+                        this._loadFragmentShader(fragmentSource, (fragmentCode) => {
+                            this._processIncludes(fragmentCode, fragmentCodeWithIncludes => {
+                                this._processShaderConversion(fragmentCodeWithIncludes, true, migratedFragmentCode => {
+                                    if (baseName) {
+                                        var vertex = baseName.vertexElement || baseName.vertex || baseName;
+                                        var fragment = baseName.fragmentElement || baseName.fragment || baseName;
+
+                                        this._vertexSourceCode = "#define SHADER_NAME vertex:" + vertex + "\n" + migratedVertexCode;
+                                        this._fragmentSourceCode = "#define SHADER_NAME fragment:" + fragment + "\n" + migratedFragmentCode;
+                                    } else {
+                                        this._vertexSourceCode = migratedVertexCode;
+                                        this._fragmentSourceCode = migratedFragmentCode;
+                                    }
+                                    this._prepareEffect();
+                                });
+                            });
+                        });
+                    });
+                });
             });
         }
 
-        private static _sourceCache: { [baseName: string]: { vertex: string, fragment: string } } = { };
-
-        private _getSourceCacheKey(baseName: string): string {
-            let cacheKey: string = baseName;
-            if (this._indexParameters) {
-                for (let key in this._indexParameters) {
-                    if (this._indexParameters.hasOwnProperty(key)) {
-                        cacheKey += "|";
-                        cacheKey += key
-                        cacheKey += "_";
-                        cacheKey += this._indexParameters[key];
-                    }
-                }
-            }
-
-            return cacheKey;
-        }
-
-        private _getFromCache(baseName: string): boolean {
-            if (typeof baseName !== "string") {
-                return false;
-            }
-
-            let cacheKey = this._getSourceCacheKey(baseName);
-            let sources = Effect._sourceCache[cacheKey];
-            if (!sources) {
-                return false;
-            }
-
-            this._vertexSourceCode = sources.vertex;
-            this._fragmentSourceCode = sources.fragment;
-            return true;
-        }
-
-        private _setInCache(baseName: string): void {
-            if (typeof baseName !== "string") {
-                return;
-            }
-
-            let cacheKey = this._getSourceCacheKey(baseName);
-            Effect._sourceCache[cacheKey] = {
-                vertex: this._vertexSourceCode,
-                fragment: this._fragmentSourceCode
-            };
-        }
-
         /**
          * Unique key for this effect
          */
@@ -423,6 +365,7 @@
         public getProgram(): WebGLProgram {
             return this._program;
         }
+
         /**
          * The set of names of attribute variables for the shader.
          * @returns An array of attribute names.
@@ -509,24 +452,27 @@
         }
 
         /** @ignore */
-        public _loadVertexShaderAsync(vertex: any): Promise<any> {
+        public _loadVertexShader(vertex: any, callback: (data: any) => void): void {
             if (Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (vertex instanceof HTMLElement) {
                     var vertexCode = Tools.GetDOMTextContent(vertex);
-                    return Promise.resolve(vertexCode);
+                    callback(vertexCode);
+                    return;
                 }
             }
 
             // Base64 encoded ?
             if (vertex.substr(0, 7) === "base64:") {
                 var vertexBinary = window.atob(vertex.substr(7));
-                return Promise.resolve(vertexBinary);
+                callback(vertexBinary);
+                return;
             }
 
             // Is in local store ?
             if (Effect.ShadersStore[vertex + "VertexShader"]) {
-                return Promise.resolve(Effect.ShadersStore[vertex + "VertexShader"]);
+                callback(Effect.ShadersStore[vertex + "VertexShader"]);
+                return;
             }
 
             var vertexShaderUrl;
@@ -538,32 +484,36 @@
             }
 
             // Vertex shader
-            return this._engine._loadFileAsync(vertexShaderUrl + ".vertex.fx");
+            this._engine._loadFile(vertexShaderUrl + ".vertex.fx", callback);
         }
 
         /** @ignore */
-        public _loadFragmentShaderAsync(fragment: any): Promise<any>  {
+        public _loadFragmentShader(fragment: any, callback: (data: any) => void): void {
             if (Tools.IsWindowObjectExist()) {
                 // DOM element ?
                 if (fragment instanceof HTMLElement) {
                     var fragmentCode = Tools.GetDOMTextContent(fragment);
-                    return Promise.resolve(fragmentCode);
+                    callback(fragmentCode);
+                    return;
                 }
             }
 
             // Base64 encoded ?
             if (fragment.substr(0, 7) === "base64:") {
                 var fragmentBinary = window.atob(fragment.substr(7));
-                return Promise.resolve(fragmentBinary);
+                callback(fragmentBinary);
+                return;
             }
 
             // Is in local store ?
             if (Effect.ShadersStore[fragment + "PixelShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "PixelShader"]);
+                callback(Effect.ShadersStore[fragment + "PixelShader"]);
+                return;
             }
 
             if (Effect.ShadersStore[fragment + "FragmentShader"]) {
-                return Promise.resolve(Effect.ShadersStore[fragment + "FragmentShader"]);
+                callback(Effect.ShadersStore[fragment + "FragmentShader"]);
+                return;
             }
 
             var fragmentShaderUrl;
@@ -575,7 +525,7 @@
             }
 
             // Fragment shader
-            return this._engine._loadFileAsync(fragmentShaderUrl + ".fragment.fx");
+            this._engine._loadFile(fragmentShaderUrl + ".fragment.fx", callback);
         }
 
         private _dumpShadersSource(vertexCode: string, fragmentCode: string, defines: string): void {
@@ -607,16 +557,19 @@
             }
         };
 
-        private _processShaderConversion(sourceCode: string, isFragment: boolean): any {
+        private _processShaderConversion(sourceCode: string, isFragment: boolean, callback: (data: any) => void): void {
+
             var preparedSourceCode = this._processPrecision(sourceCode);
 
             if (this._engine.webGLVersion == 1) {
-                return preparedSourceCode;
+                callback(preparedSourceCode);
+                return;
             }
 
             // Already converted
             if (preparedSourceCode.indexOf("#version 3") !== -1) {
-                return preparedSourceCode.replace("#version 300 es", "");
+                callback(preparedSourceCode.replace("#version 300 es", ""));
+                return;
             }
 
             var hasDrawBuffersExtension = preparedSourceCode.search(/#extension.+GL_EXT_draw_buffers.+require/) !== -1;
@@ -645,97 +598,92 @@
                 result = result.replace(/void\s+?main\s*\(/g, (hasDrawBuffersExtension ? "" : "out vec4 glFragColor;\n") + "void main(");
             }
 
-            return result;
+            callback(result);
         }
 
-        private _processIncludesAsync(sourceCode: string): Promise<any> {
-            return new Promise((resolve, reject) => {
-                var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
-                var match = regex.exec(sourceCode);
+        private _processIncludes(sourceCode: string, callback: (data: any) => void): void {
+            var regex = /#include<(.+)>(\((.*)\))*(\[(.*)\])*/g;
+            var match = regex.exec(sourceCode);
 
-                var returnValue = sourceCode;
+            var returnValue = new String(sourceCode);
 
-                while (match != null) {
-                    var includeFile = match[1];
+            while (match != null) {
+                var includeFile = match[1];
 
-                    // Uniform declaration
-                    if (includeFile.indexOf("__decl__") !== -1) {
-                        includeFile = includeFile.replace(/__decl__/, "");
-                        if (this._engine.supportsUniformBuffers) {
-                            includeFile = includeFile.replace(/Vertex/, "Ubo");
-                            includeFile = includeFile.replace(/Fragment/, "Ubo");
-                        }
-                        includeFile = includeFile + "Declaration";
+                // Uniform declaration
+                if (includeFile.indexOf("__decl__") !== -1) {
+                    includeFile = includeFile.replace(/__decl__/, "");
+                    if (this._engine.supportsUniformBuffers) {
+                        includeFile = includeFile.replace(/Vertex/, "Ubo");
+                        includeFile = includeFile.replace(/Fragment/, "Ubo");
                     }
+                    includeFile = includeFile + "Declaration";
+                }
 
-                    if (Effect.IncludesShadersStore[includeFile]) {
-                        // Substitution
-                        var includeContent = Effect.IncludesShadersStore[includeFile];
-                        if (match[2]) {
-                            var splits = match[3].split(",");
+                if (Effect.IncludesShadersStore[includeFile]) {
+                    // Substitution
+                    var includeContent = Effect.IncludesShadersStore[includeFile];
+                    if (match[2]) {
+                        var splits = match[3].split(",");
 
-                            for (var index = 0; index < splits.length; index += 2) {
-                                var source = new RegExp(splits[index], "g");
-                                var dest = splits[index + 1];
+                        for (var index = 0; index < splits.length; index += 2) {
+                            var source = new RegExp(splits[index], "g");
+                            var dest = splits[index + 1];
 
-                                includeContent = includeContent.replace(source, dest);
-                            }
+                            includeContent = includeContent.replace(source, dest);
                         }
+                    }
 
-                        if (match[4]) {
-                            var indexString = match[5];
+                    if (match[4]) {
+                        var indexString = match[5];
 
-                            if (indexString.indexOf("..") !== -1) {
-                                var indexSplits = indexString.split("..");
-                                var minIndex = parseInt(indexSplits[0]);
-                                var maxIndex = parseInt(indexSplits[1]);
-                                var sourceIncludeContent = includeContent.slice(0);
-                                includeContent = "";
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
 
-                                if (isNaN(maxIndex)) {
-                                    maxIndex = this._indexParameters[indexSplits[1]];
-                                }
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
+                            }
 
-                                for (var i = minIndex; i < maxIndex; i++) {
-                                    if (!this._engine.supportsUniformBuffers) {
-                                        // Ubo replacement
-                                        sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
-                                            return p1 + "{X}";
-                                        });
-                                    }
-                                    includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
-                                }
-                            } else {
+                            for (var i = minIndex; i < maxIndex; i++) {
                                 if (!this._engine.supportsUniformBuffers) {
                                     // Ubo replacement
-                                    includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
+                                    sourceIncludeContent = sourceIncludeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
                                         return p1 + "{X}";
                                     });
                                 }
-                                includeContent = includeContent.replace(/\{X\}/g, indexString);
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i.toString()) + "\n";
                             }
+                        } else {
+                            if (!this._engine.supportsUniformBuffers) {
+                                // Ubo replacement
+                                includeContent = includeContent.replace(/light\{X\}.(\w*)/g, (str: string, p1: string) => {
+                                    return p1 + "{X}";
+                                });
+                            }
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
                         }
-
-                        // Replace
-                        returnValue = returnValue.replace(match[0], includeContent);
-                    } else {
-                        var includeShaderUrl = Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
-
-                        this._engine._loadFileAsync(includeShaderUrl)
-                            .then((fileContent) => {
-                                Effect.IncludesShadersStore[includeFile] = fileContent as string;
-                                return this._processIncludesAsync(returnValue);
-                            })
-                            .then((returnValue) => {
-                                resolve(returnValue);
-                            });
-                        return;
                     }
 
-                    match = regex.exec(sourceCode);
+                    // Replace
+                    returnValue = returnValue.replace(match[0], includeContent);
+                } else {
+                    var includeShaderUrl = Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
+
+                    this._engine._loadFile(includeShaderUrl, (fileContent) => {
+                        Effect.IncludesShadersStore[includeFile] = fileContent as string;
+                        this._processIncludes(<string>returnValue, callback);
+                    });
+                    return;
                 }
-                resolve(returnValue);
-            });            
+
+                match = regex.exec(sourceCode);
+            }
+
+            callback(returnValue);
         }
 
         private _processPrecision(source: string): string {
@@ -947,8 +895,7 @@
         }
 
         /**
-         * (Warning! setTextureFromPostProcessOutput may be desired instead)
-         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
+         * Sets a texture to be the input of the specified post process. (To use the output, pass in the next post process in the pipeline)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the input texture from.
          */
@@ -957,7 +904,8 @@
         }
 
         /**
-         * Sets the output texture of the passed in post process to be input of this effect.
+         * (Warning! setTextureFromPostProcessOutput may be desired instead)
+         * Sets the input texture of the passed in post process to be input of this effect. (To use the output of the passed in post process use setTextureFromPostProcessOutput)
          * @param channel Name of the sampler variable.
          * @param postProcess Post process to get the output texture from.
          */
@@ -1470,7 +1418,6 @@
             return this;
         }
 
-        // Statics
         /**
          * Store of each shader (The can be looked up using effect.key)
          */
@@ -1487,4 +1434,4 @@
             Effect._baseCache = {};
         }
     }
-} 
+} 

+ 1 - 1
src/Mesh/Compression/babylon.dracoCompression.ts

@@ -188,7 +188,7 @@ module BABYLON {
         }
 
         private static _GetDefaultDecoderUrl(): Nullable<string> {
-            if (!Tools.IsWindowObjectExist) {
+            if (!Tools.IsWindowObjectExist()) {
                 return null;
             }