Explorar o código

Add better handling of errors and progress

Gary Hsu %!s(int64=8) %!d(string=hai) anos
pai
achega
ebbf412634

+ 4 - 4
loaders/src/glTF/1.0/babylon.glTFBinaryExtension.ts

@@ -25,7 +25,7 @@ module BABYLON.GLTF1 {
             super("KHR_binary_glTF");
         }
 
-        public loadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
+        public loadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: (message: string) => void): boolean {
             var extensionsUsed = (<any>data.json).extensionsUsed;
             if (!extensionsUsed || extensionsUsed.indexOf(this.name) === -1) {
                 return false;
@@ -36,7 +36,7 @@ module BABYLON.GLTF1 {
             return true;
         }
 
-        public loadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void): boolean {
+        public loadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void): boolean {
             if (gltfRuntime.extensionsUsed.indexOf(this.name) === -1) {
                 return false;
             }
@@ -49,7 +49,7 @@ module BABYLON.GLTF1 {
             return true;
         }
 
-        public loadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void): boolean {
+        public loadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void): boolean {
             var texture: IGLTFTexture = gltfRuntime.textures[id];
             var source: IGLTFImage = gltfRuntime.images[texture.source];
             if (!source.extensions || !(this.name in source.extensions)) {
@@ -63,7 +63,7 @@ module BABYLON.GLTF1 {
             return true;
         }
 
-        public loadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string) => void, onError: () => void): boolean {
+        public loadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string) => void, onError: (message: string) => void): boolean {
             var shader: IGLTFShader = gltfRuntime.shaders[id];
             if (!shader.extensions || !(this.name in shader.extensions)) {
                 return false;

+ 23 - 19
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -1115,11 +1115,10 @@ module BABYLON.GLTF1 {
     /**
     * Shader compilation failed
     */
-    var onShaderCompileError = (program: IGLTFProgram, shaderMaterial: ShaderMaterial, onError: () => void) => {
+    var onShaderCompileError = (program: IGLTFProgram, shaderMaterial: ShaderMaterial, onError: (message: string) => void) => {
         return (effect: Effect, error: string) => {
-            Tools.Error("Cannot compile program named " + program.name + ". Error: " + error + ". Default material will be applied");
             shaderMaterial.dispose(true);
-            onError();
+            onError("Cannot compile program named " + program.name + ". Error: " + error + ". Default material will be applied");
         };
     };
 
@@ -1296,22 +1295,24 @@ module BABYLON.GLTF1 {
             return gltfRuntime;
         }
 
-        public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void, onProgress?: () => void): void {
+        public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void, onProgress?: () => void): void {
             var buffer: IGLTFBuffer = gltfRuntime.buffers[id];
 
             if (GLTFUtils.IsBase64(buffer.uri)) {
                 setTimeout(() => onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(buffer.uri))));
             }
             else {
-                Tools.LoadFile(gltfRuntime.rootUrl + buffer.uri, data => onSuccess(new Uint8Array(data)), onProgress, null, true, onError);
+                Tools.LoadFile(gltfRuntime.rootUrl + buffer.uri, data => onSuccess(new Uint8Array(data)), onProgress, null, true, request => {
+                    onError(request.status + " " + request.statusText);
+                });
             }
         }
 
-        public static LoadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void): void {
+        public static LoadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void): void {
             var texture: IGLTFTexture = gltfRuntime.textures[id];
 
             if (!texture || !texture.source) {
-                onError();
+                onError(null);
                 return;
             }
 
@@ -1326,11 +1327,13 @@ module BABYLON.GLTF1 {
                 setTimeout(() => onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(source.uri))));
             }
             else {
-                Tools.LoadFile(gltfRuntime.rootUrl + source.uri, data => onSuccess(new Uint8Array(data)), null, null, true, onError);
+                Tools.LoadFile(gltfRuntime.rootUrl + source.uri, data => onSuccess(new Uint8Array(data)), null, null, true, request => {
+                    onError(request.status + " " + request.statusText);
+                });
             }
         }
 
-        public static CreateTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: () => void): void {
+        public static CreateTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: (message: string) => void): void {
             var texture: IGLTFTexture = gltfRuntime.textures[id];
 
             if (texture.babylonTexture) {
@@ -1360,7 +1363,7 @@ module BABYLON.GLTF1 {
             onSuccess(newTexture);
         }
 
-        public static LoadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string) => void, onError: () => void): void {
+        public static LoadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string) => void, onError: (message: string) => void): void {
             var shader: IGLTFShader = gltfRuntime.shaders[id];
 
             if (GLTFUtils.IsBase64(shader.uri)) {
@@ -1368,11 +1371,13 @@ module BABYLON.GLTF1 {
                 onSuccess(shaderString);
             }
             else {
-                Tools.LoadFile(gltfRuntime.rootUrl + shader.uri, onSuccess, null, null, false, onError);
+                Tools.LoadFile(gltfRuntime.rootUrl + shader.uri, onSuccess, null, null, false, request => {
+                    onError(request.status + " " + request.statusText);
+                });
             }
         }
 
-        public static LoadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: () => void): void {
+        public static LoadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: (message: string) => void): void {
             var material: IGLTFMaterial = gltfRuntime.materials[id];
             var technique: IGLTFTechnique = gltfRuntime.techniques[material.technique];
             if (!technique) {
@@ -1541,7 +1546,7 @@ module BABYLON.GLTF1 {
             GLTFLoader.Extensions[extension.name] = extension;
         }
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onError?: () => void, onProgress?: () => void): boolean {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): boolean {
             scene.useRightHandedSystem = true;
 
             var gltfRuntime = GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
@@ -1594,8 +1599,7 @@ module BABYLON.GLTF1 {
                             onSuccess(meshes, null, skeletons);
                         }
                     });
-                },
-                onProgress);
+                }, onProgress);
 
                 if (BABYLON.GLTFFileLoader.IncrementalLoading && onSuccess) {
                     onSuccess(meshes, null, skeletons);
@@ -1605,7 +1609,7 @@ module BABYLON.GLTF1 {
             return true;
         }
 
-        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onError: () => void): void {
+        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
             scene.useRightHandedSystem = true;
 
             GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
@@ -1669,7 +1673,7 @@ module BABYLON.GLTF1 {
             }
         };
 
-        private _loadBuffersAsync(gltfRuntime: IGLTFRuntime, onload: () => void, onProgress?: () => void): void {
+        private _loadBuffersAsync(gltfRuntime: IGLTFRuntime, onLoad: () => void, onProgress?: (event: ProgressEvent) => void): void {
             var hasBuffers = false;
 
             var processBuffer = (buf: string, buffer: IGLTFBuffer) => {
@@ -1685,7 +1689,7 @@ module BABYLON.GLTF1 {
                     }
 
                     if (gltfRuntime.loadedBufferCount === gltfRuntime.buffersCount) {
-                        onload();
+                        onLoad();
                     }
                 }, () => {
                     Tools.Error("Error when loading buffer named " + buf + " located at " + buffer.uri);
@@ -1705,7 +1709,7 @@ module BABYLON.GLTF1 {
             }
 
             if (!hasBuffers) {
-                onload();
+                onLoad();
             }
         }
 

+ 15 - 15
loaders/src/glTF/1.0/babylon.glTFLoaderExtension.ts

@@ -16,7 +16,7 @@ module BABYLON.GLTF1 {
         * Defines an override for loading the runtime
         * Return true to stop further extensions from loading the runtime
         */
-        public loadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
+        public loadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: (message: string) => void): boolean {
             return false;
         }
 
@@ -24,7 +24,7 @@ module BABYLON.GLTF1 {
          * Defines an onverride for creating gltf runtime
          * Return true to stop further extensions from creating the runtime
          */
-        public loadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: () => void): boolean {
+        public loadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: (message: string) => void): boolean {
             return false;
         }
 
@@ -32,7 +32,7 @@ module BABYLON.GLTF1 {
         * Defines an override for loading buffers
         * Return true to stop further extensions from loading this buffer
         */
-        public loadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void, onProgress?: () => void): boolean {
+        public loadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void, onProgress?: () => void): boolean {
             return false;
         }
 
@@ -40,7 +40,7 @@ module BABYLON.GLTF1 {
         * Defines an override for loading texture buffers
         * Return true to stop further extensions from loading this texture data
         */
-        public loadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void): boolean {
+        public loadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void): boolean {
             return false;
         }
 
@@ -48,7 +48,7 @@ module BABYLON.GLTF1 {
         * Defines an override for creating textures
         * Return true to stop further extensions from loading this texture
         */
-        public createTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: () => void): boolean {
+        public createTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: (message: string) => void): boolean {
             return false;
         }
 
@@ -56,7 +56,7 @@ module BABYLON.GLTF1 {
         * Defines an override for loading shader strings
         * Return true to stop further extensions from loading this shader data
         */
-        public loadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string) => void, onError: () => void): boolean {
+        public loadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string) => void, onError: (message: string) => void): boolean {
             return false;
         }
 
@@ -64,7 +64,7 @@ module BABYLON.GLTF1 {
         * Defines an override for loading materials
         * Return true to stop further extensions from loading this material
         */
-        public loadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: () => void): boolean {
+        public loadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: (message: string) => void): boolean {
             return false;
         }
 
@@ -72,7 +72,7 @@ module BABYLON.GLTF1 {
         // Utilities
         // ---------
 
-        public static LoadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): void {
+        public static LoadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadRuntimeAsync(scene, data, rootUrl, onSuccess, onError);
             }, () => {
@@ -82,7 +82,7 @@ module BABYLON.GLTF1 {
             });
         }
 
-        public static LoadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: () => void): void {
+        public static LoadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadRuntimeExtensionsAsync(gltfRuntime, onSuccess, onError);
             }, () => {
@@ -92,7 +92,7 @@ module BABYLON.GLTF1 {
             });
         }
 
-        public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (bufferView: ArrayBufferView) => void, onError: () => void, onProgress?: () => void): void {
+        public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (bufferView: ArrayBufferView) => void, onError: (message: string) => void, onProgress?: () => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadBufferAsync(gltfRuntime, id, onSuccess, onError, onProgress);
             }, () => {
@@ -100,13 +100,13 @@ module BABYLON.GLTF1 {
             });
         }
 
-        public static LoadTextureAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (texture: Texture) => void, onError: () => void): void {
+        public static LoadTextureAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (texture: Texture) => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.LoadTextureBufferAsync(gltfRuntime, id,
                 buffer => GLTFLoaderExtension.CreateTextureAsync(gltfRuntime, id, buffer, onSuccess, onError),
                 onError);
         }
 
-        public static LoadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderData: string) => void, onError: () => void): void {
+        public static LoadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderData: string) => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadShaderStringAsync(gltfRuntime, id, onSuccess, onError);
             }, () => {
@@ -114,7 +114,7 @@ module BABYLON.GLTF1 {
             });
         }
 
-        public static LoadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: () => void): void {
+        public static LoadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadMaterialAsync(gltfRuntime, id, onSuccess, onError);
             }, () => {
@@ -122,7 +122,7 @@ module BABYLON.GLTF1 {
             });
         }
 
-        private static LoadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void): void {
+        private static LoadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadTextureBufferAsync(gltfRuntime, id, onSuccess, onError);
             }, () => {
@@ -130,7 +130,7 @@ module BABYLON.GLTF1 {
             });
         }
 
-        private static CreateTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: () => void): void {
+        private static CreateTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: (message: string) => void): void {
             GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.createTextureAsync(gltfRuntime, id, buffer, onSuccess, onError);
             }, () => {

+ 3 - 3
loaders/src/glTF/1.0/babylon.glTFMaterialsCommonExtension.ts

@@ -61,7 +61,7 @@ module BABYLON.GLTF1 {
             super("KHR_materials_common");
         }
 
-        public loadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: () => void): boolean {
+        public loadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: (message: string) => void): boolean {
             if (!gltfRuntime.extensions) return false;
 
             var extension = gltfRuntime.extensions[this.name];
@@ -105,7 +105,7 @@ module BABYLON.GLTF1 {
             return false;
         }
 
-        public loadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: () => void): boolean {
+        public loadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: (message: string) => void): boolean {
             var material: IGLTFMaterial = gltfRuntime.materials[id];
             if (!material || !material.extensions) return false;
 
@@ -158,7 +158,7 @@ module BABYLON.GLTF1 {
             return true;
         }
 
-        private _loadTexture(gltfRuntime: IGLTFRuntime, id: string, material: StandardMaterial, propertyPath: string, onError: () => void): void {
+        private _loadTexture(gltfRuntime: IGLTFRuntime, id: string, material: StandardMaterial, propertyPath: string, onError: (message: string) => void): void {
             // Create buffer from texture url
             GLTFLoaderBase.LoadTextureBufferAsync(gltfRuntime, id, (buffer) => {
                 // Create texture from buffer

+ 3 - 7
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -43,13 +43,9 @@ module BABYLON.GLTF2.Extensions {
                     return;
                 }
 
-                // Load the next LOD once the loader has succeeded.
-                loader.executeWhenRenderReady(succeeded => {
-                    if (!succeeded) {
-                        return;
-                    }
-
-                    // Load the next LOD when all of the textures are loaded.
+                // Load the next LOD when the loader is ready to render and
+                // all active material textures of the current LOD are loaded.
+                loader.executeWhenRenderReady(() => {
                     BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), () => {
                         this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
                     });

+ 115 - 90
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -1,27 +1,27 @@
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GLTF2 {
-    export class GLTFLoader implements IGLTFLoader {
+    export class GLTFLoader implements IGLTFLoader, IDisposable {
         private _parent: GLTFFileLoader;
         private _gltf: IGLTF;
-        private _errors: string[];
         private _babylonScene: Scene;
         private _rootUrl: string;
         private _defaultMaterial: PBRMaterial;
-        private _onSuccess: () => void;
-        private _onError: () => void;
-
-        private _succeeded: boolean;
-        private _renderReady: boolean;
+        private _successCallback: () => void;
+        private _progressCallback: (event: ProgressEvent) => void;
+        private _errorCallback: (message: string) => void;
+        private _renderReady: boolean = false;
+        private _disposed: boolean = false;
+        private _objectURLs: string[] = new Array<string>();
 
         // Observable with boolean indicating success or error.
-        private _renderReadyObservable = new Observable<boolean>();
+        private _renderReadyObservable = new Observable<GLTFLoader>();
 
         // Count of pending work that needs to complete before the asset is rendered.
-        private _renderPendingCount: number;
+        private _renderPendingCount: number = 0;
 
         // Count of pending work that needs to complete before the loader is cleared.
-        private _loaderPendingCount: number;
+        private _loaderPendingCount: number = 0;
 
         public static Extensions: { [name: string]: GLTFLoaderExtension } = {};
 
@@ -45,9 +45,9 @@ module BABYLON.GLTF2 {
             return this._babylonScene;
         }
 
-        public executeWhenRenderReady(func: (succeeded: boolean) => void) {
+        public executeWhenRenderReady(func: () => void) {
             if (this._renderReady) {
-                func(this._succeeded);
+                func();
             }
             else {
                 this._renderReadyObservable.add(func);
@@ -58,7 +58,30 @@ module BABYLON.GLTF2 {
             this._parent = parent;
         }
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onError: () => void): void {
+        public dispose(): void {
+            if (this._disposed) {
+                return;
+            }
+
+            this._disposed = true;
+
+            // Revoke object urls created during load
+            this._objectURLs.forEach(url => URL.revokeObjectURL(url));
+            this._objectURLs.length = 0;
+
+            this._gltf = undefined;
+            this._babylonScene = undefined;
+            this._rootUrl = undefined;
+            this._defaultMaterial = undefined;
+            this._successCallback = undefined;
+            this._errorCallback = undefined;
+            this._renderReady = false;
+            this._renderReadyObservable.clear();
+            this._renderPendingCount = 0;
+            this._loaderPendingCount = 0;
+        }
+
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
             this._loadAsync(meshesNames, scene, data, rootUrl, () => {
                 var meshes = [];
                 if (this._gltf.nodes) {
@@ -81,22 +104,21 @@ module BABYLON.GLTF2 {
                 }
 
                 onSuccess(meshes, null, skeletons);
-            }, onError);
+            }, onProgress, onError);
         }
 
-        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onError: () => void): void {
-            this._loadAsync(null, scene, data, rootUrl, onSuccess, onError);
+        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
+            this._loadAsync(null, scene, data, rootUrl, onSuccess, onProgress, onError);
         }
 
-        private _loadAsync(nodeNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onError: () => void): void {
-            this._clear();
-
+        private _loadAsync(nodeNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
             this._loadData(data);
             this._babylonScene = scene;
             this._rootUrl = rootUrl;
 
-            this._onSuccess = onSuccess;
-            this._onError = onError;
+            this._successCallback = onSuccess;
+            this._progressCallback = onProgress;
+            this._errorCallback = onError;
 
             this.addPendingData(this);
             this._loadScene(nodeNames);
@@ -104,6 +126,15 @@ module BABYLON.GLTF2 {
             this.removePendingData(this);
         }
 
+        private _onError(message: string): void {
+            this.dispose();
+            this._errorCallback(message);
+        }
+
+        private _onProgress(event: ProgressEvent): void {
+            this._progressCallback(event);
+        }
+
         private _onRenderReady(): void {
             switch (this._parent.coordinateSystemMode) {
                 case GLTFLoaderCoordinateSystemMode.AUTO:
@@ -122,25 +153,14 @@ module BABYLON.GLTF2 {
                     break;
             }
 
-            this._succeeded = (this._errors.length === 0);
-            if (this._succeeded) {
-                this._showMeshes();
-                this._startAnimations();
-                this._onSuccess();
-            }
-            else {
-                this._errors.forEach(error => Tools.Error(error));
-                this._errors = [];
-                this._onError();
-            }
-
-            this._renderReadyObservable.notifyObservers(this._succeeded);
+            this._showMeshes();
+            this._startAnimations();
+            this._successCallback();
+            this._renderReadyObservable.notifyObservers(this);
         }
 
         private _onLoaderComplete(): void {
-            this._errors.forEach(error => Tools.Error(error));
-            this._errors = [];
-            this._clear();
+            this.dispose();
 
             if (this._parent.onComplete) {
                 this._parent.onComplete();
@@ -208,31 +228,6 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _clear(): void {
-            // Revoke object urls created during load
-            if (this._gltf && this._gltf.textures) {
-                for (var i = 0; i < this._gltf.textures.length; i++) {
-                    var texture = this._gltf.textures[i];
-                    if (texture.blobURL) {
-                        URL.revokeObjectURL(texture.blobURL);
-                    }
-                }
-            }
-
-            this._gltf = undefined;
-            this._errors = [];
-            this._babylonScene = undefined;
-            this._rootUrl = undefined;
-            this._defaultMaterial = undefined;
-            this._onSuccess = undefined;
-            this._onError = undefined;
-            this._succeeded = false;
-            this._renderReady = false;
-            this._renderReadyObservable.clear();
-            this._renderPendingCount = 0;
-            this._loaderPendingCount = 0;
-        }
-
         private _loadScene(nodeNames: any): void {
             var scene = this._gltf.scenes[this._gltf.scene || 0];
             var nodeIndices = scene.nodes;
@@ -432,7 +427,7 @@ module BABYLON.GLTF2 {
         private _loadVertexDataAsync(primitive: IGLTFMeshPrimitive, onSuccess: (vertexData: VertexData) => void): void {
             var attributes = primitive.attributes;
             if (!attributes) {
-                this._errors.push("Primitive has no attributes");
+                this._onError("Primitive has no attributes");
                 return;
             }
 
@@ -773,9 +768,15 @@ module BABYLON.GLTF2 {
                     buffer.loadedData = new Uint8Array(data);
                     buffer.loadedObservable.notifyObservers(buffer);
                     buffer.loadedObservable = null;
-                }, null, null, true, request => {
-                    this._errors.push("Failed to load file '" + buffer.uri + "': " + request.statusText + "(" + request.status + ")");
-                    this.removePendingData(buffer);
+                }, event => {
+                    if (!this._disposed) {
+                        this._onProgress(event);
+                    }
+                }, this._babylonScene.database, true, request => {
+                    if (!this._disposed) {
+                        this._onError("Failed to load file '" + buffer.uri + "': " + request.status + " " + request.statusText);
+                        this.removePendingData(buffer);
+                    }
                 });
             }
         }
@@ -785,7 +786,7 @@ module BABYLON.GLTF2 {
 
             this._loadBufferAsync(bufferView.buffer, bufferData => {
                 if (byteOffset + byteLength > bufferData.byteLength) {
-                    this._errors.push("Buffer access is out of range");
+                    this._onError("Buffer access is out of range");
                     return;
                 }
 
@@ -813,7 +814,7 @@ module BABYLON.GLTF2 {
                         bufferViewData = new Float32Array(buffer, byteOffset, byteLength);
                         break;
                     default:
-                        this._errors.push("Invalid component type (" + componentType + ")");
+                        this._onError("Invalid component type (" + componentType + ")");
                         return;
                 }
 
@@ -824,10 +825,25 @@ module BABYLON.GLTF2 {
         private _loadAccessorAsync(accessor: IGLTFAccessor, onSuccess: (data: ArrayBufferView) => void): void {
             var bufferView = this._gltf.bufferViews[accessor.bufferView];
             var byteOffset = accessor.byteOffset || 0;
-            var byteLength = accessor.count * GLTFUtils.GetByteStrideFromType(accessor);
+            var byteLength = accessor.count * this._getByteStrideFromType(accessor);
             this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
         }
 
+        private _getByteStrideFromType(accessor: IGLTFAccessor): number {
+            switch (accessor.type) {
+                case "SCALAR": return 1;
+                case "VEC2": return 2;
+                case "VEC3": return 3;
+                case "VEC4": return 4;
+                case "MAT2": return 4;
+                case "MAT3": return 9;
+                case "MAT4": return 16;
+                default:
+                    this._onError("Invalid accessor type (" + accessor.type + ")");
+                    return 0;
+            }
+        }
+
         public addPendingData(data: any) {
             if (!this._renderReady) {
                 this._renderPendingCount++;
@@ -1006,36 +1022,45 @@ module BABYLON.GLTF2 {
             }
 
             var source = this._gltf.images[texture.source];
-            var url: string;
+            var sampler = (texture.sampler === undefined ? <IGLTFSampler>{} : this._gltf.samplers[texture.sampler]);
+            var noMipMaps = (sampler.minFilter === ETextureMinFilter.NEAREST || sampler.minFilter === ETextureMinFilter.LINEAR);
+            var samplingMode = GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
+
+            this.addPendingData(texture);
+            babylonTexture = new Texture(null, this._babylonScene, noMipMaps, false, samplingMode, () => {
+                if (!this._disposed) {
+                    this.removePendingData(texture);
+                }
+            }, () => {
+                if (!this._disposed) {
+                    this._onError("Failed to load texture '" + source.uri + "'");
+                    this.removePendingData(texture);
+                }
+            });
+
+            var setTextureData = data => {
+                var url = URL.createObjectURL(new Blob([data], { type: source.mimeType }));
+                this._objectURLs.push(url);
+                babylonTexture.updateURL(url);
+            };
 
             if (!source.uri) {
                 var bufferView = this._gltf.bufferViews[source.bufferView];
-                this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE, data => {
-                    texture.blobURL = URL.createObjectURL(new Blob([data], { type: source.mimeType }));
-                    texture.babylonTextures[texCoord].updateURL(texture.blobURL);
-                });
+                this._loadBufferViewAsync(bufferView, 0, bufferView.byteLength, EComponentType.UNSIGNED_BYTE, setTextureData);
             }
             else if (GLTFUtils.IsBase64(source.uri)) {
-                var data = new Uint8Array(GLTFUtils.DecodeBase64(source.uri));
-                texture.blobURL = URL.createObjectURL(new Blob([data], { type: source.mimeType }));
-                url = texture.blobURL;
+                setTextureData(new Uint8Array(GLTFUtils.DecodeBase64(source.uri)));
             }
             else {
-                url = this._rootUrl + source.uri;
+                Tools.LoadFile(this._rootUrl + source.uri, setTextureData, event => {
+                    if (!this._disposed) {
+                        this._onProgress(event);
+                    }
+                }, this._babylonScene.database, true, request => {
+                    this._onError("Failed to load file '" + source.uri + "': " + request.status + " " + request.statusText);
+                });
             }
 
-            var sampler = (texture.sampler === undefined ? <IGLTFSampler>{} : this._gltf.samplers[texture.sampler]);
-            var noMipMaps = (sampler.minFilter === ETextureMinFilter.NEAREST || sampler.minFilter === ETextureMinFilter.LINEAR);
-            var samplingMode = GLTFUtils.GetTextureSamplingMode(sampler.magFilter, sampler.minFilter);
-
-            this.addPendingData(texture);
-            var babylonTexture = new Texture(url, this._babylonScene, noMipMaps, false, samplingMode, () => {
-                this.removePendingData(texture);
-            }, () => {
-                this._errors.push("Failed to load texture '" + source.uri + "'");
-                this.removePendingData(texture);
-            });
-
             babylonTexture.coordinatesIndex = texCoord;
             babylonTexture.wrapU = GLTFUtils.GetTextureWrapMode(sampler.wrapS);
             babylonTexture.wrapV = GLTFUtils.GetTextureWrapMode(sampler.wrapT);

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

@@ -247,7 +247,6 @@ module BABYLON.GLTF2 {
 
         // Runtime values (one per coordinate index)
         babylonTextures?: Texture[];
-        blobURL?: string;
     }
 
     export interface IGLTFTextureInfo {

+ 0 - 19
loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts

@@ -46,25 +46,6 @@ module BABYLON.GLTF2 {
             }
         }
 
-        /**
-         * Returns the byte stride giving an accessor
-         * @param accessor: the GLTF accessor objet
-         */
-        public static GetByteStrideFromType(accessor: IGLTFAccessor): number {
-            // Needs this function since "byteStride" isn't requiered in glTF format
-            var type = accessor.type;
-
-            switch (type) {
-                case "VEC2": return 2;
-                case "VEC3": return 3;
-                case "VEC4": return 4;
-                case "MAT2": return 4;
-                case "MAT3": return 9;
-                case "MAT4": return 16;
-                default: return 1;
-            }
-        }
-
         public static GetTextureSamplingMode(magFilter: ETextureMagFilter, minFilter: ETextureMinFilter): number {
             if (magFilter === ETextureMagFilter.LINEAR) {
                 switch (minFilter) {

+ 65 - 45
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -19,8 +19,8 @@ module BABYLON {
     }
 
     export interface IGLTFLoader {
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onError: () => void) => void;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onError: () => void) => void;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
     }
 
     export class GLTFFileLoader implements ISceneLoaderPluginAsync {
@@ -42,77 +42,94 @@ module BABYLON {
             ".glb": { isBinary: true }
         };
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onError: () => void): void {
-            var loaderData = GLTFFileLoader._parse(data);
-            var loader = this._getLoader(loaderData);
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
+            var loaderData = GLTFFileLoader._parse(data, onError);
+            if (!loaderData) {
+                return;
+            }
+
+            var loader = this._getLoader(loaderData, onError);
             if (!loader) {
-                onError();
                 return;
             }
 
-            loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onError);
+            loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
         }
 
-        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onError: () => void): void {
-            var loaderData = GLTFFileLoader._parse(data);
-            var loader = this._getLoader(loaderData);
+        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
+            var loaderData = GLTFFileLoader._parse(data, onError);
+            if (!loaderData) {
+                return;
+            }
+
+            var loader = this._getLoader(loaderData, onError);
             if (!loader) {
-                onError();
                 return;
             }
 
-            return loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onError);
+            return loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
         }
 
         public canDirectLoad(data: string): boolean {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         }
 
-        private static _parse(data: string | ArrayBuffer): IGLTFLoaderData {
+        private static _parse(data: string | ArrayBuffer, onError: (message: string) => void): IGLTFLoaderData {
             if (data instanceof ArrayBuffer) {
-                return GLTFFileLoader._parseBinary(data);
+                return GLTFFileLoader._parseBinary(data, onError);
             }
 
-            return {
-                json: JSON.parse(data),
-                bin: null
-            };
+            try {
+                return {
+                    json: JSON.parse(data),
+                    bin: null
+                };
+            }
+            catch (e) {
+                onError(e.message);
+                return null;
+            }
         }
 
-        private _getLoader(loaderData: IGLTFLoaderData): IGLTFLoader {
+        private _getLoader(loaderData: IGLTFLoaderData, onError: (message: string) => void): IGLTFLoader {
             const loaderVersion = { major: 2, minor: 0 };
 
             var asset = (<any>loaderData.json).asset || {};
 
             var version = GLTFFileLoader._parseVersion(asset.version);
             if (!version) {
-                Tools.Error("Invalid version");
+                onError("Invalid version: " + asset.version);
                 return null;
             }
 
-            var minVersion = GLTFFileLoader._parseVersion(asset.minVersion);
-            if (minVersion) {
+            if (asset.minVersion !== undefined) {
+                var minVersion = GLTFFileLoader._parseVersion(asset.minVersion);
+                if (!minVersion) {
+                    onError("Invalid minimum version: " + asset.minVersion);
+                    return null;
+                }
+
                 if (GLTFFileLoader._compareVersion(minVersion, loaderVersion) > 0) {
-                    Tools.Error("Incompatible version");
+                    onError("Incompatible minimum version: " + asset.minVersion);
                     return null;
                 }
             }
 
-            var createLoader = {
+            var createLoaders = {
                 1: GLTFFileLoader.CreateGLTFLoaderV1,
                 2: GLTFFileLoader.CreateGLTFLoaderV2
             };
 
-            var loader = createLoader[version.major](this);
-            if (loader === null) {
-                Tools.Error("Unsupported version");
+            var createLoader = createLoaders[version.major];
+            if (!createLoader) {
+                onError("Unsupported version: " + asset.version);
                 return null;
             }
 
-            return loader;
+            return createLoader(this);
         }
 
-        private static _parseBinary(data: ArrayBuffer): IGLTFLoaderData {
+        private static _parseBinary(data: ArrayBuffer, onError: (message: string) => void): IGLTFLoaderData {
             const Binary = {
                 Magic: 0x46546C67
             };
@@ -121,28 +138,28 @@ module BABYLON {
 
             var magic = binaryReader.readUint32();
             if (magic !== Binary.Magic) {
-                Tools.Error("Unexpected magic: " + magic);
+                onError("Unexpected magic: " + magic);
                 return null;
             }
 
             var version = binaryReader.readUint32();
             switch (version) {
-                case 1: return GLTFFileLoader._parseV1(binaryReader);
-                case 2: return GLTFFileLoader._parseV2(binaryReader);
+                case 1: return GLTFFileLoader._parseV1(binaryReader, onError);
+                case 2: return GLTFFileLoader._parseV2(binaryReader, onError);
             }
 
-            Tools.Error("Unsupported version: " + version);
+            onError("Unsupported version: " + version);
             return null;
         }
 
-        private static _parseV1(binaryReader: BinaryReader): IGLTFLoaderData {
+        private static _parseV1(binaryReader: BinaryReader, onError: (message: string) => void): IGLTFLoaderData {
             const ContentFormat = {
                 JSON: 0
             };
             
             var length = binaryReader.readUint32();
             if (length != binaryReader.getLength()) {
-                Tools.Error("Length in header does not match actual data length: " + length + " != " + binaryReader.getLength());
+                onError("Length in header does not match actual data length: " + length + " != " + binaryReader.getLength());
                 return null;
             }
 
@@ -155,7 +172,7 @@ module BABYLON {
                     content = JSON.parse(GLTFFileLoader._decodeBufferToText(binaryReader.readUint8Array(contentLength)));
                     break;
                 default:
-                    Tools.Error("Unexpected content format: " + contentFormat);
+                    onError("Unexpected content format: " + contentFormat);
                     return null;
             }
 
@@ -168,7 +185,7 @@ module BABYLON {
             };
         }
 
-        private static _parseV2(binaryReader: BinaryReader): IGLTFLoaderData {
+        private static _parseV2(binaryReader: BinaryReader, onError: (message: string) => void): IGLTFLoaderData {
             const ChunkFormat = {
                 JSON: 0x4E4F534A,
                 BIN: 0x004E4942
@@ -176,7 +193,7 @@ module BABYLON {
 
             var length = binaryReader.readUint32();
             if (length !== binaryReader.getLength()) {
-                Tools.Error("Length in header does not match actual data length: " + length + " != " + binaryReader.getLength());
+                onError("Length in header does not match actual data length: " + length + " != " + binaryReader.getLength());
                 return null;
             }
 
@@ -184,7 +201,7 @@ module BABYLON {
             var chunkLength = binaryReader.readUint32();
             var chunkFormat = binaryReader.readUint32();
             if (chunkFormat !== ChunkFormat.JSON) {
-                Tools.Error("First chunk format is not JSON");
+                onError("First chunk format is not JSON");
                 return null;
             }
             var json = JSON.parse(GLTFFileLoader._decodeBufferToText(binaryReader.readUint8Array(chunkLength)));
@@ -196,7 +213,7 @@ module BABYLON {
                 chunkFormat = binaryReader.readUint32();
                 switch (chunkFormat) {
                     case ChunkFormat.JSON:
-                        Tools.Error("Unexpected JSON chunk");
+                        onError("Unexpected JSON chunk");
                         return null;
                     case ChunkFormat.BIN:
                         bin = binaryReader.readUint8Array(chunkLength);
@@ -220,20 +237,23 @@ module BABYLON {
             }
 
             var parts = version.split(".");
-            if (parts.length === 0) {
+            if (parts.length != 2) {
                 return null;
             }
 
-            var major = parseInt(parts[0]);
-            if (major > 1 && parts.length != 2) {
+            var major = +parts[0];
+            if (isNaN(major)) {
                 return null;
             }
 
-            var minor = parseInt(parts[1]);
+            var minor = +parts[1];
+            if (isNaN(minor)) {
+                return null;
+            }
 
             return {
                 major: major,
-                minor: parseInt(parts[0])
+                minor: minor
             };
         }
 

+ 126 - 139
src/Loading/babylon.sceneLoader.ts

@@ -7,15 +7,15 @@
 
     export interface ISceneLoaderPlugin {
         extensions: string | ISceneLoaderPluginExtensions;
-        importMesh: (meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => boolean;
-        load: (scene: Scene, data: string, rootUrl: string) => boolean;        
+        importMesh: (meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], onError: (message: string) => void) => boolean;
+        load: (scene: Scene, data: string, rootUrl: string, onError: (message: string) => void) => boolean;
         canDirectLoad?: (data: string) => boolean;
     }
 
     export interface ISceneLoaderPluginAsync {
         extensions: string | ISceneLoaderPluginExtensions;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onsuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onerror: () => void) => void;
-        loadAsync: (scene: Scene, data: string, rootUrl: string, onsuccess: () => void, onerror: () => void) => void;        
+        importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
+        loadAsync: (scene: Scene, data: string, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
         canDirectLoad?: (data: string) => boolean;
     }
 
@@ -125,6 +125,55 @@
             return null;
         }
 
+        private static _loadData(rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: (plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync, data: any) => void, onProgress: (event: ProgressEvent) => void, onError: (message?: string, exception?: any) => void): void {
+            var directLoad = SceneLoader._getDirectLoad(sceneFilename);
+            var registeredPlugin = directLoad ? SceneLoader._getPluginForDirectLoad(sceneFilename) : SceneLoader._getPluginForFilename(sceneFilename);
+            var plugin = registeredPlugin.plugin;
+            var useArrayBuffer = registeredPlugin.isBinary;
+            var database: Database;
+
+            var dataCallback = data => {
+                if (scene.isDisposed) {
+                    onError("Scene has been disposed");
+                    return;
+                }
+
+                scene.database = database;
+
+                try {
+                    onSuccess(plugin, data);
+                }
+                catch (e) {
+                    onError(null, e);
+                }
+            };
+
+            var manifestChecked = success => {
+                Tools.LoadFile(rootUrl + sceneFilename, dataCallback, onProgress, database, useArrayBuffer, request => {
+                    onError(request.status + " " + request.statusText);
+                });
+            };
+
+            if (directLoad) {
+                dataCallback(directLoad);
+                return;
+            }
+
+            if (rootUrl.indexOf("file:") === -1) {
+                if (scene.getEngine().enableOfflineSupport) {
+                    // Checking if a manifest file has been set for this scene and if offline mode has been requested
+                    database = new Database(rootUrl + sceneFilename, manifestChecked);
+                }
+                else {
+                    manifestChecked(true);
+                }
+            }
+            // Loading file from disk via input file or drag'n'drop
+            else {
+                Tools.ReadFile(sceneFilename, dataCallback, onProgress, useArrayBuffer);
+            }
+        }
+
         // Public functions
         public static GetPluginForExtension(extension: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
             return SceneLoader._getPluginForExtension(extension).plugin;
@@ -149,112 +198,78 @@
             }
         }
 
-        public static ImportMesh(meshesNames: any, rootUrl: string, sceneFilename: string, scene: Scene, onsuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, progressCallBack?: () => void, onerror?: (scene: Scene, message: string, exception?: any) => void): void {            
-            if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
-                Tools.Error("Wrong sceneFilename parameter");
-                return;
-            }
-
+        /**
+        * Import meshes into a scene
+        * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported 
+        * @param rootUrl a string that defines the root url for scene and resources
+        * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
+        * @param scene the instance of BABYLON.Scene to append to
+        * @param onSuccess a callback with a list of imported meshes, particleSystems, and skeletons when import succeeds
+        * @param onProgress a callback with a progress event for each file being loaded
+        * @param onError a callback with the scene, a message, and possibly an exception when import fails
+        */
+        public static ImportMesh(meshNames: any, rootUrl: string, sceneFilename: string, scene: Scene, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (scene: Scene, message: string, exception?: any) => void): void {
             if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
                 Tools.Error("Wrong sceneFilename parameter");
                 return;
             }
 
-            var directLoad = SceneLoader._getDirectLoad(sceneFilename);
-
             var loadingToken = {};
             scene._addPendingData(loadingToken);
 
-            var manifestChecked = success => {
-                scene.database = database;
+            var errorHandler = (message?: string, exception?: any) => {
+                if (onError) {
+                    onError(scene, "Unable to import meshes from " + rootUrl + sceneFilename + (message ? ": " + message : ""));
+                }
+                scene._removePendingData(loadingToken);
+            };
 
-                var registeredPlugin = directLoad ? SceneLoader._getPluginForDirectLoad(directLoad) : SceneLoader._getPluginForFilename(sceneFilename);
-                var plugin = registeredPlugin.plugin;
-                var useArrayBuffer = registeredPlugin.isBinary;
+            var progressHandler = (event: ProgressEvent) => {
+                if (onProgress) {
+                    onProgress(event);
+                }
+            };
 
-                var importMeshFromData = data => {
+            SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data) => {
+                if ((<any>plugin).importMesh) {
+                    var syncedPlugin = <ISceneLoaderPlugin>plugin;
                     var meshes = [];
                     var particleSystems = [];
                     var skeletons = [];
-
-                    if (scene.isDisposed) {
-                        if (onerror) {
-                            onerror(scene, 'Scene was disposed before being able to load ' + rootUrl + sceneFilename);
-                        }
+                    if (!syncedPlugin.importMesh(meshNames, scene, data, rootUrl, meshes, particleSystems, skeletons, errorHandler)) {
                         return;
                     }
 
-                    try {
-                        if ((<any>plugin).importMesh) {
-                            var syncedPlugin = <ISceneLoaderPlugin>plugin;
-                            if (!syncedPlugin.importMesh(meshesNames, scene, data, rootUrl, meshes, particleSystems, skeletons)) {
-                                if (onerror) {
-                                    onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename);
-                                }
-                                scene._removePendingData(loadingToken);
-                                return;
-                            }
-
-                            if (onsuccess) {
-                                scene.importedMeshesFiles.push(rootUrl + sceneFilename);
-                                onsuccess(meshes, particleSystems, skeletons);
-                                scene._removePendingData(loadingToken);
-                            }
-                        } else {
-                            var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                            asyncedPlugin.importMeshAsync(meshesNames, scene, data, rootUrl, (meshes, particleSystems, skeletons) => {
-                                if (onsuccess) {
-                                    scene.importedMeshesFiles.push(rootUrl + sceneFilename);
-                                    onsuccess(meshes, particleSystems, skeletons);
-                                    scene._removePendingData(loadingToken);
-                                }
-                            }, () => {
-                                if (onerror) {
-                                    onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename);
-                                }
-                                scene._removePendingData(loadingToken);
-                            });
-                        }
-                    } catch (e) {
-                        if (onerror) {
-                            onerror(scene, 'Unable to import meshes from ' + rootUrl + sceneFilename, e);
-                        }
+                    if (onSuccess) {
+                        scene.importedMeshesFiles.push(rootUrl + sceneFilename);
+                        onSuccess(meshes, particleSystems, skeletons);
                         scene._removePendingData(loadingToken);
                     }
-                };
-
-                if (directLoad) {
-                    importMeshFromData(directLoad);
-                    return;
                 }
-
-                Tools.LoadFile(rootUrl + sceneFilename, data => {
-                    importMeshFromData(data);
-                }, progressCallBack, database, useArrayBuffer, () => {
-                    if (onerror) {
-                        onerror(scene, 'Unable to load file ' + rootUrl + sceneFilename)
-                    }
-                });
-            };
-
-            if (scene.getEngine().enableOfflineSupport && !directLoad) {
-                // Checking if a manifest file has been set for this scene and if offline mode has been requested
-                var database = new Database(rootUrl + sceneFilename, manifestChecked);
-            }
-            else {
-                // If the scene is a data stream or offline support is not enabled, it's a direct load
-                manifestChecked(true);
-            }
+                else {
+                    var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
+                    asyncedPlugin.importMeshAsync(meshNames, scene, data, rootUrl, (meshes, particleSystems, skeletons) => {
+                        if (onSuccess) {
+                            scene.importedMeshesFiles.push(rootUrl + sceneFilename);
+                            onSuccess(meshes, particleSystems, skeletons);
+                            scene._removePendingData(loadingToken);
+                        }
+                    }, progressHandler, errorHandler);
+                }
+            }, progressHandler, errorHandler);
         }
-        
+
         /**
         * Load a scene
         * @param rootUrl a string that defines the root url for scene and resources
         * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
         * @param engine is the instance of BABYLON.Engine to use to create the scene
+        * @param onSuccess a callback with the scene when import succeeds
+        * @param onProgress a callback with a progress event for each file being loaded
+        * @param onError a callback with the scene, a message, and possibly an exception when import fails
         */
-        public static Load(rootUrl: string, sceneFilename: any, engine: Engine, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void {
-            SceneLoader.Append(rootUrl, sceneFilename, new Scene(engine), onsuccess, progressCallBack, onerror);
+        public static Load(rootUrl: string, sceneFilename: any, engine: Engine, onSuccess?: (scene: Scene) => void, onProgress?: (event: ProgressEvent) => void, onError?: (scene: Scene, message: string, exception?: any) => void): void {
+            SceneLoader.Append(rootUrl, sceneFilename, new Scene(engine), onSuccess, onProgress, onError);
         }
 
         /**
@@ -262,62 +277,57 @@
         * @param rootUrl a string that defines the root url for scene and resources
         * @param sceneFilename a string that defines the name of the scene file. can start with "data:" following by the stringified version of the scene
         * @param scene is the instance of BABYLON.Scene to append to
+        * @param onSuccess a callback with the scene when import succeeds
+        * @param onProgress a callback with a progress event for each file being loaded
+        * @param onError a callback with the scene, a message, and possibly an exception when import fails
         */
-        public static Append(rootUrl: string, sceneFilename: any, scene: Scene, onsuccess?: (scene: Scene) => void, progressCallBack?: any, onerror?: (scene: Scene) => void): void {
-
+        public static Append(rootUrl: string, sceneFilename: any, scene: Scene, onSuccess?: (scene: Scene) => void, onProgress?: (event: ProgressEvent) => void, onError?: (scene: Scene, message: string, exception?: any) => void): void {
             if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
                 Tools.Error("Wrong sceneFilename parameter");
                 return;
             }
 
-            var directLoad = SceneLoader._getDirectLoad(sceneFilename);
-            var registeredPlugin = directLoad ? SceneLoader._getPluginForDirectLoad(sceneFilename) : SceneLoader._getPluginForFilename(sceneFilename);
-            var plugin = registeredPlugin.plugin;
-            var useArrayBuffer = registeredPlugin.isBinary;
-            var database;
+            if (SceneLoader.ShowLoadingScreen) {
+                scene.getEngine().displayLoadingUI();
+            }
 
             var loadingToken = {};
             scene._addPendingData(loadingToken);
 
-            if (SceneLoader.ShowLoadingScreen) {
-                scene.getEngine().displayLoadingUI();
-            }
+            var errorHandler = (message?: string, exception?: any) => {
+                if (onError) {
+                    onError(scene, "Unable to load from " + rootUrl + sceneFilename + (message ? ": " + message : ""));
+                }
+                scene._removePendingData(loadingToken);
+                scene.getEngine().hideLoadingUI();
+            };
 
-            var loadSceneFromData = data => {
-                scene.database = database;
+            var progressHandler = (event: ProgressEvent) => {
+                if (onProgress) {
+                    onProgress(event);
+                }
+            };
 
+            SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data) => {
                 if ((<any>plugin).load) {
                     var syncedPlugin = <ISceneLoaderPlugin>plugin;
-                    if (!syncedPlugin.load(scene, data, rootUrl)) {
-                        if (onerror) {
-                            onerror(scene);
-                        }
-
-                        scene._removePendingData(loadingToken);
-                        scene.getEngine().hideLoadingUI();
+                    if (!syncedPlugin.load(scene, data, rootUrl, errorHandler)) {
                         return;
                     }
 
-                    if (onsuccess) {
-                        onsuccess(scene);
+                    if (onSuccess) {
+                        onSuccess(scene);
                     }
                     scene._removePendingData(loadingToken);
                 } else {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
                     asyncedPlugin.loadAsync(scene, data, rootUrl, () => {
-                        if (onsuccess) {
-                            onsuccess(scene);
+                        if (onSuccess) {
+                            onSuccess(scene);
                         }
 
                         scene._removePendingData(loadingToken);
-                    }, () => {
-                        if (onerror) {
-                            onerror(scene);
-                        }
-
-                        scene._removePendingData(loadingToken);
-                        scene.getEngine().hideLoadingUI();
-                    });
+                    }, progressHandler, errorHandler);
                 }
 
                 if (SceneLoader.ShowLoadingScreen) {
@@ -325,30 +335,7 @@
                         scene.getEngine().hideLoadingUI();
                     });
                 }
-            };
-
-            var manifestChecked = success => {
-                Tools.LoadFile(rootUrl + sceneFilename, loadSceneFromData, progressCallBack, database, useArrayBuffer);
-            };
-
-            if (directLoad) {
-                loadSceneFromData(directLoad);
-                return;
-            }
-
-            if (rootUrl.indexOf("file:") === -1) {
-                if (scene.getEngine().enableOfflineSupport) {
-                    // Checking if a manifest file has been set for this scene and if offline mode has been requested
-                    database = new Database(rootUrl + sceneFilename, manifestChecked);
-                }
-                else {
-                    manifestChecked(true);
-                }
-            }
-            // Loading file from disk via input file or drag'n'drop
-            else {
-                Tools.ReadFile(sceneFilename, loadSceneFromData, progressCallBack, useArrayBuffer);
-            }
+            }, progressHandler, errorHandler);
         }
     };
 }