浏览代码

Merge pull request #4616 from bghgary/EXT_lights_imageBased

Add support for experimental EXT_lights_imageBased to glTF loader
David Catuhe 7 年之前
父节点
当前提交
3959f85580

+ 5 - 2
Tools/Gulp/config.json

@@ -541,6 +541,7 @@
         "additionalTextures": {
         "additionalTextures": {
             "files": [
             "files": [
                 "../../src/Materials/Textures/babylon.cubeTexture.js",
                 "../../src/Materials/Textures/babylon.cubeTexture.js",
+                "../../src/Materials/Textures/babylon.rawCubeTexture.js",
                 "../../src/Materials/Textures/babylon.renderTargetTexture.js",
                 "../../src/Materials/Textures/babylon.renderTargetTexture.js",
                 "../../src/Materials/Textures/babylon.multiRenderTarget.js",
                 "../../src/Materials/Textures/babylon.multiRenderTarget.js",
                 "../../src/Materials/Textures/babylon.mirrorTexture.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_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.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_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,
                 "doNotIncludeInBundle": true,
                 "output": "babylon.glTF2FileLoader.js"
                 "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_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.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_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"
                 "output": "babylon.glTFFileLoader.js"
             }
             }

+ 0 - 237
dist/preview release/typedocValidationBaseline.json

@@ -16650,243 +16650,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": {
     "Sprite": {
       "Class": {
       "Class": {
         "Comments": {
         "Comments": {

+ 3 - 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))
 - `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))
 - 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))
 - 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 internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 - Added canvas toBlob polyfill in tools ([sebavan](http://www.github.com/sebavan))
 - 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))
 - 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
 ### glTF Loader
@@ -70,6 +71,7 @@
 - Added `onNodeLODsLoadedObservable` and `onMaterialLODsLoadedObservable` to MSFT_lod loader extension ([bghgary](http://www.github.com/bghgary))
 - 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 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 debug logging and performance counters ([bghgary](http://www.github.com/bghgary))
+- Added support for EXT_lights_imageBased ([bghgary](http://www.github.com/bghgary))
 
 
 ### Viewer
 ### Viewer
 
 

+ 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;
         type: LightType;
         color?: number[];
         color?: number[];
         intensity?: number;
         intensity?: number;
-    }
-
-    interface ISpotLight extends ILight {
-        innerConeAngle?: number;
-        outerConeAngle?: number;
+        spot?: {
+            innerConeAngle?: number;
+            outerConeAngle?: number;
+        };
     }
     }
 
 
     interface ILights {
     interface ILights {
@@ -71,10 +70,9 @@ module BABYLON.GLTF2.Extensions {
                         break;
                         break;
                     }
                     }
                     case LightType.SPOT: {
                     case LightType.SPOT: {
-                        const spotLight = light as ISpotLight;
                         // TODO: support inner and outer cone angles
                         // TODO: support inner and outer cone angles
                         //const innerConeAngle = spotLight.innerConeAngle || 0;
                         //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);
                         babylonLight = new SpotLight(name, Vector3.Zero(), Vector3.Forward(), outerConeAngle, 2, this._loader._babylonScene);
                         break;
                         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;
             babylonMaterial.microSurface = properties.glossinessFactor == undefined ? 1 : properties.glossinessFactor;
 
 
             if (properties.diffuseTexture) {
             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;
                     babylonMaterial.albedoTexture = texture;
                 }));
                 }));
             }
             }
 
 
             if (properties.specularGlossinessTexture) {
             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;
                     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) {
                 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;
                         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 {
     export class KHR_texture_transform extends GLTFLoaderExtension {
         public readonly name = NAME;
         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._loadExtensionAsync<IKHRTextureTransform>(context, textureInfo, (extensionContext, extension) => {
-                return this._loader._loadTextureAsync(context, textureInfo, babylonTexture => {
+                return this._loader._loadTextureInfoAsync(context, textureInfo, babylonTexture => {
                     if (extension.offset) {
                     if (extension.offset) {
                         babylonTexture.uOffset = extension.offset[0];
                         babylonTexture.uOffset = extension.offset[0];
                         babylonTexture.vOffset = extension.offset[1];
                         babylonTexture.vOffset = extension.offset[1];

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

@@ -10,7 +10,8 @@ module BABYLON.GLTF2 {
         _total?: number;
         _total?: number;
     }
     }
 
 
-    class _ArrayItem {
+    /** @hidden */
+    export class _ArrayItem {
         public static Assign(values?: _IArrayItem[]): void {
         public static Assign(values?: _IArrayItem[]): void {
             if (values) {
             if (values) {
                 for (let index = 0; index < values.length; index++) {
                 for (let index = 0; index < values.length; index++) {
@@ -320,9 +321,9 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         public _loadSceneAsync(context: string, scene: _ILoaderScene): Promise<void> {
         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>>();
             const promises = new Array<Promise<void>>();
@@ -436,9 +437,9 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         public _loadNodeAsync(context: string, node: _ILoaderNode): Promise<void> {
         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) {
             if (node._babylonMesh) {
@@ -549,9 +550,9 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private _loadVertexDataAsync(context: string, primitive: _ILoaderMeshPrimitive, babylonMesh: Mesh): Promise<Geometry> {
         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;
             const attributes = primitive.attributes;
@@ -1254,13 +1255,13 @@ module BABYLON.GLTF2 {
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
                 babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
 
 
                 if (properties.baseColorTexture) {
                 if (properties.baseColorTexture) {
-                    promises.push(this._loadTextureAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
+                    promises.push(this._loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, texture => {
                         babylonMaterial.albedoTexture = texture;
                         babylonMaterial.albedoTexture = texture;
                     }));
                     }));
                 }
                 }
 
 
                 if (properties.metallicRoughnessTexture) {
                 if (properties.metallicRoughnessTexture) {
-                    promises.push(this._loadTextureAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
+                    promises.push(this._loadTextureInfoAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, texture => {
                         babylonMaterial.metallicTexture = 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> {
         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 || {};
             material._babylonData = material._babylonData || {};
@@ -1315,9 +1316,9 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         public _loadMaterialPropertiesAsync(context: string, material: _ILoaderMaterial, babylonMaterial: Material): Promise<void> {
         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>>();
             const promises = new Array<Promise<void>>();
@@ -1346,7 +1347,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (material.normalTexture) {
             if (material.normalTexture) {
-                promises.push(this._loadTextureAsync(`${context}/normalTexture`, material.normalTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/normalTexture`, material.normalTexture, texture => {
                     babylonMaterial.bumpTexture = texture;
                     babylonMaterial.bumpTexture = texture;
                 }));
                 }));
 
 
@@ -1358,7 +1359,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (material.occlusionTexture) {
             if (material.occlusionTexture) {
-                promises.push(this._loadTextureAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/occlusionTexture`, material.occlusionTexture, texture => {
                     babylonMaterial.ambientTexture = texture;
                     babylonMaterial.ambientTexture = texture;
                 }));
                 }));
 
 
@@ -1369,7 +1370,7 @@ module BABYLON.GLTF2 {
             }
             }
 
 
             if (material.emissiveTexture) {
             if (material.emissiveTexture) {
-                promises.push(this._loadTextureAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
+                promises.push(this._loadTextureInfoAsync(`${context}/emissiveTexture`, material.emissiveTexture, texture => {
                     babylonMaterial.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}`);
             this._parent._logOpen(`${context}`);
 
 
             const texture = GLTFLoader._GetProperty(`${context}/index`, this._gltf.textures, textureInfo.index);
             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>>();
             const promises = new Array<Promise<void>>();
 
 
@@ -1439,19 +1454,17 @@ module BABYLON.GLTF2 {
             babylonTexture.name = texture.name || `texture${texture._index}`;
             babylonTexture.name = texture.name || `texture${texture._index}`;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapU = samplerData.wrapU;
             babylonTexture.wrapV = samplerData.wrapV;
             babylonTexture.wrapV = samplerData.wrapV;
-            babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
 
 
             const image = GLTFLoader._GetProperty(`${context}/source`, this._gltf.images, texture.source);
             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}`}`;
                 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);
             assign(babylonTexture);
             this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
             this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
 
 
             this._parent._logClose();
             this._parent._logClose();
-            this._parent._logClose();
 
 
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
@@ -1469,33 +1482,28 @@ module BABYLON.GLTF2 {
             return sampler._data;
             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 || ""}`);
                 this._parent._logOpen(`${context} ${image.name || ""}`);
 
 
-                let promise: Promise<ArrayBufferView>;
                 if (image.uri) {
                 if (image.uri) {
-                    promise = this._loadUriAsync(context, image.uri);
+                    image._data = this._loadUriAsync(context, image.uri);
                 }
                 }
                 else {
                 else {
                     const bufferView = GLTFLoader._GetProperty(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
                     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();
                 this._parent._logClose();
             }
             }
 
 
-            return image._blob;
+            return image._data;
         }
         }
 
 
         public _loadUriAsync(context: string, uri: string): Promise<ArrayBufferView> {
         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)) {
             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; }
         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.
          * Override this method to modify the default behavior for loading textures.
          * @hidden
          * @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.
          * 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.
          * Helper method called by the loader to allow extensions to override loading textures.
          * @hidden
          * @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 */
     /** @hidden */
     export interface _ILoaderImage extends IImage, _IArrayItem {
     export interface _ILoaderImage extends IImage, _IArrayItem {
-        _blob?: Promise<Blob>;
+        _data?: Promise<ArrayBufferView>;
     }
     }
 
 
     /** @hidden */
     /** @hidden */

二进制
sandbox/Assets/environment.dds


+ 11 - 5
sandbox/index.js

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

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

@@ -5654,8 +5654,8 @@
                         texture.width = info.width;
                         texture.width = info.width;
                         texture.height = 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;
                             texture.isReady = true;
                             if (onLoad) {
                             if (onLoad) {
                                 onLoad();
                                 onLoad();
@@ -5874,15 +5874,15 @@
          * @param data defines the array of data to use to create each face
          * @param data defines the array of data to use to create each face
          * @param size defines the size of the textures
          * @param size defines the size of the textures
          * @param format defines the format of the data
          * @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 generateMipMaps  defines if the engine should generate the mip levels
          * @param invertY defines if data must be stored with Y axis inverted
          * @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 samplingMode defines the required sampling mode (like BABYLON.Texture.NEAREST_SAMPLINGMODE)
          * @param compression defines the compression used (null by default)
          * @param compression defines the compression used (null by default)
          * @returns the cube texture as an InternalTexture
          * @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 {
                                     compression: Nullable<string> = null): InternalTexture {
             var gl = this._gl;
             var gl = this._gl;
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBERAW);
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_CUBERAW);

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

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

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

@@ -53,6 +53,10 @@ module BABYLON {
          * Texture content is a depth texture
          * Texture content is a depth texture
          */
          */
         public static DATASOURCE_DEPTHTEXTURE = 11;
         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
          * Defines if the texture is ready
@@ -146,6 +150,8 @@ module BABYLON {
         /** @hidden */
         /** @hidden */
         public _bufferViewArray: Nullable<ArrayBufferView[]>;
         public _bufferViewArray: Nullable<ArrayBufferView[]>;
         /** @hidden */
         /** @hidden */
+        public _bufferViewArrayArray: Nullable<ArrayBufferView[][]>;
+        /** @hidden */
         public _size: number;
         public _size: number;
         /** @hidden */
         /** @hidden */
         public _extension: string;
         public _extension: string;
@@ -186,7 +192,7 @@ module BABYLON {
         /** @hidden */
         /** @hidden */
         public _comparisonFunction: number = 0;
         public _comparisonFunction: number = 0;
         /** @hidden */
         /** @hidden */
-        public _sphericalPolynomial: Nullable<SphericalPolynomial>;
+        public _sphericalPolynomial: Nullable<SphericalPolynomial> = null;
         /** @hidden */
         /** @hidden */
         public _lodGenerationScale: number = 0;
         public _lodGenerationScale: number = 0;
         /** @hidden */
         /** @hidden */
@@ -350,12 +356,19 @@ module BABYLON {
                     return;
                     return;
 
 
                 case InternalTexture.DATASOURCE_CUBERAW:
                 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);
                     proxy._swapAndDie(this);
-
                     this.isReady = true;
                     this.isReady = true;
                     return;
                     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:
                 case InternalTexture.DATASOURCE_CUBEPREFILTERED:
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, (proxy) => {
                     proxy = this._engine.createPrefilteredCubeTexture(this.url, null, this._lodGenerationScale, this._lodGenerationOffset, (proxy) => {
                         if (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 {
 module BABYLON {
+    /**
+     * Class representing spherical polynomial coefficients to the 3rd degree
+     */
     export class SphericalPolynomial {
     export class SphericalPolynomial {
+        /**
+         * The x coefficients of the spherical polynomial
+         */
         public x: Vector3 = Vector3.Zero();
         public x: Vector3 = Vector3.Zero();
+
+        /**
+         * The y coefficients of the spherical polynomial
+         */
         public y: Vector3 = Vector3.Zero();
         public y: Vector3 = Vector3.Zero();
+
+        /**
+         * The z coefficients of the spherical polynomial
+         */
         public z: Vector3 = Vector3.Zero();
         public z: Vector3 = Vector3.Zero();
+
+        /**
+         * The xx coefficients of the spherical polynomial
+         */
         public xx: Vector3 = Vector3.Zero();
         public xx: Vector3 = Vector3.Zero();
+
+        /**
+         * The yy coefficients of the spherical polynomial
+         */
         public yy: Vector3 = Vector3.Zero();
         public yy: Vector3 = Vector3.Zero();
+
+        /**
+         * The zz coefficients of the spherical polynomial
+         */
         public zz: Vector3 = Vector3.Zero();
         public zz: Vector3 = Vector3.Zero();
+
+        /**
+         * The xy coefficients of the spherical polynomial
+         */
         public xy: Vector3 = Vector3.Zero();
         public xy: Vector3 = Vector3.Zero();
+
+        /**
+         * The yz coefficients of the spherical polynomial
+         */
         public yz: Vector3 = Vector3.Zero();
         public yz: Vector3 = Vector3.Zero();
+
+        /**
+         * The zx coefficients of the spherical polynomial
+         */
         public zx: Vector3 = Vector3.Zero();
         public zx: Vector3 = Vector3.Zero();
 
 
+        /**
+         * Adds an ambient color to the spherical polynomial
+         * @param color the color to add
+         */
         public addAmbient(color: Color3): void {
         public addAmbient(color: Color3): void {
             var colorVector = new Vector3(color.r, color.g, color.b);
             var colorVector = new Vector3(color.r, color.g, color.b);
             this.xx = this.xx.add(colorVector);
             this.xx = this.xx.add(colorVector);
@@ -17,26 +59,10 @@ module BABYLON {
             this.zz = this.zz.add(colorVector);
             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)
         public scale(scale: number)
         {
         {
             this.x = this.x.scale(scale);
             this.x = this.x.scale(scale);
@@ -49,105 +75,228 @@ module BABYLON {
             this.zx = this.zx.scale(scale);
             this.zx = this.zx.scale(scale);
             this.xy = this.xy.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 {
     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 {
         public addLight(direction: Vector3, color: Color3, deltaSolidAngle: number): void {
             var colorVector = new Vector3(color.r, color.g, color.b);
             var colorVector = new Vector3(color.r, color.g, color.b);
             var c = colorVector.scale(deltaSolidAngle);
             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 {
         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
         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)
             // Constant (Band 0)
-            this.L00 = this.L00.scale(3.141593);
+            this.l00 = this.l00.scale(3.141593);
 
 
             // Linear (Band 1)
             // 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)
             // 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
         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);
             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 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).
             // (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();
             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);
             result.scale(Math.PI);
 
 
             return result;
             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;
+        }
     }
     }
 }
 }

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

@@ -172,7 +172,7 @@ module BABYLON {
             sphericalHarmonics.convertIncidentRadianceToIrradiance();
             sphericalHarmonics.convertIncidentRadianceToIrradiance();
             sphericalHarmonics.convertIrradianceToLambertianRadiance();
             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.
      * Defines the required storage to save the environment irradiance information.
      */
      */
     interface EnvironmentTextureIrradianceInfoV1 {
     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>;
         x: Array<number>;
         y: Array<number>;
         y: Array<number>;
         z: Array<number>;
         z: Array<number>;
@@ -112,7 +98,7 @@ module BABYLON {
                     return null;
                     return null;
                 }
                 }
             }
             }
-            
+
             // Read json manifest - collect characters up to null terminator
             // Read json manifest - collect characters up to null terminator
             let manifestString = '';
             let manifestString = '';
             let charCode = 0x00;
             let charCode = 0x00;
@@ -164,7 +150,7 @@ module BABYLON {
 
 
             let cubeWidth = internalTexture.width;
             let cubeWidth = internalTexture.width;
             let hostingScene = new Scene(engine);
             let hostingScene = new Scene(engine);
-            let specularTextures: { [key: number]: ArrayBuffer } = { };
+            let specularTextures: { [key: number]: ArrayBuffer } = {};
             let promises: Promise<void>[] = [];
             let promises: Promise<void>[] = [];
 
 
             // Read and collect all mipmaps data from the cube.
             // Read and collect all mipmaps data from the cube.
@@ -186,7 +172,7 @@ module BABYLON {
                             rgbdPostProcess.onApply = (effect) => {
                             rgbdPostProcess.onApply = (effect) => {
                                 effect._bindTexture("textureSampler", tempTexture);
                                 effect._bindTexture("textureSampler", tempTexture);
                             }
                             }
-            
+
                             // As the process needs to happen on the main canvas, keep track of the current size
                             // As the process needs to happen on the main canvas, keep track of the current size
                             let currentW = engine.getRenderWidth();
                             let currentW = engine.getRenderWidth();
                             let currentH = engine.getRenderHeight();
                             let currentH = engine.getRenderHeight();
@@ -246,7 +232,7 @@ module BABYLON {
                 let infoString = JSON.stringify(info);
                 let infoString = JSON.stringify(info);
                 let infoBuffer = new ArrayBuffer(infoString.length + 1);
                 let infoBuffer = new ArrayBuffer(infoString.length + 1);
                 let infoView = new Uint8Array(infoBuffer); // Limited to ascii subset matching unicode.
                 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);
                     infoView[i] = infoString.charCodeAt(i);
                 }
                 }
                 // Ends up with a null terminator for easier parsing
                 // Ends up with a null terminator for easier parsing
@@ -287,15 +273,13 @@ module BABYLON {
          * @param texture defines the texture containing the polynomials
          * @param texture defines the texture containing the polynomials
          * @return the JSON representation of the spherical info
          * @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;
             let polynmials = texture.sphericalPolynomial;
             if (polynmials == null) {
             if (polynmials == null) {
                 return null;
                 return null;
             }
             }
 
 
             return {
             return {
-                polynomials: true,
-
                 x: [polynmials.x.x, polynmials.x.y, polynmials.x.z],
                 x: [polynmials.x.x, polynmials.x.y, polynmials.x.z],
                 y: [polynmials.y.x, polynmials.y.y, polynmials.y.z],
                 y: [polynmials.y.x, polynmials.y.y, polynmials.y.z],
                 z: [polynmials.z.x, polynmials.z.y, polynmials.z.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 texture defines the internal texture to upload to
          * @param arrayBuffer defines the buffer cotaining the data to load
          * @param arrayBuffer defines the buffer cotaining the data to load
          * @param info defines the texture info retrieved through the GetEnvInfo method
          * @param info defines the texture info retrieved through the GetEnvInfo method
          * @returns a promise
          * @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) {
             if (info.version !== 1) {
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
             }
             }
@@ -335,13 +319,34 @@ module BABYLON {
                 Tools.Warn('Unsupported specular mipmaps number "' + specularInfo.mipmaps.length + '"');
                 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.
             // Gets everything ready.
             let engine = texture.getEngine();
             let engine = texture.getEngine();
             let expandTexture = false;
             let expandTexture = false;
             let generateNonLODTextures = false;
             let generateNonLODTextures = false;
             let rgbdPostProcess: Nullable<PostProcess> = null;
             let rgbdPostProcess: Nullable<PostProcess> = null;
             let cubeRtt: Nullable<InternalTexture> = null;
             let cubeRtt: Nullable<InternalTexture> = null;
-            let lodTextures: Nullable<{ [lod: number]: BaseTexture}> = null;
+            let lodTextures: Nullable<{ [lod: number]: BaseTexture }> = null;
             let caps = engine.getCaps();
             let caps = engine.getCaps();
 
 
             texture.format = Engine.TEXTUREFORMAT_RGBA;
             texture.format = Engine.TEXTUREFORMAT_RGBA;
@@ -352,7 +357,7 @@ module BABYLON {
             if (!caps.textureLOD) {
             if (!caps.textureLOD) {
                 expandTexture = false;
                 expandTexture = false;
                 generateNonLODTextures = true;
                 generateNonLODTextures = true;
-                lodTextures = { };
+                lodTextures = {};
             }
             }
             // in webgl 1 there are no ways to either render or copy lod level information for float textures.
             // in webgl 1 there are no ways to either render or copy lod level information for float textures.
             else if (engine.webGLVersion < 2) {
             else if (engine.webGLVersion < 2) {
@@ -373,7 +378,7 @@ module BABYLON {
             if (expandTexture) {
             if (expandTexture) {
                 // Simply run through the decode PP
                 // 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);
                 rgbdPostProcess = new PostProcess("rgbdDecode", "rgbdDecode", null, null, 1, null, Texture.TRILINEAR_SAMPLINGMODE, engine, false, undefined, texture.type, undefined, null, false);
-                
+
                 texture._isRGBD = false;
                 texture._isRGBD = false;
                 texture.invertY = false;
                 texture.invertY = false;
                 cubeRtt = engine.createRenderTargetCubeTexture(texture.width, {
                 cubeRtt = engine.createRenderTargetCubeTexture(texture.width, {
@@ -394,24 +399,24 @@ module BABYLON {
                     let mipSlices = 3;
                     let mipSlices = 3;
                     let scale = texture._lodGenerationScale;
                     let scale = texture._lodGenerationScale;
                     let offset = texture._lodGenerationOffset;
                     let offset = texture._lodGenerationOffset;
-    
+
                     for (let i = 0; i < mipSlices; i++) {
                     for (let i = 0; i < mipSlices; i++) {
                         //compute LOD from even spacing in smoothness (matching shader calculation)
                         //compute LOD from even spacing in smoothness (matching shader calculation)
                         let smoothness = i / (mipSlices - 1);
                         let smoothness = i / (mipSlices - 1);
                         let roughness = 1 - smoothness;
                         let roughness = 1 - smoothness;
-    
+
                         let minLODIndex = offset; // roughness = 0
                         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 lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
                         let mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
                         let mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
-    
+
                         let glTextureFromLod = new InternalTexture(engine, InternalTexture.DATASOURCE_TEMP);
                         let glTextureFromLod = new InternalTexture(engine, InternalTexture.DATASOURCE_TEMP);
                         glTextureFromLod.isCube = true;
                         glTextureFromLod.isCube = true;
                         glTextureFromLod.invertY = true;
                         glTextureFromLod.invertY = true;
                         glTextureFromLod.generateMipMaps = false;
                         glTextureFromLod.generateMipMaps = false;
                         engine.updateTextureSamplingMode(Texture.LINEAR_LINEAR, glTextureFromLod);
                         engine.updateTextureSamplingMode(Texture.LINEAR_LINEAR, glTextureFromLod);
-    
+
                         // Wrap in a base texture for easy binding.
                         // Wrap in a base texture for easy binding.
                         let lodTexture = new BaseTexture(null);
                         let lodTexture = new BaseTexture(null);
                         lodTexture.isCube = true;
                         lodTexture.isCube = true;
@@ -420,14 +425,14 @@ module BABYLON {
 
 
                         switch (i) {
                         switch (i) {
                             case 0:
                             case 0:
-                            texture._lodTextureLow = lodTexture;
-                            break;
+                                texture._lodTextureLow = lodTexture;
+                                break;
                             case 1:
                             case 1:
-                            texture._lodTextureMid = lodTexture;
-                            break;
+                                texture._lodTextureMid = lodTexture;
+                                break;
                             case 2:
                             case 2:
-                            texture._lodTextureHigh = lodTexture;
-                            break;
+                                texture._lodTextureHigh = lodTexture;
+                                break;
                         }
                         }
                     }
                     }
                 }
                 }
@@ -438,25 +443,22 @@ module BABYLON {
             for (let i = 0; i < mipmapsCount; i++) {
             for (let i = 0; i < mipmapsCount; i++) {
                 // All faces
                 // All faces
                 for (let face = 0; face < 6; face++) {
                 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 blob = new Blob([bytes], { type: 'image/png' });
                     let url = URL.createObjectURL(blob);
                     let url = URL.createObjectURL(blob);
                     let image = new Image();
                     let image = new Image();
                     image.src = url;
                     image.src = url;
 
 
                     // Enqueue promise to upload to the texture.
                     // Enqueue promise to upload to the texture.
-                    let promise = new Promise<void>((resolve, reject) => {;
+                    let promise = new Promise<void>((resolve, reject) => {
                         image.onload = () => {
                         image.onload = () => {
                             if (expandTexture) {
                             if (expandTexture) {
                                 let tempTexture = engine.createTexture(null, true, true, null, Texture.NEAREST_SAMPLINGMODE, null,
                                 let tempTexture = engine.createTexture(null, true, true, null, Texture.NEAREST_SAMPLINGMODE, null,
-                                (message) => {
-                                    reject(message);
-                                },
-                                image);
+                                    (message) => {
+                                        reject(message);
+                                    },
+                                    image);
 
 
                                 rgbdPostProcess!.getEffect().executeWhenCompiled(() => {
                                 rgbdPostProcess!.getEffect().executeWhenCompiled(() => {
                                     // Uncompress the data to a RTT
                                     // Uncompress the data to a RTT
@@ -464,7 +466,7 @@ module BABYLON {
                                         effect._bindTexture("textureSampler", tempTexture);
                                         effect._bindTexture("textureSampler", tempTexture);
                                         effect.setFloat2("scale", 1, 1);
                                         effect.setFloat2("scale", 1, 1);
                                     }
                                     }
-                                    
+
                                     engine.scenes[0].postProcessManager.directRender([rgbdPostProcess!], cubeRtt, true, face, i);
                                     engine.scenes[0].postProcessManager.directRender([rgbdPostProcess!], cubeRtt, true, face, i);
 
 
                                     // Cleanup
                                     // Cleanup
@@ -524,10 +526,9 @@ module BABYLON {
         /**
         /**
          * Uploads spherical polynomials information to the texture.
          * Uploads spherical polynomials information to the texture.
          * @param texture defines the texture we are trying to upload the information to
          * @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
          * @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) {
             if (info.version !== 1) {
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
                 Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
             }
             }
@@ -536,151 +537,18 @@ module BABYLON {
             if (!irradianceInfo) {
             if (!irradianceInfo) {
                 return;
                 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;
         }
         }
     }
     }
 }
 }