瀏覽代碼

Factor out binary extension into its own file

Gary Hsu 9 年之前
父節點
當前提交
ef7cd846da

+ 4 - 2
loaders/config.json

@@ -16,7 +16,9 @@
       "files": [
         "glTF/babylon.glTFFileLoaderInterfaces.ts",
         "glTF/babylon.glTFFileLoader.ts",
-        "glTF/babylon.glTFFileLoaderUtils.ts"
+        "glTF/babylon.glTFFileLoaderUtils.ts",
+        "glTF/babylon.glTFFileLoaderExtension.ts",
+        "glTF/babylon.glTFBinaryExtension.ts"
       ],
       "output": "./glTF",
       "filename": "babylon.glTFFileLoader.js"
@@ -24,6 +26,6 @@
   },
 
   "defines": [
-    "../dist/*.d.ts"
+    "../dist/preview release/*.d.ts"
   ]
 }

+ 181 - 0
loaders/glTF/babylon.glTFBinaryExtension.ts

@@ -0,0 +1,181 @@
+module BABYLON {
+    const BinaryExtensionBufferName = "binary_glTF";
+
+    enum EContentFormat {
+        JSON = 0
+    };
+
+    interface IGLTFBinaryExtension {
+        content: Object;
+        body: Uint8Array;
+    };
+
+    interface IGLTFBinaryExtensionShader {
+        bufferView: string;
+    };
+
+    interface IGLTFBinaryExtensionImage {
+        bufferView: string;
+        mimeType: string;
+        height: number;
+        width: number;
+    };
+
+    export class GLTFBinaryExtension extends GLTFFileLoaderExtension {
+        public constructor() {
+            super("KHR_binary_glTF");
+        }
+
+        public loadRuntimeAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
+            if (!(data instanceof ArrayBuffer)) {
+                return false;
+            }
+
+            var binary: IGLTFBinaryExtension = this._parseBinary(<ArrayBuffer>data);
+            if (!binary) {
+                onError();
+                return true;
+            }
+
+            var gltfRuntime = GLTFUtils.CreateGlTFRuntime(binary.content, scene, rootUrl);
+
+            if (gltfRuntime.extensionsUsed.indexOf(this.name) == -1) {
+                Tools.Warn("glTF binary file does not have " + this.name + " specified in extensionsUsed");
+            }
+
+            gltfRuntime.loadedBufferViews[BinaryExtensionBufferName] = binary.body;
+
+            onSuccess(gltfRuntime);
+            return true;
+        }
+
+        public loadBufferAsync(gltfRuntime: IGLTFRuntime, buffer: IGLTFBuffer, onSuccess: (data: ArrayBufferView) => void, onError: () => void): boolean {
+            if (gltfRuntime.extensionsUsed.indexOf(this.name) == -1) {
+                return false;
+            }
+
+            onSuccess(gltfRuntime.loadedBufferViews[BinaryExtensionBufferName]);
+            return true;
+        }
+
+        public loadTextureAsync(gltfRuntime: IGLTFRuntime, texture: IGLTFTexture, onSuccess: (texture: Texture) => void, onError: () => void): boolean {
+            var source: IGLTFImage = gltfRuntime.images[texture.source];
+            if (!source.extensions || !(this.name in source.extensions)) {
+                return false;
+            }
+
+            var sourceExt: IGLTFBinaryExtensionImage = source.extensions[this.name];
+            var sampler: IGLTFSampler = gltfRuntime.samplers[texture.sampler];
+
+            var createMipMaps =
+                (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_NEAREST) ||
+                (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_LINEAR) ||
+                (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_NEAREST) ||
+                (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_LINEAR);
+
+            var samplingMode = Texture.BILINEAR_SAMPLINGMODE;
+
+            var bufferView: IGLTFBufferView = gltfRuntime.bufferViews[sourceExt.bufferView];
+            var imageBytes = GLTFUtils.GetBufferFromBufferView(gltfRuntime, bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE);
+            var blob = new Blob([imageBytes], { type: sourceExt.mimeType });
+            var blobURL = URL.createObjectURL(blob);
+            var revokeBlobURL = () => URL.revokeObjectURL(blobURL);
+            var newTexture = new Texture(blobURL, gltfRuntime.scene, !createMipMaps, true, samplingMode, revokeBlobURL, revokeBlobURL);
+
+            newTexture.wrapU = GLTFUtils.GetWrapMode(sampler.wrapS);
+            newTexture.wrapV = GLTFUtils.GetWrapMode(sampler.wrapT);
+            newTexture.name = name;
+
+            texture.babylonTexture = newTexture;
+            onSuccess(newTexture);
+            return true;
+        }
+
+        public loadShaderStringAsync(gltfRuntime: IGLTFRuntime, shader: IGLTFShader, onSuccess: (shaderString: string) => void, onError: () => void): boolean {
+            if (!shader.extensions || !(this.name in shader.extensions)) {
+                return false;
+            }
+
+            var binaryExtensionShader: IGLTFBinaryExtensionShader = shader.extensions[this.name];
+            var bufferView: IGLTFBufferView = gltfRuntime.bufferViews[binaryExtensionShader.bufferView];
+            var shaderBytes = GLTFUtils.GetBufferFromBufferView(gltfRuntime, bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE);
+            var shaderString = GLTFUtils.DecodeBufferToText(shaderBytes);
+            onSuccess(shaderString);
+            return true;
+        }
+
+        // Parses a glTF binary array buffer into its content and body
+        private _parseBinary(data: ArrayBuffer): IGLTFBinaryExtension {
+            var binaryReader = new BinaryReader(data);
+
+            var magic = GLTFUtils.DecodeBufferToText(binaryReader.getUint8Array(4));
+            if (magic != "glTF") {
+                Tools.Error("Unexpected magic: " + magic);
+                return null;
+            }
+
+            var version = binaryReader.getUint32();
+            if (version != 1) {
+                Tools.Error("Unsupported version: " + version);
+                return null;
+            }
+
+            var length = binaryReader.getUint32();
+            if (length != data.byteLength) {
+                Tools.Error("Length in header does not match actual data length: " + length + " != " + data.byteLength);
+                return null;
+            }
+
+            var contentLength = binaryReader.getUint32();
+            var contentFormat = <EContentFormat>binaryReader.getUint32();
+
+            var content: Object;
+            switch (contentFormat) {
+                case EContentFormat.JSON:
+                    var jsonText = GLTFUtils.DecodeBufferToText(binaryReader.getUint8Array(contentLength));
+                    content = JSON.parse(jsonText);
+                    break;
+                default:
+                    Tools.Error("Unexpected content format: " + contentFormat);
+                    return null;
+            }
+
+            var body = binaryReader.getUint8Array();
+
+            return {
+                content: content,
+                body: body
+            };
+        };
+    }
+
+    class BinaryReader {
+        private _arrayBuffer: ArrayBuffer;
+        private _dataView: DataView;
+        private _byteOffset: number;
+
+        constructor(arrayBuffer: ArrayBuffer) {
+            this._arrayBuffer = arrayBuffer;
+            this._dataView = new DataView(arrayBuffer);
+            this._byteOffset = 0;
+        }
+
+        public getUint32(): number {
+            var value = this._dataView.getUint32(this._byteOffset, true);
+            this._byteOffset += 4;
+            return value;
+        }
+
+        public getUint8Array(length?: number): Uint8Array {
+            if (!length) {
+                length = this._arrayBuffer.byteLength - this._byteOffset;
+            }
+
+            var value = new Uint8Array(this._arrayBuffer, this._byteOffset, length);
+            this._byteOffset += length;
+            return value;
+        }
+    }
+
+    GLTFFileLoader.RegisterExtension(new GLTFBinaryExtension());
+}

File diff suppressed because it is too large
+ 554 - 401
loaders/glTF/babylon.glTFFileLoader.js


+ 168 - 540
loaders/glTF/babylon.glTFFileLoader.ts

@@ -63,37 +63,11 @@
     /**
     * Values
     */
-    var glTFTransforms =    ["MODEL", "VIEW", "PROJECTION", "MODELVIEW", "MODELVIEWPROJECTION", "JOINTMATRIX"];
+    var glTFTransforms = ["MODEL", "VIEW", "PROJECTION", "MODELVIEW", "MODELVIEWPROJECTION", "JOINTMATRIX"];
     var babylonTransforms = ["world", "view", "projection", "worldView", "worldViewProjection", "mBones"];
 
-    var glTFAnimationPaths =    ["translation", "rotation",           "scale"];
-    var babylonAnimationPaths = ["position",    "rotationQuaternion", "scaling"];
-
-    /**
-    * Parse
-    */
-    var parseBuffers = (parsedBuffers: any, gltfRuntime: IGLTFRuntime) => {
-        for (var buf in parsedBuffers) {
-            var parsedBuffer = parsedBuffers[buf];
-            gltfRuntime.buffers[buf] = parsedBuffer;
-            gltfRuntime.buffersCount++;
-        }
-    };
-
-    var parseShaders = (parsedShaders: any, gltfRuntime: IGLTFRuntime) => {
-        for (var sha in parsedShaders) {
-            var parsedShader = parsedShaders[sha];
-            gltfRuntime.shaders[sha] = parsedShader;
-            gltfRuntime.shaderscount++;
-        }
-    };
-
-    var parseObject = (parsedObjects: any, runtimeProperty: string, gltfRuntime: IGLTFRuntime) => {
-        for (var object in parsedObjects) {
-            var parsedObject = parsedObjects[object];
-            gltfRuntime[runtimeProperty][object] = parsedObject;
-        }
-    };
+    var glTFAnimationPaths = ["translation", "rotation", "scale"];
+    var babylonAnimationPaths = ["position", "rotationQuaternion", "scaling"];
 
     /**
     * Utils
@@ -133,59 +107,48 @@
         }
     };
 
-    // Returns if at least one extension needs the loader to skip its behavior
-    var extensionNeedToSkipDefaultBehavior = (gltfRuntime: IGLTFRuntime, id: string, name: string, extensionsObject: any, object?: any): boolean => {
-        if (!extensionsObject) {
-            return false;
-        }
-
-        for (var i = 0; i < GLTFFileLoader.Extensions.length; i++) {
-            var loaderExtension = <IGLTFLoaderExtension<any, any>>GLTFFileLoader.Extensions[i];
-
-            for (var extensionName in extensionsObject) {
-                var extension = extensionsObject[extensionName];
-                if (loaderExtension.extensionName == extensionName && loaderExtension.needToSkipDefaultLoaderBehavior(id, extension)) {
-                    loaderExtension.apply(gltfRuntime, id, name, extension, null);
-
-                    // If the extensions is defined but not set in top-root "extensionUsed" string array
-                    if (gltfRuntime.extensionsUsed.indexOf(extensionName) === -1) {
-                        Tools.Warn("Extension \"" + extensionName + "\" is used but not declared in \"extensionsUsed\"");
-                    }
-
-                    return true;
-                }
+    var applyExtensions = <ObjectT extends Object>(func: (loaderExtension: GLTFFileLoaderExtension) => boolean, defaultFunc: () => void): void => {
+        for (var extensionName in GLTFFileLoader.Extensions) {
+            var loaderExtension = GLTFFileLoader.Extensions[extensionName];
+            if (func(loaderExtension)) {
+                return;
             }
         }
 
-        return false;
+        defaultFunc();
     }
 
-    // Call an extension giving a specific extension object
-    var callExtension = <T>(gltfRuntime: IGLTFRuntime, id: string, name: string, extensionsObject: any, object: T): boolean => {
-        if (!extensionsObject) {
-            return;
-        }
-
-        var foundAtLeastOneExtension = false;
-
-        for (var i = 0; i < GLTFFileLoader.Extensions.length; i++) {
-            var loaderExtension = <IGLTFLoaderExtension<any, T>>GLTFFileLoader.Extensions[i];
+    var loadRuntimeAsync = (scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): void => {
+        applyExtensions<IGLTFRuntime>(loaderExtension => {
+            return loaderExtension.loadRuntimeAsync(scene, data, rootUrl, onSuccess, onError);
+        }, () => {
+            onSuccess(GLTFUtils.CreateGlTFRuntime(JSON.parse(<string>data), scene, rootUrl));
+        });
+    }
 
-            for (var extensionName in extensionsObject) {
-                if (loaderExtension.extensionName == extensionName) {
-                    loaderExtension.apply(gltfRuntime, id, name, extensionsObject[extensionName], object);
-                    foundAtLeastOneExtension = true;
+    var loadBufferAsync = (gltfRuntime: IGLTFRuntime, buffer: IGLTFBuffer, onSuccess: (bufferView: ArrayBufferView) => void, onError: () => void): void => {
+        applyExtensions<Texture>(loaderExtension => {
+            return loaderExtension.loadBufferAsync(gltfRuntime, buffer, onSuccess, onError);
+        }, () => {
+            GLTFUtils.LoadBufferAsync(gltfRuntime, buffer, onSuccess, onError);
+        });
+    }
 
-                    // If the extensions is defined but not set in top-root "extensionUsed" string array
-                    if (gltfRuntime.extensionsUsed.indexOf(extensionName) === -1) {
-                        Tools.Warn("Extension \"" + extensionName + "\" is used but not declared in \"extensionsUsed\"");
-                    }
-                }
-            }
-        }
+    var loadTextureAsync = (gltfRuntime: IGLTFRuntime, texture: IGLTFTexture, onSuccess: (texture: Texture) => void, onError: () => void): void => {
+        applyExtensions<Texture>(loaderExtension => {
+            return loaderExtension.loadTextureAsync(gltfRuntime, texture, onSuccess, onError);
+        }, () => {
+            GLTFUtils.LoadTextureAsync(gltfRuntime, texture, onSuccess, onError);
+        });
+    }
 
-        return foundAtLeastOneExtension;
-    };
+    var loadShaderStringAsync = (gltfRuntime: IGLTFRuntime, shader: IGLTFShader, onSuccess: (shaderString: string) => void, onError: () => void): void => {
+        applyExtensions<Texture>(loaderExtension => {
+            return loaderExtension.loadShaderStringAsync(gltfRuntime, shader, onSuccess, onError);
+        }, () => {
+            GLTFUtils.LoadShaderAsync(gltfRuntime, shader, onSuccess, onError);
+        });
+    }
 
     /**
     * Returns the animation path (glTF -> Babylon)
@@ -207,10 +170,6 @@
         for (var anim in gltfRuntime.animations) {
             var animation: IGLTFAnimation = gltfRuntime.animations[anim];
 
-            if (extensionNeedToSkipDefaultBehavior(gltfRuntime, anim, animation.name, animation.extensions)) {
-                continue;
-            }
-
             var lastAnimation: Animation = null;
 
             for (var i = 0; i < animation.channels.length; i++) {
@@ -338,9 +297,6 @@
 
                 lastAnimation = babylonAnimation;
 
-                // If extension for animations, call it
-                callExtension(gltfRuntime, anim, animation.name, animation.extensions, lastAnimation);
-
                 gltfRuntime.scene.stopAnimation(targetNode);
                 gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true, 1.0);
             }
@@ -532,10 +488,6 @@
             return newSkeleton;
         }
 
-        if (extensionNeedToSkipDefaultBehavior(gltfRuntime, id, skins.name, skins.extensions, newSkeleton)) {
-            return newSkeleton;
-        }
-
         // Matrices
         var accessor = gltfRuntime.accessors[skins.inverseBindMatrices];
         var buffer = GLTFUtils.GetBufferFromAccessor(gltfRuntime, accessor);
@@ -632,14 +584,8 @@
 
             var bone = new Bone(node.name, newSkeleton, parentBone, mat);
             bone.id = id;
-
-            // If there is an extension for bones, call it
-            callExtension(gltfRuntime, id, node.name, node.extensions, bone);
         }
 
-        // If there is an extension for skinning, call it
-        callExtension(gltfRuntime, id, skins.name, skins.extensions, newSkeleton);
-
         // Polish
         var bones = newSkeleton.bones;
         newSkeleton.bones = [];
@@ -810,9 +756,6 @@
                 var subMesh = new SubMesh(index, verticesStarts[index], verticesCounts[index], indexStarts[index], indexCounts[index], newMesh, newMesh, true);
                 index++;
             }
-
-            // If there is an extension for mesh, call it
-            callExtension(gltfRuntime, id, mesh.name, mesh.extensions, newMesh);
         }
 
         // Finish
@@ -896,9 +839,6 @@
                 }
 
                 lastNode = newMesh;
-
-                // If extension
-                callExtension(gltfRuntime, node.skin, node.name, node.extensions, newMesh);
             }
         }
         else if (node.meshes) {
@@ -965,9 +905,6 @@
 
                     lastNode = spLight;
                 }
-
-                // If there is an extension for light, call it
-                callExtension(gltfRuntime, node.light, light.name, light.extensions, lastNode);
             }
         }
         // Cameras
@@ -1003,9 +940,6 @@
 
                     lastNode = persCamera;
                 }
-
-                // If there is an extension for camera, call it
-                callExtension(gltfRuntime, node.camera, camera.name, camera.extensions, lastNode);
             }
         }
 
@@ -1034,9 +968,6 @@
             node.babylonNode = lastNode;
         }
 
-        // If there is an extension for the current node, call it
-        callExtension(gltfRuntime, id, node.name, node.extensions, lastNode);
-
         return lastNode;
     };
 
@@ -1094,83 +1025,6 @@
     };
 
     /**
-    * On a buffer is loaded
-    */
-    var onLoadBuffer = (gltfRuntime: IGLTFRuntime, buf: string) => {
-        return (data: any) => {
-            gltfRuntime.loadedBufferCount++;
-
-            if (!(data instanceof ArrayBuffer)) {
-                Tools.Error("Buffer named " + buf + " is not an array buffer");
-            }
-            else if ((<ArrayBuffer>data).byteLength != gltfRuntime.buffers[buf].byteLength) {
-                Tools.Error("Buffer named " + buf + " is length " + data.byteLength + ". Expected: " + gltfRuntime.buffers[buf].byteLength); // Improve error message
-            }
-
-            gltfRuntime.loadedBufferViews[buf] = new Uint8Array(data);
-
-            if (gltfRuntime.loadedBufferCount === gltfRuntime.buffersCount) {
-                onBuffersLoaded(gltfRuntime);
-            }
-        };
-    };
-
-    /**
-    * Error when loaded buffer
-    */
-    var onLoadBufferError = (gltfRuntime: IGLTFRuntime, buf: string) => {
-        return () => {
-            Tools.Error("Error when loading buffer named " + buf + " located at " + gltfRuntime.buffers[buf].uri);
-        };
-    };
-
-    /**
-    * Decode array buffer from base64
-    */
-    var decodeArrayBuffer = (base64: string): ArrayBuffer => {
-        var decodedString = atob(base64);
-        var bufferLength = decodedString.length;
-        var arraybuffer = new Uint8Array(new ArrayBuffer(bufferLength));
-
-        for (var i = 0; i < bufferLength; i++) {
-            arraybuffer[i] = decodedString.charCodeAt(i);
-        }
-
-        return arraybuffer.buffer;
-    };
-
-    /**
-    * Loads buffers (geometries)
-    */
-    var loadBuffers = (gltfRuntime: IGLTFRuntime) => {
-        for (var buf in gltfRuntime.buffers) {
-            if (gltfRuntime.extensionsUsed.indexOf(BinaryExtensionName) != -1 && buf === BinaryExtensionBufferName) {
-                gltfRuntime.loadedBufferCount++;
-
-                if (gltfRuntime.loadedBufferCount === gltfRuntime.buffersCount) {
-                    onBuffersLoaded(gltfRuntime);
-                }
-            }
-            else {
-                var buffer: IGLTFBuffer = gltfRuntime.buffers[buf];
-
-                if (buffer) {
-                    if (GLTFUtils.IsBase64(buffer.uri)) {
-                        var arrayBuffer = decodeArrayBuffer(buffer.uri.split(",")[1]);
-                        onLoadBuffer(gltfRuntime, buf)(arrayBuffer);
-                    }
-                    else {
-                        Tools.LoadFile(gltfRuntime.rootUrl + buffer.uri, onLoadBuffer(gltfRuntime, buf), null, null, true, onLoadBufferError(gltfRuntime, buf));
-                    }
-                }
-                else {
-                    Tools.Error("No buffer named : " + buf);
-                }
-            }
-        }
-    };
-
-    /**
     * onBind shaderrs callback to set uniforms and matrices
     */
     var onBindShaderMaterial = (mesh: Mesh, gltfRuntime: IGLTFRuntime, unTreatedUniforms: Object, shaderMaterial: ShaderMaterial, technique: IGLTFTechnique, material: IGLTFMaterial) => {
@@ -1236,56 +1090,17 @@
                 continue;
             }
 
-            // Texture (sampler2D)
-            if (type === EParameterType.SAMPLER_2D) {
-                var texture: IGLTFTexture = gltfRuntime.textures[<string>value];
-                var sampler: IGLTFSampler = gltfRuntime.samplers[texture.sampler];
-
-                if (!texture || !texture.source) {
-                    continue;
-                }
-
-                var source: IGLTFImage = gltfRuntime.images[texture.source];
-                var newTexture: Texture = null;
-
-                var createMipMaps = (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_NEAREST) ||
-                    (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_LINEAR) ||
-                    (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_NEAREST) ||
-                    (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_LINEAR);
-
-                var samplingMode = Texture.BILINEAR_SAMPLINGMODE;
-
-                if (gltfRuntime.extensionsUsed.indexOf(BinaryExtensionName) != -1 && BinaryExtensionName in source.extensions) {
-                    var binaryExtensionImage: IGLTFBinaryExtensionImage = source.extensions[BinaryExtensionName];
-                    var bufferView: IGLTFBufferView = gltfRuntime.bufferViews[binaryExtensionImage.bufferView];
-                    var imageBytes = GLTFUtils.GetBufferFromBufferView(gltfRuntime, bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE);
-                    var blob = new Blob([imageBytes], { type: binaryExtensionImage.mimeType });
-                    var blobURL = URL.createObjectURL(blob);
-                    var revokeBlobURL = () => URL.revokeObjectURL(blobURL);
-                    newTexture = new Texture(blobURL, gltfRuntime.scene, !createMipMaps, true, samplingMode, revokeBlobURL, revokeBlobURL);
-                } else if (GLTFUtils.IsBase64(source.uri)) {
-                    newTexture = new Texture(source.uri, gltfRuntime.scene, !createMipMaps, true, samplingMode, null, null, source.uri, true);
-                }
-                else {
-                    newTexture = new Texture(gltfRuntime.rootUrl + source.uri, gltfRuntime.scene, !createMipMaps, true, samplingMode);
-                }
-
-                newTexture.wrapU = GLTFUtils.GetWrapMode(sampler.wrapS);
-                newTexture.wrapV = GLTFUtils.GetWrapMode(sampler.wrapT);
-                newTexture.name = <string>value;
-
-                texture.babylonTexture = newTexture;
-
-                // If there is an extension for texture, sampler or image, call it
-                callExtension(gltfRuntime, <string>value, texture.name, texture.extensions, newTexture);
-                callExtension(gltfRuntime, texture.sampler, sampler.name, sampler.extensions, newTexture);
-                callExtension(gltfRuntime, texture.source, source.name, source.extensions, newTexture);
-
+            var onLoadTexture = (texture: Texture) => {
                 if (uniform.value) {
                     // Static uniform
-                    shaderMaterial.setTexture(unif, newTexture);
+                    shaderMaterial.setTexture(unif, texture);
                     delete unTreatedUniforms[unif];
                 }
+            };
+
+            // Texture (sampler2D)
+            if (type === EParameterType.SAMPLER_2D) {
+                loadTextureAsync(gltfRuntime, gltfRuntime.textures[<string>value], onLoadTexture, () => onLoadTexture(null));
             }
             // Others
             else {
@@ -1295,9 +1110,6 @@
                 }
             }
         }
-
-        // If there is an extension, call it
-        callExtension(gltfRuntime, material.name, material.name, material.extensions, shaderMaterial);
     };
 
     /**
@@ -1354,10 +1166,6 @@
         for (var mat in gltfRuntime.materials) {
             var material: IGLTFMaterial = gltfRuntime.materials[mat];
 
-            if (extensionNeedToSkipDefaultBehavior(gltfRuntime, mat, material.name, material.extensions)) {
-                continue;
-            }
-
             var technique: IGLTFTechnique = gltfRuntime.techniques[material.technique];
 
             var program: IGLTFProgram = gltfRuntime.programs[technique.program];
@@ -1501,104 +1309,12 @@
                 }
             }
         }
-
-        // Finish
-        loadBuffers(gltfRuntime);
-    };
-
-    /**
-    * Shader loaded
-    */
-    var onLoadShader = (gltfRuntime: IGLTFRuntime, sha: string) => {
-        return (data: any) => {
-            gltfRuntime.loadedShaderCount++;
-            Effect.ShadersStore[sha + (gltfRuntime.shaders[sha].type === EShaderType.VERTEX ? "VertexShader" : "PixelShader")] = data;
-
-            if (gltfRuntime.loadedShaderCount === gltfRuntime.shaderscount) {
-                onShadersLoaded(gltfRuntime);
-            }
-        };
-    };
-
-    /**
-    * Error callback when loading a shader
-    */
-    var onLoadShaderError = (gltfRuntime: IGLTFRuntime, sha: string) => {
-        return () => {
-            Tools.Error("Error when loading shader program named " + sha + " located at " + gltfRuntime.shaders[sha].uri);
-        };
     };
 
     /**
-    * Load shaders
-    */
-    var load = (gltfRuntime: IGLTFRuntime) => {
-        // Begin with shaders
-        var atLeastOneShader = false;
-
-        for (var sha in gltfRuntime.shaders) {
-            atLeastOneShader = true;
-
-            var shader: IGLTFShader = gltfRuntime.shaders[sha];
-
-            if (shader) {
-                if (gltfRuntime.extensionsUsed.indexOf(BinaryExtensionName) != -1 && BinaryExtensionName in shader.extensions) {
-                    var binaryExtensionShader: IGLTFBinaryExtensionShader = shader.extensions[BinaryExtensionName];
-                    var bufferView: IGLTFBufferView = gltfRuntime.bufferViews[binaryExtensionShader.bufferView];
-                    var shaderBytes = GLTFUtils.GetBufferFromBufferView(gltfRuntime, bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE);
-                    var shaderString = GLTFUtils.DecodeBufferToText(shaderBytes);
-                    onLoadShader(gltfRuntime, sha)(shaderString);
-                }
-                else if (GLTFUtils.IsBase64(shader.uri)) {
-                    var shaderString = atob(shader.uri.split(",")[1]);
-                    onLoadShader(gltfRuntime, sha)(shaderString);
-                }
-                else {
-                    Tools.LoadFile(gltfRuntime.rootUrl + shader.uri, onLoadShader(gltfRuntime, sha), null, null, false, onLoadShaderError(gltfRuntime, sha));
-                }
-            }
-            else {
-                Tools.Error("No shader file named " + shader.uri);
-            }
-        }
-
-        if (!atLeastOneShader) {
-            loadBuffers(gltfRuntime);
-        }
-    };
-
-    class BinaryReader {
-        private _arrayBuffer: ArrayBuffer;
-        private _dataView: DataView;
-        private _byteOffset: number;
-
-        constructor(arrayBuffer: ArrayBuffer) {
-            this._arrayBuffer = arrayBuffer;
-            this._dataView = new DataView(arrayBuffer);
-            this._byteOffset = 0;
-        }
-
-        public getUint32(): number {
-            var value = this._dataView.getUint32(this._byteOffset, true);
-            this._byteOffset += 4;
-            return value;
-        }
-
-        public getUint8Array(length?: number): Uint8Array {
-            if (!length) {
-                length = this._arrayBuffer.byteLength - this._byteOffset;
-            }
-
-            var value = new Uint8Array(this._arrayBuffer, this._byteOffset, length);
-            this._byteOffset += length;
-            return value;
-        }
-    }
-
-    /**
     * glTF File Loader Plugin
     */
-    export class GLTFFileLoader implements ISceneLoaderPlugin {
+    export class GLTFFileLoader implements ISceneLoaderPluginAsync {
         /**
         * Public members
         */
@@ -1618,60 +1334,73 @@
         public static MakeYUP: boolean = false;
         public static HomogeneousCoordinates: boolean = false;
 
-        public static Extensions: IGLTFExtension[] = [];
+        public static Extensions: { [name: string]: GLTFFileLoaderExtension } = {};
 
-        public static RegisterExtension(extension: IGLTFExtension): boolean {
-            if (GLTFFileLoader.Extensions.indexOf(extension) !== -1) {
-                return false;
+        public static RegisterExtension(extension: GLTFFileLoaderExtension): void {
+            if (GLTFFileLoader.Extensions[extension.name]) {
+                Tools.Error("Tool with the same name \"" + extension.name + "\" already exists");
+                return;
             }
 
-            GLTFFileLoader.Extensions.push(extension);
-            return true;
+            GLTFFileLoader.Extensions[extension.name] = extension;
         }
 
         /**
         * Import meshes
         */
-        public importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]): boolean {
-            var gltfRuntime = this._loadRuntime(scene, data, rootUrl);
-            gltfRuntime.importOnlyMeshes = true;
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onError?: () => void): boolean {
+            var gltfRuntime = loadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
+                gltfRuntime.importOnlyMeshes = true;
 
-            if (meshesNames === "") {
-                gltfRuntime.importMeshesNames = [];
-            }
-            else if (typeof meshesNames === "string") {
-                gltfRuntime.importMeshesNames = [meshesNames];
-            }
-            else if (meshesNames && !(meshesNames instanceof Array)) {
-                gltfRuntime.importMeshesNames = [meshesNames];
-            }
-            else {
-                gltfRuntime.importMeshesNames = [];
-                Tools.Warn("Argument meshesNames must be of type string or string[]");
-            }
+                if (meshesNames === "") {
+                    gltfRuntime.importMeshesNames = [];
+                }
+                else if (typeof meshesNames === "string") {
+                    gltfRuntime.importMeshesNames = [meshesNames];
+                }
+                else if (meshesNames && !(meshesNames instanceof Array)) {
+                    gltfRuntime.importMeshesNames = [meshesNames];
+                }
+                else {
+                    gltfRuntime.importMeshesNames = [];
+                    Tools.Warn("Argument meshesNames must be of type string or string[]");
+                }
 
-            // Create nodes
-            this._createNodes(gltfRuntime);
+                // Create nodes
+                this._createNodes(gltfRuntime);
 
-            // Fill arrays of meshes and skeletons
-            for (var nde in gltfRuntime.nodes) {
-                var node: IGLTFNode = gltfRuntime.nodes[nde];
+                var meshes = [];
+                var skeletons = [];
 
-                if (node.babylonNode instanceof AbstractMesh) {
-                    meshes.push(<AbstractMesh>node.babylonNode);
+                // Fill arrays of meshes and skeletons
+                for (var nde in gltfRuntime.nodes) {
+                    var node: IGLTFNode = gltfRuntime.nodes[nde];
+
+                    if (node.babylonNode instanceof AbstractMesh) {
+                        meshes.push(<AbstractMesh>node.babylonNode);
+                    }
                 }
-            }
 
-            for (var skl in gltfRuntime.skins) {
-                var skin: IGLTFSkins = gltfRuntime.skins[skl];
+                for (var skl in gltfRuntime.skins) {
+                    var skin: IGLTFSkins = gltfRuntime.skins[skl];
 
-                if (skin.babylonSkeleton instanceof Skeleton) {
-                    skeletons.push(skin.babylonSkeleton);
+                    if (skin.babylonSkeleton instanceof Skeleton) {
+                        skeletons.push(skin.babylonSkeleton);
+                    }
                 }
-            }
 
-            // Load shaders and buffers to apply
-            load(gltfRuntime);
+                // Load shaders and then buffers
+                this._loadShadersAsync(gltfRuntime, () => {
+                    onShadersLoaded(gltfRuntime);
+                    this._loadBuffersAsync(gltfRuntime, () => {
+                        onBuffersLoaded(gltfRuntime);
+                    });
+                });
+
+                if (onSuccess) {
+                    onSuccess(meshes, null, skeletons);
+                }
+            }, onError);
 
             return true;
         }
@@ -1679,203 +1408,102 @@
         /**
         * Load scene
         */
-        public load(scene: Scene, data: string | ArrayBuffer, rootUrl: string): boolean {
-            var gltfRuntime = this._loadRuntime(scene, data, rootUrl);
-
-            // Create nodes
-            this._createNodes(gltfRuntime);
+        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onError: () => void): boolean {
+            loadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
+                // Create nodes
+                this._createNodes(gltfRuntime);
+
+                // Load shaders and then buffers
+                this._loadShadersAsync(gltfRuntime, () => {
+                    onShadersLoaded(gltfRuntime);
+                    this._loadBuffersAsync(gltfRuntime, () => {
+                        onBuffersLoaded(gltfRuntime);
+                    });
+                });
+
+                onSuccess();
+            }, onError);
 
-            // Load shaders and buffers to apply
-            load(gltfRuntime);
-
-            // Finish
             return true;
         }
 
-        // Loads data into a runtime object
-        private _loadRuntime(scene: Scene, data: string | ArrayBuffer, rootUrl: string): IGLTFRuntime {
-            if (typeof data === "string") {
-                return this._createGlTFRuntime(JSON.parse(data), scene, rootUrl);
-            }
-            else {
-                var binary = this._parseBinary(data);
-
-                var gltfRuntime = this._createGlTFRuntime(binary.content, scene, rootUrl);
-
-                if (gltfRuntime.extensionsUsed.indexOf(BinaryExtensionName) == -1) {
-                    Tools.Warn("glTF binary file does not have " + BinaryExtensionName + " specified in extensionsUsed");
-                }
-
-                gltfRuntime.loadedBufferViews[BinaryExtensionBufferName] = binary.body;
-
-                return gltfRuntime;
-            }
-        }
-
-        // Parses a glTF binary array buffer into its content and body
-        private _parseBinary(data: ArrayBuffer): IGLTFBinaryExtension {
-            var binaryReader = new BinaryReader(data);
-
-            var magic = GLTFUtils.DecodeBufferToText(binaryReader.getUint8Array(4));
-            if (magic != "glTF") {
-                throw new Error("Unexpected magic: " + magic);
-            }
-
-            var version = binaryReader.getUint32();
-            if (version != 1) {
-                throw new Error("Unsupported version: " + version);
-            }
-
-            var length = binaryReader.getUint32();
-            if (length != data.byteLength) {
-                throw new Error("Length in header does not match actual data length: " + length + " != " + data.byteLength);
-            }
-
-            var contentLength = binaryReader.getUint32();
-            var contentFormat = <EContentFormat>binaryReader.getUint32();
-
-            var content: Object;
-            switch (contentFormat) {
-                case EContentFormat.JSON:
-                    var jsonText = GLTFUtils.DecodeBufferToText(binaryReader.getUint8Array(contentLength));
-                    content = JSON.parse(jsonText);
-                    break;
-                default:
-                    throw new Error("Unexpected content format: " + contentFormat);
-            }
-
-            var body = binaryReader.getUint8Array();
-
-            return {
-                content: content,
-                body: body
-            };
-        };
-
-        // Creates nodes before loading buffers and shaders
-        private _createNodes(gltfRuntime: IGLTFRuntime): void {
-            var currentScene = <IGLTFScene>gltfRuntime.currentScene;
-
-            for (var i = 0; i < currentScene.nodes.length; i++) {
-                traverseNodes(gltfRuntime, currentScene.nodes[i], null);
-            }
-
-            // If there is an extension for scene, call it
-            callExtension(gltfRuntime, currentScene.name, currentScene.name, currentScene.extensions, gltfRuntime.scene);
-        }
+        private _loadShadersAsync(gltfRuntime: IGLTFRuntime, onload: () => void): void {
+            var hasShaders = false;
 
-        // Creates the gltfRuntime
-        private _createGlTFRuntime(parsedData: any, scene: Scene, rootUrl: string): IGLTFRuntime {
-            var gltfRuntime: IGLTFRuntime = {
-                accessors: {},
-                buffers: {},
-                bufferViews: {},
-                meshes: {},
-                lights: {},
-                cameras: {},
-                nodes: {},
-                images: {},
-                textures: {},
-                shaders: {},
-                programs: {},
-                samplers: {},
-                techniques: {},
-                materials: {},
-                animations: {},
-                skins: {},
-                currentScene: {},
-                extensionsUsed: [],
-
-                buffersCount: 0,
-                shaderscount: 0,
-
-                scene: scene,
-                rootUrl: rootUrl,
-
-                loadedBufferCount: 0,
-                loadedBufferViews: {},
-
-                loadedShaderCount: 0,
-
-                importOnlyMeshes: false,
-
-                dummyNodes: []
-            }
-
-            // Parse
-            if (parsedData.extensionsUsed) {
-                parseObject(parsedData.extensionsUsed, "extensionsUsed", gltfRuntime);
-            }
-
-            if (parsedData.buffers) {
-                parseBuffers(parsedData.buffers, gltfRuntime);
-            }
-
-            if (parsedData.bufferViews) {
-                parseObject(parsedData.bufferViews, "bufferViews", gltfRuntime);
-            }
-
-            if (parsedData.accessors) {
-                parseObject(parsedData.accessors, "accessors", gltfRuntime);
-            }
-
-            if (parsedData.meshes) {
-                parseObject(parsedData.meshes, "meshes", gltfRuntime);
-            }
+            var processShader = (sha: string, shader: IGLTFShader) => {
+                loadShaderStringAsync(gltfRuntime, shader, shaderString => {
+                    gltfRuntime.loadedShaderCount++;
+                    Effect.ShadersStore[sha + (shader.type === EShaderType.VERTEX ? "VertexShader" : "PixelShader")] = shaderString;
+                    if (gltfRuntime.loadedShaderCount === gltfRuntime.shaderscount) {
+                        onload();
+                    }
+                }, () => {
+                    Tools.Error("Error when loading shader program named " + sha + " located at " + shader.uri);
+                });
+            };
 
-            if (parsedData.lights) {
-                parseObject(parsedData.lights, "lights", gltfRuntime);
-            }
+            for (var sha in gltfRuntime.shaders) {
+                hasShaders = true;
 
-            if (parsedData.cameras) {
-                parseObject(parsedData.cameras, "cameras", gltfRuntime);
+                var shader: IGLTFShader = gltfRuntime.shaders[sha];
+                if (shader) {
+                    processShader.bind(this, sha, shader)();
+                }
+                else {
+                    Tools.Error("No shader named: " + sha);
+                }
             }
 
-            if (parsedData.nodes) {
-                parseObject(parsedData.nodes, "nodes", gltfRuntime);
+            if (!hasShaders) {
+                onload();
             }
+        };
 
-            if (parsedData.images) {
-                parseObject(parsedData.images, "images", gltfRuntime);
-            }
+        private _loadBuffersAsync(gltfRuntime: IGLTFRuntime, onload: () => void): void {
+            var hasBuffers = false;
 
-            if (parsedData.textures) {
-                parseObject(parsedData.textures, "textures", gltfRuntime);
-            }
+            var processBuffer = (buf: string, buffer: IGLTFBuffer) => {
+                loadBufferAsync(gltfRuntime, buffer, bufferView => {
+                    gltfRuntime.loadedBufferCount++;
 
-            if (parsedData.shaders) {
-                parseShaders(parsedData.shaders, gltfRuntime);
-            }
+                    if (bufferView.byteLength != gltfRuntime.buffers[buf].byteLength) {
+                        Tools.Error("Buffer named " + buf + " is length " + bufferView.byteLength + ". Expected: " + buffer.byteLength); // Improve error message
+                    }
 
-            if (parsedData.programs) {
-                parseObject(parsedData.programs, "programs", gltfRuntime);
-            }
+                    gltfRuntime.loadedBufferViews[buf] = bufferView;
 
-            if (parsedData.samplers) {
-                parseObject(parsedData.samplers, "samplers", gltfRuntime);
-            }
+                    if (gltfRuntime.loadedBufferCount === gltfRuntime.buffersCount) {
+                        onload();
+                    }
+                }, () => {
+                    Tools.Error("Error when loading buffer named " + buf + " located at " + buffer.uri);
+                });
+            };
 
-            if (parsedData.techniques) {
-                parseObject(parsedData.techniques, "techniques", gltfRuntime);
-            }
+            for (var buf in gltfRuntime.buffers) {
+                hasBuffers = true;
 
-            if (parsedData.materials) {
-                parseObject(parsedData.materials, "materials", gltfRuntime);
+                var buffer: IGLTFBuffer = gltfRuntime.buffers[buf];
+                if (buffer) {
+                    processBuffer.bind(this, buf, buffer)();
+                }
+                else {
+                    Tools.Error("No buffer named: " + buf);
+                }
             }
 
-            if (parsedData.animations) {
-                parseObject(parsedData.animations, "animations", gltfRuntime);
+            if (!hasBuffers) {
+                onload();
             }
+        }
 
-            if (parsedData.skins) {
-                parseObject(parsedData.skins, "skins", gltfRuntime);
-            }
+        // Creates nodes before loading buffers and shaders
+        private _createNodes(gltfRuntime: IGLTFRuntime): void {
+            var currentScene = <IGLTFScene>gltfRuntime.currentScene;
 
-            if (parsedData.scene && parsedData.scenes) {
-                gltfRuntime.currentScene = parsedData.scenes[parsedData.scene];
+            for (var i = 0; i < currentScene.nodes.length; i++) {
+                traverseNodes(gltfRuntime, currentScene.nodes[i], null);
             }
-
-            return gltfRuntime;
         }
     };
 

+ 45 - 0
loaders/glTF/babylon.glTFFileLoaderExtension.ts

@@ -0,0 +1,45 @@
+module BABYLON {
+    export abstract class GLTFFileLoaderExtension {
+        private _name: string;
+
+        public constructor(name: string) {
+            this._name = name;
+        }
+
+        public get name(): string {
+            return this._name;
+        }
+
+        /**
+        * Defines an override for loading the runtime
+        * Return true to stop further extensions from processing this buffer
+        */
+        public loadRuntimeAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
+            return false;
+        }
+
+        /**
+        * Defines an override for loading buffers
+        * Return true to stop further extensions from processing this buffer
+        */
+        public loadBufferAsync(gltfRuntime: IGLTFRuntime, bufferName: IGLTFBuffer, onSuccess: (bufferView: ArrayBufferView) => void, onError: () => void): boolean {
+            return false;
+        }
+
+        /**
+        * Defines an override for loading textures
+        * Return true to stop further extensions from processing this texture
+        */
+        public loadTextureAsync(gltfRuntime: IGLTFRuntime, texture: IGLTFTexture, onSuccess: (texture: Texture) => void, onError: () => void): boolean {
+            return false;
+        }
+
+        /**
+        * Defines an override for loading shader strings
+        * Return true to stop further extensions from processing this shader
+        */
+        public loadShaderStringAsync(gltfRuntime: IGLTFRuntime, shader: IGLTFShader, onSuccess: (shaderString: string) => void, onError: () => void): boolean {
+            return false;
+        }
+    }
+}

+ 1 - 48
loaders/glTF/babylon.glTFFileLoaderInterfaces.ts

@@ -376,51 +376,4 @@
         node: IGLTFNode;
         id: string;
     }
-
-    /**
-    * Extensions
-    */
-    export interface IGLTFExtension {
-        /**
-        * The name of the extension (example: "KHR_materials_pbr" cf. https://github.com/tsturm/glTF/tree/master/extensions/Vendor/FRAUNHOFER_materials_pbr)
-        */
-        extensionName: string;
-    }
-
-    export interface IGLTFLoaderExtension<ExtensionType extends Object, ExtensionObject extends Object> extends IGLTFExtension {
-        /**
-        * If the extensions needs the loader to skip its default behavior
-        * Example, when loading materials, if the loader should use only the extension
-        * or load the shader material and call the extension to customize the shader material
-        */
-        needToSkipDefaultLoaderBehavior(id: string, extension: ExtensionType): boolean;
-
-        /**
-        * Apply extension method
-        */
-        apply(gltfRuntime: IGLTFRuntime, id: string, name: string, extension: ExtensionType, object: ExtensionObject): ExtensionObject;
-    }
-
-    export enum EContentFormat {
-        JSON = 0
-    };
-
-    export const BinaryExtensionName = "KHR_binary_glTF";
-    export const BinaryExtensionBufferName = "binary_glTF";
-
-    export interface IGLTFBinaryExtension {
-        content: Object;
-        body: Uint8Array;
-    };
-
-    export interface IGLTFBinaryExtensionShader {
-        bufferView: string;
-    };
-
-    export interface IGLTFBinaryExtensionImage {
-        bufferView: string;
-        mimeType: string;
-        height: number;
-        width: number;
-    };
-}
+}

+ 213 - 42
loaders/glTF/babylon.glTFFileLoaderUtils.ts

@@ -101,48 +101,6 @@
         }
 
         /**
-         * Loads a texture from its name
-         * @param gltfRuntime: the gltf runtime
-         * @param name: the name of the texture
-         */
-        public static LoadTexture(gltfRuntime: IGLTFRuntime, name: string): Texture {
-            var texture: IGLTFTexture = gltfRuntime.textures[name];
-
-            if (!texture || !texture.source) {
-                return null;
-            }
-
-            if (texture.babylonTexture) {
-                return texture.babylonTexture;
-            }
-
-            var sampler: IGLTFSampler = gltfRuntime.samplers[texture.sampler];
-            var source: IGLTFImage = gltfRuntime.images[texture.source];
-            var newTexture: Texture = null;
-
-            var createMipMaps = (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_NEAREST) ||
-                (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_LINEAR) ||
-                (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_NEAREST) ||
-                (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_LINEAR);
-
-            var samplingMode = Texture.BILINEAR_SAMPLINGMODE;
-
-            if (GLTFUtils.IsBase64(source.uri)) {
-                newTexture = new Texture(source.uri, gltfRuntime.scene, !createMipMaps, true, samplingMode, null, null, source.uri, true);
-            }
-            else {
-                newTexture = new Texture(gltfRuntime.rootUrl + source.uri, gltfRuntime.scene, !createMipMaps, true, samplingMode);
-            }
-
-            newTexture.wrapU = GLTFUtils.GetWrapMode(sampler.wrapS);
-            newTexture.wrapV = GLTFUtils.GetWrapMode(sampler.wrapT);
-            newTexture.name = name;
-
-            texture.babylonTexture = newTexture;
-            return newTexture;
-        }
-
-        /**
          * Returns the byte stride giving an accessor
          * @param accessor: the GLTF accessor objet
          */
@@ -221,5 +179,218 @@
 
             return result;
         }
+
+        /**
+         * Loads a texture from its name
+         * @param gltfRuntime: the gltf runtime
+         * @param name: the name of the texture
+         */
+        public static LoadTextureAsync(gltfRuntime: IGLTFRuntime, texture: IGLTFTexture, onSuccess: (texture: Texture) => void, onError: () => void): void {
+            if (!texture || !texture.source) {
+                onError();
+                return;
+            }
+
+            if (texture.babylonTexture) {
+                onSuccess(texture.babylonTexture);
+                return;
+            }
+
+            var sampler: IGLTFSampler = gltfRuntime.samplers[texture.sampler];
+            var source: IGLTFImage = gltfRuntime.images[texture.source];
+            var newTexture: Texture;
+
+            var createMipMaps =
+                (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_NEAREST) ||
+                (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_LINEAR) ||
+                (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_NEAREST) ||
+                (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_LINEAR);
+
+            var samplingMode = Texture.BILINEAR_SAMPLINGMODE;
+
+            if (GLTFUtils.IsBase64(source.uri)) {
+                newTexture = new Texture(source.uri, gltfRuntime.scene, !createMipMaps, true, samplingMode, () => onSuccess(newTexture), onError, source.uri, true);
+            }
+            else {
+                newTexture = new Texture(gltfRuntime.rootUrl + source.uri, gltfRuntime.scene, !createMipMaps, true, samplingMode, () => onSuccess(newTexture), onError);
+            }
+
+            newTexture.wrapU = GLTFUtils.GetWrapMode(sampler.wrapS);
+            newTexture.wrapV = GLTFUtils.GetWrapMode(sampler.wrapT);
+            newTexture.name = name;
+
+            texture.babylonTexture = newTexture;
+        }
+
+        public static LoadShaderAsync(gltfRuntime: IGLTFRuntime, shader: IGLTFShader, onSuccess: (shaderString: string) => void, onError: () => void): void {
+            if (GLTFUtils.IsBase64(shader.uri)) {
+                var shaderString = atob(shader.uri.split(",")[1]);
+                onSuccess(shaderString);
+            }
+            else {
+                Tools.LoadFile(gltfRuntime.rootUrl + shader.uri, onSuccess, null, null, false, onError);
+            }
+        }
+
+        /**
+        * Decode array buffer from base64
+        */
+        public static DecodeArrayBuffer(base64: string): ArrayBuffer {
+            var decodedString = atob(base64);
+            var bufferLength = decodedString.length;
+            var arraybuffer = new Uint8Array(new ArrayBuffer(bufferLength));
+
+            for (var i = 0; i < bufferLength; i++) {
+                arraybuffer[i] = decodedString.charCodeAt(i);
+            }
+
+            return arraybuffer.buffer;
+        };
+
+        public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, buffer: IGLTFBuffer, onSuccess: (bufferView: ArrayBufferView) => void, onError: () => void): void {
+            if (GLTFUtils.IsBase64(buffer.uri)) {
+                var decodedBuffer = GLTFUtils.DecodeArrayBuffer(buffer.uri.split(",")[1]);
+                onSuccess(new Uint8Array(decodedBuffer));
+            }
+            else {
+                Tools.LoadFile(gltfRuntime.rootUrl + buffer.uri, data => onSuccess(new Uint8Array(data)), null, null, true, onError);
+            }
+        }
+
+        public static ParseObject(parsedObjects: any, runtimeProperty: string, gltfRuntime: IGLTFRuntime): void {
+            for (var object in parsedObjects) {
+                var parsedObject = parsedObjects[object];
+                gltfRuntime[runtimeProperty][object] = parsedObject;
+            }
+        }
+
+        public static ParseBuffers(parsedBuffers: any, gltfRuntime: IGLTFRuntime): void {
+            for (var buf in parsedBuffers) {
+                var parsedBuffer = parsedBuffers[buf];
+                gltfRuntime.buffers[buf] = parsedBuffer;
+                gltfRuntime.buffersCount++;
+            }
+        }
+
+        public static ParseShaders(parsedShaders: any, gltfRuntime: IGLTFRuntime): void {
+            for (var sha in parsedShaders) {
+                var parsedShader = parsedShaders[sha];
+                gltfRuntime.shaders[sha] = parsedShader;
+                gltfRuntime.shaderscount++;
+            }
+        }
+
+        public static CreateGlTFRuntime(parsedData: any, scene: Scene, rootUrl: string): IGLTFRuntime {
+            var gltfRuntime: IGLTFRuntime = {
+                accessors: {},
+                buffers: {},
+                bufferViews: {},
+                meshes: {},
+                lights: {},
+                cameras: {},
+                nodes: {},
+                images: {},
+                textures: {},
+                shaders: {},
+                programs: {},
+                samplers: {},
+                techniques: {},
+                materials: {},
+                animations: {},
+                skins: {},
+                currentScene: {},
+                extensionsUsed: [],
+
+                buffersCount: 0,
+                shaderscount: 0,
+
+                scene: scene,
+                rootUrl: rootUrl,
+
+                loadedBufferCount: 0,
+                loadedBufferViews: {},
+
+                loadedShaderCount: 0,
+
+                importOnlyMeshes: false,
+
+                dummyNodes: []
+            }
+
+            // Parse
+            if (parsedData.extensionsUsed) {
+                GLTFUtils.ParseObject(parsedData.extensionsUsed, "extensionsUsed", gltfRuntime);
+            }
+
+            if (parsedData.buffers) {
+                GLTFUtils.ParseBuffers(parsedData.buffers, gltfRuntime);
+            }
+
+            if (parsedData.bufferViews) {
+                GLTFUtils.ParseObject(parsedData.bufferViews, "bufferViews", gltfRuntime);
+            }
+
+            if (parsedData.accessors) {
+                GLTFUtils.ParseObject(parsedData.accessors, "accessors", gltfRuntime);
+            }
+
+            if (parsedData.meshes) {
+                GLTFUtils.ParseObject(parsedData.meshes, "meshes", gltfRuntime);
+            }
+
+            if (parsedData.lights) {
+                GLTFUtils.ParseObject(parsedData.lights, "lights", gltfRuntime);
+            }
+
+            if (parsedData.cameras) {
+                GLTFUtils.ParseObject(parsedData.cameras, "cameras", gltfRuntime);
+            }
+
+            if (parsedData.nodes) {
+                GLTFUtils.ParseObject(parsedData.nodes, "nodes", gltfRuntime);
+            }
+
+            if (parsedData.images) {
+                GLTFUtils.ParseObject(parsedData.images, "images", gltfRuntime);
+            }
+
+            if (parsedData.textures) {
+                GLTFUtils.ParseObject(parsedData.textures, "textures", gltfRuntime);
+            }
+
+            if (parsedData.shaders) {
+                GLTFUtils.ParseShaders(parsedData.shaders, gltfRuntime);
+            }
+
+            if (parsedData.programs) {
+                GLTFUtils.ParseObject(parsedData.programs, "programs", gltfRuntime);
+            }
+
+            if (parsedData.samplers) {
+                GLTFUtils.ParseObject(parsedData.samplers, "samplers", gltfRuntime);
+            }
+
+            if (parsedData.techniques) {
+                GLTFUtils.ParseObject(parsedData.techniques, "techniques", gltfRuntime);
+            }
+
+            if (parsedData.materials) {
+                GLTFUtils.ParseObject(parsedData.materials, "materials", gltfRuntime);
+            }
+
+            if (parsedData.animations) {
+                GLTFUtils.ParseObject(parsedData.animations, "animations", gltfRuntime);
+            }
+
+            if (parsedData.skins) {
+                GLTFUtils.ParseObject(parsedData.skins, "skins", gltfRuntime);
+            }
+
+            if (parsedData.scene && parsedData.scenes) {
+                gltfRuntime.currentScene = parsedData.scenes[parsedData.scene];
+            }
+
+            return gltfRuntime;
+        }
     }
 }

+ 2 - 0
src/Loading/babylon.sceneLoader.ts

@@ -279,6 +279,8 @@
                         if (onsuccess) {
                             onsuccess(scene);
                         }
+
+                        scene._removePendingData(loadingToken);
                     }, () => {
                         if (onerror) {
                             onerror(scene);