瀏覽代碼

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

David Catuhe 7 年之前
父節點
當前提交
f8dcc91f4e

+ 3 - 0
Tools/Gulp/config.json

@@ -1613,6 +1613,9 @@
                     "../../serializers/src/glTF/2.0/babylon.glTFAnimation.ts",
                     "../../serializers/src/glTF/2.0/babylon.glTFUtilities.ts"
                 ],
+                "shaderFiles": [
+                    "../../serializers/src/glTF/2.0/shaders/setAlphaToOne.fragment.fx"
+                ],
                 "output": "babylon.glTF2Serializer.js"
             }
         ],

+ 141 - 164
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -110,6 +110,9 @@ module BABYLON.GLTF2 {
          */
         private animationSampleRate: number;
 
+        /**
+         * Callback which specifies if a transform node should be exported or not
+         */
         private shouldExportTransformNode: ((babylonTransformNode: TransformNode) => boolean);
 
         /**
@@ -591,36 +594,39 @@ module BABYLON.GLTF2 {
          * @param glTFPrefix Text to use when prefixing a glTF file
          * @returns GLTFData with glTF file data
          */
-        public _generateGLTF(glTFPrefix: string): GLTFData {
-            const binaryBuffer = this.generateBinary();
-            const jsonText = this.generateJSON(false, glTFPrefix, true);
-            const bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
+        public _generateGLTFAsync(glTFPrefix: string): Promise<GLTFData> {
+            return this._generateBinaryAsync().then(binaryBuffer => {
+                const jsonText = this.generateJSON(false, glTFPrefix, true);
+                const bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
 
-            const glTFFileName = glTFPrefix + '.gltf';
-            const glTFBinFile = glTFPrefix + '.bin';
+                const glTFFileName = glTFPrefix + '.gltf';
+                const glTFBinFile = glTFPrefix + '.bin';
 
-            const container = new GLTFData();
+                const container = new GLTFData();
 
-            container.glTFFiles[glTFFileName] = jsonText;
-            container.glTFFiles[glTFBinFile] = bin;
+                container.glTFFiles[glTFFileName] = jsonText;
+                container.glTFFiles[glTFBinFile] = bin;
 
-            if (this.imageData) {
-                for (let image in this.imageData) {
-                    container.glTFFiles[image] = new Blob([this.imageData[image].data], { type: this.imageData[image].mimeType });
+                if (this.imageData) {
+                    for (let image in this.imageData) {
+                        container.glTFFiles[image] = new Blob([this.imageData[image].data], { type: this.imageData[image].mimeType });
+                    }
                 }
-            }
 
-            return container;
+                return container;
+            });
+
         }
 
         /**
          * Creates a binary buffer for glTF
          * @returns array buffer for binary data
          */
-        private generateBinary(): ArrayBuffer {
+        private _generateBinaryAsync(): Promise<ArrayBuffer> {
             let binaryWriter = new _BinaryWriter(4);
-            this.createScene(this.babylonScene, binaryWriter);
-            return binaryWriter.getArrayBuffer();
+            return this.createSceneAsync(this.babylonScene, binaryWriter).then(() => {
+                return binaryWriter.getArrayBuffer();
+            });
         }
 
         /**
@@ -641,84 +647,85 @@ module BABYLON.GLTF2 {
          * @param glTFPrefix 
          * @returns object with glb filename as key and data as value
          */
-        public _generateGLB(glTFPrefix: string): GLTFData {
-            const binaryBuffer = this.generateBinary();
-            const jsonText = this.generateJSON(true);
-            const glbFileName = glTFPrefix + '.glb';
-            const headerLength = 12;
-            const chunkLengthPrefix = 8;
-            const jsonLength = jsonText.length;
-            let imageByteLength = 0;
-
-            for (let key in this.imageData) {
-                imageByteLength += this.imageData[key].data.byteLength;
-            }
-            const jsonPadding = this._getPadding(jsonLength);
-            const binPadding = this._getPadding(binaryBuffer.byteLength);
-            const imagePadding = this._getPadding(imageByteLength);
-
-            const byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding;
-
-            //header
-            const headerBuffer = new ArrayBuffer(headerLength);
-            const headerBufferView = new DataView(headerBuffer);
-            headerBufferView.setUint32(0, 0x46546C67, true); //glTF
-            headerBufferView.setUint32(4, 2, true); // version
-            headerBufferView.setUint32(8, byteLength, true); // total bytes in file
-
-            //json chunk
-            const jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
-            const jsonChunkBufferView = new DataView(jsonChunkBuffer);
-            jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
-            jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
-
-            //json chunk bytes
-            const jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
-            for (let i = 0; i < jsonLength; ++i) {
-                jsonData[i] = jsonText.charCodeAt(i);
-            }
+        public _generateGLBAsync(glTFPrefix: string): Promise<GLTFData> {
+            return this._generateBinaryAsync().then(binaryBuffer => {
+                const jsonText = this.generateJSON(true);
+                const glbFileName = glTFPrefix + '.glb';
+                const headerLength = 12;
+                const chunkLengthPrefix = 8;
+                const jsonLength = jsonText.length;
+                let imageByteLength = 0;
+
+                for (let key in this.imageData) {
+                    imageByteLength += this.imageData[key].data.byteLength;
+                }
+                const jsonPadding = this._getPadding(jsonLength);
+                const binPadding = this._getPadding(binaryBuffer.byteLength);
+                const imagePadding = this._getPadding(imageByteLength);
+
+                const byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding + imageByteLength + imagePadding;
+
+                //header
+                const headerBuffer = new ArrayBuffer(headerLength);
+                const headerBufferView = new DataView(headerBuffer);
+                headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+                headerBufferView.setUint32(4, 2, true); // version
+                headerBufferView.setUint32(8, byteLength, true); // total bytes in file
+
+                //json chunk
+                const jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+                const jsonChunkBufferView = new DataView(jsonChunkBuffer);
+                jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+                jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+
+                //json chunk bytes
+                const jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+                for (let i = 0; i < jsonLength; ++i) {
+                    jsonData[i] = jsonText.charCodeAt(i);
+                }
 
-            //json padding
-            const jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
-            for (let i = 0; i < jsonPadding; ++i) {
-                jsonPaddingView[i] = 0x20;
-            }
+                //json padding
+                const jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+                for (let i = 0; i < jsonPadding; ++i) {
+                    jsonPaddingView[i] = 0x20;
+                }
 
-            //binary chunk
-            const binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
-            const binaryChunkBufferView = new DataView(binaryChunkBuffer);
-            binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength + imagePadding, true);
-            binaryChunkBufferView.setUint32(4, 0x004E4942, true);
-
-            // binary padding
-            const binPaddingBuffer = new ArrayBuffer(binPadding);
-            const binPaddingView = new Uint8Array(binPaddingBuffer);
-            for (let i = 0; i < binPadding; ++i) {
-                binPaddingView[i] = 0;
-            }
+                //binary chunk
+                const binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+                const binaryChunkBufferView = new DataView(binaryChunkBuffer);
+                binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength + imageByteLength + imagePadding, true);
+                binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+
+                // binary padding
+                const binPaddingBuffer = new ArrayBuffer(binPadding);
+                const binPaddingView = new Uint8Array(binPaddingBuffer);
+                for (let i = 0; i < binPadding; ++i) {
+                    binPaddingView[i] = 0;
+                }
 
-            const imagePaddingBuffer = new ArrayBuffer(imagePadding);
-            const imagePaddingView = new Uint8Array(imagePaddingBuffer);
-            for (let i = 0; i < imagePadding; ++i) {
-                imagePaddingView[i] = 0;
-            }
+                const imagePaddingBuffer = new ArrayBuffer(imagePadding);
+                const imagePaddingView = new Uint8Array(imagePaddingBuffer);
+                for (let i = 0; i < imagePadding; ++i) {
+                    imagePaddingView[i] = 0;
+                }
 
-            const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
+                const glbData = [headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer];
 
-            // binary data
-            for (let key in this.imageData) {
-                glbData.push(this.imageData[key].data.buffer);
-            }
-            glbData.push(binPaddingBuffer);
+                // binary data
+                for (let key in this.imageData) {
+                    glbData.push(this.imageData[key].data.buffer);
+                }
+                glbData.push(binPaddingBuffer);
 
-            glbData.push(imagePaddingBuffer);
+                glbData.push(imagePaddingBuffer);
 
-            const glbFile = new Blob(glbData, { type: 'application/octet-stream' });
+                const glbFile = new Blob(glbData, { type: 'application/octet-stream' });
 
-            const container = new GLTFData();
-            container.glTFFiles[glbFileName] = glbFile;
+                const container = new GLTFData();
+                container.glTFFiles[glbFileName] = glbFile;
 
-            return container;
+                return container;
+            });
         }
 
         /**
@@ -792,7 +799,7 @@ module BABYLON.GLTF2 {
          * @param babylonMesh The BabylonJS mesh
          */
         private getMeshPrimitiveMode(babylonMesh: AbstractMesh): number {
-            return babylonMesh.material ? babylonMesh.material.fillMode : Material.TriangleFanDrawMode;
+            return babylonMesh.material ? babylonMesh.material.fillMode : Material.TriangleFillMode;
         }
 
         /**
@@ -937,13 +944,35 @@ module BABYLON.GLTF2 {
                 }
 
                 if (bufferMesh.subMeshes) {
-                    uvCoordsPresent = false;
                     // go through all mesh primitives (submeshes)
                     for (const submesh of bufferMesh.subMeshes) {
+                        uvCoordsPresent = false;
+                        let babylonMaterial = submesh.getMaterial();
+
+                        let materialIndex: Nullable<number> = null;
+                        if (babylonMaterial) {
+                            if (babylonMaterial instanceof MultiMaterial) {
+                                babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
+                                if (babylonMaterial) {
+                                    materialIndex = this.babylonScene.materials.indexOf(babylonMaterial);
+                                }
+                            }
+                            else {
+                                materialIndex = this.babylonScene.materials.indexOf(babylonMaterial);
+                            }
+                        }
+
+                        let glTFMaterial: Nullable<IMaterial> = materialIndex != null ? this.materials[materialIndex] : null;
+
                         const meshPrimitive: IMeshPrimitive = { attributes: {} };
 
                         for (const attribute of attributeData) {
                             const attributeKind = attribute.kind;
+                            if (attributeKind === VertexBuffer.UVKind || attributeKind === VertexBuffer.UV2Kind) {
+                                if (glTFMaterial && !_GLTFMaterial._HasTexturesPresent(glTFMaterial)) {
+                                    continue;
+                                }
+                            }
                             let vertexData = bufferMesh.getVerticesData(attributeKind);
                             if (vertexData) {
                                 const vertexBuffer = this.getVertexBufferFromMesh(attributeKind, bufferMesh);
@@ -971,26 +1000,9 @@ module BABYLON.GLTF2 {
                             this.accessors.push(accessor);
                             meshPrimitive.indices = this.accessors.length - 1;
                         }
-                        if (bufferMesh.material) {
-                            let materialIndex: Nullable<number> = null;
-                            if (bufferMesh.material instanceof StandardMaterial || bufferMesh.material instanceof PBRMetallicRoughnessMaterial || bufferMesh.material instanceof PBRMaterial) {
-                                materialIndex = babylonTransformNode.getScene().materials.indexOf(bufferMesh.material);
-                            }
-                            else if (bufferMesh.material instanceof MultiMaterial) {
-                                const babylonMultiMaterial = bufferMesh.material as MultiMaterial;
-                                const material = babylonMultiMaterial.subMaterials[submesh.materialIndex];
-
-                                if (material) {
-                                    materialIndex = babylonTransformNode.getScene().materials.indexOf(material);
-                                }
-                            }
-                            else {
-                                Tools.Warn("Material type " + bufferMesh.material.getClassName() + " for material " + bufferMesh.material.name + " is not yet implemented in glTF serializer.");
-                            }
-
+                        if (babylonMaterial) {
                             if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
                                 let sideOrientation = this.babylonScene.materials[materialIndex].sideOrientation;
-
                                 this.setPrimitiveMode(meshPrimitive, primitiveMode);
 
                                 if (this.convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
@@ -1002,7 +1014,7 @@ module BABYLON.GLTF2 {
                                         babylonIndices = bufferMesh.getIndices();
                                     }
                                     if (babylonIndices) {
-                                        this.reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);    
+                                        this.reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
                                     }
                                     else {
                                         for (let attribute of attributeData) {
@@ -1018,49 +1030,13 @@ module BABYLON.GLTF2 {
                                     }
                                 }
 
-                                if (uvCoordsPresent) {
-                                    if (!_GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
-                                        delete meshPrimitive.attributes.TEXCOORD_0;
-                                        delete meshPrimitive.attributes.TEXCOORD_1;
-                                    }
-                                    meshPrimitive.material = materialIndex;
-                                }
-                                else {
-                                    if (_GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
-                                        const newMat = _GLTFMaterial._StripTexturesFromMaterial(this.materials[materialIndex]);
-                                        this.materials.push(newMat);
-                                        meshPrimitive.material = this.materials.length - 1;
-                                    }
-                                    else {
-                                        meshPrimitive.material = materialIndex;
-                                    }
-                                }
-                            }
-                        }
-                        else {
-                            const sideOrientation = this.babylonScene.defaultMaterial.sideOrientation;
-                            let byteOffset = indexBufferViewIndex != null ? this.bufferViews[indexBufferViewIndex].byteOffset : null;
-                            if (byteOffset == null) { byteOffset = 0; }
-                            let babylonIndices: Nullable<IndicesArray> = null;
-                            if (indexBufferViewIndex != null) {
-                                babylonIndices = bufferMesh.getIndices();
-                            }
-                            if (babylonIndices) {
-                                if (sideOrientation === Material.ClockWiseSideOrientation) {
-                                    this.reorderIndicesBasedOnPrimitiveMode(submesh, primitiveMode, babylonIndices, byteOffset, binaryWriter);
-                                }
-                            }
-                            else {
-                                for (let attribute of attributeData) {
-                                    let vertexData = bufferMesh.getVerticesData(attribute.kind);
-                                    if (vertexData) {
-                                        let byteOffset = this.bufferViews[vertexAttributeBufferViews[attribute.kind]].byteOffset;
-                                        if (!byteOffset) {
-                                            byteOffset = 0
-                                        }
-                                        this.reorderVertexAttributeDataBasedOnPrimitiveMode(submesh, primitiveMode, sideOrientation, attribute.kind, vertexData, byteOffset, binaryWriter);
-                                    }
+                                if (!uvCoordsPresent && _GLTFMaterial._HasTexturesPresent(this.materials[materialIndex])) {
+                                    const newMat = _GLTFMaterial._StripTexturesFromMaterial(this.materials[materialIndex]);
+                                    this.materials.push(newMat);
+                                    materialIndex = this.materials.length - 1;
                                 }
+
+                                meshPrimitive.material = materialIndex;
                             }
                         }
                         mesh.primitives.push(meshPrimitive);
@@ -1075,15 +1051,14 @@ module BABYLON.GLTF2 {
          * @param babylonScene Babylon scene to get the mesh data from
          * @param binaryWriter Buffer to write binary data to
          */
-        private createScene(babylonScene: Scene, binaryWriter: _BinaryWriter): void {
-            if (this.setNodeTransformation.length) {
-                const scene: IScene = { nodes: [] };
-                let glTFNodeIndex: number;
-                let glTFNode: INode;
-                let directDescendents: Node[];
-                const nodes = [...babylonScene.transformNodes, ...babylonScene.meshes];
-
-                _GLTFMaterial._ConvertMaterialsToGLTF(babylonScene.materials, ImageMimeType.PNG, this.images, this.textures, this.samplers, this.materials, this.imageData, true);
+        private createSceneAsync(babylonScene: Scene, binaryWriter: _BinaryWriter): Promise<void> {
+            const scene: IScene = { nodes: [] };
+            let glTFNodeIndex: number;
+            let glTFNode: INode;
+            let directDescendents: Node[];
+            const nodes = [...babylonScene.transformNodes, ...babylonScene.meshes];
+
+            return _GLTFMaterial._ConvertMaterialsToGLTFAsync(babylonScene.materials, ImageMimeType.PNG, this.images, this.textures, this.samplers, this.materials, this.imageData, true).then(() => {
                 this.nodeMap = this.createNodeMapAndAnimations(babylonScene, nodes, this.shouldExportTransformNode, binaryWriter);
 
                 this.totalByteLength = binaryWriter.getByteOffset();
@@ -1100,11 +1075,11 @@ module BABYLON.GLTF2 {
                             }
                             else {
                                 if (this.convertToRightHandedSystem) {
-                                        if (glTFNode.translation) {
-                                            glTFNode.translation[2] *= -1;
-                                            glTFNode.translation[0] *= -1;
-                                        }
-                                        glTFNode.rotation = glTFNode.rotation ? Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(glTFNode.rotation)).asArray() : (Quaternion.FromArray([0, 1, 0, 0])).asArray();
+                                    if (glTFNode.translation) {
+                                        glTFNode.translation[2] *= -1;
+                                        glTFNode.translation[0] *= -1;
+                                    }
+                                    glTFNode.rotation = glTFNode.rotation ? Quaternion.FromArray([0, 1, 0, 0]).multiply(Quaternion.FromArray(glTFNode.rotation)).asArray() : (Quaternion.FromArray([0, 1, 0, 0])).asArray();
                                 }
 
                                 scene.nodes.push(glTFNodeIndex);
@@ -1125,12 +1100,14 @@ module BABYLON.GLTF2 {
                 if (scene.nodes.length) {
                     this.scenes.push(scene);
                 }
-            }
+            });
         }
 
         /**
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
+         * @param nodes Babylon transform nodes
+         * @param shouldExportTransformNode Callback specifying if a transform node should be exported
          * @param binaryWriter Buffer to write binary data to
          * @returns Node mapping of unique id to index
          */

+ 341 - 236
serializers/src/glTF/2.0/babylon.glTFMaterial.ts

@@ -89,21 +89,24 @@ module BABYLON.GLTF2 {
          * @param imageData mapping of texture names to base64 textures
          * @param hasTextureCoords specifies if texture coordinates are present on the material
          */
-        public static _ConvertMaterialsToGLTF(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertMaterialsToGLTFAsync(babylonMaterials: Material[], mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+            let promises: Promise<void>[] = [];
             for (let babylonMaterial of babylonMaterials) {
                 if (babylonMaterial instanceof StandardMaterial) {
-                    _GLTFMaterial._ConvertStandardMaterial(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
+                    promises.push(_GLTFMaterial._ConvertStandardMaterialAsync(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords));
                 }
                 else if (babylonMaterial instanceof PBRMetallicRoughnessMaterial) {
-                    _GLTFMaterial._ConvertPBRMetallicRoughnessMaterial(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
+                    promises.push(_GLTFMaterial._ConvertPBRMetallicRoughnessMaterialAsync(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords));
                 }
                 else if (babylonMaterial instanceof PBRMaterial) {
-                    _GLTFMaterial._ConvertPBRMaterial(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
+                    promises.push(_GLTFMaterial._ConvertPBRMaterialAsync(babylonMaterial, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords));
                 }
                 else {
-                    Tools.Error("Unsupported material type: " + babylonMaterial.name);
+                    Tools.Warn(`Unsupported material type: ${babylonMaterial.name}`);
                 }
             }
+
+            return Promise.all(promises).then(() => { /* do nothing */ });
         }
 
         /**
@@ -238,7 +241,7 @@ module BABYLON.GLTF2 {
         public static _GetAlphaMode(babylonMaterial: Material): Nullable<MaterialAlphaMode> {
             if (babylonMaterial instanceof StandardMaterial) {
                 const babylonStandardMaterial = babylonMaterial as StandardMaterial;
-                if ((babylonStandardMaterial.alpha != 1.0) ||
+                if ((babylonStandardMaterial.alpha !== 1.0) ||
                     (babylonStandardMaterial.diffuseTexture != null && babylonStandardMaterial.diffuseTexture.hasAlpha) ||
                     (babylonStandardMaterial.opacityTexture != null)) {
                     return MaterialAlphaMode.BLEND;
@@ -309,7 +312,10 @@ module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
-        public static _ConvertStandardMaterial(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertStandardMaterialAsync(babylonStandardMaterial: StandardMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): Promise<void> {
+            const alphaMode = this._GetAlphaMode(babylonStandardMaterial);
+            let useAlpha = alphaMode !== MaterialAlphaMode.OPAQUE ? true : false;
+            let promises = [];
             const glTFPbrMetallicRoughness = _GLTFMaterial._ConvertToGLTFPBRMetallicRoughness(babylonStandardMaterial);
 
             const glTFMaterial: IMaterial = { name: babylonStandardMaterial.name };
@@ -321,41 +327,48 @@ module BABYLON.GLTF2 {
             }
             if (hasTextureCoords) {
                 if (babylonStandardMaterial.diffuseTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture != null) {
-                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                    }
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonStandardMaterial.diffuseTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    });
+                    promises.push(promise);
                 }
                 if (babylonStandardMaterial.bumpTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.bumpTexture, mimeType, images, textures, samplers, imageData)
-                    if (glTFTexture) {
-                        glTFMaterial.normalTexture = glTFTexture;
-                        if (babylonStandardMaterial.bumpTexture.level !== 1) {
-                            glTFMaterial.normalTexture.scale = babylonStandardMaterial.bumpTexture.level;
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonStandardMaterial.bumpTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
+                            if (babylonStandardMaterial.bumpTexture != null && babylonStandardMaterial.bumpTexture.level !== 1) {
+                                glTFMaterial.normalTexture.scale = babylonStandardMaterial.bumpTexture.level;
+                            }
                         }
-                    }
+                    });
+                    promises.push(promise);
                 }
                 if (babylonStandardMaterial.emissiveTexture) {
-                    const glTFEmissiveTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData)
-                    if (glTFEmissiveTexture) {
-                        glTFMaterial.emissiveTexture = glTFEmissiveTexture;
-                    }
-                    glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonStandardMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFEmissiveTexture => {
+                        if (glTFEmissiveTexture) {
+                            glTFMaterial.emissiveTexture = glTFEmissiveTexture;
+                        }
+                        glTFMaterial.emissiveFactor = [1.0, 1.0, 1.0];
+                    });
+                    promises.push(promise);
                 }
                 if (babylonStandardMaterial.ambientTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonStandardMaterial.ambientTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture) {
-                        const occlusionTexture: IMaterialOcclusionTextureInfo = {
-                            index: glTFTexture.index
-                        };
-                        glTFMaterial.occlusionTexture = occlusionTexture;
-                        occlusionTexture.strength = 1.0;
-                    }
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonStandardMaterial.ambientTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            const occlusionTexture: IMaterialOcclusionTextureInfo = {
+                                index: glTFTexture.index
+                            };
+                            glTFMaterial.occlusionTexture = occlusionTexture;
+                            occlusionTexture.strength = 1.0;
+                        }
+                    });
+                    promises.push(promise);
                 }
             }
 
             if (babylonStandardMaterial.alpha < 1.0 || babylonStandardMaterial.opacityTexture) {
-
                 if (babylonStandardMaterial.alphaMode === Engine.ALPHA_COMBINE) {
                     glTFMaterial.alphaMode = GLTF2.MaterialAlphaMode.BLEND;
                 }
@@ -370,6 +383,41 @@ module BABYLON.GLTF2 {
             glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
 
             materials.push(glTFMaterial);
+
+            return Promise.all(promises).then(() => { /* do nothing */ });
+        }
+
+        /**
+         * 
+         * @param texture Texture with alpha to overwrite to one
+         * @param useAlpha Specifies if alpha should be preserved or not
+         * @returns Promise with texture
+         */
+        public static _SetAlphaToOneAsync(texture: BaseTexture, useAlpha: boolean): Promise<Texture> {
+            return new Promise((resolve, reject) => {
+                if (useAlpha) {
+                    resolve(texture as Texture);
+                }
+                else {
+                    const scene = texture.getScene();
+                    if (scene) {
+                        const proceduralTexture = new ProceduralTexture('texture', texture.getSize(), 'setAlphaToOne', scene);
+                        
+                        if (proceduralTexture) {
+                            proceduralTexture.setTexture('textureSampler', texture as Texture);
+                            proceduralTexture.onLoadObservable.add(() => { resolve(proceduralTexture) });
+                        }
+                        else {
+                            reject(`Cannot create procedural texture for ${texture.name}!`);
+                        }
+                    }
+                    else {
+                        reject(`Scene not available for texture ${texture.name}`);
+                    }
+                }
+
+            })
+
         }
 
         /**
@@ -382,7 +430,8 @@ module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
-        public static _ConvertPBRMetallicRoughnessMaterial(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertPBRMetallicRoughnessMaterialAsync(babylonPBRMetalRoughMaterial: PBRMetallicRoughnessMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): Promise<void> {
+            let promises: Promise<void>[] = [];
             const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
 
             if (babylonPBRMetalRoughMaterial.baseColor) {
@@ -407,58 +456,73 @@ module BABYLON.GLTF2 {
             if (babylonPBRMetalRoughMaterial.doubleSided) {
                 glTFMaterial.doubleSided = babylonPBRMetalRoughMaterial.doubleSided;
             }
+            let alphaMode: Nullable<MaterialAlphaMode> = null;
+            let useAlpha = false;
+            if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
+                alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
+                if (alphaMode) {
+                    if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
+                        glTFMaterial.alphaMode = alphaMode;
+                        if (alphaMode === MaterialAlphaMode.MASK) {
+                            glTFMaterial.alphaCutoff = babylonPBRMetalRoughMaterial.alphaCutOff;
+                        }
+                    }
+                }
+            }
+            if (alphaMode !== MaterialAlphaMode.OPAQUE) {
+                useAlpha = true;
+            }
 
             if (hasTextureCoords) {
                 if (babylonPBRMetalRoughMaterial.baseTexture != null) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture != null) {
-                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                    }
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMetalRoughMaterial.baseTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    });
+                    promises.push(promise);
                 }
                 if (babylonPBRMetalRoughMaterial.normalTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture) {
-                        glTFMaterial.normalTexture = glTFTexture;
-                        if (babylonPBRMetalRoughMaterial.normalTexture.level !== 1) {
-                            glTFMaterial.normalTexture.scale = babylonPBRMetalRoughMaterial.normalTexture.level;
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMetalRoughMaterial.normalTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFMaterial.normalTexture = glTFTexture;
+                            if (babylonPBRMetalRoughMaterial.normalTexture.level !== 1) {
+                                glTFMaterial.normalTexture.scale = babylonPBRMetalRoughMaterial.normalTexture.level;
+                            }
                         }
-                    }
+                    });
+                    promises.push(promise);
                 }
                 if (babylonPBRMetalRoughMaterial.occlusionTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture) {
-                        glTFMaterial.occlusionTexture = glTFTexture;
-                        if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
-                            glTFMaterial.occlusionTexture.strength = babylonPBRMetalRoughMaterial.occlusionStrength;
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMetalRoughMaterial.occlusionTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFMaterial.occlusionTexture = glTFTexture;
+                            if (babylonPBRMetalRoughMaterial.occlusionStrength != null) {
+                                glTFMaterial.occlusionTexture.strength = babylonPBRMetalRoughMaterial.occlusionStrength;
+                            }
                         }
-                    }
+                    });
+                    promises.push(promise);
                 }
                 if (babylonPBRMetalRoughMaterial.emissiveTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture != null) {
-                        glTFMaterial.emissiveTexture = glTFTexture;
-                    }
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMetalRoughMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFMaterial.emissiveTexture = glTFTexture;
+                        }
+                    });
+                    promises.push(promise);
                 }
             }
 
             if (this.FuzzyEquals(babylonPBRMetalRoughMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
                 glTFMaterial.emissiveFactor = babylonPBRMetalRoughMaterial.emissiveColor.asArray();
             }
-            if (babylonPBRMetalRoughMaterial.transparencyMode != null) {
-                const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMetalRoughMaterial);
-                if (alphaMode) {
-                    if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
-                        glTFMaterial.alphaMode = alphaMode;
-                        if (alphaMode === MaterialAlphaMode.MASK) {
-                            glTFMaterial.alphaCutoff = babylonPBRMetalRoughMaterial.alphaCutOff;
-                        }
-                    }
-                }
-            }
 
             glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
 
             materials.push(glTFMaterial);
+
+            return Promise.all(promises).then(() => { /* do nothing */ });
         }
 
         /**
@@ -474,7 +538,7 @@ module BABYLON.GLTF2 {
             imageCanvas.width = width;
             imageCanvas.height = height;
             imageCanvas.id = "WriteCanvas";
- 
+
             const ctx = imageCanvas.getContext('2d') as CanvasRenderingContext2D;
 
             const imgData = ctx.createImageData(width, height);
@@ -558,135 +622,139 @@ module BABYLON.GLTF2 {
          */
         private static _ConvertSpecularGlossinessTexturesToMetallicRoughness(diffuseTexture: BaseTexture, specularGlossinessTexture: BaseTexture, factors: _IPBRSpecularGlossiness, mimeType: ImageMimeType): Nullable<_IPBRMetallicRoughness> {
             if (!(diffuseTexture || specularGlossinessTexture)) {
+                Tools.Warn('_ConvertSpecularGlosinessTexturesToMetallicRoughness: diffuse and specular glossiness textures are not defined!');
                 return null;
             }
 
-            const scene = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture.getScene();
-            if (!scene) {
-                Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
-                return null;
-            }
+            const scene: Nullable<Scene> = diffuseTexture ? diffuseTexture.getScene() : specularGlossinessTexture ? specularGlossinessTexture.getScene() : null;
+            if (scene) {
+                const resizedTextures = this._ResizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
 
-            const resizedTextures = this._ResizeTexturesToSameDimensions(diffuseTexture, specularGlossinessTexture, scene);
+                let diffuseSize = resizedTextures.texture1.getSize();
 
-            let diffuseSize = resizedTextures.texture1.getSize();
+                let diffuseBuffer: Uint8Array;
+                let specularGlossinessBuffer: Uint8Array;
 
-            let diffuseBuffer: Uint8Array;
-            let specularGlossinessBuffer: Uint8Array;
+                const width = diffuseSize.width;
+                const height = diffuseSize.height;
 
-            const width = diffuseSize.width;
-            const height = diffuseSize.height;
+                let pixels = (resizedTextures.texture1.readPixels());
+                if (pixels instanceof Uint8Array) {
+                    diffuseBuffer = (resizedTextures.texture1.readPixels()) as Uint8Array;
 
-            let pixels = (resizedTextures.texture1.readPixels());
-            if (pixels instanceof Uint8Array) {
-                diffuseBuffer = (resizedTextures.texture1.readPixels()) as Uint8Array;
-            }
-            else {
-                Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
-                return null;
-            }
-            pixels = resizedTextures.texture2.readPixels();
+                    pixels = resizedTextures.texture2.readPixels();
 
-            if (pixels instanceof Uint8Array) {
-                specularGlossinessBuffer = (resizedTextures.texture2.readPixels()) as Uint8Array;
-            }
-            else {
-                Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
-                return null;
-            }
+                    if (pixels instanceof Uint8Array) {
+                        specularGlossinessBuffer = (resizedTextures.texture2.readPixels()) as Uint8Array;
 
-            const byteLength = specularGlossinessBuffer.byteLength;
-
-            const metallicRoughnessBuffer = new Uint8Array(byteLength);
-            const baseColorBuffer = new Uint8Array(byteLength);
-
-            const strideSize = 4;
-            const maxBaseColor = Color3.Black();
-            let maxMetallic = 0;
-            let maxRoughness = 0;
-
-            for (let h = 0; h < height; ++h) {
-                for (let w = 0; w < width; ++w) {
-                    const offset = (width * h + w) * strideSize;
-
-                    const diffuseColor = Color3.FromInts(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2]).toLinearSpace().multiply(factors.diffuseColor);
-                    const specularColor = Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).toLinearSpace().multiply(factors.specularColor);
-                    const glossiness = (specularGlossinessBuffer[offset + 3] / 255) * factors.glossiness;
-
-                    const specularGlossiness: _IPBRSpecularGlossiness = {
-                        diffuseColor: diffuseColor,
-                        specularColor: specularColor,
-                        glossiness: glossiness
-                    };
-
-                    const metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
-                    maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
-                    maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
-                    maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
-                    maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
-                    maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
-
-                    baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
-                    baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
-                    baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
-                    baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] : 255;
-
-                    metallicRoughnessBuffer[offset] = 0;
-                    metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
-                    metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
-                    metallicRoughnessBuffer[offset + 3] = 255;
-                }
-            }
+                        const byteLength = specularGlossinessBuffer.byteLength;
 
-            // Retrieves the metallic roughness factors from the maximum texture values.
-            const metallicRoughnessFactors: _IPBRMetallicRoughness = {
-                baseColor: maxBaseColor,
-                metallic: maxMetallic,
-                roughness: maxRoughness
-            };
+                        const metallicRoughnessBuffer = new Uint8Array(byteLength);
+                        const baseColorBuffer = new Uint8Array(byteLength);
 
-            let writeOutMetallicRoughnessTexture = false;
-            let writeOutBaseColorTexture = false;
+                        const strideSize = 4;
+                        const maxBaseColor = Color3.Black();
+                        let maxMetallic = 0;
+                        let maxRoughness = 0;
 
-            for (let h = 0; h < height; ++h) {
-                for (let w = 0; w < width; ++w) {
-                    const destinationOffset = (width * h + w) * strideSize;
+                        for (let h = 0; h < height; ++h) {
+                            for (let w = 0; w < width; ++w) {
+                                const offset = (width * h + w) * strideSize;
 
-                    baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > this._epsilon ? metallicRoughnessFactors.baseColor.r : 1;
-                    baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > this._epsilon ? metallicRoughnessFactors.baseColor.g : 1;
-                    baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > this._epsilon ? metallicRoughnessFactors.baseColor.b : 1;
+                                const diffuseColor = Color3.FromInts(diffuseBuffer[offset], diffuseBuffer[offset + 1], diffuseBuffer[offset + 2]).toLinearSpace().multiply(factors.diffuseColor);
+                                const specularColor = Color3.FromInts(specularGlossinessBuffer[offset], specularGlossinessBuffer[offset + 1], specularGlossinessBuffer[offset + 2]).toLinearSpace().multiply(factors.specularColor);
+                                const glossiness = (specularGlossinessBuffer[offset + 3] / 255) * factors.glossiness;
 
-                    const linearBaseColorPixel = Color3.FromInts(baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]);
-                    const sRGBBaseColorPixel = linearBaseColorPixel.toGammaSpace();
-                    baseColorBuffer[destinationOffset] = sRGBBaseColorPixel.r * 255;
-                    baseColorBuffer[destinationOffset + 1] = sRGBBaseColorPixel.g * 255;
-                    baseColorBuffer[destinationOffset + 2] = sRGBBaseColorPixel.b * 255;
+                                const specularGlossiness: _IPBRSpecularGlossiness = {
+                                    diffuseColor: diffuseColor,
+                                    specularColor: specularColor,
+                                    glossiness: glossiness
+                                };
 
-                    if (!this.FuzzyEquals(sRGBBaseColorPixel, Color3.White(), this._epsilon)) {
-                        writeOutBaseColorTexture = true;
-                    }
+                                const metallicRoughness = this._ConvertSpecularGlossinessToMetallicRoughness(specularGlossiness);
+                                maxBaseColor.r = Math.max(maxBaseColor.r, metallicRoughness.baseColor.r);
+                                maxBaseColor.g = Math.max(maxBaseColor.g, metallicRoughness.baseColor.g);
+                                maxBaseColor.b = Math.max(maxBaseColor.b, metallicRoughness.baseColor.b);
+                                maxMetallic = Math.max(maxMetallic, metallicRoughness.metallic);
+                                maxRoughness = Math.max(maxRoughness, metallicRoughness.roughness);
+
+                                baseColorBuffer[offset] = metallicRoughness.baseColor.r * 255;
+                                baseColorBuffer[offset + 1] = metallicRoughness.baseColor.g * 255;
+                                baseColorBuffer[offset + 2] = metallicRoughness.baseColor.b * 255;
+                                baseColorBuffer[offset + 3] = resizedTextures.texture1.hasAlpha ? diffuseBuffer[offset + 3] : 255;
+
+                                metallicRoughnessBuffer[offset] = 0;
+                                metallicRoughnessBuffer[offset + 1] = metallicRoughness.roughness * 255;
+                                metallicRoughnessBuffer[offset + 2] = metallicRoughness.metallic * 255;
+                                metallicRoughnessBuffer[offset + 3] = 255;
+
+                            }
+                        }
+
+                        // Retrieves the metallic roughness factors from the maximum texture values.
+                        const metallicRoughnessFactors: _IPBRMetallicRoughness = {
+                            baseColor: maxBaseColor,
+                            metallic: maxMetallic,
+                            roughness: maxRoughness
+                        };
+
+                        let writeOutMetallicRoughnessTexture = false;
+                        let writeOutBaseColorTexture = false;
+
+                        for (let h = 0; h < height; ++h) {
+                            for (let w = 0; w < width; ++w) {
+                                const destinationOffset = (width * h + w) * strideSize;
+
+                                baseColorBuffer[destinationOffset] /= metallicRoughnessFactors.baseColor.r > this._epsilon ? metallicRoughnessFactors.baseColor.r : 1;
+                                baseColorBuffer[destinationOffset + 1] /= metallicRoughnessFactors.baseColor.g > this._epsilon ? metallicRoughnessFactors.baseColor.g : 1;
+                                baseColorBuffer[destinationOffset + 2] /= metallicRoughnessFactors.baseColor.b > this._epsilon ? metallicRoughnessFactors.baseColor.b : 1;
 
-                    metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > this._epsilon ? metallicRoughnessFactors.roughness : 1;
-                    metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > this._epsilon ? metallicRoughnessFactors.metallic : 1;
+                                const linearBaseColorPixel = Color3.FromInts(baseColorBuffer[destinationOffset], baseColorBuffer[destinationOffset + 1], baseColorBuffer[destinationOffset + 2]);
+                                const sRGBBaseColorPixel = linearBaseColorPixel.toGammaSpace();
+                                baseColorBuffer[destinationOffset] = sRGBBaseColorPixel.r * 255;
+                                baseColorBuffer[destinationOffset + 1] = sRGBBaseColorPixel.g * 255;
+                                baseColorBuffer[destinationOffset + 2] = sRGBBaseColorPixel.b * 255;
 
-                    const metallicRoughnessPixel = Color3.FromInts(255, metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]);
+                                if (!this.FuzzyEquals(sRGBBaseColorPixel, Color3.White(), this._epsilon)) {
+                                    writeOutBaseColorTexture = true;
+                                }
 
-                    if (!this.FuzzyEquals(metallicRoughnessPixel, Color3.White(), this._epsilon)) {
-                        writeOutMetallicRoughnessTexture = true;
+                                metallicRoughnessBuffer[destinationOffset + 1] /= metallicRoughnessFactors.roughness > this._epsilon ? metallicRoughnessFactors.roughness : 1;
+                                metallicRoughnessBuffer[destinationOffset + 2] /= metallicRoughnessFactors.metallic > this._epsilon ? metallicRoughnessFactors.metallic : 1;
+
+                                const metallicRoughnessPixel = Color3.FromInts(255, metallicRoughnessBuffer[destinationOffset + 1], metallicRoughnessBuffer[destinationOffset + 2]);
+
+                                if (!this.FuzzyEquals(metallicRoughnessPixel, Color3.White(), this._epsilon)) {
+                                    writeOutMetallicRoughnessTexture = true;
+                                }
+                            }
+                        }
+
+                        if (writeOutMetallicRoughnessTexture) {
+                            const metallicRoughnessBase64 = this._CreateBase64FromCanvas(metallicRoughnessBuffer, width, height, mimeType);
+                            metallicRoughnessFactors.metallicRoughnessTextureBase64 = metallicRoughnessBase64;
+                        }
+                        if (writeOutBaseColorTexture) {
+                            const baseColorBase64 = this._CreateBase64FromCanvas(baseColorBuffer, width, height, mimeType);
+                            metallicRoughnessFactors.baseColorTextureBase64 = baseColorBase64;
+
+                        }
+
+                        return metallicRoughnessFactors;
+                    }
+                    else {
+                        Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture2.name);
                     }
                 }
+                else {
+                    Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Pixel array buffer type not supported for texture: " + resizedTextures.texture1.name);
+                }
             }
-
-            if (writeOutMetallicRoughnessTexture) {
-                const metallicRoughnessBase64 = this._CreateBase64FromCanvas(metallicRoughnessBuffer, width, height, mimeType);
-                metallicRoughnessFactors.metallicRoughnessTextureBase64 = metallicRoughnessBase64;
-            }
-            if (writeOutBaseColorTexture) {
-                const baseColorBase64 = this._CreateBase64FromCanvas(baseColorBuffer, width, height, mimeType);
-                metallicRoughnessFactors.baseColorTextureBase64 = baseColorBase64;
+            else {
+                Tools.Error("_ConvertSpecularGlossinessTexturesToMetallicRoughness: Scene from textures is missing!");
             }
 
-            return metallicRoughnessFactors;
+            return null;
         }
 
         /**
@@ -748,8 +816,11 @@ module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private static _ConvertMetalRoughFactorsToMetallicRoughness(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): _IPBRMetallicRoughness {
-            const metallicRoughness = {
+        private static _ConvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): Promise<_IPBRMetallicRoughness> {
+            const alphaMode = this._GetAlphaMode(babylonPBRMaterial);
+            const useAlpha = alphaMode !== MaterialAlphaMode.OPAQUE ? true : false;
+            const promises = [];
+            const metallicRoughness: _IPBRMetallicRoughness = {
                 baseColor: babylonPBRMaterial.albedoColor,
                 metallic: babylonPBRMaterial.metallic,
                 roughness: babylonPBRMaterial.roughness
@@ -757,19 +828,26 @@ module BABYLON.GLTF2 {
 
             if (hasTextureCoords) {
                 if (babylonPBRMaterial.albedoTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.albedoTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture) {
-                        glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
-                    }
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMaterial.albedoTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFPbrMetallicRoughness.baseColorTexture = glTFTexture;
+                        }
+                    });
+                    promises.push(promise);
+
                 }
                 if (babylonPBRMaterial.metallicTexture) {
-                    const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.metallicTexture, mimeType, images, textures, samplers, imageData);
-                    if (glTFTexture != null) {
-                        glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
-                    }
+                    let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMaterial.metallicTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                        if (glTFTexture) {
+                            glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFTexture;
+                        }
+                    });
+                    promises.push(promise);
                 }
             }
-            return metallicRoughness;
+            return Promise.all(promises).then(() => {
+                return metallicRoughness;
+            });
         }
 
         private static _GetGLTFTextureSampler(texture: BaseTexture): ISampler {
@@ -897,33 +975,31 @@ module BABYLON.GLTF2 {
                 samplerIndex = samplers.length - 1;
             }
             if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                Tools.Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture currently not supported");
+                Tools.Error("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
                 return null;
             }
 
-            let metallicRoughnessFactors = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
-
-
-            if (!metallicRoughnessFactors) {
-                metallicRoughnessFactors = this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
-            }
-            else {
+            const metallicRoughnessFactors = this._ConvertSpecularGlossinessTexturesToMetallicRoughness(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType);
+            if (metallicRoughnessFactors) {
                 if (hasTextureCoords) {
                     if (metallicRoughnessFactors.baseColorTextureBase64) {
-                        const glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, babylonPBRMaterial.albedoTexture.coordinatesIndex, samplerIndex, imageData);
+                        const glTFBaseColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, images, textures, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex, imageData);
                         if (glTFBaseColorTexture != null) {
                             glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
                         }
                     }
                     if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                        const glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, babylonPBRMaterial.reflectivityTexture.coordinatesIndex, samplerIndex, imageData);
+                        const glTFMRColorTexture = _GLTFMaterial._GetTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, images, textures, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex, imageData);
                         if (glTFMRColorTexture != null) {
                             glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
                         }
                     }
+
+                    return metallicRoughnessFactors;
                 }
             }
-            return metallicRoughnessFactors
+            return this._ConvertSpecularGlossinessToMetallicRoughness(specGloss);
+
         }
 
         /**
@@ -936,21 +1012,50 @@ module BABYLON.GLTF2 {
          * @param imageData map of image file name to data
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          */
-        public static _ConvertPBRMaterial(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean) {
+        public static _ConvertPBRMaterialAsync(babylonPBRMaterial: PBRMaterial, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): Promise<void> {
             const glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness = {};
-            let metallicRoughness: Nullable<_IPBRMetallicRoughness>;
+            //  let metallicRoughness: Nullable<_IPBRMetallicRoughness>;
             const glTFMaterial: IMaterial = {
                 name: babylonPBRMaterial.name
             };
             const useMetallicRoughness = babylonPBRMaterial.isMetallicWorkflow();
 
             if (useMetallicRoughness) {
-                metallicRoughness = this._ConvertMetalRoughFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
+                if (babylonPBRMaterial.albedoColor) {
+                    glTFPbrMetallicRoughness.baseColorFactor = [
+                        babylonPBRMaterial.albedoColor.r,
+                        babylonPBRMaterial.albedoColor.g,
+                        babylonPBRMaterial.albedoColor.b,
+                        babylonPBRMaterial.alpha
+                    ]
+                }
+                return this._ConvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords).then(metallicRoughness => {
+                    return _GLTFMaterial.SetMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
+                });
             }
             else {
-                metallicRoughness = this._ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
+                const metallicRoughness = this._ConvertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, images, textures, samplers, glTFPbrMetallicRoughness, imageData, hasTextureCoords);
+                return _GLTFMaterial.SetMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, images, textures, samplers, materials, imageData, hasTextureCoords);
             }
+        }
+
+        private static SetMetallicRoughnessPbrMaterial(metallicRoughness: Nullable<_IPBRMetallicRoughness>, babylonPBRMaterial: PBRMaterial, glTFMaterial: IMaterial, glTFPbrMetallicRoughness: IMaterialPbrMetallicRoughness, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], materials: IMaterial[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, hasTextureCoords: boolean): Promise<void> {
+            let promises = [];
             if (metallicRoughness) {
+                let alphaMode: Nullable<MaterialAlphaMode> = null;
+                let useAlpha = false;
+                if (babylonPBRMaterial.transparencyMode != null) {
+                    alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMaterial);
+                    if (alphaMode) {
+                        if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
+                            useAlpha = true;
+                            glTFMaterial.alphaMode = alphaMode;
+                            if (alphaMode === MaterialAlphaMode.MASK) {
+                                glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
+                            }
+                        }
+                    }
+                }
                 if (!(this.FuzzyEquals(metallicRoughness.baseColor, Color3.White(), this._epsilon) && babylonPBRMaterial.alpha >= this._epsilon)) {
                     glTFPbrMetallicRoughness.baseColorFactor = [
                         metallicRoughness.baseColor.r,
@@ -973,60 +1078,60 @@ module BABYLON.GLTF2 {
                     }
                     glTFMaterial.doubleSided = true;
                 }
+
                 if (hasTextureCoords) {
                     if (babylonPBRMaterial.bumpTexture) {
-                        const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.bumpTexture, mimeType, images, textures, samplers, imageData);
-                        if (glTFTexture) {
-                            glTFMaterial.normalTexture = glTFTexture;
-                            if (babylonPBRMaterial.bumpTexture.level !== 1) {
-                                glTFMaterial.normalTexture.scale = babylonPBRMaterial.bumpTexture.level;
+                        let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMaterial.bumpTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                            if (glTFTexture) {
+                                glTFMaterial.normalTexture = glTFTexture;
+                                if (babylonPBRMaterial.bumpTexture.level !== 1) {
+                                    glTFMaterial.normalTexture.scale = babylonPBRMaterial.bumpTexture.level;
+                                }
                             }
                         }
+                        );
+                        promises.push(promise);
+
 
                     }
                     if (babylonPBRMaterial.ambientTexture) {
-                        const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.ambientTexture, mimeType, images, textures, samplers, imageData);
-                        if (glTFTexture) {
-                            let occlusionTexture: IMaterialOcclusionTextureInfo = {
-                                index: glTFTexture.index
-                            };
+                        let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMaterial.ambientTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                            if (glTFTexture) {
+                                let occlusionTexture: IMaterialOcclusionTextureInfo = {
+                                    index: glTFTexture.index
+                                };
 
-                            glTFMaterial.occlusionTexture = occlusionTexture;
+                                glTFMaterial.occlusionTexture = occlusionTexture;
 
-                            if (babylonPBRMaterial.ambientTextureStrength) {
-                                occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
+                                if (babylonPBRMaterial.ambientTextureStrength) {
+                                    occlusionTexture.strength = babylonPBRMaterial.ambientTextureStrength;
+                                }
                             }
-                        }
+                        });
+                        promises.push(promise);
+
                     }
                     if (babylonPBRMaterial.emissiveTexture) {
-                        const glTFTexture = _GLTFMaterial._ExportTexture(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData);
-                        if (glTFTexture != null) {
-                            glTFMaterial.emissiveTexture = glTFTexture;
-                        }
+                        let promise = _GLTFMaterial._ExportTextureAsync(babylonPBRMaterial.emissiveTexture, mimeType, images, textures, samplers, imageData, useAlpha).then(glTFTexture => {
+                            if (glTFTexture) {
+                                glTFMaterial.emissiveTexture = glTFTexture;
+                            }
+                        });
+                        promises.push(promise);
                     }
                 }
                 if (!this.FuzzyEquals(babylonPBRMaterial.emissiveColor, Color3.Black(), this._epsilon)) {
                     glTFMaterial.emissiveFactor = babylonPBRMaterial.emissiveColor.asArray();
                 }
-                if (babylonPBRMaterial.transparencyMode != null) {
-                    const alphaMode = _GLTFMaterial._GetAlphaMode(babylonPBRMaterial);
-                    if (alphaMode) {
-                        if (alphaMode !== MaterialAlphaMode.OPAQUE) { //glTF defaults to opaque
-                            glTFMaterial.alphaMode = alphaMode;
-                            if (alphaMode === MaterialAlphaMode.MASK) {
-                                glTFMaterial.alphaCutoff = babylonPBRMaterial.alphaCutOff;
-                            }
-                        }
-                    }
-                }
 
                 glTFMaterial.pbrMetallicRoughness = glTFPbrMetallicRoughness;
                 materials.push(glTFMaterial);
             }
+            return Promise.all(promises).then(result => { /* do nothing */ });
         }
 
         private static GetPixelsFromTexture(babylonTexture: Texture): Uint8Array | Float32Array {
-            let pixels = babylonTexture.textureType === Engine.TEXTURETYPE_UNSIGNED_INT ? babylonTexture.readPixels() as Uint8Array : babylonTexture.readPixels() as Float32Array;
+            const pixels = babylonTexture.textureType === Engine.TEXTURETYPE_UNSIGNED_INT ? babylonTexture.readPixels() as Uint8Array : babylonTexture.readPixels() as Float32Array;
             return pixels;
         }
 
@@ -1040,7 +1145,7 @@ module BABYLON.GLTF2 {
          * @param imageData map of image file name and data
          * @return glTF texture info, or null if the texture format is not supported
          */
-        private static _ExportTexture(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
+        private static _ExportTextureAsync(babylonTexture: BaseTexture, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], samplers: ISampler[], imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }, useAlpha: boolean): Promise<Nullable<ITextureInfo>> {
             const sampler = _GLTFMaterial._GetGLTFTextureSampler(babylonTexture);
             let samplerIndex: Nullable<number> = null;
 
@@ -1081,17 +1186,17 @@ module BABYLON.GLTF2 {
                 extension = ".png";
             }
             else {
-                Tools.Error("Unsupported mime type " + mimeType);
-                return null;
+                return Promise.reject("Unsupported mime type " + mimeType);
             }
             textureName = baseFile + extension;
 
-
-            const pixels = _GLTFMaterial.GetPixelsFromTexture(babylonTexture as Texture);
-            const size = babylonTexture.getSize();
-            const base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
-
-            return this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, babylonTexture.coordinatesIndex, samplerIndex, imageData);
+            return this._SetAlphaToOneAsync(babylonTexture, useAlpha).then((texture) => {
+                const pixels = _GLTFMaterial.GetPixelsFromTexture(texture);
+                const size = babylonTexture.getSize();
+                const base64Data = this._CreateBase64FromCanvas(pixels, size.width, size.height, mimeType);
+                const textureInfo = this._GetTextureInfoFromBase64(base64Data, textureName, mimeType, images, textures, babylonTexture.coordinatesIndex, samplerIndex, imageData);
+                return textureInfo;
+            });
         }
 
         /**
@@ -1104,7 +1209,7 @@ module BABYLON.GLTF2 {
          * @param imageData map of image data
          * @returns glTF texture info, or null if the texture format is not supported
          */
-        private static _GetTextureInfoFromBase64(base64Texture: string, textureName: string, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], texCoordIndex: number, samplerIndex: Nullable<number>, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
+        private static _GetTextureInfoFromBase64(base64Texture: string, textureName: string, mimeType: ImageMimeType, images: IImage[], textures: ITexture[], texCoordIndex: Nullable<number>, samplerIndex: Nullable<number>, imageData: { [fileName: string]: { data: Uint8Array, mimeType: ImageMimeType } }): Nullable<ITextureInfo> {
             let textureInfo: Nullable<ITextureInfo> = null;
 
             const glTFTexture: ITexture = {
@@ -1149,7 +1254,7 @@ module BABYLON.GLTF2 {
                 textureInfo = {
                     index: textures.length - 1
                 }
-                if (texCoordIndex) {
+                if (texCoordIndex != null) {
                     textureInfo.texCoord = texCoordIndex;
                 }
             }

+ 8 - 31
serializers/src/glTF/2.0/babylon.glTFSerializer.ts

@@ -22,20 +22,6 @@ module BABYLON {
      */
     export class GLTF2Export {
         /**
-         * Exports the geometry of the scene to .gltf file format synchronously
-         * @param scene Babylon scene with scene hierarchy information
-         * @param filePrefix File prefix to use when generating the glTF file
-         * @param options Exporter options
-         * @returns Returns an object with a .gltf file and associates texture names
-         * as keys and their data and paths as values
-         */
-        private static GLTF(scene: Scene, filePrefix: string, options?: IExportOptions): GLTFData {
-            const glTFPrefix = filePrefix.replace(/\.[^/.]+$/, "");
-            const gltfGenerator = new GLTF2._Exporter(scene, options);
-            return gltfGenerator._generateGLTF(glTFPrefix);
-        }
-
-        /**
          * Exports the geometry of the scene to .gltf file format asynchronously
          * @param scene Babylon scene with scene hierarchy information
          * @param filePrefix File prefix to use when generating the glTF file
@@ -44,25 +30,14 @@ module BABYLON {
          * as keys and their data and paths as values
          */
         public static GLTFAsync(scene: Scene, filePrefix: string, options?: IExportOptions): Promise<GLTFData> {
-            return Promise.resolve(scene.whenReadyAsync()).then(() => {
-                return GLTF2Export.GLTF(scene, filePrefix, options);
+            return scene.whenReadyAsync().then(() => {
+                const glTFPrefix = filePrefix.replace(/\.[^/.]+$/, "");
+                const gltfGenerator = new GLTF2._Exporter(scene, options);
+                return gltfGenerator._generateGLTFAsync(glTFPrefix);
             });
         }
 
         /**
-         * Exports the geometry of the scene to .glb file format synchronously
-         * @param scene Babylon scene with scene hierarchy information
-         * @param filePrefix File prefix to use when generating glb file
-         * @param options Exporter options
-         * @returns Returns an object with a .glb filename as key and data as value
-         */
-        private static GLB(scene: Scene, filePrefix: string, options?: IExportOptions): GLTFData {
-            const glTFPrefix = filePrefix.replace(/\.[^/.]+$/, "");
-            const gltfGenerator = new GLTF2._Exporter(scene, options);
-            return gltfGenerator._generateGLB(glTFPrefix);
-        }
-
-        /**
          * Exports the geometry of the scene to .glb file format asychronously
          * @param scene Babylon scene with scene hierarchy information
          * @param filePrefix File prefix to use when generating glb file
@@ -70,8 +45,10 @@ module BABYLON {
          * @returns Returns an object with a .glb filename as key and data as value
          */
         public static GLBAsync(scene: Scene, filePrefix: string, options?: IExportOptions): Promise<GLTFData> {
-            return Promise.resolve(scene.whenReadyAsync()).then(() => {
-                return GLTF2Export.GLB(scene, filePrefix, options);
+            return scene.whenReadyAsync().then(() => {
+                const glTFPrefix = filePrefix.replace(/\.[^/.]+$/, "");
+                const gltfGenerator = new GLTF2._Exporter(scene, options);
+                return gltfGenerator._generateGLBAsync(glTFPrefix);
             });
         }
     }

+ 10 - 0
serializers/src/glTF/2.0/shaders/setAlphaToOne.fragment.fx

@@ -0,0 +1,10 @@
+precision highp float;
+
+uniform sampler2D textureSampler;
+
+varying vec2 vUV;
+
+void main(void) {
+    vec4 color = texture2D(textureSampler, vUV);
+    gl_FragColor = vec4(color.rgb, 1.0);
+}

+ 217 - 244
tests/unit/babylon/serializers/babylon.glTFSerializer.tests.ts

@@ -12,6 +12,8 @@ describe('Babylon glTF Serializer', () => {
         (BABYLONDEVTOOLS).Loader
             .useDist()
             .load(function () {
+                // Force apply promise polyfill for consistent behavior between PhantomJS, IE11, and other browsers.
+                BABYLON.PromisePolyfill.Apply(true);
                 done();
             });
     });
@@ -78,63 +80,53 @@ describe('Babylon glTF Serializer', () => {
             BABYLON.GLTF2._GLTFMaterial._SolveMetallic(0.0, 1.0, 1.0).should.be.approximately(1, 1e-6);
         });
 
-        it('should serialize empty Babylon scene to glTF with only asset property', (done) => {
-            mocha.timeout(10000);
-
+        it('should serialize empty Babylon scene to glTF with only asset property', () => {
             const scene = new BABYLON.Scene(subject);
-            scene.executeWhenReady(function () {
-                const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-                const glTFData = glTFExporter._generateGLTF('test');
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
                 const jsonString = glTFData.glTFFiles['test.gltf'] as string;
                 const jsonData = JSON.parse(jsonString);
 
                 Object.keys(jsonData).length.should.be.equal(1);
                 jsonData.asset.version.should.be.equal("2.0");
                 jsonData.asset.generator.should.be.equal("BabylonJS");
-
-                done();
             });
         });
 
-        it('should serialize sphere geometry in scene to glTF', (done) => {
-            mocha.timeout(10000);
+        it('should serialize sphere geometry in scene to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             BABYLON.Mesh.CreateSphere('sphere', 16, 2, scene);
 
-            scene.executeWhenReady(function () {
-                const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-                const glTFData = glTFExporter._generateGLTF('test');
-                const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-                const jsonData = JSON.parse(jsonString);
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test')
+                .then(glTFData => {
+                    const jsonString = glTFData.glTFFiles['test.gltf'] as string;
+                    const jsonData = JSON.parse(jsonString);
 
-                // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials
-                Object.keys(jsonData).length.should.be.equal(9);
+                    // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials
+                    Object.keys(jsonData).length.should.be.equal(9);
 
-                // positions, normals, texture coords, indices
-                jsonData.accessors.length.should.be.equal(4);
+                    // positions, normals, indices
+                    jsonData.accessors.length.should.be.equal(3);
 
-                // generator, version
-                Object.keys(jsonData.asset).length.should.be.equal(2);
+                    // generator, version
+                    Object.keys(jsonData.asset).length.should.be.equal(2);
 
-                jsonData.buffers.length.should.be.equal(1);
+                    jsonData.buffers.length.should.be.equal(1);
 
-                // positions, normals, texture coords, indices
-                jsonData.bufferViews.length.should.be.equal(4);
+                    // positions, normals, texture coords, indices
+                    jsonData.bufferViews.length.should.be.equal(4);
 
-                jsonData.meshes.length.should.be.equal(1);
+                    jsonData.meshes.length.should.be.equal(1);
 
-                jsonData.nodes.length.should.be.equal(1);
+                    jsonData.nodes.length.should.be.equal(1);
 
-                jsonData.scenes.length.should.be.equal(1);
-
-                jsonData.scene.should.be.equal(0);
+                    jsonData.scenes.length.should.be.equal(1);
 
-                done();
-            });
+                    jsonData.scene.should.be.equal(0);
+                });
         });
 
-        it('should serialize alpha mode and cutoff', (done) => {
-            mocha.timeout(10000);
+        it('should serialize alpha mode and cutoff', () => {
             const scene = new BABYLON.Scene(subject);
 
             const plane = BABYLON.Mesh.CreatePlane('plane', 120, scene);
@@ -145,9 +137,8 @@ describe('Babylon glTF Serializer', () => {
 
             plane.material = babylonPBRMetalRoughMaterial;
 
-            scene.executeWhenReady(function () {
-                const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-                const glTFData = glTFExporter._generateGLTF('test');
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
                 const jsonString = glTFData.glTFFiles['test.gltf'] as string;
                 const jsonData = JSON.parse(jsonString);
 
@@ -159,243 +150,228 @@ describe('Babylon glTF Serializer', () => {
                 jsonData.materials[0].alphaMode.should.be.equal('MASK');
 
                 jsonData.materials[0].alphaCutoff.should.be.equal(alphaCutoff);
-
-                done();
             });
         });
-        it('should serialize single component translation animation to glTF', (done) => {
-            mocha.timeout(10000);
+        it('should serialize single component translation animation to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);
             let keys: BABYLON.IAnimationKey[] = [];
             keys.push({
-            frame: 0,
-            value: 1
+                frame: 0,
+                value: 1
             });
             keys.push({
-            frame: 20,
-            value: 0.2
+                frame: 20,
+                value: 0.2
             });
             keys.push({
-            frame: 40,
-            value: 1
+                frame: 40,
+                value: 1
             });
             let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'position.y', 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             animationBoxT.setKeys(keys);
             box.animations.push(animationBoxT);
-            scene.executeWhenReady(function () {
-            const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-            const glTFData = glTFExporter._generateGLTF('test');
-            const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-            const jsonData = JSON.parse(jsonString);
-            jsonData.animations.length.should.be.equal(1);
-            const animation = jsonData.animations[0];
-            animation.channels.length.should.be.equal(1);
-            animation.channels[0].sampler.should.be.equal(0);
-            animation.channels[0].target.node.should.be.equal(0);
-            animation.channels[0].target.path.should.be.equal('translation');
-            jsonData.animations[0].samplers.length.should.be.equal(1);
-            // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
-            Object.keys(jsonData).length.should.be.equal(10);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.accessors.length.should.be.equal(6);
-            // generator, version
-            Object.keys(jsonData.asset).length.should.be.equal(2);
-            jsonData.buffers.length.should.be.equal(1);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.bufferViews.length.should.be.equal(6);
-            jsonData.meshes.length.should.be.equal(1);
-            jsonData.nodes.length.should.be.equal(1);
-            jsonData.scenes.length.should.be.equal(1);
-            jsonData.scene.should.be.equal(0);
-            done();
-            });
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
+                const jsonString = glTFData.glTFFiles['test.gltf'] as string;
+                const jsonData = JSON.parse(jsonString);
+                jsonData.animations.length.should.be.equal(1);
+                const animation = jsonData.animations[0];
+                animation.channels.length.should.be.equal(1);
+                animation.channels[0].sampler.should.be.equal(0);
+                animation.channels[0].target.node.should.be.equal(0);
+                animation.channels[0].target.path.should.be.equal('translation');
+                jsonData.animations[0].samplers.length.should.be.equal(1);
+                // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
+                Object.keys(jsonData).length.should.be.equal(10);
+                // positions, normals, indices, animation keyframe data, animation data
+                jsonData.accessors.length.should.be.equal(5);
+                // generator, version
+                Object.keys(jsonData.asset).length.should.be.equal(2);
+                jsonData.buffers.length.should.be.equal(1);
+                // positions, normals, texture coords, indices, animation keyframe data, animation data
+                jsonData.bufferViews.length.should.be.equal(6);
+                jsonData.meshes.length.should.be.equal(1);
+                jsonData.nodes.length.should.be.equal(1);
+                jsonData.scenes.length.should.be.equal(1);
+                jsonData.scene.should.be.equal(0);
             });
-            it('should serialize translation animation to glTF', (done) => {
-            mocha.timeout(10000);
+        });
+        it('should serialize translation animation to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);
             let keys: BABYLON.IAnimationKey[] = [];
             keys.push({
-            frame: 0,
-            value: new BABYLON.Vector3(0.1, 0.1, 0.1)
+                frame: 0,
+                value: new BABYLON.Vector3(0.1, 0.1, 0.1)
             });
             keys.push({
-            frame: 20,
-            value: BABYLON.Vector3.One()
+                frame: 20,
+                value: BABYLON.Vector3.One()
             });
             keys.push({
-            frame: 40,
-            value: new BABYLON.Vector3(0.1, 0.1, 0.1)
+                frame: 40,
+                value: new BABYLON.Vector3(0.1, 0.1, 0.1)
             });
             let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'position', 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             animationBoxT.setKeys(keys);
             box.animations.push(animationBoxT);
-            scene.executeWhenReady(function () {
-            const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-            const glTFData = glTFExporter._generateGLTF('test');
-            const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-            const jsonData = JSON.parse(jsonString);
-            jsonData.animations.length.should.be.equal(1);
-            const animation = jsonData.animations[0];
-            animation.channels.length.should.be.equal(1);
-            animation.channels[0].sampler.should.be.equal(0);
-            animation.channels[0].target.node.should.be.equal(0);
-            animation.channels[0].target.path.should.be.equal('translation');
-            animation.samplers.length.should.be.equal(1);
-            animation.samplers[0].interpolation.should.be.equal('LINEAR');
-            animation.samplers[0].input.should.be.equal(4);
-            animation.samplers[0].output.should.be.equal(5);
-            jsonData.animations[0].samplers.length.should.be.equal(1);
-            // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
-            Object.keys(jsonData).length.should.be.equal(10);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.accessors.length.should.be.equal(6);
-            // generator, version
-            Object.keys(jsonData.asset).length.should.be.equal(2);
-            jsonData.buffers.length.should.be.equal(1);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.bufferViews.length.should.be.equal(6);
-            jsonData.meshes.length.should.be.equal(1);
-            jsonData.nodes.length.should.be.equal(1);
-            jsonData.scenes.length.should.be.equal(1);
-            jsonData.scene.should.be.equal(0);
-            done();
-            });
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
+                const jsonString = glTFData.glTFFiles['test.gltf'] as string;
+                const jsonData = JSON.parse(jsonString);
+                jsonData.animations.length.should.be.equal(1);
+                const animation = jsonData.animations[0];
+                animation.channels.length.should.be.equal(1);
+                animation.channels[0].sampler.should.be.equal(0);
+                animation.channels[0].target.node.should.be.equal(0);
+                animation.channels[0].target.path.should.be.equal('translation');
+                animation.samplers.length.should.be.equal(1);
+                animation.samplers[0].interpolation.should.be.equal('LINEAR');
+                animation.samplers[0].input.should.be.equal(3);
+                animation.samplers[0].output.should.be.equal(4);
+                jsonData.animations[0].samplers.length.should.be.equal(1);
+                // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
+                Object.keys(jsonData).length.should.be.equal(10);
+                // positions, normals, indices, animation keyframe data, animation data
+                jsonData.accessors.length.should.be.equal(5);
+                // generator, version
+                Object.keys(jsonData.asset).length.should.be.equal(2);
+                jsonData.buffers.length.should.be.equal(1);
+                // positions, normals, texture coords, indices, animation keyframe data, animation data
+                jsonData.bufferViews.length.should.be.equal(6);
+                jsonData.meshes.length.should.be.equal(1);
+                jsonData.nodes.length.should.be.equal(1);
+                jsonData.scenes.length.should.be.equal(1);
+                jsonData.scene.should.be.equal(0);
             });
-            it('should serialize scale animation to glTF', (done) => {
-            mocha.timeout(10000);
+        });
+        it('should serialize scale animation to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);
             let keys: BABYLON.IAnimationKey[] = [];
             keys.push({
-            frame: 0,
-            value: new BABYLON.Vector3(0.1, 0.1, 0.1)
+                frame: 0,
+                value: new BABYLON.Vector3(0.1, 0.1, 0.1)
             });
             keys.push({
-            frame: 20,
-            value: BABYLON.Vector3.One()
+                frame: 20,
+                value: BABYLON.Vector3.One()
             });
             keys.push({
-            frame: 40,
-            value: new BABYLON.Vector3(0.1, 0.1, 0.1)
+                frame: 40,
+                value: new BABYLON.Vector3(0.1, 0.1, 0.1)
             });
             let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'scaling', 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             animationBoxT.setKeys(keys);
             box.animations.push(animationBoxT);
-            scene.executeWhenReady(function () {
-            const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-            const glTFData = glTFExporter._generateGLTF('test');
-            const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-            const jsonData = JSON.parse(jsonString);
-            jsonData.animations.length.should.be.equal(1);
-            const animation = jsonData.animations[0];
-            animation.channels.length.should.be.equal(1);
-            animation.channels[0].sampler.should.be.equal(0);
-            animation.channels[0].target.node.should.be.equal(0);
-            animation.channels[0].target.path.should.be.equal('scale');
-            animation.samplers.length.should.be.equal(1);
-            animation.samplers[0].interpolation.should.be.equal('LINEAR');
-            animation.samplers[0].input.should.be.equal(4);
-            animation.samplers[0].output.should.be.equal(5);
-            jsonData.animations[0].samplers.length.should.be.equal(1);
-            // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
-            Object.keys(jsonData).length.should.be.equal(10);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.accessors.length.should.be.equal(6);
-            // generator, version
-            Object.keys(jsonData.asset).length.should.be.equal(2);
-            jsonData.buffers.length.should.be.equal(1);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.bufferViews.length.should.be.equal(6);
-            jsonData.meshes.length.should.be.equal(1);
-            jsonData.nodes.length.should.be.equal(1);
-            jsonData.scenes.length.should.be.equal(1);
-            jsonData.scene.should.be.equal(0);
-            done();
-            });
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
+                const jsonString = glTFData.glTFFiles['test.gltf'] as string;
+                const jsonData = JSON.parse(jsonString);
+                jsonData.animations.length.should.be.equal(1);
+                const animation = jsonData.animations[0];
+                animation.channels.length.should.be.equal(1);
+                animation.channels[0].sampler.should.be.equal(0);
+                animation.channels[0].target.node.should.be.equal(0);
+                animation.channels[0].target.path.should.be.equal('scale');
+                animation.samplers.length.should.be.equal(1);
+                animation.samplers[0].interpolation.should.be.equal('LINEAR');
+                animation.samplers[0].input.should.be.equal(3);
+                animation.samplers[0].output.should.be.equal(4);
+                jsonData.animations[0].samplers.length.should.be.equal(1);
+                // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
+                Object.keys(jsonData).length.should.be.equal(10);
+                // positions, normals, indices, animation keyframe data, animation data
+                jsonData.accessors.length.should.be.equal(5);
+                // generator, version
+                Object.keys(jsonData.asset).length.should.be.equal(2);
+                jsonData.buffers.length.should.be.equal(1);
+                // positions, normals, texture coords, indices, animation keyframe data, animation data
+                jsonData.bufferViews.length.should.be.equal(6);
+                jsonData.meshes.length.should.be.equal(1);
+                jsonData.nodes.length.should.be.equal(1);
+                jsonData.scenes.length.should.be.equal(1);
+                jsonData.scene.should.be.equal(0);
             });
-            it('should serialize rotation quaternion animation to glTF', (done) => {
-            mocha.timeout(10000);
+        });
+        it('should serialize rotation quaternion animation to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);
             let keys: BABYLON.IAnimationKey[] = [];
             keys.push({
-            frame: 0,
-            value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
+                frame: 0,
+                value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
             });
             keys.push({
-            frame: 20,
-            value: BABYLON.Quaternion.Identity()
+                frame: 20,
+                value: BABYLON.Quaternion.Identity()
             });
             keys.push({
-            frame: 40,
-            value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
+                frame: 40,
+                value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
             });
             let animationBoxT = new BABYLON.Animation('boxAnimation_translation', 'rotationQuaternion', 30, BABYLON.Animation.ANIMATIONTYPE_QUATERNION, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             animationBoxT.setKeys(keys);
             box.animations.push(animationBoxT);
-            scene.executeWhenReady(function () {
-            const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-            const glTFData = glTFExporter._generateGLTF('test');
-            const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-            const jsonData = JSON.parse(jsonString);
-            jsonData.animations.length.should.be.equal(1);
-            const animation = jsonData.animations[0];
-            animation.channels.length.should.be.equal(1);
-            animation.channels[0].sampler.should.be.equal(0);
-            animation.channels[0].target.node.should.be.equal(0);
-            animation.channels[0].target.path.should.be.equal('rotation');
-            animation.samplers.length.should.be.equal(1);
-            animation.samplers[0].interpolation.should.be.equal('LINEAR');
-            animation.samplers[0].input.should.be.equal(4);
-            animation.samplers[0].output.should.be.equal(5);
-            jsonData.animations[0].samplers.length.should.be.equal(1);
-            // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
-            Object.keys(jsonData).length.should.be.equal(10);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.accessors.length.should.be.equal(6);
-            // generator, version
-            Object.keys(jsonData.asset).length.should.be.equal(2);
-            jsonData.buffers.length.should.be.equal(1);
-            // positions, normals, texture coords, indices, animation keyframe data, animation data
-            jsonData.bufferViews.length.should.be.equal(6);
-            jsonData.meshes.length.should.be.equal(1);
-            jsonData.nodes.length.should.be.equal(1);
-            jsonData.scenes.length.should.be.equal(1);
-            jsonData.scene.should.be.equal(0);
-            done();
-            });
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
+                const jsonString = glTFData.glTFFiles['test.gltf'] as string;
+                const jsonData = JSON.parse(jsonString);
+                jsonData.animations.length.should.be.equal(1);
+                const animation = jsonData.animations[0];
+                animation.channels.length.should.be.equal(1);
+                animation.channels[0].sampler.should.be.equal(0);
+                animation.channels[0].target.node.should.be.equal(0);
+                animation.channels[0].target.path.should.be.equal('rotation');
+                animation.samplers.length.should.be.equal(1);
+                animation.samplers[0].interpolation.should.be.equal('LINEAR');
+                animation.samplers[0].input.should.be.equal(3);
+                animation.samplers[0].output.should.be.equal(4);
+                jsonData.animations[0].samplers.length.should.be.equal(1);
+                // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
+                Object.keys(jsonData).length.should.be.equal(10);
+                // positions, normals, indices, animation keyframe data, animation data
+                jsonData.accessors.length.should.be.equal(5);
+                // generator, version
+                Object.keys(jsonData.asset).length.should.be.equal(2);
+                jsonData.buffers.length.should.be.equal(1);
+                // positions, normals, texture coords, indices, animation keyframe data, animation data
+                jsonData.bufferViews.length.should.be.equal(6);
+                jsonData.meshes.length.should.be.equal(1);
+                jsonData.nodes.length.should.be.equal(1);
+                jsonData.scenes.length.should.be.equal(1);
+                jsonData.scene.should.be.equal(0);
             });
-            it('should serialize combined animations to glTF', (done) => {
-            mocha.timeout(10000);
+        });
+        it('should serialize combined animations to glTF', () => {
             const scene = new BABYLON.Scene(subject);
             const box = BABYLON.Mesh.CreateBox('box', 1, scene);
             const rotationKeyFrames: BABYLON.IAnimationKey[] = [];
             rotationKeyFrames.push({
-            frame: 0,
-            value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
+                frame: 0,
+                value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
             });
             rotationKeyFrames.push({
-            frame: 20,
-            value: BABYLON.Quaternion.Identity()
+                frame: 20,
+                value: BABYLON.Quaternion.Identity()
             });
             rotationKeyFrames.push({
-            frame: 40,
-            value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
+                frame: 40,
+                value: new BABYLON.Quaternion(0.707, 0.0, 0.0, 0.707)
             });
             const scaleKeyFrames: BABYLON.IAnimationKey[] = [];
             scaleKeyFrames.push({
-            frame: 0,
-            value: new BABYLON.Vector3(0.1, 0.1, 0.1)
+                frame: 0,
+                value: new BABYLON.Vector3(0.1, 0.1, 0.1)
             });
             scaleKeyFrames.push({
-            frame: 20,
-            value: BABYLON.Vector3.One()
+                frame: 20,
+                value: BABYLON.Vector3.One()
             });
             scaleKeyFrames.push({
-            frame: 40,
-            value: new BABYLON.Vector3(0.1, 0.1, 0.1)
+                frame: 40,
+                value: new BABYLON.Vector3(0.1, 0.1, 0.1)
             });
             let rotationAnimationBox = new BABYLON.Animation('boxAnimation_rotation', 'rotationQuaternion', 30, BABYLON.Animation.ANIMATIONTYPE_QUATERNION, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             rotationAnimationBox.setKeys(rotationKeyFrames);
@@ -403,46 +379,43 @@ describe('Babylon glTF Serializer', () => {
             let scaleAnimationBox = new BABYLON.Animation('boxAnimation_scale', 'scaling', 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
             scaleAnimationBox.setKeys(scaleKeyFrames);
             box.animations.push(scaleAnimationBox);
-            scene.executeWhenReady(function () {
-            const glTFExporter = new BABYLON.GLTF2._Exporter(scene);
-            const glTFData = glTFExporter._generateGLTF('test');
-            const jsonString = glTFData.glTFFiles['test.gltf'] as string;
-            const jsonData = JSON.parse(jsonString);
-            jsonData.animations.length.should.be.equal(2);
-            
-            let animation = jsonData.animations[0];
-            animation.channels.length.should.be.equal(1);
-            animation.channels[0].sampler.should.be.equal(0);
-            animation.channels[0].target.node.should.be.equal(0);
-            animation.channels[0].target.path.should.be.equal('rotation');
-            animation.samplers.length.should.be.equal(1);
-            animation.samplers[0].interpolation.should.be.equal('LINEAR');
-            animation.samplers[0].input.should.be.equal(4);
-            animation.samplers[0].output.should.be.equal(5);
-            animation = jsonData.animations[1];
-            animation.channels[0].sampler.should.be.equal(0);
-            animation.channels[0].target.node.should.be.equal(0);
-            animation.channels[0].target.path.should.be.equal('scale');
-            animation.samplers.length.should.be.equal(1);
-            animation.samplers[0].interpolation.should.be.equal('LINEAR');
-            animation.samplers[0].input.should.be.equal(6);
-            animation.samplers[0].output.should.be.equal(7);
-            // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
-            Object.keys(jsonData).length.should.be.equal(10);
-            // positions, normals, texture coords, indices, rotation animation keyframe data, rotation animation data, scale animation keyframe data, scale animation data
-            jsonData.accessors.length.should.be.equal(8);
-            // generator, version
-            Object.keys(jsonData.asset).length.should.be.equal(2);
-            jsonData.buffers.length.should.be.equal(1);
-            // positions, normals, texture coords, indices, rotation animation keyframe data, rotation animation data, scale animation keyframe data, scale animation data 
-            jsonData.bufferViews.length.should.be.equal(8);
-            jsonData.meshes.length.should.be.equal(1);
-            jsonData.nodes.length.should.be.equal(1);
-            jsonData.scenes.length.should.be.equal(1);
-            jsonData.scene.should.be.equal(0);
-            done();
-            });
+
+            return BABYLON.GLTF2Export.GLTFAsync(scene, 'test').then(glTFData => {
+                const jsonString = glTFData.glTFFiles['test.gltf'] as string;
+                const jsonData = JSON.parse(jsonString);
+                jsonData.animations.length.should.be.equal(2);
+
+                let animation = jsonData.animations[0];
+                animation.channels.length.should.be.equal(1);
+                animation.channels[0].sampler.should.be.equal(0);
+                animation.channels[0].target.node.should.be.equal(0);
+                animation.channels[0].target.path.should.be.equal('rotation');
+                animation.samplers.length.should.be.equal(1);
+                animation.samplers[0].interpolation.should.be.equal('LINEAR');
+                animation.samplers[0].input.should.be.equal(3);
+                animation.samplers[0].output.should.be.equal(4);
+                animation = jsonData.animations[1];
+                animation.channels[0].sampler.should.be.equal(0);
+                animation.channels[0].target.node.should.be.equal(0);
+                animation.channels[0].target.path.should.be.equal('scale');
+                animation.samplers.length.should.be.equal(1);
+                animation.samplers[0].interpolation.should.be.equal('LINEAR');
+                animation.samplers[0].input.should.be.equal(5);
+                animation.samplers[0].output.should.be.equal(6);
+                // accessors, asset, buffers, bufferViews, meshes, nodes, scene, scenes, materials, animations
+                Object.keys(jsonData).length.should.be.equal(10);
+                // positions, normals, indices, rotation animation keyframe data, rotation animation data, scale animation keyframe data, scale animation data
+                jsonData.accessors.length.should.be.equal(7);
+                // generator, version
+                Object.keys(jsonData.asset).length.should.be.equal(2);
+                jsonData.buffers.length.should.be.equal(1);
+                // positions, normals, texture coords, indices, rotation animation keyframe data, rotation animation data, scale animation keyframe data, scale animation data 
+                jsonData.bufferViews.length.should.be.equal(8);
+                jsonData.meshes.length.should.be.equal(1);
+                jsonData.nodes.length.should.be.equal(1);
+                jsonData.scenes.length.should.be.equal(1);
+                jsonData.scene.should.be.equal(0);
             });
-            
+        });
     });
 });