Browse Source

Merge remote-tracking branch 'upstream/master'

sebastien 7 years ago
parent
commit
5f1d529410
51 changed files with 23852 additions and 21032 deletions
  1. 11915 11626
      Playground/babylon.d.txt
  2. 5 2
      Tools/Gulp/config.json
  3. BIN
      Viewer/dist/babylon.woff
  4. 16 1
      Viewer/src/configuration/loader.ts
  5. 4 4
      Viewer/src/configuration/types/index.ts
  6. 4 3
      Viewer/src/templating/templateManager.ts
  7. 3 0
      Viewer/tests/commons/helper.ts
  8. 1 1
      Viewer/tests/unit/src/configuration/updateConfiguration.ts
  9. 7410 7149
      dist/preview release/babylon.d.ts
  10. 47 47
      dist/preview release/babylon.js
  11. 651 304
      dist/preview release/babylon.max.js
  12. 651 304
      dist/preview release/babylon.no-module.max.js
  13. 47 47
      dist/preview release/babylon.worker.js
  14. 653 306
      dist/preview release/es6.js
  15. 34 6
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  16. 187 51
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  17. 3 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  18. 34 6
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  19. 185 51
      dist/preview release/loaders/babylon.glTFFileLoader.js
  20. 4 4
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  21. 34 6
      dist/preview release/loaders/babylonjs.loaders.d.ts
  22. 176 51
      dist/preview release/loaders/babylonjs.loaders.js
  23. 4 4
      dist/preview release/loaders/babylonjs.loaders.min.js
  24. 34 6
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  25. 2 256
      dist/preview release/typedocValidationBaseline.json
  26. 56 56
      dist/preview release/viewer/babylon.viewer.js
  27. 852 365
      dist/preview release/viewer/babylon.viewer.max.js
  28. 4 1
      dist/preview release/what's new.md
  29. 122 0
      loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts
  30. 5 7
      loaders/src/glTF/2.0/Extensions/KHR_lights.ts
  31. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts
  32. 1 1
      loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts
  33. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts
  34. 51 43
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  35. 17 3
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  36. 1 1
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  37. BIN
      sandbox/Assets/environment.dds
  38. 11 5
      sandbox/index.js
  39. 16 11
      src/Engine/babylon.engine.ts
  40. 1 1
      src/Materials/Textures/babylon.cubeTexture.ts
  41. 16 3
      src/Materials/Textures/babylon.internalTexture.ts
  42. 85 0
      src/Materials/Textures/babylon.rawCubeTexture.ts
  43. 228 79
      src/Math/babylon.sphericalPolynomial.ts
  44. 4 3
      src/Mesh/babylon.mesh.ts
  45. 22 2
      src/Particles/babylon.IParticleSystem.ts
  46. 69 4
      src/Particles/babylon.gpuParticleSystem.ts
  47. 13 1
      src/Particles/babylon.particle.ts
  48. 89 4
      src/Particles/babylon.particleSystem.ts
  49. 17 5
      src/Shaders/gpuUpdateParticles.vertex.fx
  50. 1 1
      src/Tools/HDR/babylon.cubemapToSphericalPolynomial.ts
  51. 63 195
      src/Tools/babylon.environmentTextureTools.ts

File diff suppressed because it is too large
+ 11915 - 11626
Playground/babylon.d.txt


+ 5 - 2
Tools/Gulp/config.json

@@ -541,6 +541,7 @@
         "additionalTextures": {
             "files": [
                 "../../src/Materials/Textures/babylon.cubeTexture.js",
+                "../../src/Materials/Textures/babylon.rawCubeTexture.js",
                 "../../src/Materials/Textures/babylon.renderTargetTexture.js",
                 "../../src/Materials/Textures/babylon.multiRenderTarget.js",
                 "../../src/Materials/Textures/babylon.mirrorTexture.js",
@@ -1638,7 +1639,8 @@
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts",
-                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts"
                 ],
                 "doNotIncludeInBundle": true,
                 "output": "babylon.glTF2FileLoader.js"
@@ -1662,7 +1664,8 @@
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts",
-                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts"
                 ],
                 "output": "babylon.glTFFileLoader.js"
             }

BIN
Viewer/dist/babylon.woff


+ 16 - 1
Viewer/src/configuration/loader.ts

@@ -33,8 +33,9 @@ export class ConfigurationLoader {
     public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
 
         let loadedConfig: ViewerConfiguration = deepmerge({}, initConfig);
+        this._processInitialConfiguration(loadedConfig);
 
-        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "");
+        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "extended");
 
         if (loadedConfig.configuration) {
 
@@ -94,6 +95,20 @@ export class ConfigurationLoader {
         this._loadRequests.length = 0;
     }
 
+    /**
+     * This function will process the initial configuration and make needed changes for the viewer to work.
+     * @param config the mutable(!) initial configuration to process
+     */
+    private _processInitialConfiguration(config: ViewerConfiguration) {
+        if (config.model) {
+            if (typeof config.model === "string") {
+                config.model = {
+                    url: config.model
+                }
+            }
+        }
+    }
+
     private _loadFile(url: string): Promise<any> {
         let cacheReference = this._configurationCache;
         if (this._enableCache && cacheReference[url]) {

+ 4 - 4
Viewer/src/configuration/types/index.ts

@@ -28,17 +28,17 @@ let getConfigurationType = function (types: string): ViewerConfiguration {
             case 'shadowSpotLight':
                 config = deepmerge(config, shadowSpotlLightConfiguration);
                 break;
-            case 'extended':
-                config = deepmerge(config, extendedConfiguration);
+            case 'default':
+                config = deepmerge(config, defaultConfiguration);
                 break;
             case 'minimal':
                 config = deepmerge(config, minimalConfiguration);
                 break;
             case 'none':
                 break;
-            case 'default':
+            case 'extended':
             default:
-                config = deepmerge(config, defaultConfiguration);
+                config = deepmerge(config, extendedConfiguration);
                 break;
         }
 

+ 4 - 3
Viewer/src/templating/templateManager.ts

@@ -80,8 +80,9 @@ export class TemplateManager {
             // register the observers
             //template.onLoaded.add(() => {
             let addToParent = () => {
-                let containingElement = parentTemplate && parentTemplate.parent.querySelector(camelToKebab(name)) || this.containerElement;
-                template.appendTo(containingElement);
+                let lastElements = parentTemplate && parentTemplate.parent.querySelectorAll(camelToKebab(name));
+                let containingElement = (lastElements && lastElements.length && lastElements.item(lastElements.length - 1)) || this.containerElement;
+                template.appendTo(<HTMLElement>containingElement);
                 this._checkLoadedState();
             }
 
@@ -543,7 +544,7 @@ export class Template {
     private _getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
         if (!templateConfig) {
             return Promise.reject('No templateConfig provided');
-        } else if (templateConfig.html) {
+        } else if (templateConfig.html && !templateConfig.location) {
             return Promise.resolve(templateConfig.html);
         } else {
             let location = this._getTemplateLocation(templateConfig);

+ 3 - 0
Viewer/tests/commons/helper.ts

@@ -8,6 +8,9 @@ export const useNullEngine = true;
 export class Helper {
 
     public static getNewViewerInstance(element: HTMLElement = Helper.getViewerContainer(), configuration?: ViewerConfiguration, useAbstractViewer?: boolean) {
+        if (configuration) {
+            configuration.extends = configuration.extends || "default";
+        }
         if (useNullEngine) {
             if (useAbstractViewer) {
                 return new NullEngineAbstractViewer(element, configuration);

+ 1 - 1
Viewer/tests/unit/src/configuration/updateConfiguration.ts

@@ -36,7 +36,7 @@ describe(name + " scene", () => {
             }
         })
         viewer.onInitDoneObservable.add(() => {
-            assert.isUndefined(viewer.configuration.scene);
+            // assert.isUndefined(viewer.configuration.scene);
             assert.equal(showCalled, 0);
             assert.equal(hideCalled, 0);
             viewer.updateConfiguration({

File diff suppressed because it is too large
+ 7410 - 7149
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 47 - 47
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 651 - 304
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 651 - 304
dist/preview release/babylon.no-module.max.js


File diff suppressed because it is too large
+ 47 - 47
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 653 - 306
dist/preview release/es6.js


+ 34 - 6
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -368,7 +368,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -441,6 +441,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -516,9 +520,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -584,10 +589,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -629,10 +639,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -764,6 +779,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

+ 187 - 51
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -677,6 +677,7 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        /** @hidden */
         var _ArrayItem = /** @class */ (function () {
             function _ArrayItem() {
             }
@@ -689,6 +690,7 @@ var BABYLON;
             };
             return _ArrayItem;
         }());
+        GLTF2._ArrayItem = _ArrayItem;
         /** @hidden */
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
@@ -941,9 +943,9 @@ var BABYLON;
                 return rootNode;
             };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (scene.name || ""));
@@ -1044,9 +1046,9 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadNodeAsync = function (context, node) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (node._babylonMesh) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
@@ -1137,9 +1139,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var attributes = primitive.attributes;
                 if (!attributes) {
@@ -1746,12 +1748,12 @@ var BABYLON;
                     babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
                     babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                     if (properties.baseColorTexture) {
-                        promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.metallicRoughnessTexture) {
-                        promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
                             babylonMaterial.metallicTexture = texture;
                         }));
                         babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
@@ -1763,9 +1765,9 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 material._babylonData = material._babylonData || {};
                 var babylonData = material._babylonData[babylonDrawMode];
@@ -1793,9 +1795,9 @@ var BABYLON;
                 return babylonData.loaded;
             };
             GLTFLoader.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -1819,7 +1821,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/normalTexture", material.normalTexture, function (texture) {
                         babylonMaterial.bumpTexture = texture;
                     }));
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
@@ -1829,7 +1831,7 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
                         babylonMaterial.ambientTexture = texture;
                     }));
                     babylonMaterial.useAmbientInGrayScale = true;
@@ -1838,7 +1840,7 @@ var BABYLON;
                     }
                 }
                 if (material.emissiveTexture) {
-                    promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
                         babylonMaterial.emissiveTexture = texture;
                     }));
                 }
@@ -1872,15 +1874,26 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
-                var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-                if (promise) {
-                    return promise;
+            GLTFLoader.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 this._parent._logOpen("" + context);
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-                context = "#/textures/" + textureInfo.index;
+                var promise = this._loadTextureAsync("#/textures/" + textureInfo.index, texture, function (babylonTexture) {
+                    babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                    assign(babylonTexture);
+                });
+                this._parent._logClose();
+                return promise;
+            };
+            GLTFLoader.prototype._loadTextureAsync = function (context, texture, assign) {
+                var _this = this;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
+                }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
@@ -1899,16 +1912,14 @@ var BABYLON;
                 babylonTexture.name = texture.name || "texture" + texture._index;
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
                 var image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (blob) {
+                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (data) {
                     var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image._index);
-                    babylonTexture.updateURL(dataUrl, blob);
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 assign(babylonTexture);
                 this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 this._parent._logClose();
-                this._parent._logClose();
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -1924,28 +1935,24 @@ var BABYLON;
                 return sampler._data;
             };
             GLTFLoader.prototype._loadImageAsync = function (context, image) {
-                if (!image._blob) {
+                if (!image._data) {
                     this._parent._logOpen(context + " " + (image.name || ""));
-                    var promise = void 0;
                     if (image.uri) {
-                        promise = this._loadUriAsync(context, image.uri);
+                        image._data = this._loadUriAsync(context, image.uri);
                     }
                     else {
                         var bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                        promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                        image._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
                     }
-                    image._blob = promise.then(function (data) {
-                        return new Blob([data], { type: image.mimeType });
-                    });
                     this._parent._logClose();
                 }
-                return image._blob;
+                return image._data;
             };
             GLTFLoader.prototype._loadUriAsync = function (context, uri) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (!GLTFLoader._ValidateUri(uri)) {
                     throw new Error(context + ": Uri '" + uri + "' is invalid");
@@ -2230,10 +2237,15 @@ var BABYLON;
              */
             GLTFLoaderExtension.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) { return null; };
             /**
+             * Override this method to modify the default behavior for loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) { return null; };
+            /**
              * Override this method to modify the default behavior for loading textures.
              * @hidden
              */
-            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, textureInfo, assign) { return null; };
+            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, texture, assign) { return null; };
             /**
              * Override this method to modify the default behavior for loading uris.
              * @hidden
@@ -2322,11 +2334,18 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialPropertiesAsync(context, material, babylonMaterial); });
             };
             /**
+             * Helper method called by the loader to allow extensions to override loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension._LoadTextureInfoAsync = function (loader, context, textureInfo, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureInfoAsync(context, textureInfo, assign); });
+            };
+            /**
              * Helper method called by the loader to allow extensions to override loading textures.
              * @hidden
              */
-            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, textureInfo, assign) {
-                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, textureInfo, assign); });
+            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, texture, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, texture, assign); });
             };
             /**
              * Helper method called by the loader to allow extensions to override loading uris.
@@ -2840,12 +2859,12 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
@@ -2914,7 +2933,7 @@ var BABYLON;
                             babylonMaterial.albedoColor = BABYLON.Color3.White();
                         }
                         if (properties.baseColorTexture) {
-                            promises.push(this._loader._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                            promises.push(this._loader._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                                 babylonMaterial.albedoTexture = texture;
                             }));
                         }
@@ -3003,10 +3022,9 @@ var BABYLON;
                                 break;
                             }
                             case LightType.SPOT: {
-                                var spotLight = light;
                                 // TODO: support inner and outer cone angles
                                 //const innerConeAngle = spotLight.innerConeAngle || 0;
-                                var outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                                var outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                                 babylonLight = new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Forward(), outerConeAngle, 2, _this._loader._babylonScene);
                                 break;
                             }
@@ -3070,10 +3088,10 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_texture_transform.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                KHR_texture_transform.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, textureInfo, function (extensionContext, extension) {
-                        return _this._loader._loadTextureAsync(context, textureInfo, function (babylonTexture) {
+                        return _this._loader._loadTextureInfoAsync(context, textureInfo, function (babylonTexture) {
                             if (extension.offset) {
                                 babylonTexture.uOffset = extension.offset[0];
                                 babylonTexture.vOffset = extension.offset[1];
@@ -3104,3 +3122,121 @@ var BABYLON;
 })(BABYLON || (BABYLON = {}));
 
 //# sourceMappingURL=KHR_texture_transform.js.map
+
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
+            var NAME = "EXT_lights_imageBased";
+            /**
+             * [Specification](TODO) (Experimental)
+             */
+            var EXT_lights_imageBased = /** @class */ (function (_super) {
+                __extends(EXT_lights_imageBased, _super);
+                function EXT_lights_imageBased() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                EXT_lights_imageBased.prototype._loadSceneAsync = function (context, scene) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, scene, function (extensionContext, extension) {
+                        var promises = new Array();
+                        promises.push(_this._loader._loadSceneAsync(context, scene));
+                        _this._loader._parent._logOpen("" + extensionContext);
+                        var light = GLTF2.GLTFLoader._GetProperty(extensionContext + "/light", _this._lights, extension.light);
+                        promises.push(_this._loadLightAsync("#/extensions/" + _this.name + "/lights/" + extension.light, light).then(function (texture) {
+                            _this._loader._babylonScene.environmentTexture = texture;
+                        }));
+                        _this._loader._parent._logClose();
+                        return Promise.all(promises).then(function () { });
+                    });
+                };
+                EXT_lights_imageBased.prototype._loadLightAsync = function (context, light) {
+                    var _this = this;
+                    if (!light._loaded) {
+                        var promises = new Array();
+                        this._loader._parent._logOpen("" + context);
+                        var imageData_1 = new Array(light.specularImages.length);
+                        var _loop_1 = function (mipmap) {
+                            var faces = light.specularImages[mipmap];
+                            imageData_1[mipmap] = new Array(faces.length);
+                            var _loop_2 = function (face) {
+                                var specularImageContext = context + "/specularImages/" + mipmap + "/" + face;
+                                this_1._loader._parent._logOpen("" + specularImageContext);
+                                var index = faces[face];
+                                var image = GLTF2.GLTFLoader._GetProperty(specularImageContext, this_1._loader._gltf.images, index);
+                                promises.push(this_1._loader._loadImageAsync("#/images/" + index, image).then(function (data) {
+                                    imageData_1[mipmap][face] = data;
+                                }));
+                                this_1._loader._parent._logClose();
+                            };
+                            for (var face = 0; face < faces.length; face++) {
+                                _loop_2(face);
+                            }
+                        };
+                        var this_1 = this;
+                        for (var mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                            _loop_1(mipmap);
+                        }
+                        this._loader._parent._logClose();
+                        light._loaded = Promise.all(promises).then(function () {
+                            var size = Math.pow(2, imageData_1.length - 1);
+                            var babylonTexture = new BABYLON.RawCubeTexture(_this._loader._babylonScene, null, size);
+                            light._babylonTexture = babylonTexture;
+                            if (light.intensity != undefined) {
+                                babylonTexture.level = light.intensity;
+                            }
+                            if (light.rotation) {
+                                var rotation = BABYLON.Quaternion.FromArray(light.rotation);
+                                // Invert the rotation so that positive rotation is counter-clockwise.
+                                if (!_this._loader._babylonScene.useRightHandedSystem) {
+                                    rotation = BABYLON.Quaternion.Inverse(rotation);
+                                }
+                                BABYLON.Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                            }
+                            var sphericalHarmonics = BABYLON.SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                            sphericalHarmonics.scale(light.intensity);
+                            sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                            var sphericalPolynomial = BABYLON.SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+                            return babylonTexture.updateRGBDAsync(imageData_1, sphericalPolynomial);
+                        });
+                    }
+                    return light._loaded.then(function () {
+                        return light._babylonTexture;
+                    });
+                };
+                Object.defineProperty(EXT_lights_imageBased.prototype, "_lights", {
+                    get: function () {
+                        var extensions = this._loader._gltf.extensions;
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: '" + this.name + "' not found");
+                        }
+                        var extension = extensions[this.name];
+                        return extension.lights;
+                    },
+                    enumerable: true,
+                    configurable: true
+                });
+                return EXT_lights_imageBased;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.EXT_lights_imageBased = EXT_lights_imageBased;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new EXT_lights_imageBased(loader); });
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=EXT_lights_imageBased.js.map

File diff suppressed because it is too large
+ 3 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 34 - 6
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -930,7 +930,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -1003,6 +1003,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -1078,9 +1082,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -1146,10 +1151,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -1191,10 +1201,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -1326,6 +1341,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

+ 185 - 51
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2878,6 +2878,7 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        /** @hidden */
         var _ArrayItem = /** @class */ (function () {
             function _ArrayItem() {
             }
@@ -2890,6 +2891,7 @@ var BABYLON;
             };
             return _ArrayItem;
         }());
+        GLTF2._ArrayItem = _ArrayItem;
         /** @hidden */
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
@@ -3142,9 +3144,9 @@ var BABYLON;
                 return rootNode;
             };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (scene.name || ""));
@@ -3245,9 +3247,9 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadNodeAsync = function (context, node) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (node._babylonMesh) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
@@ -3338,9 +3340,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var attributes = primitive.attributes;
                 if (!attributes) {
@@ -3947,12 +3949,12 @@ var BABYLON;
                     babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
                     babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                     if (properties.baseColorTexture) {
-                        promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.metallicRoughnessTexture) {
-                        promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
                             babylonMaterial.metallicTexture = texture;
                         }));
                         babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
@@ -3964,9 +3966,9 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 material._babylonData = material._babylonData || {};
                 var babylonData = material._babylonData[babylonDrawMode];
@@ -3994,9 +3996,9 @@ var BABYLON;
                 return babylonData.loaded;
             };
             GLTFLoader.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -4020,7 +4022,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/normalTexture", material.normalTexture, function (texture) {
                         babylonMaterial.bumpTexture = texture;
                     }));
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
@@ -4030,7 +4032,7 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
                         babylonMaterial.ambientTexture = texture;
                     }));
                     babylonMaterial.useAmbientInGrayScale = true;
@@ -4039,7 +4041,7 @@ var BABYLON;
                     }
                 }
                 if (material.emissiveTexture) {
-                    promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
                         babylonMaterial.emissiveTexture = texture;
                     }));
                 }
@@ -4073,15 +4075,26 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
-                var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-                if (promise) {
-                    return promise;
+            GLTFLoader.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 this._parent._logOpen("" + context);
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-                context = "#/textures/" + textureInfo.index;
+                var promise = this._loadTextureAsync("#/textures/" + textureInfo.index, texture, function (babylonTexture) {
+                    babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                    assign(babylonTexture);
+                });
+                this._parent._logClose();
+                return promise;
+            };
+            GLTFLoader.prototype._loadTextureAsync = function (context, texture, assign) {
+                var _this = this;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
+                }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
@@ -4100,16 +4113,14 @@ var BABYLON;
                 babylonTexture.name = texture.name || "texture" + texture._index;
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
                 var image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (blob) {
+                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (data) {
                     var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image._index);
-                    babylonTexture.updateURL(dataUrl, blob);
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 assign(babylonTexture);
                 this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 this._parent._logClose();
-                this._parent._logClose();
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -4125,28 +4136,24 @@ var BABYLON;
                 return sampler._data;
             };
             GLTFLoader.prototype._loadImageAsync = function (context, image) {
-                if (!image._blob) {
+                if (!image._data) {
                     this._parent._logOpen(context + " " + (image.name || ""));
-                    var promise = void 0;
                     if (image.uri) {
-                        promise = this._loadUriAsync(context, image.uri);
+                        image._data = this._loadUriAsync(context, image.uri);
                     }
                     else {
                         var bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                        promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                        image._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
                     }
-                    image._blob = promise.then(function (data) {
-                        return new Blob([data], { type: image.mimeType });
-                    });
                     this._parent._logClose();
                 }
-                return image._blob;
+                return image._data;
             };
             GLTFLoader.prototype._loadUriAsync = function (context, uri) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (!GLTFLoader._ValidateUri(uri)) {
                     throw new Error(context + ": Uri '" + uri + "' is invalid");
@@ -4431,10 +4438,15 @@ var BABYLON;
              */
             GLTFLoaderExtension.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) { return null; };
             /**
+             * Override this method to modify the default behavior for loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) { return null; };
+            /**
              * Override this method to modify the default behavior for loading textures.
              * @hidden
              */
-            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, textureInfo, assign) { return null; };
+            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, texture, assign) { return null; };
             /**
              * Override this method to modify the default behavior for loading uris.
              * @hidden
@@ -4523,11 +4535,18 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialPropertiesAsync(context, material, babylonMaterial); });
             };
             /**
+             * Helper method called by the loader to allow extensions to override loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension._LoadTextureInfoAsync = function (loader, context, textureInfo, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureInfoAsync(context, textureInfo, assign); });
+            };
+            /**
              * Helper method called by the loader to allow extensions to override loading textures.
              * @hidden
              */
-            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, textureInfo, assign) {
-                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, textureInfo, assign); });
+            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, texture, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, texture, assign); });
             };
             /**
              * Helper method called by the loader to allow extensions to override loading uris.
@@ -5041,12 +5060,12 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
@@ -5115,7 +5134,7 @@ var BABYLON;
                             babylonMaterial.albedoColor = BABYLON.Color3.White();
                         }
                         if (properties.baseColorTexture) {
-                            promises.push(this._loader._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                            promises.push(this._loader._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                                 babylonMaterial.albedoTexture = texture;
                             }));
                         }
@@ -5204,10 +5223,9 @@ var BABYLON;
                                 break;
                             }
                             case LightType.SPOT: {
-                                var spotLight = light;
                                 // TODO: support inner and outer cone angles
                                 //const innerConeAngle = spotLight.innerConeAngle || 0;
-                                var outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                                var outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                                 babylonLight = new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Forward(), outerConeAngle, 2, _this._loader._babylonScene);
                                 break;
                             }
@@ -5269,10 +5287,10 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_texture_transform.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                KHR_texture_transform.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, textureInfo, function (extensionContext, extension) {
-                        return _this._loader._loadTextureAsync(context, textureInfo, function (babylonTexture) {
+                        return _this._loader._loadTextureInfoAsync(context, textureInfo, function (babylonTexture) {
                             if (extension.offset) {
                                 babylonTexture.uOffset = extension.offset[0];
                                 babylonTexture.vOffset = extension.offset[1];
@@ -5301,3 +5319,119 @@ var BABYLON;
         })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
+
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
+            var NAME = "EXT_lights_imageBased";
+            /**
+             * [Specification](TODO) (Experimental)
+             */
+            var EXT_lights_imageBased = /** @class */ (function (_super) {
+                __extends(EXT_lights_imageBased, _super);
+                function EXT_lights_imageBased() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                EXT_lights_imageBased.prototype._loadSceneAsync = function (context, scene) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, scene, function (extensionContext, extension) {
+                        var promises = new Array();
+                        promises.push(_this._loader._loadSceneAsync(context, scene));
+                        _this._loader._parent._logOpen("" + extensionContext);
+                        var light = GLTF2.GLTFLoader._GetProperty(extensionContext + "/light", _this._lights, extension.light);
+                        promises.push(_this._loadLightAsync("#/extensions/" + _this.name + "/lights/" + extension.light, light).then(function (texture) {
+                            _this._loader._babylonScene.environmentTexture = texture;
+                        }));
+                        _this._loader._parent._logClose();
+                        return Promise.all(promises).then(function () { });
+                    });
+                };
+                EXT_lights_imageBased.prototype._loadLightAsync = function (context, light) {
+                    var _this = this;
+                    if (!light._loaded) {
+                        var promises = new Array();
+                        this._loader._parent._logOpen("" + context);
+                        var imageData_1 = new Array(light.specularImages.length);
+                        var _loop_1 = function (mipmap) {
+                            var faces = light.specularImages[mipmap];
+                            imageData_1[mipmap] = new Array(faces.length);
+                            var _loop_2 = function (face) {
+                                var specularImageContext = context + "/specularImages/" + mipmap + "/" + face;
+                                this_1._loader._parent._logOpen("" + specularImageContext);
+                                var index = faces[face];
+                                var image = GLTF2.GLTFLoader._GetProperty(specularImageContext, this_1._loader._gltf.images, index);
+                                promises.push(this_1._loader._loadImageAsync("#/images/" + index, image).then(function (data) {
+                                    imageData_1[mipmap][face] = data;
+                                }));
+                                this_1._loader._parent._logClose();
+                            };
+                            for (var face = 0; face < faces.length; face++) {
+                                _loop_2(face);
+                            }
+                        };
+                        var this_1 = this;
+                        for (var mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                            _loop_1(mipmap);
+                        }
+                        this._loader._parent._logClose();
+                        light._loaded = Promise.all(promises).then(function () {
+                            var size = Math.pow(2, imageData_1.length - 1);
+                            var babylonTexture = new BABYLON.RawCubeTexture(_this._loader._babylonScene, null, size);
+                            light._babylonTexture = babylonTexture;
+                            if (light.intensity != undefined) {
+                                babylonTexture.level = light.intensity;
+                            }
+                            if (light.rotation) {
+                                var rotation = BABYLON.Quaternion.FromArray(light.rotation);
+                                // Invert the rotation so that positive rotation is counter-clockwise.
+                                if (!_this._loader._babylonScene.useRightHandedSystem) {
+                                    rotation = BABYLON.Quaternion.Inverse(rotation);
+                                }
+                                BABYLON.Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                            }
+                            var sphericalHarmonics = BABYLON.SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                            sphericalHarmonics.scale(light.intensity);
+                            sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                            var sphericalPolynomial = BABYLON.SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+                            return babylonTexture.updateRGBDAsync(imageData_1, sphericalPolynomial);
+                        });
+                    }
+                    return light._loaded.then(function () {
+                        return light._babylonTexture;
+                    });
+                };
+                Object.defineProperty(EXT_lights_imageBased.prototype, "_lights", {
+                    get: function () {
+                        var extensions = this._loader._gltf.extensions;
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: '" + this.name + "' not found");
+                        }
+                        var extension = extensions[this.name];
+                        return extension.lights;
+                    },
+                    enumerable: true,
+                    configurable: true
+                });
+                return EXT_lights_imageBased;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.EXT_lights_imageBased = EXT_lights_imageBased;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new EXT_lights_imageBased(loader); });
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));

File diff suppressed because it is too large
+ 4 - 4
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 34 - 6
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -1031,7 +1031,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -1104,6 +1104,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -1179,9 +1183,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -1247,10 +1252,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -1292,10 +1302,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -1427,6 +1442,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

+ 176 - 51
dist/preview release/loaders/babylonjs.loaders.js

@@ -3879,6 +3879,7 @@ var BABYLON;
 (function (BABYLON) {
     var GLTF2;
     (function (GLTF2) {
+        /** @hidden */
         var _ArrayItem = /** @class */ (function () {
             function _ArrayItem() {
             }
@@ -3891,6 +3892,7 @@ var BABYLON;
             };
             return _ArrayItem;
         }());
+        GLTF2._ArrayItem = _ArrayItem;
         /** @hidden */
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader(parent) {
@@ -4143,9 +4145,9 @@ var BABYLON;
                 return rootNode;
             };
             GLTFLoader.prototype._loadSceneAsync = function (context, scene) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (scene.name || ""));
@@ -4246,9 +4248,9 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._loadNodeAsync = function (context, node) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (node._babylonMesh) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
@@ -4339,9 +4341,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var attributes = primitive.attributes;
                 if (!attributes) {
@@ -4948,12 +4950,12 @@ var BABYLON;
                     babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
                     babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                     if (properties.baseColorTexture) {
-                        promises.push(this._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.metallicRoughnessTexture) {
-                        promises.push(this._loadTextureAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
+                        promises.push(this._loadTextureInfoAsync(context + "/metallicRoughnessTexture", properties.metallicRoughnessTexture, function (texture) {
                             babylonMaterial.metallicTexture = texture;
                         }));
                         babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
@@ -4965,9 +4967,9 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 material._babylonData = material._babylonData || {};
                 var babylonData = material._babylonData[babylonDrawMode];
@@ -4995,9 +4997,9 @@ var BABYLON;
                 return babylonData.loaded;
             };
             GLTFLoader.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) {
-                var promise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 var promises = new Array();
                 promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
@@ -5021,7 +5023,7 @@ var BABYLON;
                     babylonMaterial.twoSidedLighting = true;
                 }
                 if (material.normalTexture) {
-                    promises.push(this._loadTextureAsync(context + "/normalTexture", material.normalTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/normalTexture", material.normalTexture, function (texture) {
                         babylonMaterial.bumpTexture = texture;
                     }));
                     babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
@@ -5031,7 +5033,7 @@ var BABYLON;
                     }
                 }
                 if (material.occlusionTexture) {
-                    promises.push(this._loadTextureAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/occlusionTexture", material.occlusionTexture, function (texture) {
                         babylonMaterial.ambientTexture = texture;
                     }));
                     babylonMaterial.useAmbientInGrayScale = true;
@@ -5040,7 +5042,7 @@ var BABYLON;
                     }
                 }
                 if (material.emissiveTexture) {
-                    promises.push(this._loadTextureAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
+                    promises.push(this._loadTextureInfoAsync(context + "/emissiveTexture", material.emissiveTexture, function (texture) {
                         babylonMaterial.emissiveTexture = texture;
                     }));
                 }
@@ -5074,15 +5076,26 @@ var BABYLON;
                     }
                 }
             };
-            GLTFLoader.prototype._loadTextureAsync = function (context, textureInfo, assign) {
-                var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-                if (promise) {
-                    return promise;
+            GLTFLoader.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 this._parent._logOpen("" + context);
                 var texture = GLTFLoader._GetProperty(context + "/index", this._gltf.textures, textureInfo.index);
-                context = "#/textures/" + textureInfo.index;
+                var promise = this._loadTextureAsync("#/textures/" + textureInfo.index, texture, function (babylonTexture) {
+                    babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                    assign(babylonTexture);
+                });
+                this._parent._logClose();
+                return promise;
+            };
+            GLTFLoader.prototype._loadTextureAsync = function (context, texture, assign) {
+                var _this = this;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+                if (extensionPromise) {
+                    return extensionPromise;
+                }
                 var promises = new Array();
                 this._parent._logOpen(context + " " + (texture.name || ""));
                 var sampler = (texture.sampler == undefined ? this._defaultSampler : GLTFLoader._GetProperty(context + "/sampler", this._gltf.samplers, texture.sampler));
@@ -5101,16 +5114,14 @@ var BABYLON;
                 babylonTexture.name = texture.name || "texture" + texture._index;
                 babylonTexture.wrapU = samplerData.wrapU;
                 babylonTexture.wrapV = samplerData.wrapV;
-                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
                 var image = GLTFLoader._GetProperty(context + "/source", this._gltf.images, texture.source);
-                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (blob) {
+                promises.push(this._loadImageAsync("#/images/" + image._index, image).then(function (data) {
                     var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image._index);
-                    babylonTexture.updateURL(dataUrl, blob);
+                    babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 assign(babylonTexture);
                 this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 this._parent._logClose();
-                this._parent._logClose();
                 return Promise.all(promises).then(function () { });
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -5126,28 +5137,24 @@ var BABYLON;
                 return sampler._data;
             };
             GLTFLoader.prototype._loadImageAsync = function (context, image) {
-                if (!image._blob) {
+                if (!image._data) {
                     this._parent._logOpen(context + " " + (image.name || ""));
-                    var promise = void 0;
                     if (image.uri) {
-                        promise = this._loadUriAsync(context, image.uri);
+                        image._data = this._loadUriAsync(context, image.uri);
                     }
                     else {
                         var bufferView = GLTFLoader._GetProperty(context + "/bufferView", this._gltf.bufferViews, image.bufferView);
-                        promise = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
+                        image._data = this._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView);
                     }
-                    image._blob = promise.then(function (data) {
-                        return new Blob([data], { type: image.mimeType });
-                    });
                     this._parent._logClose();
                 }
-                return image._blob;
+                return image._data;
             };
             GLTFLoader.prototype._loadUriAsync = function (context, uri) {
                 var _this = this;
-                var promise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-                if (promise) {
-                    return promise;
+                var extensionPromise = GLTF2.GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+                if (extensionPromise) {
+                    return extensionPromise;
                 }
                 if (!GLTFLoader._ValidateUri(uri)) {
                     throw new Error(context + ": Uri '" + uri + "' is invalid");
@@ -5432,10 +5439,15 @@ var BABYLON;
              */
             GLTFLoaderExtension.prototype._loadMaterialPropertiesAsync = function (context, material, babylonMaterial) { return null; };
             /**
+             * Override this method to modify the default behavior for loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) { return null; };
+            /**
              * Override this method to modify the default behavior for loading textures.
              * @hidden
              */
-            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, textureInfo, assign) { return null; };
+            GLTFLoaderExtension.prototype._loadTextureAsync = function (context, texture, assign) { return null; };
             /**
              * Override this method to modify the default behavior for loading uris.
              * @hidden
@@ -5524,11 +5536,18 @@ var BABYLON;
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialPropertiesAsync(context, material, babylonMaterial); });
             };
             /**
+             * Helper method called by the loader to allow extensions to override loading texture infos.
+             * @hidden
+             */
+            GLTFLoaderExtension._LoadTextureInfoAsync = function (loader, context, textureInfo, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureInfoAsync(context, textureInfo, assign); });
+            };
+            /**
              * Helper method called by the loader to allow extensions to override loading textures.
              * @hidden
              */
-            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, textureInfo, assign) {
-                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, textureInfo, assign); });
+            GLTFLoaderExtension._LoadTextureAsync = function (loader, context, texture, assign) {
+                return loader._applyExtensions(function (extension) { return extension._loadTextureAsync(context, texture, assign); });
             };
             /**
              * Helper method called by the loader to allow extensions to override loading uris.
@@ -5997,12 +6016,12 @@ var BABYLON;
                     babylonMaterial.reflectivityColor = properties.specularFactor ? BABYLON.Color3.FromArray(properties.specularFactor) : BABYLON.Color3.White();
                     babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
                     if (properties.diffuseTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/diffuseTexture", properties.diffuseTexture, function (texture) {
                             babylonMaterial.albedoTexture = texture;
                         }));
                     }
                     if (properties.specularGlossinessTexture) {
-                        promises.push(this._loader._loadTextureAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
+                        promises.push(this._loader._loadTextureInfoAsync(context + "/specularGlossinessTexture", properties.specularGlossinessTexture, function (texture) {
                             babylonMaterial.reflectivityTexture = texture;
                         }));
                         babylonMaterial.reflectivityTexture.hasAlpha = true;
@@ -6062,7 +6081,7 @@ var BABYLON;
                             babylonMaterial.albedoColor = BABYLON.Color3.White();
                         }
                         if (properties.baseColorTexture) {
-                            promises.push(this._loader._loadTextureAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
+                            promises.push(this._loader._loadTextureInfoAsync(context + "/baseColorTexture", properties.baseColorTexture, function (texture) {
                                 babylonMaterial.albedoTexture = texture;
                             }));
                         }
@@ -6142,10 +6161,9 @@ var BABYLON;
                                 break;
                             }
                             case LightType.SPOT: {
-                                var spotLight = light;
                                 // TODO: support inner and outer cone angles
                                 //const innerConeAngle = spotLight.innerConeAngle || 0;
-                                var outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                                var outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                                 babylonLight = new BABYLON.SpotLight(name, BABYLON.Vector3.Zero(), BABYLON.Vector3.Forward(), outerConeAngle, 2, _this._loader._babylonScene);
                                 break;
                             }
@@ -6198,10 +6216,10 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                KHR_texture_transform.prototype._loadTextureAsync = function (context, textureInfo, assign) {
+                KHR_texture_transform.prototype._loadTextureInfoAsync = function (context, textureInfo, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, textureInfo, function (extensionContext, extension) {
-                        return _this._loader._loadTextureAsync(context, textureInfo, function (babylonTexture) {
+                        return _this._loader._loadTextureInfoAsync(context, textureInfo, function (babylonTexture) {
                             if (extension.offset) {
                                 babylonTexture.uOffset = extension.offset[0];
                                 babylonTexture.vOffset = extension.offset[1];
@@ -6231,6 +6249,113 @@ var BABYLON;
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
 
+
+
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
+            var NAME = "EXT_lights_imageBased";
+            /**
+             * [Specification](TODO) (Experimental)
+             */
+            var EXT_lights_imageBased = /** @class */ (function (_super) {
+                __extends(EXT_lights_imageBased, _super);
+                function EXT_lights_imageBased() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                EXT_lights_imageBased.prototype._loadSceneAsync = function (context, scene) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, scene, function (extensionContext, extension) {
+                        var promises = new Array();
+                        promises.push(_this._loader._loadSceneAsync(context, scene));
+                        _this._loader._parent._logOpen("" + extensionContext);
+                        var light = GLTF2.GLTFLoader._GetProperty(extensionContext + "/light", _this._lights, extension.light);
+                        promises.push(_this._loadLightAsync("#/extensions/" + _this.name + "/lights/" + extension.light, light).then(function (texture) {
+                            _this._loader._babylonScene.environmentTexture = texture;
+                        }));
+                        _this._loader._parent._logClose();
+                        return Promise.all(promises).then(function () { });
+                    });
+                };
+                EXT_lights_imageBased.prototype._loadLightAsync = function (context, light) {
+                    var _this = this;
+                    if (!light._loaded) {
+                        var promises = new Array();
+                        this._loader._parent._logOpen("" + context);
+                        var imageData_1 = new Array(light.specularImages.length);
+                        var _loop_1 = function (mipmap) {
+                            var faces = light.specularImages[mipmap];
+                            imageData_1[mipmap] = new Array(faces.length);
+                            var _loop_2 = function (face) {
+                                var specularImageContext = context + "/specularImages/" + mipmap + "/" + face;
+                                this_1._loader._parent._logOpen("" + specularImageContext);
+                                var index = faces[face];
+                                var image = GLTF2.GLTFLoader._GetProperty(specularImageContext, this_1._loader._gltf.images, index);
+                                promises.push(this_1._loader._loadImageAsync("#/images/" + index, image).then(function (data) {
+                                    imageData_1[mipmap][face] = data;
+                                }));
+                                this_1._loader._parent._logClose();
+                            };
+                            for (var face = 0; face < faces.length; face++) {
+                                _loop_2(face);
+                            }
+                        };
+                        var this_1 = this;
+                        for (var mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                            _loop_1(mipmap);
+                        }
+                        this._loader._parent._logClose();
+                        light._loaded = Promise.all(promises).then(function () {
+                            var size = Math.pow(2, imageData_1.length - 1);
+                            var babylonTexture = new BABYLON.RawCubeTexture(_this._loader._babylonScene, null, size);
+                            light._babylonTexture = babylonTexture;
+                            if (light.intensity != undefined) {
+                                babylonTexture.level = light.intensity;
+                            }
+                            if (light.rotation) {
+                                var rotation = BABYLON.Quaternion.FromArray(light.rotation);
+                                // Invert the rotation so that positive rotation is counter-clockwise.
+                                if (!_this._loader._babylonScene.useRightHandedSystem) {
+                                    rotation = BABYLON.Quaternion.Inverse(rotation);
+                                }
+                                BABYLON.Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                            }
+                            var sphericalHarmonics = BABYLON.SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                            sphericalHarmonics.scale(light.intensity);
+                            sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                            var sphericalPolynomial = BABYLON.SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+                            return babylonTexture.updateRGBDAsync(imageData_1, sphericalPolynomial);
+                        });
+                    }
+                    return light._loaded.then(function () {
+                        return light._babylonTexture;
+                    });
+                };
+                Object.defineProperty(EXT_lights_imageBased.prototype, "_lights", {
+                    get: function () {
+                        var extensions = this._loader._gltf.extensions;
+                        if (!extensions || !extensions[this.name]) {
+                            throw new Error("#/extensions: '" + this.name + "' not found");
+                        }
+                        var extension = extensions[this.name];
+                        return extension.lights;
+                    },
+                    enumerable: true,
+                    configurable: true
+                });
+                return EXT_lights_imageBased;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.EXT_lights_imageBased = EXT_lights_imageBased;
+            GLTF2.GLTFLoader._Register(NAME, function (loader) { return new EXT_lights_imageBased(loader); });
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
     
 
     return BABYLON;

File diff suppressed because it is too large
+ 4 - 4
dist/preview release/loaders/babylonjs.loaders.min.js


+ 34 - 6
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -1038,7 +1038,7 @@ declare module BABYLON.GLTF2 {
     }
     /** @hidden */
     interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     /** @hidden */
     interface _ILoaderMaterial extends IMaterial, _IArrayItem {
@@ -1111,6 +1111,10 @@ declare module BABYLON.GLTF2 {
  */
 declare module BABYLON.GLTF2 {
     /** @hidden */
+    class _ArrayItem {
+        static Assign(values?: _IArrayItem[]): void;
+    }
+    /** @hidden */
     class GLTFLoader implements IGLTFLoader {
         _parent: GLTFFileLoader;
         _gltf: _ILoaderGLTF;
@@ -1186,9 +1190,10 @@ declare module BABYLON.GLTF2 {
         _createMaterial(name: string, drawMode: number): PBRMaterial;
         _loadMaterialBasePropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void>;
         _loadMaterialAlphaProperties(context: string, material: _ILoaderMaterial, babylonMaterial: PBRMaterial): void;
-        _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void>;
+        _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void>;
+        _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void>;
         private _loadSampler(context, sampler);
-        private _loadImageAsync(context, image);
+        _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView>;
         _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView>;
         private _onProgress();
         static _GetProperty<T>(context: string, array: ArrayLike<T> | undefined, index: number | undefined): T;
@@ -1254,10 +1259,15 @@ declare module BABYLON.GLTF2 {
          */
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Override this method to modify the default behavior for loading uris.
          * @hidden
@@ -1299,10 +1309,15 @@ declare module BABYLON.GLTF2 {
          */
         static _LoadMaterialPropertiesAsync(loader: GLTFLoader, context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
         /**
          * Helper method called by the loader to allow extensions to override loading uris.
          * @hidden
@@ -1434,6 +1449,19 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_texture_transform extends GLTFLoaderExtension {
         readonly name: string;
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>>;
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    class EXT_lights_imageBased extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>>;
+        private _loadLightAsync(context, light);
+        private readonly _lights;
     }
 }

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

@@ -1,7 +1,7 @@
 {
-  "errors": 4283,
+  "errors": 4233,
   "babylon.typedoc.json": {
-    "errors": 4283,
+    "errors": 4233,
     "AbstractScene": {
       "Property": {
         "effectLayers": {
@@ -8451,23 +8451,6 @@
             }
           }
         },
-        "render": {
-          "Comments": {
-            "MissingReturn": true
-          },
-          "Parameter": {
-            "subMesh": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "enableAlphaMode": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
         "setMaterialByID": {
           "Comments": {
             "MissingReturn": true
@@ -16650,243 +16633,6 @@
         }
       }
     },
-    "SphericalHarmonics": {
-      "Class": {
-        "Comments": {
-          "MissingText": true
-        }
-      },
-      "Property": {
-        "L00": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L10": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L11": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L1_1": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L20": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L21": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L22": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L2_1": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "L2_2": {
-          "Naming": {
-            "NotCamelCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
-      "Method": {
-        "addLight": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "direction": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "color": {
-              "Comments": {
-                "MissingText": true
-              }
-            },
-            "deltaSolidAngle": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "convertIncidentRadianceToIrradiance": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "convertIrradianceToLambertianRadiance": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "scale": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "scale": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getsphericalHarmonicsFromPolynomial": {
-          "Naming": {
-            "NotPascalCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "polynomial": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
-    "SphericalPolynomial": {
-      "Class": {
-        "Comments": {
-          "MissingText": true
-        }
-      },
-      "Property": {
-        "x": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "xx": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "xy": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "y": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "yy": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "yz": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "z": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "zx": {
-          "Comments": {
-            "MissingText": true
-          }
-        },
-        "zz": {
-          "Comments": {
-            "MissingText": true
-          }
-        }
-      },
-      "Method": {
-        "addAmbient": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "color": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "scale": {
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "scale": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        },
-        "getSphericalPolynomialFromHarmonics": {
-          "Naming": {
-            "NotPascalCase": true
-          },
-          "Comments": {
-            "MissingText": true
-          },
-          "Parameter": {
-            "harmonics": {
-              "Comments": {
-                "MissingText": true
-              }
-            }
-          }
-        }
-      }
-    },
     "Sprite": {
       "Class": {
         "Comments": {

File diff suppressed because it is too large
+ 56 - 56
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 852 - 365
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -59,9 +59,10 @@
 - `Sound` now accepts `MediaStream` as source to enable easier WebAudio and WebRTC integrations ([menduz](https://github.com/menduz))
 - Vector x, y and z constructor parameters are now optional and default to 0 ([TrevorDev](https://github.com/TrevorDev))
 - New vertical mode for sliders in 2D GUI. [Demo](https://www.babylonjs-playground.com/#U9AC0N#53) ([Saket Saurabh](https://github.com/ssaket))
-- Added and remove camera methods in the default pipeline ([TrevorDev](https://github.com/TrevorDev))
+- Added and removed camera methods in the default pipeline ([TrevorDev](https://github.com/TrevorDev))
 - Added internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 - Added canvas toBlob polyfill in tools ([sebavan](http://www.github.com/sebavan))
+- Added `RawCubeTexture` class with RGBD and mipmap support ([bghgary](http://www.github.com/bghgary))
 - Added effect layer per rendering group addressing [Issue 4463](https://github.com/BabylonJS/Babylon.js/issues/4463) ([sebavan](http://www.github.com/sebavan))
 
 ### glTF Loader
@@ -70,6 +71,7 @@
 - Added `onNodeLODsLoadedObservable` and `onMaterialLODsLoadedObservable` to MSFT_lod loader extension ([bghgary](http://www.github.com/bghgary))
 - Added glTF loader settings to the GLTF tab in the debug layer ([bghgary](http://www.github.com/bghgary))
 - Added debug logging and performance counters ([bghgary](http://www.github.com/bghgary))
+- Added support for EXT_lights_imageBased ([bghgary](http://www.github.com/bghgary))
 
 ### Viewer
 
@@ -84,6 +86,7 @@
 - An initial support for WebVR is implemented ([RaananW](https://github.com/RaananW))
 - It is now possible to choose the element that goes fullscreen in the default viewer ([RaananW](https://github.com/RaananW))
 - The default viewer has a plugin system with which new buttons can be added externally ([RaananW](https://github.com/RaananW))
+- The extended configuration is now the default when not providing the "extended" parameter ([RaananW](https://github.com/RaananW))
 
 ### Documentation
 

+ 122 - 0
loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts

@@ -0,0 +1,122 @@
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Extensions {
+    const NAME = "EXT_lights_imageBased";
+
+    interface ILightReference {
+        light: number;
+    }
+
+    interface ILight extends IChildRootProperty {
+        intensity: number;
+        rotation: number[];
+        specularImages: number[][];
+        irradianceCoefficients: number[][];
+
+        _babylonTexture?: BaseTexture;
+        _loaded?: Promise<void>;
+    }
+
+    interface ILights {
+        lights: ILight[];
+    }
+
+    /**
+     * [Specification](TODO) (Experimental)
+     */
+    export class EXT_lights_imageBased extends GLTFLoaderExtension {
+        public readonly name = NAME;
+
+        protected _loadSceneAsync(context: string, scene: _ILoaderScene): Nullable<Promise<void>> { 
+            return this._loadExtensionAsync<ILightReference>(context, scene, (extensionContext, extension) => {
+                const promises = new Array<Promise<void>>();
+
+                promises.push(this._loader._loadSceneAsync(context, scene));
+
+                this._loader._parent._logOpen(`${extensionContext}`);
+
+                const light = GLTFLoader._GetProperty(`${extensionContext}/light`, this._lights, extension.light);
+                promises.push(this._loadLightAsync(`#/extensions/${this.name}/lights/${extension.light}`, light).then(texture => {
+                    this._loader._babylonScene.environmentTexture = texture;
+                }));
+
+                this._loader._parent._logClose();
+
+                return Promise.all(promises).then(() => {});
+            });
+        }
+
+        private _loadLightAsync(context: string, light: ILight): Promise<BaseTexture> {
+            if (!light._loaded) {
+                const promises = new Array<Promise<void>>();
+
+                this._loader._parent._logOpen(`${context}`);
+
+                const imageData = new Array<Array<ArrayBufferView>>(light.specularImages.length);
+                for (let mipmap = 0; mipmap < light.specularImages.length; mipmap++) {
+                    const faces = light.specularImages[mipmap];
+                    imageData[mipmap] = new Array<ArrayBufferView>(faces.length);
+                    for (let face = 0; face < faces.length; face++) {
+                        const specularImageContext = `${context}/specularImages/${mipmap}/${face}`;
+                        this._loader._parent._logOpen(`${specularImageContext}`);
+
+                        const index = faces[face];
+                        const image = GLTFLoader._GetProperty(specularImageContext, this._loader._gltf.images, index);
+                        promises.push(this._loader._loadImageAsync(`#/images/${index}`, image).then(data => {
+                            imageData[mipmap][face] = data;
+                        }));
+
+                        this._loader._parent._logClose();
+                    }
+                }
+
+                this._loader._parent._logClose();
+
+                light._loaded = Promise.all(promises).then(() => {
+                    const size = Math.pow(2, imageData.length - 1);
+                    const babylonTexture = new RawCubeTexture(this._loader._babylonScene, null, size);
+                    light._babylonTexture = babylonTexture;
+
+                    if (light.intensity != undefined) {
+                        babylonTexture.level = light.intensity;
+                    }
+
+                    if (light.rotation) {
+                        let rotation = Quaternion.FromArray(light.rotation);
+
+                        // Invert the rotation so that positive rotation is counter-clockwise.
+                        if (!this._loader._babylonScene.useRightHandedSystem) {
+                            rotation = Quaternion.Inverse(rotation);
+                        }
+
+                        Matrix.FromQuaternionToRef(rotation, babylonTexture.getReflectionTextureMatrix());
+                    }
+
+                    const sphericalHarmonics = SphericalHarmonics.FromArray(light.irradianceCoefficients);
+                    sphericalHarmonics.scale(light.intensity);
+
+                    sphericalHarmonics.convertIrradianceToLambertianRadiance();
+                    const sphericalPolynomial = SphericalPolynomial.FromHarmonics(sphericalHarmonics);
+
+                    return babylonTexture.updateRGBDAsync(imageData, sphericalPolynomial);
+                });
+            }
+
+            return light._loaded.then(() => {
+                return light._babylonTexture!;
+            });
+        }
+
+        private get _lights(): Array<ILight> {
+            const extensions = this._loader._gltf.extensions;
+            if (!extensions || !extensions[this.name]) {
+                throw new Error(`#/extensions: '${this.name}' not found`);
+            }
+
+            const extension = extensions[this.name] as ILights;
+            return extension.lights;
+        }
+    }
+
+    GLTFLoader._Register(NAME, loader => new EXT_lights_imageBased(loader));
+}

+ 5 - 7
loaders/src/glTF/2.0/Extensions/KHR_lights.ts

@@ -18,11 +18,10 @@ module BABYLON.GLTF2.Extensions {
         type: LightType;
         color?: number[];
         intensity?: number;
-    }
-
-    interface ISpotLight extends ILight {
-        innerConeAngle?: number;
-        outerConeAngle?: number;
+        spot?: {
+            innerConeAngle?: number;
+            outerConeAngle?: number;
+        };
     }
 
     interface ILights {
@@ -71,10 +70,9 @@ module BABYLON.GLTF2.Extensions {
                         break;
                     }
                     case LightType.SPOT: {
-                        const spotLight = light as ISpotLight;
                         // TODO: support inner and outer cone angles
                         //const innerConeAngle = spotLight.innerConeAngle || 0;
-                        const outerConeAngle = spotLight.outerConeAngle || Math.PI / 4;
+                        const outerConeAngle = light.spot && light.spot.outerConeAngle || Math.PI / 4;
                         babylonLight = new SpotLight(name, Vector3.Zero(), Vector3.Forward(), outerConeAngle, 2, this._loader._babylonScene);
                         break;
                     }

+ 2 - 2
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -41,13 +41,13 @@ module BABYLON.GLTF2.Extensions {
             babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
 
             if (properties.diffuseTexture) {
-                promises.push(this._loader._loadTextureAsync(`${context}/diffuseTexture`, properties.diffuseTexture, texture => {
+                promises.push(this._loader._loadTextureInfoAsync(`${context}/diffuseTexture`, properties.diffuseTexture, texture => {
                     babylonMaterial.albedoTexture = texture;
                 }));
             }
 
             if (properties.specularGlossinessTexture) {
-                promises.push(this._loader._loadTextureAsync(`${context}/specularGlossinessTexture`, properties.specularGlossinessTexture, texture => {
+                promises.push(this._loader._loadTextureInfoAsync(`${context}/specularGlossinessTexture`, properties.specularGlossinessTexture, texture => {
                     babylonMaterial.reflectivityTexture = texture;
                 }));
 

+ 1 - 1
loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts

@@ -35,7 +35,7 @@ module BABYLON.GLTF2.Extensions {
                 }
 
                 if (properties.baseColorTexture) {
-                    promises.push(this._loader._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
+                    promises.push(this._loader._loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                     }));
                 }

+ 2 - 2
loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts

@@ -16,9 +16,9 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_texture_transform extends GLTFLoaderExtension {
         public readonly name = NAME;
 
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> {
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRTextureTransform>(context, textureInfo, (extensionContext, extension) => {
-                return this._loader._loadTextureAsync(context, textureInfo, babylonTexture => {
+                return this._loader._loadTextureInfoAsync(context, textureInfo, babylonTexture => {
                     if (extension.offset) {
                         babylonTexture.uOffset = extension.offset[0];
                         babylonTexture.vOffset = extension.offset[1];

+ 51 - 43
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -10,7 +10,8 @@ module BABYLON.GLTF2 {
         _total?: number;
     }
 
-    class _ArrayItem {
+    /** @hidden */
+    export class _ArrayItem {
         public static Assign(values?: _IArrayItem[]): void {
             if (values) {
                 for (let index = 0; index < values.length; index++) {
@@ -320,9 +321,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadSceneAsync(context: string, scene: _ILoaderScene): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadSceneAsync(this, context, scene);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             const promises = new Array<Promise<void>>();
@@ -436,9 +437,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadNodeAsync(context: string, node: _ILoaderNode): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadNodeAsync(this, context, node);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadNodeAsync(this, context, node);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             if (node._babylonMesh) {
@@ -549,9 +550,9 @@ module BABYLON.GLTF2 {
         }
 
         private _loadVertexDataAsync(context: string, primitive: _ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<Geometry> {
-            const promise = GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             const attributes = primitive.attributes;
@@ -1254,13 +1255,13 @@ module BABYLON.GLTF2 {
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
 
                 if (properties.baseColorTexture) {
-                    promises.push(this._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
+                    promises.push(this._loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                     }));
                 }
 
                 if (properties.metallicRoughnessTexture) {
-                    promises.push(this._loadTextureAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
+                    promises.push(this._loadTextureInfoAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
                         babylonMaterial.metallicTexture = texture;
                     }));
 
@@ -1276,9 +1277,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, mesh, babylonMesh, babylonDrawMode, assign);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             material._babylonData = material._babylonData || {};
@@ -1315,9 +1316,9 @@ module BABYLON.GLTF2 {
         }
 
         public _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadMaterialPropertiesAsync(this, context, material, babylonMaterial);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             const promises = new Array<Promise<void>>();
@@ -1346,7 +1347,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.normalTexture) {
-                promises.push(this._loadTextureAsync(`${context}/normalTexture`, material.normalTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/normalTexture`, material.normalTexture, texture => {
                     babylonMaterial.bumpTexture = texture;
                 }));
 
@@ -1358,7 +1359,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.occlusionTexture) {
-                promises.push(this._loadTextureAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
                     babylonMaterial.ambientTexture = texture;
                 }));
 
@@ -1369,7 +1370,7 @@ module BABYLON.GLTF2 {
             }
 
             if (material.emissiveTexture) {
-                promises.push(this._loadTextureAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
                     babylonMaterial.emissiveTexture = texture;
                 }));
             }
@@ -1406,16 +1407,30 @@ module BABYLON.GLTF2 {
             }
         }
 
-        public _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadTextureAsync(this, context, textureInfo, assign);
-            if (promise) {
-                return promise;
+        public _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Promise<void> {
+            const extensionPromise = GLTFLoaderExtension._LoadTextureInfoAsync(this, context, textureInfo, assign);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             this._parent._logOpen(`${context}`);
 
             const texture = GLTFLoader._GetProperty(`${context}/index`, this._gltf.textures, textureInfo.index);
-            context = `#/textures/${textureInfo.index}`;
+            const promise = this._loadTextureAsync(`#/textures/${textureInfo.index}`, texture, babylonTexture => {
+                babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
+                assign(babylonTexture);
+            });
+
+            this._parent._logClose();
+
+            return promise;
+        }
+
+        public _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Promise<void> {
+            const extensionPromise = GLTFLoaderExtension._LoadTextureAsync(this, context, texture, assign);
+            if (extensionPromise) {
+                return extensionPromise;
+            }
 
             const promises = new Array<Promise<void>>();
 
@@ -1439,19 +1454,17 @@ module BABYLON.GLTF2 {
             babylonTexture.name = texture.name || `texture${texture._index}`;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapV = samplerData.wrapV;
-            babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
 
             const image = GLTFLoader._GetProperty(`${context}/source`, this._gltf.images, texture.source);
-            promises.push(this._loadImageAsync(`#/images/${image._index}`, image).then(blob => {
+            promises.push(this._loadImageAsync(`#/images/${image._index}`, image).then(data => {
                 const dataUrl = `data:${this._rootUrl}${image.uri || `image${image._index}`}`;
-                babylonTexture.updateURL(dataUrl, blob);
+                babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
             }));
 
             assign(babylonTexture);
             this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
 
             this._parent._logClose();
-            this._parent._logClose();
 
             return Promise.all(promises).then(() => {});
         }
@@ -1469,33 +1482,28 @@ module BABYLON.GLTF2 {
             return sampler._data;
         }
 
-        private _loadImageAsync(context: string, image: _ILoaderImage): Promise<Blob> {
-            if (!image._blob) {
+        public _loadImageAsync(context: string, image: _ILoaderImage): Promise<ArrayBufferView> {
+            if (!image._data) {
                 this._parent._logOpen(`${context} ${image.name || ""}`);
 
-                let promise: Promise<ArrayBufferView>;
                 if (image.uri) {
-                    promise = this._loadUriAsync(context, image.uri);
+                    image._data = this._loadUriAsync(context, image.uri);
                 }
                 else {
                     const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
-                    promise = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView);
+                    image._data = this._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView);
                 }
 
-                image._blob = promise.then(data => {
-                    return new Blob([data], { type: image.mimeType });
-                });
-
                 this._parent._logClose();
             }
 
-            return image._blob;
+            return image._data;
         }
 
         public _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView> {
-            const promise = GLTFLoaderExtension._LoadUriAsync(this, context, uri);
-            if (promise) {
-                return promise;
+            const extensionPromise = GLTFLoaderExtension._LoadUriAsync(this, context, uri);
+            if (extensionPromise) {
+                return extensionPromise;
             }
 
             if (!GLTFLoader._ValidateUri(uri)) {

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

@@ -65,10 +65,16 @@ module BABYLON.GLTF2 {
         protected _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Nullable<Promise<void>> { return null; }
 
         /**
+         * Override this method to modify the default behavior for loading texture infos.
+         * @hidden
+         */
+        protected _loadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> { return null; }
+
+        /**
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          */
-        protected _loadTextureAsync(context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> { return null; }
+        protected _loadTextureAsync(context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> { return null; }
 
         /**
          * Override this method to modify the default behavior for loading uris.
@@ -175,11 +181,19 @@ module BABYLON.GLTF2 {
         }
 
         /**
+         * Helper method called by the loader to allow extensions to override loading texture infos.
+         * @hidden
+         */
+        public static _LoadTextureInfoAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadTextureInfoAsync(context, textureInfo, assign));
+        }
+
+        /**
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          */
-        public static _LoadTextureAsync(loader: GLTFLoader, context: string, textureInfo: ITextureInfo, assign: (texture: Texture) => void): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadTextureAsync(context, textureInfo, assign));
+        public static _LoadTextureAsync(loader: GLTFLoader, context: string, texture: _ILoaderTexture, assign: (babylonTexture: Texture) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadTextureAsync(context, texture, assign));
         }
 
         /**

+ 1 - 1
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -54,7 +54,7 @@ module BABYLON.GLTF2 {
 
     /** @hidden */
     export interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
 
     /** @hidden */

BIN
sandbox/Assets/environment.dds


+ 11 - 5
sandbox/index.js

@@ -53,7 +53,7 @@ if (BABYLON.Engine.isSupported()) {
     var currentScene;
     var currentSkybox;
     var currentPluginName;
-    var skyboxPath = "Assets/environment.dds";
+    var skyboxPath = "https://assets.babylonjs.com/environments/environmentSpecular.env";
     var debugLayerEnabled = false;
     var debugLayerLastActiveTab = 0;
 
@@ -126,7 +126,7 @@ if (BABYLON.Engine.isSupported()) {
 
         // Attach camera to canvas inputs
         if (!currentScene.activeCamera || currentScene.lights.length === 0) {
-            currentScene.createDefaultCameraOrLight(true);
+            currentScene.createDefaultCamera(true);
 
             if (cameraPosition) {
                 currentScene.activeCamera.setPosition(cameraPosition);
@@ -160,10 +160,16 @@ if (BABYLON.Engine.isSupported()) {
 
         currentScene.activeCamera.attachControl(canvas);
 
-        // Environment
+        // Lighting
         if (currentPluginName === "gltf") {
-            var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(skyboxPath, currentScene);
-            currentSkybox = currentScene.createDefaultSkybox(hdrTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3);
+            if (!currentScene.environmentTexture) {
+                currentScene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(skyboxPath, currentScene);
+            }
+
+            currentSkybox = currentScene.createDefaultSkybox(currentScene.environmentTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3, false);
+        }
+        else {
+            currentScene.createDefaultLight();
         }
 
         // In case of error during loading, meshes will be empty and clearColor is set to red

+ 16 - 11
src/Engine/babylon.engine.ts

@@ -781,6 +781,9 @@
          */
         public postProcesses = new Array<PostProcess>();
 
+        /** Gets or sets a boolean indicating if the engine should validate programs after compilation */
+        public validateShaderPrograms = false;
+
         // Observables
 
         /**
@@ -3543,13 +3546,15 @@
                 }
             }
 
-            context.validateProgram(shaderProgram);
-            var validated = context.getProgramParameter(shaderProgram, context.VALIDATE_STATUS);
+            if (this.validateShaderPrograms) {
+                context.validateProgram(shaderProgram);
+                var validated = context.getProgramParameter(shaderProgram, context.VALIDATE_STATUS);
 
-            if(!validated) {
-                var error = context.getProgramInfoLog(shaderProgram);
-                if (error) {
-                    throw new Error(error);
+                if(!validated) {
+                    var error = context.getProgramInfoLog(shaderProgram);
+                    if (error) {
+                        throw new Error(error);
+                    }
                 }
             }
 
@@ -5654,8 +5659,8 @@
                         texture.width = info.width;
                         texture.height = info.width;
 
-                        EnvironmentTextureTools.UploadPolynomials(texture, data, info!);
-                        EnvironmentTextureTools.UploadLevelsAsync(texture, data, info!).then(() => {
+                        EnvironmentTextureTools.UploadEnvSpherical(texture, info);
+                        EnvironmentTextureTools.UploadEnvLevelsAsync(texture, data, info).then(() => {
                             texture.isReady = true;
                             if (onLoad) {
                                 onLoad();
@@ -5874,15 +5879,15 @@
          * @param data defines the array of data to use to create each face
          * @param size defines the size of the textures
          * @param format defines the format of the data
-         * @param type defines the type fo the data (like BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT)
+         * @param type defines the type of the data (like BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT)
          * @param generateMipMaps  defines if the engine should generate the mip levels
          * @param invertY defines if data must be stored with Y axis inverted
          * @param samplingMode defines the required sampling mode (like BABYLON.Texture.NEAREST_SAMPLINGMODE)
          * @param compression defines the compression used (null by default)
          * @returns the cube texture as an InternalTexture
          */
-        public createRawCubeTexture(data: Nullable<ArrayBufferView[]>, size: number, format: number, type: number, 
-                                    generateMipMaps: boolean, invertY: boolean, samplingMode: number, 
+        public createRawCubeTexture(data: Nullable<ArrayBufferView[]>, size: number, format: number, type: number,
+                                    generateMipMaps: boolean, invertY: boolean, samplingMode: number,
                                     compression: Nullable<string> = null): InternalTexture {
             var gl = this._gl;
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBERAW);

+ 1 - 1
src/Materials/Textures/babylon.cubeTexture.ts

@@ -1,7 +1,6 @@
 module BABYLON {
     export class CubeTexture extends BaseTexture {
         public url: string;
-        public coordinatesMode = Texture.CUBIC_MODE;
 
         /**
          * Gets or sets the center of the bounding box associated with the cube texture
@@ -110,6 +109,7 @@
             this.isCube = true;
             this._textureMatrix = Matrix.Identity();
             this._createPolynomials = createPolynomials;
+            this.coordinatesMode = Texture.CUBIC_MODE;
 
             if (!rootUrl && !files) {
                 return;

+ 16 - 3
src/Materials/Textures/babylon.internalTexture.ts

@@ -53,6 +53,10 @@ module BABYLON {
          * Texture content is a depth texture
          */
         public static DATASOURCE_DEPTHTEXTURE = 11;
+        /**
+         * Texture data comes from a raw cube data encoded with RGBD
+         */
+        public static DATASOURCE_CUBERAW_RGBD = 12;
 
         /**
          * Defines if the texture is ready
@@ -146,6 +150,8 @@ module BABYLON {
         /** @hidden */
         public _bufferViewArray: Nullable<ArrayBufferView[]>;
         /** @hidden */
+        public _bufferViewArrayArray: Nullable<ArrayBufferView[][]>;
+        /** @hidden */
         public _size: number;
         /** @hidden */
         public _extension: string;
@@ -186,7 +192,7 @@ module BABYLON {
         /** @hidden */
         public _comparisonFunction: number = 0;
         /** @hidden */
-        public _sphericalPolynomial: Nullable<SphericalPolynomial>;
+        public _sphericalPolynomial: Nullable<SphericalPolynomial> = null;
         /** @hidden */
         public _lodGenerationScale: number = 0;
         /** @hidden */
@@ -350,12 +356,19 @@ module BABYLON {
                     return;
 
                 case InternalTexture.DATASOURCE_CUBERAW:
-                    proxy = this._engine.createRawCubeTexture(this._bufferViewArray, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
+                    proxy = this._engine.createRawCubeTexture(this._bufferViewArray!, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
                     proxy._swapAndDie(this);
-
                     this.isReady = true;
                     return;
 
+                case InternalTexture.DATASOURCE_CUBERAW_RGBD:
+                    proxy = this._engine.createRawCubeTexture(null, this.width, this.format, this.type, this.generateMipMaps, this.invertY, this.samplingMode, this._compression);
+                    RawCubeTexture._UpdateRGBDAsync(proxy, this._bufferViewArrayArray!, this._sphericalPolynomial, this._lodGenerationScale, this._lodGenerationOffset).then(() => {
+                        this.isReady = true;
+                    });
+                    proxy._swapAndDie(this);
+                    return;
+
                 case InternalTexture.DATASOURCE_CUBEPREFILTERED:
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, (proxy) => {
                         if (proxy) {

+ 85 - 0
src/Materials/Textures/babylon.rawCubeTexture.ts

@@ -0,0 +1,85 @@
+module BABYLON {
+    /**
+     * Raw cube texture where the raw buffers are passed in
+     */
+    export class RawCubeTexture extends CubeTexture {
+        /**
+         * Creates a cube texture where the raw buffers are passed in.
+         * @param scene defines the scene the texture is attached to
+         * @param data defines the array of data to use to create each face
+         * @param size defines the size of the textures
+         * @param format defines the format of the data
+         * @param type defines the type of the data (like BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT)
+         * @param generateMipMaps  defines if the engine should generate the mip levels
+         * @param invertY defines if data must be stored with Y axis inverted
+         * @param samplingMode defines the required sampling mode (like BABYLON.Texture.NEAREST_SAMPLINGMODE)
+         * @param compression defines the compression used (null by default)
+         */
+        constructor(scene: Scene, data: Nullable<ArrayBufferView[]>, size: number,
+                    format: number = Engine.TEXTUREFORMAT_RGBA, type: number = Engine.TEXTURETYPE_UNSIGNED_INT,
+                    generateMipMaps: boolean = false, invertY: boolean = false, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE,
+                    compression: Nullable<string> = null) {
+            super("", scene);
+
+            this._texture = scene.getEngine().createRawCubeTexture(data, size, format, type, generateMipMaps, invertY, samplingMode, compression);
+        }
+
+        /**
+         * Updates the raw cube texture.
+         * @param data defines the data to store
+         * @param format defines the data format
+         * @param type defines the type fo the data (BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT by default)
+         * @param invertY defines if data must be stored with Y axis inverted
+         * @param compression defines the compression used (null by default)
+         * @param level defines which level of the texture to update
+         */
+        public update(data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable<string> = null, level = 0): void {
+            this._texture!.getEngine().updateRawCubeTexture(this._texture!, data, format, type, invertY, compression);
+        }
+
+        /**
+         * Updates a raw cube texture with RGBD encoded data.
+         * @param data defines the array of data [mipmap][face] to use to create each face
+         * @param sphericalPolynomial defines the spherical polynomial for irradiance
+         * @param lodScale defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness
+         * @param lodOffset defines the offset applied to environment texture. This manages first LOD level used for IBL according to the roughness
+         * @returns a promsie that resolves when the operation is complete
+         */
+        public updateRGBDAsync(data: ArrayBufferView[][], sphericalPolynomial: Nullable<SphericalPolynomial> = null, lodScale: number = 0.8, lodOffset: number = 0): Promise<void> {
+            return RawCubeTexture._UpdateRGBDAsync(this._texture!, data, sphericalPolynomial, lodScale, lodOffset);
+        }
+
+        /**
+         * Clones the raw cube texture.
+         * @return a new cube texture
+         */
+        public clone(): CubeTexture {
+            return SerializationHelper.Clone(() => {
+                const scene = this.getScene()!;
+                const internalTexture = this._texture!;
+
+                const texture = new RawCubeTexture(scene, internalTexture._bufferViewArray!, internalTexture.width, internalTexture.format, internalTexture.type,
+                    internalTexture.generateMipMaps, internalTexture.invertY, internalTexture.samplingMode, internalTexture._compression);
+
+                if (internalTexture.dataSource === InternalTexture.DATASOURCE_CUBERAW_RGBD) {
+                    texture.updateRGBDAsync(internalTexture._bufferViewArrayArray!, internalTexture._sphericalPolynomial, internalTexture._lodGenerationScale, internalTexture._lodGenerationOffset);
+                }
+
+                return texture;
+            }, this);
+        }
+
+        /** @hidden */
+        public static _UpdateRGBDAsync(internalTexture: InternalTexture, data: ArrayBufferView[][], sphericalPolynomial: Nullable<SphericalPolynomial>, lodScale: number, lodOffset: number): Promise<void> {
+            internalTexture._dataSource = InternalTexture.DATASOURCE_CUBERAW_RGBD;
+            internalTexture._bufferViewArrayArray = data;
+            internalTexture._lodGenerationScale = lodScale;
+            internalTexture._lodGenerationOffset = lodOffset;
+            internalTexture._sphericalPolynomial = sphericalPolynomial;
+
+            return EnvironmentTextureTools.UploadLevelsAsync(internalTexture, data).then(() => {
+                internalTexture.isReady = true;
+            });
+        }
+    }
+}

+ 228 - 79
src/Math/babylon.sphericalPolynomial.ts

@@ -1,15 +1,57 @@
 module BABYLON {
+    /**
+     * Class representing spherical polynomial coefficients to the 3rd degree
+     */
     export class SphericalPolynomial {
+        /**
+         * The x coefficients of the spherical polynomial
+         */
         public x: Vector3 = Vector3.Zero();
+
+        /**
+         * The y coefficients of the spherical polynomial
+         */
         public y: Vector3 = Vector3.Zero();
+
+        /**
+         * The z coefficients of the spherical polynomial
+         */
         public z: Vector3 = Vector3.Zero();
+
+        /**
+         * The xx coefficients of the spherical polynomial
+         */
         public xx: Vector3 = Vector3.Zero();
+
+        /**
+         * The yy coefficients of the spherical polynomial
+         */
         public yy: Vector3 = Vector3.Zero();
+
+        /**
+         * The zz coefficients of the spherical polynomial
+         */
         public zz: Vector3 = Vector3.Zero();
+
+        /**
+         * The xy coefficients of the spherical polynomial
+         */
         public xy: Vector3 = Vector3.Zero();
+
+        /**
+         * The yz coefficients of the spherical polynomial
+         */
         public yz: Vector3 = Vector3.Zero();
+
+        /**
+         * The zx coefficients of the spherical polynomial
+         */
         public zx: Vector3 = Vector3.Zero();
 
+        /**
+         * Adds an ambient color to the spherical polynomial
+         * @param color the color to add
+         */
         public addAmbient(color: Color3): void {
             var colorVector = new Vector3(color.r, color.g, color.b);
             this.xx = this.xx.add(colorVector);
@@ -17,26 +59,10 @@ module BABYLON {
             this.zz = this.zz.add(colorVector);
         }
 
-        public static getSphericalPolynomialFromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
-            var result = new SphericalPolynomial();
-
-            result.x = harmonics.L11.scale(1.02333);
-            result.y = harmonics.L1_1.scale(1.02333);
-            result.z = harmonics.L10.scale(1.02333);
-
-            result.xx = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).add(harmonics.L22.scale(0.429043));
-            result.yy = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).subtract(harmonics.L22.scale(0.429043));
-            result.zz = harmonics.L00.scale(0.886277).add(harmonics.L20.scale(0.495417));
-
-            result.yz = harmonics.L2_1.scale(0.858086);
-            result.zx = harmonics.L21.scale(0.858086);
-            result.xy = harmonics.L2_2.scale(0.858086);
-
-            result.scale(1.0 / Math.PI);
-
-            return result;
-        }
-
+        /**
+         * Scales the spherical polynomial by the given amount
+         * @param scale the amount to scale
+         */
         public scale(scale: number)
         {
             this.x = this.x.scale(scale);
@@ -49,105 +75,228 @@ module BABYLON {
             this.zx = this.zx.scale(scale);
             this.xy = this.xy.scale(scale);
         }
+
+        /**
+         * Gets the spherical polynomial from harmonics
+         * @param harmonics the spherical harmonics
+         * @returns the spherical polynomial
+         */
+        public static FromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
+            var result = new SphericalPolynomial();
+
+            result.x = harmonics.l11.scale(1.02333);
+            result.y = harmonics.l1_1.scale(1.02333);
+            result.z = harmonics.l10.scale(1.02333);
+
+            result.xx = harmonics.l00.scale(0.886277).subtract(harmonics.l20.scale(0.247708)).add(harmonics.lL22.scale(0.429043));
+            result.yy = harmonics.l00.scale(0.886277).subtract(harmonics.l20.scale(0.247708)).subtract(harmonics.lL22.scale(0.429043));
+            result.zz = harmonics.l00.scale(0.886277).add(harmonics.l20.scale(0.495417));
+
+            result.yz = harmonics.l2_1.scale(0.858086);
+            result.zx = harmonics.l21.scale(0.858086);
+            result.xy = harmonics.l2_2.scale(0.858086);
+
+            result.scale(1.0 / Math.PI);
+
+            return result;
+        }
+
+        /**
+         * Constructs a spherical polynomial from an array.
+         * @param data defines the 9x3 coefficients (x, y, z, xx, yy, zz, yz, zx, xy)
+         * @returns the spherical polynomial
+         */
+        public static FromArray(data: ArrayLike<ArrayLike<number>>): SphericalPolynomial {
+            const sp = new SphericalPolynomial();
+            Vector3.FromArrayToRef(data[0], 0, sp.x);
+            Vector3.FromArrayToRef(data[1], 0, sp.y);
+            Vector3.FromArrayToRef(data[2], 0, sp.z);
+            Vector3.FromArrayToRef(data[3], 0, sp.xx);
+            Vector3.FromArrayToRef(data[4], 0, sp.yy);
+            Vector3.FromArrayToRef(data[5], 0, sp.zz);
+            Vector3.FromArrayToRef(data[6], 0, sp.yz);
+            Vector3.FromArrayToRef(data[7], 0, sp.zx);
+            Vector3.FromArrayToRef(data[8], 0, sp.xy);
+            return sp;
+        }
     }
 
+    /**
+     * Class representing spherical harmonics coefficients to the 3rd degree
+     */
     export class SphericalHarmonics {
-        public L00: Vector3 = Vector3.Zero();
-        public L1_1: Vector3 = Vector3.Zero();
-        public L10: Vector3 = Vector3.Zero();
-        public L11: Vector3 = Vector3.Zero();
-        public L2_2: Vector3 = Vector3.Zero();
-        public L2_1: Vector3 = Vector3.Zero();
-        public L20: Vector3 = Vector3.Zero();
-        public L21: Vector3 = Vector3.Zero();
-        public L22: Vector3 = Vector3.Zero();
+        /**
+         * The l0,0 coefficients of the spherical harmonics
+         */
+        public l00: Vector3 = Vector3.Zero();
+
+        /**
+         * The l1,-1 coefficients of the spherical harmonics
+         */
+        public l1_1: Vector3 = Vector3.Zero();
+
+        /**
+         * The l1,0 coefficients of the spherical harmonics
+         */
+        public l10: Vector3 = Vector3.Zero();
 
+        /**
+         * The l1,1 coefficients of the spherical harmonics
+         */
+        public l11: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,-2 coefficients of the spherical harmonics
+         */
+        public l2_2: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,-1 coefficients of the spherical harmonics
+         */
+        public l2_1: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,0 coefficients of the spherical harmonics
+         */
+        public l20: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,1 coefficients of the spherical harmonics
+         */
+        public l21: Vector3 = Vector3.Zero();
+
+        /**
+         * The l2,2 coefficients of the spherical harmonics
+         */
+        public lL22: Vector3 = Vector3.Zero();
+
+        /**
+         * Adds a light to the spherical harmonics
+         * @param direction the direction of the light
+         * @param color the color of the light
+         * @param deltaSolidAngle the delta solid angle of the light
+         */
         public addLight(direction: Vector3, color: Color3, deltaSolidAngle: number): void {
             var colorVector = new Vector3(color.r, color.g, color.b);
             var c = colorVector.scale(deltaSolidAngle);
 
-            this.L00 = this.L00.add(c.scale(0.282095));
+            this.l00 = this.l00.add(c.scale(0.282095));
 
-            this.L1_1 = this.L1_1.add(c.scale(0.488603 * direction.y));
-            this.L10 = this.L10.add(c.scale(0.488603 * direction.z));
-            this.L11 = this.L11.add(c.scale(0.488603 * direction.x));
+            this.l1_1 = this.l1_1.add(c.scale(0.488603 * direction.y));
+            this.l10 = this.l10.add(c.scale(0.488603 * direction.z));
+            this.l11 = this.l11.add(c.scale(0.488603 * direction.x));
 
-            this.L2_2 = this.L2_2.add(c.scale(1.092548 * direction.x * direction.y));
-            this.L2_1 = this.L2_1.add(c.scale(1.092548 * direction.y * direction.z));
-            this.L21 = this.L21.add(c.scale(1.092548 * direction.x * direction.z));
+            this.l2_2 = this.l2_2.add(c.scale(1.092548 * direction.x * direction.y));
+            this.l2_1 = this.l2_1.add(c.scale(1.092548 * direction.y * direction.z));
+            this.l21 = this.l21.add(c.scale(1.092548 * direction.x * direction.z));
 
-            this.L20 = this.L20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
-            this.L22 = this.L22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
+            this.l20 = this.l20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
+            this.lL22 = this.lL22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
         }
 
+        /**
+         * Scales the spherical harmonics by the given amount
+         * @param scale the amount to scale
+         */
         public scale(scale: number): void {
-            this.L00 = this.L00.scale(scale);
-            this.L1_1 = this.L1_1.scale(scale);
-            this.L10 = this.L10.scale(scale);
-            this.L11 = this.L11.scale(scale);
-            this.L2_2 = this.L2_2.scale(scale);
-            this.L2_1 = this.L2_1.scale(scale);
-            this.L20 = this.L20.scale(scale);
-            this.L21 = this.L21.scale(scale);
-            this.L22 = this.L22.scale(scale);
+            this.l00 = this.l00.scale(scale);
+            this.l1_1 = this.l1_1.scale(scale);
+            this.l10 = this.l10.scale(scale);
+            this.l11 = this.l11.scale(scale);
+            this.l2_2 = this.l2_2.scale(scale);
+            this.l2_1 = this.l2_1.scale(scale);
+            this.l20 = this.l20.scale(scale);
+            this.l21 = this.l21.scale(scale);
+            this.lL22 = this.lL22.scale(scale);
         }
 
+        /**
+         * Convert from incident radiance (Li) to irradiance (E) by applying convolution with the cosine-weighted hemisphere.
+         *
+         * ```
+         * E_lm = A_l * L_lm
+         * ```
+         *
+         * In spherical harmonics this convolution amounts to scaling factors for each frequency band.
+         * This corresponds to equation 5 in "An Efficient Representation for Irradiance Environment Maps", where
+         * the scaling factors are given in equation 9.
+         */
         public convertIncidentRadianceToIrradiance(): void
         {
-            // Convert from incident radiance (Li) to irradiance (E) by applying convolution with the cosine-weighted hemisphere.
-            //
-            //      E_lm = A_l * L_lm
-            // 
-            // In spherical harmonics this convolution amounts to scaling factors for each frequency band.
-            // This corresponds to equation 5 in "An Efficient Representation for Irradiance Environment Maps", where
-            // the scaling factors are given in equation 9.
-
             // Constant (Band 0)
-            this.L00 = this.L00.scale(3.141593);
+            this.l00 = this.l00.scale(3.141593);
 
             // Linear (Band 1)
-            this.L1_1 = this.L1_1.scale(2.094395);
-            this.L10 = this.L10.scale(2.094395);
-            this.L11 = this.L11.scale(2.094395);
+            this.l1_1 = this.l1_1.scale(2.094395);
+            this.l10 = this.l10.scale(2.094395);
+            this.l11 = this.l11.scale(2.094395);
 
             // Quadratic (Band 2)
-            this.L2_2 = this.L2_2.scale(0.785398);
-            this.L2_1 = this.L2_1.scale(0.785398);
-            this.L20 = this.L20.scale(0.785398);
-            this.L21 = this.L21.scale(0.785398);
-            this.L22 = this.L22.scale(0.785398);
+            this.l2_2 = this.l2_2.scale(0.785398);
+            this.l2_1 = this.l2_1.scale(0.785398);
+            this.l20 = this.l20.scale(0.785398);
+            this.l21 = this.l21.scale(0.785398);
+            this.lL22 = this.lL22.scale(0.785398);
         }
 
+        /**
+         * Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
+         *
+         * ```
+         * L = (1/pi) * E * rho
+         * ```
+         *
+         * This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
+         */
         public convertIrradianceToLambertianRadiance(): void
         {
-            // Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
-            //      L = (1/pi) * E * rho
-            // 
-            // This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
-
             this.scale(1.0 / Math.PI);
 
             // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
             // (The pixel shader must apply albedo after texture fetches, etc).
         }
 
-        public static getsphericalHarmonicsFromPolynomial(polynomial: SphericalPolynomial): SphericalHarmonics
+        /**
+         * Gets the spherical harmonics from polynomial
+         * @param polynomial the spherical polynomial
+         * @returns the spherical harmonics
+         */
+        public static FromPolynomial(polynomial: SphericalPolynomial): SphericalHarmonics
         {
             var result = new SphericalHarmonics();
 
-            result.L00 = polynomial.xx.scale(0.376127).add(polynomial.yy.scale(0.376127)).add(polynomial.zz.scale(0.376126));
-            result.L1_1 = polynomial.y.scale(0.977204);
-            result.L10 = polynomial.z.scale(0.977204);
-            result.L11 = polynomial.x.scale(0.977204);
-            result.L2_2 = polynomial.xy.scale(1.16538);
-            result.L2_1 = polynomial.yz.scale(1.16538);
-            result.L20 = polynomial.zz.scale(1.34567).subtract(polynomial.xx.scale(0.672834)).subtract(polynomial.yy.scale(0.672834));
-            result.L21 = polynomial.zx.scale(1.16538);
-            result.L22 = polynomial.xx.scale(1.16538).subtract(polynomial.yy.scale(1.16538));
+            result.l00 = polynomial.xx.scale(0.376127).add(polynomial.yy.scale(0.376127)).add(polynomial.zz.scale(0.376126));
+            result.l1_1 = polynomial.y.scale(0.977204);
+            result.l10 = polynomial.z.scale(0.977204);
+            result.l11 = polynomial.x.scale(0.977204);
+            result.l2_2 = polynomial.xy.scale(1.16538);
+            result.l2_1 = polynomial.yz.scale(1.16538);
+            result.l20 = polynomial.zz.scale(1.34567).subtract(polynomial.xx.scale(0.672834)).subtract(polynomial.yy.scale(0.672834));
+            result.l21 = polynomial.zx.scale(1.16538);
+            result.lL22 = polynomial.xx.scale(1.16538).subtract(polynomial.yy.scale(1.16538));
 
             result.scale(Math.PI);
 
             return result;
         }
+
+        /**
+         * Constructs a spherical harmonics from an array.
+         * @param data defines the 9x3 coefficients (l00, l1-1, l10, l11, l2-2, l2-1, l20, l21, l22)
+         * @returns the spherical harmonics
+         */
+        public static FromArray(data: ArrayLike<ArrayLike<number>>): SphericalHarmonics {
+            const sh = new SphericalHarmonics();
+            Vector3.FromArrayToRef(data[0], 0, sh.l00);
+            Vector3.FromArrayToRef(data[1], 0, sh.l1_1);
+            Vector3.FromArrayToRef(data[2], 0, sh.l10);
+            Vector3.FromArrayToRef(data[3], 0, sh.l11);
+            Vector3.FromArrayToRef(data[4], 0, sh.l2_2);
+            Vector3.FromArrayToRef(data[5], 0, sh.l2_1);
+            Vector3.FromArrayToRef(data[6], 0, sh.l20);
+            Vector3.FromArrayToRef(data[7], 0, sh.l21);
+            Vector3.FromArrayToRef(data[8], 0, sh.lL22);
+            return sh;
+        }
     }
 }

+ 4 - 3
src/Mesh/babylon.mesh.ts

@@ -1285,9 +1285,10 @@
         }
 
         /**
-         * Triggers the draw call for the mesh.
-         * Usually, you don't need to call this method by your own because the mesh rendering is handled by the scene rendering manager.   
-         * Returns the Mesh.   
+         * Triggers the draw call for the mesh. Usually, you don't need to call this method by your own because the mesh rendering is handled by the scene rendering manager
+         * @param subMesh defines the subMesh to render
+         * @param enableAlphaMode defines if alpha mode can be changed
+         * @returns the current mesh
          */
         public render(subMesh: SubMesh, enableAlphaMode: boolean): Mesh {
 

+ 22 - 2
src/Particles/babylon.IParticleSystem.ts

@@ -292,7 +292,7 @@ module BABYLON {
         getSizeGradients(): Nullable<Array<FactorGradient>>;
         /**
          * Gets the current list of angular speed gradients.
-         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
          * @returns the list of angular speed gradients
          */
         getAngularSpeedGradients(): Nullable<Array<FactorGradient>>;   
@@ -309,6 +309,26 @@ module BABYLON {
          * @param gradient defines the gradient to remove
          * @returns the current particle system
          */
-        removeAngularSpeedGradient(gradient: number): IParticleSystem;        
+        removeAngularSpeedGradient(gradient: number): IParticleSystem;   
+        /**
+         * Gets the current list of velocity gradients.
+         * You must use addVelocityGradient and removeVelocityGradient to udpate this list
+         * @returns the list of velocity gradients
+         */
+        getVelocityGradients(): Nullable<Array<FactorGradient>>;
+        /**
+         * Adds a new velocity gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addVelocityGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific velocity gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeVelocityGradient(gradient: number): IParticleSystem;
     }  
 }

+ 69 - 4
src/Particles/babylon.gpuParticleSystem.ts

@@ -447,13 +447,22 @@
         
         /**
          * Gets the current list of angular speed gradients.
-         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
          * @returns the list of angular speed gradients
          */
         public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
             return this._angularSpeedGradients;
         } 
 
+        /**
+         * Gets the current list of velocity gradients.
+         * You must use addVelocityGradient and removeVelocityGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getVelocityGradients(): Nullable<Array<FactorGradient>> {
+            return this._velocityGradients;
+        }         
+
         private _removeGradient(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): GPUParticleSystem {
             if (!gradients) {
                 return this;
@@ -532,6 +541,9 @@
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
         private _sizeGradientsTexture: RawTexture;     
         
+        private _velocityGradients: Nullable<Array<FactorGradient>> = null;
+        private _velocityGradientsTexture: RawTexture;    
+
         private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number) {
             let valueGradient = new FactorGradient();
             valueGradient.gradient = gradient;
@@ -619,7 +631,42 @@
             (<any>this._angularSpeedGradientsTexture) = null;
 
             return this;           
-        }            
+        }           
+        
+        /**
+         * Adds a new velocity gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient    
+         * @returns the current particle system     
+         */
+        public addVelocityGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._velocityGradients) {
+                this._velocityGradients = [];
+            }
+
+            this._addFactorGradient(this._velocityGradients, gradient, factor);
+
+            if (this._velocityGradientsTexture) {
+                this._velocityGradientsTexture.dispose();
+                (<any>this._velocityGradientsTexture) = null;
+            }
+
+            this._releaseBuffers();   
+
+            return this;
+        }
+
+        /**
+         * Remove a specific velocity gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeVelocityGradient(gradient: number): GPUParticleSystem {
+            this._removeGradient(gradient, this._velocityGradients, this._velocityGradientsTexture);
+            (<any>this._velocityGradientsTexture) = null;
+
+            return this;           
+        }          
 
         /**
          * Instantiates a GPU particle system.
@@ -666,7 +713,7 @@
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "angleRange", "radiusRange", "cellInfos"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -910,7 +957,11 @@
 
             if (this._angularSpeedGradientsTexture) {
                 defines += "\n#define ANGULARSPEEDGRADIENTS";
-            }                 
+            }               
+            
+            if (this._velocityGradientsTexture) {
+                defines += "\n#define VELOCITYGRADIENTS";
+            }                    
             
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
@@ -1022,6 +1073,10 @@
         private _createAngularSpeedGradientTexture() {
             this._createFactorGradientTexture(this._angularSpeedGradients, "_angularSpeedGradientsTexture");
         }     
+
+        private _createVelocityGradientTexture() {
+            this._createFactorGradientTexture(this._velocityGradients, "_velocityGradientsTexture");
+        }          
             
         private _createColorGradientTexture() {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
@@ -1061,6 +1116,7 @@
             this._createColorGradientTexture();
             this._createSizeGradientTexture();
             this._createAngularSpeedGradientTexture();
+            this._createVelocityGradientTexture();
 
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
@@ -1128,6 +1184,10 @@
                 this._updateEffect.setTexture("angularSpeedGradientSampler", this._angularSpeedGradientsTexture);      
             }
 
+            if (this._velocityGradientsTexture) {      
+                this._updateEffect.setTexture("velocityGradientSampler", this._velocityGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
@@ -1293,6 +1353,11 @@
                 this._angularSpeedGradientsTexture.dispose();
                 (<any>this._angularSpeedGradientsTexture) = null;
             }             
+
+            if (this._velocityGradientsTexture) {
+                this._velocityGradientsTexture.dispose();
+                (<any>this._velocityGradientsTexture) = null;
+            }                
          
             if (this._randomTexture) {
                 this._randomTexture.dispose();

+ 13 - 1
src/Particles/babylon.particle.ts

@@ -88,6 +88,13 @@
         /** @hidden */
         public _currentAngularSpeed2 = 0;          
 
+        /** @hidden */
+        public _currentVelocityGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentVelocity1 = 0;
+        /** @hidden */
+        public _currentVelocity2 = 0;             
+
         /**
          * Creates a new instance Particle
          * @param particleSystem the particle system the particle belongs to
@@ -158,7 +165,12 @@
                 other._currentAngularSpeedGradient = this._currentAngularSpeedGradient;
                 other._currentAngularSpeed1 = this._currentAngularSpeed1;
                 other._currentAngularSpeed2 = this._currentAngularSpeed2;
-            }            
+            }        
+            if (this._currentVelocityGradient) {
+                other._currentVelocityGradient = this._currentVelocityGradient;
+                other._currentVelocity1 = this._currentVelocity1;
+                other._currentVelocity2 = this._currentVelocity2;
+            }                  
             if (this.particleSystem.isAnimationSheetEnabled) {
                 other._initialStartSpriteCellID = this._initialStartSpriteCellID;
                 other._initialEndSpriteCellID = this._initialEndSpriteCellID;

+ 89 - 4
src/Particles/babylon.particleSystem.ts

@@ -188,6 +188,7 @@
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
         private _lifeTimeGradients: Nullable<Array<FactorGradient>> = null;
         private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
+        private _velocityGradients: Nullable<Array<FactorGradient>> = null;
 
         /**
          * Gets the current list of color gradients.
@@ -218,7 +219,7 @@
         
         /**
          * Gets the current list of angular speed gradients.
-         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * You must use addAngularSpeedGradient and removeAngularSpeedGradient to udpate this list
          * @returns the list of angular speed gradients
          */
         public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
@@ -226,6 +227,15 @@
         } 
 
         /**
+         * Gets the current list of velocity gradients.
+         * You must use addVelocityGradient and removeVelocityGradient to udpate this list
+         * @returns the list of velocity gradients
+         */
+        public getVelocityGradients(): Nullable<Array<FactorGradient>> {
+            return this._velocityGradients;
+        }         
+
+        /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
@@ -546,7 +556,18 @@
                         }                        
                         particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
 
-                        particle.direction.scaleToRef(this._scaledUpdateSpeed, this._scaledDirection);
+                        let directionScale = this._scaledUpdateSpeed;
+                        if (this._velocityGradients && this._velocityGradients.length > 0) {                  
+                            Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
+                                if (currentGradient !== particle._currentVelocityGradient) {
+                                    particle._currentVelocity1 = particle._currentVelocity2;
+                                    particle._currentVelocity2 = (<FactorGradient>nextGradient).getFactor();    
+                                    particle._currentVelocityGradient = (<FactorGradient>currentGradient);
+                                }                                
+                                directionScale *= Scalar.Lerp(particle._currentVelocity1, particle._currentVelocity2, scale);
+                            });
+                        }                          
+                        particle.direction.scaleToRef(directionScale, this._scaledDirection);
                         particle.position.addInPlace(this._scaledDirection);
 
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
@@ -687,7 +708,35 @@
             this._removeFactorGradient(this._angularSpeedGradients, gradient);
 
             return this;
-        }           
+        }          
+        
+        /**
+         * Adds a new velocity gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addVelocityGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
+            if (!this._velocityGradients) {
+                this._velocityGradients = [];
+            }
+
+            this._addFactorGradient(this._velocityGradients, gradient, factor, factor2);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific velocity gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeVelocityGradient(gradient: number): ParticleSystem {
+            this._removeFactorGradient(this._velocityGradients, gradient);
+
+            return this;
+        }         
 
         /**
          * Adds a new color gradient
@@ -1111,6 +1160,18 @@
                 }
                 particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
 
+                // Velocity
+                if (this._velocityGradients && this._velocityGradients.length > 0) {
+                    particle._currentVelocityGradient = this._velocityGradients[0];
+                    particle._currentVelocity1 = particle._currentVelocityGradient.getFactor();
+
+                    if (this._velocityGradients.length > 1) {
+                        particle._currentVelocity2 = this._velocityGradients[1].getFactor();
+                    } else {
+                        particle._currentVelocity2 = particle._currentVelocity1;
+                    }
+                }                
+
                 // Color
                 if (!this._colorGradients || this._colorGradients.length === 0) {
                     var step = Scalar.RandomRange(0, 1.0);
@@ -1664,6 +1725,24 @@
                     serializationObject.angularSpeedGradients.push(serializedGradient);
                 }
             }  
+
+            let velocityGradients = particleSystem.getVelocityGradients();
+            if (velocityGradients) {
+                serializationObject.velocityGradients = [];
+                for (var velocityGradient of velocityGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: velocityGradient.gradient,
+                        factor1: velocityGradient.factor1
+                    };
+
+                    if (velocityGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = velocityGradient.factor2;
+                    }
+
+                    serializationObject.velocityGradients.push(serializedGradient);
+                }
+            }              
         }
 
         /** @hidden */
@@ -1758,7 +1837,13 @@
                 for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
                     particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ?  angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
                 }
-            }               
+            }       
+            
+            if (parsedParticleSystem.velocityGradients) {
+                for (var velocityGradient of parsedParticleSystem.velocityGradients) {
+                    particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ?  velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
+                }
+            }              
             
             // Emitter
             let emitterType: IParticleEmitterType;

+ 17 - 5
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -96,6 +96,11 @@ uniform sampler2D sizeGradientSampler;
 uniform sampler2D angularSpeedGradientSampler;
 #endif 
 
+#ifdef VELOCITYGRADIENTS
+uniform sampler2D velocityGradientSampler;
+#endif
+
+
 #ifdef ANIMATESHEET
 uniform vec3 cellInfos;
 #endif
@@ -227,7 +232,7 @@ void main() {
     float power = emitPower.x + (emitPower.y - emitPower.x) * randoms.a;
 
     outPosition = (emitterWM * vec4(position, 1.)).xyz;
-    vec3 initial = (emitterWM * vec4(direction, 0.)).xyz;
+    vec3 initial = (emitterWM * vec4(normalize(direction), 0.)).xyz;
     outDirection = initial * power;
 #ifndef BILLBOARD        
     outInitialDirection = initial;
@@ -237,7 +242,14 @@ void main() {
 #endif
 
   } else {   
-    outPosition = position + direction * timeDelta;
+    float directionScale = timeDelta;
+    float ageGradient = age / life;
+
+#ifdef VELOCITYGRADIENTS
+    directionScale *= texture(velocityGradientSampler, vec2(ageGradient, 0)).r;
+#endif
+
+    outPosition = position + direction * directionScale;
     outAge = age + timeDelta;
     outLife = life;
     outSeed = seed;
@@ -246,7 +258,7 @@ void main() {
 #endif
 
 #ifdef SIZEGRADIENTS
-	outSize.x = texture(sizeGradientSampler, vec2(age / life, 0)).r;
+	outSize.x = texture(sizeGradientSampler, vec2(ageGradient, 0)).r;
     outSize.yz = size.yz;
 #else
     outSize = size;
@@ -255,10 +267,10 @@ void main() {
 #ifndef BILLBOARD    
     outInitialDirection = initialDirection;
 #endif
-    outDirection = direction + gravity * timeDelta;
+    outDirection = direction;// + gravity * timeDelta;
 
 #ifdef ANGULARSPEEDGRADIENTS
-    float angularSpeed = texture(angularSpeedGradientSampler, vec2(age / life, 0)).r;
+    float angularSpeed = texture(angularSpeedGradientSampler, vec2(ageGradient, 0)).r;
     outAngle = angle + angularSpeed * timeDelta;
 #else
     outAngle = vec2(angle.x + angle.y * timeDelta, angle.y);

+ 1 - 1
src/Tools/HDR/babylon.cubemapToSphericalPolynomial.ts

@@ -172,7 +172,7 @@ module BABYLON {
             sphericalHarmonics.convertIncidentRadianceToIrradiance();
             sphericalHarmonics.convertIrradianceToLambertianRadiance();
 
-            return SphericalPolynomial.getSphericalPolynomialFromHarmonics(sphericalHarmonics);
+            return SphericalPolynomial.FromHarmonics(sphericalHarmonics);
         }
     }
 } 

+ 63 - 195
src/Tools/babylon.environmentTextureTools.ts

@@ -58,20 +58,6 @@ module BABYLON {
      * Defines the required storage to save the environment irradiance information.
      */
     interface EnvironmentTextureIrradianceInfoV1 {
-        polynomials: boolean;
-
-        l00: Array<number>;
-
-        l1_1: Array<number>;
-        l10: Array<number>;
-        l11: Array<number>;
-
-        l2_2: Array<number>;
-        l2_1: Array<number>;
-        l20: Array<number>;
-        l21: Array<number>;
-        l22: Array<number>;
-
         x: Array<number>;
         y: Array<number>;
         z: Array<number>;
@@ -112,7 +98,7 @@ module BABYLON {
                     return null;
                 }
             }
-            
+
             // Read json manifest - collect characters up to null terminator
             let manifestString = '';
             let charCode = 0x00;
@@ -164,7 +150,7 @@ module BABYLON {
 
             let cubeWidth = internalTexture.width;
             let hostingScene = new Scene(engine);
-            let specularTextures: { [key: number]: ArrayBuffer } = { };
+            let specularTextures: { [key: number]: ArrayBuffer } = {};
             let promises: Promise<void>[] = [];
 
             // Read and collect all mipmaps data from the cube.
@@ -186,7 +172,7 @@ module BABYLON {
                             rgbdPostProcess.onApply = (effect) => {
                                 effect._bindTexture("textureSampler", tempTexture);
                             }
-            
+
                             // As the process needs to happen on the main canvas, keep track of the current size
                             let currentW = engine.getRenderWidth();
                             let currentH = engine.getRenderHeight();
@@ -246,7 +232,7 @@ module BABYLON {
                 let infoString = JSON.stringify(info);
                 let infoBuffer = new ArrayBuffer(infoString.length + 1);
                 let infoView = new Uint8Array(infoBuffer); // Limited to ascii subset matching unicode.
-                for (let i= 0, strLen = infoString.length; i < strLen; i++) {
+                for (let i = 0, strLen = infoString.length; i < strLen; i++) {
                     infoView[i] = infoString.charCodeAt(i);
                 }
                 // Ends up with a null terminator for easier parsing
@@ -287,15 +273,13 @@ module BABYLON {
          * @param texture defines the texture containing the polynomials
          * @return the JSON representation of the spherical info
          */
-        private static _CreateEnvTextureIrradiance(texture: CubeTexture) : Nullable<EnvironmentTextureIrradianceInfoV1> {
+        private static _CreateEnvTextureIrradiance(texture: CubeTexture): Nullable<EnvironmentTextureIrradianceInfoV1> {
             let polynmials = texture.sphericalPolynomial;
             if (polynmials == null) {
                 return null;
             }
 
             return {
-                polynomials: true,
-
                 x: [polynmials.x.x, polynmials.x.y, polynmials.x.z],
                 y: [polynmials.y.x, polynmials.y.y, polynmials.y.z],
                 z: [polynmials.z.x, polynmials.z.y, polynmials.z.z],
@@ -311,13 +295,13 @@ module BABYLON {
         }
 
         /**
-         * Uploads the texture info contained in the env file to te GPU.
+         * Uploads the texture info contained in the env file to the GPU.
          * @param texture defines the internal texture to upload to
          * @param arrayBuffer defines the buffer cotaining the data to load
          * @param info defines the texture info retrieved through the GetEnvInfo method
          * @returns a promise
          */
-        public static UploadLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void> {
+        public static UploadEnvLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void> {
             if (info.version !== 1) {
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
             }
@@ -335,13 +319,34 @@ module BABYLON {
                 Tools.Warn('Unsupported specular mipmaps number "' + specularInfo.mipmaps.length + '"');
             }
 
+            const imageData = new Array<Array<ArrayBufferView>>(mipmapsCount);
+            for (let i = 0; i < mipmapsCount; i++) {
+                imageData[i] = new Array<ArrayBufferView>(6);
+                for (let face = 0; face < 6; face++) {
+                    const imageInfo = specularInfo.mipmaps[i * 6 + face];
+                    imageData[i][face] = new Uint8Array(arrayBuffer, specularInfo.specularDataPosition! + imageInfo.position, imageInfo.length);
+                }
+            }
+
+            return EnvironmentTextureTools.UploadLevelsAsync(texture, imageData);
+        }
+
+        /**
+         * Uploads the levels of image data to the GPU.
+         * @param texture defines the internal texture to upload to
+         * @param imageData defines the array buffer views of image data [mipmap][face]
+         * @returns a promise
+         */
+        public static UploadLevelsAsync(texture: InternalTexture, imageData: ArrayBufferView[][]): Promise<void> {
+            const mipmapsCount = imageData.length;
+
             // Gets everything ready.
             let engine = texture.getEngine();
             let expandTexture = false;
             let generateNonLODTextures = false;
             let rgbdPostProcess: Nullable<PostProcess> = null;
             let cubeRtt: Nullable<InternalTexture> = null;
-            let lodTextures: Nullable<{ [lod: number]: BaseTexture}> = null;
+            let lodTextures: Nullable<{ [lod: number]: BaseTexture }> = null;
             let caps = engine.getCaps();
 
             texture.format = Engine.TEXTUREFORMAT_RGBA;
@@ -352,7 +357,7 @@ module BABYLON {
             if (!caps.textureLOD) {
                 expandTexture = false;
                 generateNonLODTextures = true;
-                lodTextures = { };
+                lodTextures = {};
             }
             // in webgl 1 there are no ways to either render or copy lod level information for float textures.
             else if (engine.webGLVersion < 2) {
@@ -373,7 +378,7 @@ module BABYLON {
             if (expandTexture) {
                 // Simply run through the decode PP
                 rgbdPostProcess = new PostProcess("rgbdDecode", "rgbdDecode", null, null, 1, null, Texture.TRILINEAR_SAMPLINGMODE, engine, false, undefined, texture.type, undefined, null, false);
-                
+
                 texture._isRGBD = false;
                 texture.invertY = false;
                 cubeRtt = engine.createRenderTargetCubeTexture(texture.width, {
@@ -394,24 +399,24 @@ module BABYLON {
                     let mipSlices = 3;
                     let scale = texture._lodGenerationScale;
                     let offset = texture._lodGenerationOffset;
-    
+
                     for (let i = 0; i < mipSlices; i++) {
                         //compute LOD from even spacing in smoothness (matching shader calculation)
                         let smoothness = i / (mipSlices - 1);
                         let roughness = 1 - smoothness;
-    
+
                         let minLODIndex = offset; // roughness = 0
-                        let maxLODIndex = Scalar.Log2(info.width) * scale + offset; // roughness = 1
-    
+                        let maxLODIndex = (mipmapsCount - 1) * scale + offset; // roughness = 1 (mipmaps start from 0)
+
                         let lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
                         let mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
-    
+
                         let glTextureFromLod = new InternalTexture(engine, InternalTexture.DATASOURCE_TEMP);
                         glTextureFromLod.isCube = true;
                         glTextureFromLod.invertY = true;
                         glTextureFromLod.generateMipMaps = false;
                         engine.updateTextureSamplingMode(Texture.LINEAR_LINEAR, glTextureFromLod);
-    
+
                         // Wrap in a base texture for easy binding.
                         let lodTexture = new BaseTexture(null);
                         lodTexture.isCube = true;
@@ -420,14 +425,14 @@ module BABYLON {
 
                         switch (i) {
                             case 0:
-                            texture._lodTextureLow = lodTexture;
-                            break;
+                                texture._lodTextureLow = lodTexture;
+                                break;
                             case 1:
-                            texture._lodTextureMid = lodTexture;
-                            break;
+                                texture._lodTextureMid = lodTexture;
+                                break;
                             case 2:
-                            texture._lodTextureHigh = lodTexture;
-                            break;
+                                texture._lodTextureHigh = lodTexture;
+                                break;
                         }
                     }
                 }
@@ -438,25 +443,22 @@ module BABYLON {
             for (let i = 0; i < mipmapsCount; i++) {
                 // All faces
                 for (let face = 0; face < 6; face++) {
-                    // Retrieves the face data
-                    let imageData = specularInfo.mipmaps[i * 6 + face];
-                    let bytes = new Uint8Array(arrayBuffer, specularInfo.specularDataPosition! + imageData.position, imageData.length);
-
-                    // Constructs an image element from bytes
+                    // Constructs an image element from image data
+                    let bytes = imageData[i][face];
                     let blob = new Blob([bytes], { type: 'image/png' });
                     let url = URL.createObjectURL(blob);
                     let image = new Image();
                     image.src = url;
 
                     // Enqueue promise to upload to the texture.
-                    let promise = new Promise<void>((resolve, reject) => {;
+                    let promise = new Promise<void>((resolve, reject) => {
                         image.onload = () => {
                             if (expandTexture) {
                                 let tempTexture = engine.createTexture(null, true, true, null, Texture.NEAREST_SAMPLINGMODE, null,
-                                (message) => {
-                                    reject(message);
-                                },
-                                image);
+                                    (message) => {
+                                        reject(message);
+                                    },
+                                    image);
 
                                 rgbdPostProcess!.getEffect().executeWhenCompiled(() => {
                                     // Uncompress the data to a RTT
@@ -464,7 +466,7 @@ module BABYLON {
                                         effect._bindTexture("textureSampler", tempTexture);
                                         effect.setFloat2("scale", 1, 1);
                                     }
-                                    
+
                                     engine.scenes[0].postProcessManager.directRender([rgbdPostProcess!], cubeRtt, true, face, i);
 
                                     // Cleanup
@@ -524,10 +526,9 @@ module BABYLON {
         /**
          * Uploads spherical polynomials information to the texture.
          * @param texture defines the texture we are trying to upload the information to
-         * @param arrayBuffer defines the array buffer holding the data
          * @param info defines the environment texture info retrieved through the GetEnvInfo method
          */
-        public static UploadPolynomials(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): void {
+        public static UploadEnvSpherical(texture: InternalTexture, info: EnvironmentTextureInfo): void {
             if (info.version !== 1) {
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
             }
@@ -536,151 +537,18 @@ module BABYLON {
             if (!irradianceInfo) {
                 return;
             }
-            
-            //harmonics now represent radiance
-            texture._sphericalPolynomial = new SphericalPolynomial();
-
-            if (irradianceInfo.polynomials) {
-                EnvironmentTextureTools._UploadSP(irradianceInfo, texture._sphericalPolynomial);
-            }
-            else {
-                // convert From SH to SP.
-                EnvironmentTextureTools._ConvertSHIrradianceToLambertianRadiance(irradianceInfo);
-                EnvironmentTextureTools._ConvertSHToSP(irradianceInfo, texture._sphericalPolynomial);
-            }
-        }
 
-        /**
-         * Upload spherical polynomial coefficients to the texture
-         * @param polynmials Spherical polynmial coefficients (9)
-         * @param outPolynomialCoefficents Polynomial coefficients (9) object to store result
-         */
-        private static _UploadSP(polynmials: EnvironmentTextureIrradianceInfoV1, outPolynomialCoefficents: SphericalPolynomial) {
-            outPolynomialCoefficents.x.x = polynmials.x[0];
-            outPolynomialCoefficents.x.y = polynmials.x[1];
-            outPolynomialCoefficents.x.z = polynmials.x[2];
-
-            outPolynomialCoefficents.y.x = polynmials.y[0];
-            outPolynomialCoefficents.y.y = polynmials.y[1];
-            outPolynomialCoefficents.y.z = polynmials.y[2];
-
-            outPolynomialCoefficents.z.x = polynmials.z[0];
-            outPolynomialCoefficents.z.y = polynmials.z[1];
-            outPolynomialCoefficents.z.z = polynmials.z[2];
-
-            //xx
-            outPolynomialCoefficents.xx.x = polynmials.xx[0];
-            outPolynomialCoefficents.xx.y = polynmials.xx[1];
-            outPolynomialCoefficents.xx.z = polynmials.xx[2];
-
-            outPolynomialCoefficents.yy.x = polynmials.yy[0];
-            outPolynomialCoefficents.yy.y = polynmials.yy[1];
-            outPolynomialCoefficents.yy.z = polynmials.yy[2];
-
-            outPolynomialCoefficents.zz.x = polynmials.zz[0];
-            outPolynomialCoefficents.zz.y = polynmials.zz[1];
-            outPolynomialCoefficents.zz.z = polynmials.zz[2];
-
-            //yz
-            outPolynomialCoefficents.yz.x = polynmials.yz[0];
-            outPolynomialCoefficents.yz.y = polynmials.yz[1];
-            outPolynomialCoefficents.yz.z = polynmials.yz[2];
-
-            outPolynomialCoefficents.zx.x = polynmials.zx[0];
-            outPolynomialCoefficents.zx.y = polynmials.zx[1];
-            outPolynomialCoefficents.zx.z = polynmials.zx[2];
-
-            outPolynomialCoefficents.xy.x = polynmials.xy[0];
-            outPolynomialCoefficents.xy.y = polynmials.xy[1];
-            outPolynomialCoefficents.xy.z = polynmials.xy[2];
-        }
-
-        /**
-         * Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
-         *	  L = (1/pi) * E * rho
-         * 
-         * This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
-         * @param harmonics Spherical harmonic coefficients (9)
-         */
-        private static _ConvertSHIrradianceToLambertianRadiance(harmonics: any): void {
-            let scaleFactor = 1 / Math.PI;
-            // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
-            // (The pixel shader must apply albedo after texture fetches, etc).
-            harmonics.l00[0] *= scaleFactor;
-            harmonics.l00[1] *= scaleFactor;
-            harmonics.l00[2] *= scaleFactor;
-            harmonics.l1_1[0] *= scaleFactor;
-            harmonics.l1_1[1] *= scaleFactor;
-            harmonics.l1_1[2] *= scaleFactor;
-            harmonics.l10[0] *= scaleFactor;
-            harmonics.l10[1] *= scaleFactor;
-            harmonics.l10[2] *= scaleFactor;
-            harmonics.l11[0] *= scaleFactor;
-            harmonics.l11[1] *= scaleFactor;
-            harmonics.l11[2] *= scaleFactor;
-            harmonics.l2_2[0] *= scaleFactor;
-            harmonics.l2_2[1] *= scaleFactor;
-            harmonics.l2_2[2] *= scaleFactor;
-            harmonics.l2_1[0] *= scaleFactor;
-            harmonics.l2_1[1] *= scaleFactor;
-            harmonics.l2_1[2] *= scaleFactor;
-            harmonics.l20[0] *= scaleFactor;
-            harmonics.l20[1] *= scaleFactor;
-            harmonics.l20[2] *= scaleFactor;
-            harmonics.l21[0] *= scaleFactor;
-            harmonics.l21[1] *= scaleFactor;
-            harmonics.l21[2] *= scaleFactor;
-            harmonics.l22[0] *= scaleFactor;
-            harmonics.l22[1] *= scaleFactor;
-            harmonics.l22[2] *= scaleFactor;
-        }
-
-        /**
-         * Convert spherical harmonics to spherical polynomial coefficients
-         * @param harmonics Spherical harmonic coefficients (9)
-         * @param outPolynomialCoefficents Polynomial coefficients (9) object to store result
-         */
-        private static _ConvertSHToSP(harmonics: any, outPolynomialCoefficents: SphericalPolynomial) {
-            let rPi = 1 / Math.PI;
-
-            //x
-            outPolynomialCoefficents.x.x = 1.02333 * harmonics.l11[0] * rPi;
-            outPolynomialCoefficents.x.y = 1.02333 * harmonics.l11[1] * rPi;
-            outPolynomialCoefficents.x.z = 1.02333 * harmonics.l11[2] * rPi;
-
-            outPolynomialCoefficents.y.x = 1.02333 * harmonics.l1_1[0] * rPi;
-            outPolynomialCoefficents.y.y = 1.02333 * harmonics.l1_1[1] * rPi;
-            outPolynomialCoefficents.y.z = 1.02333 * harmonics.l1_1[2] * rPi;
-
-            outPolynomialCoefficents.z.x = 1.02333 * harmonics.l10[0] * rPi;
-            outPolynomialCoefficents.z.y = 1.02333 * harmonics.l10[1] * rPi;
-            outPolynomialCoefficents.z.z = 1.02333 * harmonics.l10[2] * rPi;
-
-            //xx
-            outPolynomialCoefficents.xx.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] + 0.429043 * harmonics.l22[0]) * rPi;
-            outPolynomialCoefficents.xx.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] + 0.429043 * harmonics.l22[1]) * rPi;
-            outPolynomialCoefficents.xx.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] + 0.429043 * harmonics.l22[2]) * rPi;
-
-            outPolynomialCoefficents.yy.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] - 0.429043 * harmonics.l22[0]) * rPi;
-            outPolynomialCoefficents.yy.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] - 0.429043 * harmonics.l22[1]) * rPi;
-            outPolynomialCoefficents.yy.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] - 0.429043 * harmonics.l22[2]) * rPi;
-
-            outPolynomialCoefficents.zz.x = (0.886277 * harmonics.l00[0] + 0.495417 * harmonics.l20[0]) * rPi;
-            outPolynomialCoefficents.zz.y = (0.886277 * harmonics.l00[1] + 0.495417 * harmonics.l20[1]) * rPi;
-            outPolynomialCoefficents.zz.z = (0.886277 * harmonics.l00[2] + 0.495417 * harmonics.l20[2]) * rPi;
-
-            //yz
-            outPolynomialCoefficents.yz.x = 0.858086 * harmonics.l2_1[0] * rPi;
-            outPolynomialCoefficents.yz.y = 0.858086 * harmonics.l2_1[1] * rPi;
-            outPolynomialCoefficents.yz.z = 0.858086 * harmonics.l2_1[2] * rPi;
-
-            outPolynomialCoefficents.zx.x = 0.858086 * harmonics.l21[0] * rPi;
-            outPolynomialCoefficents.zx.y = 0.858086 * harmonics.l21[1] * rPi;
-            outPolynomialCoefficents.zx.z = 0.858086 * harmonics.l21[2] * rPi;
-
-            outPolynomialCoefficents.xy.x = 0.858086 * harmonics.l2_2[0] * rPi;
-            outPolynomialCoefficents.xy.y = 0.858086 * harmonics.l2_2[1] * rPi;
-            outPolynomialCoefficents.xy.z = 0.858086 * harmonics.l2_2[2] * rPi;
+            const sp = new SphericalPolynomial();
+            Vector3.FromArrayToRef(irradianceInfo.x, 0, sp.x);
+            Vector3.FromArrayToRef(irradianceInfo.y, 0, sp.y);
+            Vector3.FromArrayToRef(irradianceInfo.z, 0, sp.z);
+            Vector3.FromArrayToRef(irradianceInfo.xx, 0, sp.xx);
+            Vector3.FromArrayToRef(irradianceInfo.yy, 0, sp.yy);
+            Vector3.FromArrayToRef(irradianceInfo.zz, 0, sp.zz);
+            Vector3.FromArrayToRef(irradianceInfo.yz, 0, sp.yz);
+            Vector3.FromArrayToRef(irradianceInfo.zx, 0, sp.zx);
+            Vector3.FromArrayToRef(irradianceInfo.xy, 0, sp.xy);
+            texture._sphericalPolynomial = sp;
         }
     }
 }