Explorar o código

Merge pull request #3140 from bghgary/loader-updates

glTF loader updates
David Catuhe %!s(int64=7) %!d(string=hai) anos
pai
achega
1b0284e612

+ 1 - 0
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -20,6 +20,7 @@ module BABYLON.GLTF2.Extensions {
                 loader._loadMaterialBaseProperties(context, material);
                 this._loadSpecularGlossinessProperties(loader, context, material, extension);
                 assign(material.babylonMaterial, true);
+                onComplete();
             });
         }
 

+ 22 - 16
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -10,7 +10,7 @@ module BABYLON.GLTF2.Extensions {
         /**
          * Specify the minimal delay between LODs in ms (default = 250)
          */
-        public static MinimalLODDelay = 250;
+        public Delay = 250;
 
         public get name() {
             return "MSFT_lod";
@@ -70,7 +70,7 @@ module BABYLON.GLTF2.Extensions {
                     loader._tryCatchOnError(() => {
                         this._loadNodeLOD(loader, context, nodes, index - 1, onComplete);
                     });
-                }, MSFTLOD.MinimalLODDelay);
+                }, this.Delay);
             });
         }
 
@@ -96,24 +96,30 @@ module BABYLON.GLTF2.Extensions {
 
         private _loadMaterialLOD(loader: GLTFLoader, context: string, materials: IGLTFMaterial[], index: number, assign: (babylonMaterial: Material, isNew: boolean) => void, onComplete: () => void): void {
             loader._loadMaterial(context, materials[index], (babylonMaterial, isNew) => {
-                assign(babylonMaterial, isNew);
+                if (index === materials.length - 1) {
+                    assign(babylonMaterial, isNew);
 
-                if (index === 0) {
-                    onComplete();
-                    return;
+                    // Load the next LOD when the loader is ready to render.
+                    loader._executeWhenRenderReady(() => {
+                        this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete);
+                    });
                 }
-
-                // Load the next LOD when the loader is ready to render and
-                // all active material textures of the current LOD are loaded.
-                loader._executeWhenRenderReady(() => {
+                else {
                     BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), () => {
-                        setTimeout(() => {
-                            loader._tryCatchOnError(() => {
-                                this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete);
-                            });
-                        }, MSFTLOD.MinimalLODDelay);
+                        assign(babylonMaterial, isNew);
+
+                        if (index === 0) {
+                            onComplete();
+                        }
+                        else {
+                            setTimeout(() => {
+                                loader._tryCatchOnError(() => {
+                                    this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete);
+                                });
+                            }, this.Delay);
+                        }
                     });
-                });
+                }
             });
         }
     }

+ 162 - 47
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -33,6 +33,11 @@ module BABYLON.GLTF2 {
         readonly BYTES_PER_ELEMENT: number;
     }
 
+    interface GLTFLoaderRequest extends XMLHttpRequest {
+        _loaded: Nullable<number>;
+        _total: Nullable<number>;
+    }
+
     export class GLTFLoader implements IGLTFLoader {
         public _gltf: IGLTF;
         public _babylonScene: Scene;
@@ -46,7 +51,7 @@ module BABYLON.GLTF2 {
         private _progressCallback: (event: ProgressEvent) => void;
         private _errorCallback: (message: string) => void;
         private _renderReady = false;
-        private _requests = new Array<XMLHttpRequest>();
+        private _requests = new Array<GLTFLoaderRequest>();
 
         private _renderReadyObservable = new Observable<GLTFLoader>();
 
@@ -127,10 +132,27 @@ module BABYLON.GLTF2 {
             });
         }
 
-        private _onProgress(event: ProgressEvent): void {
-            if (this._progressCallback) {
-                this._progressCallback(event);
+        private _onProgress(): void {
+            if (!this._progressCallback) {
+                return;
             }
+
+            let loaded = 0;
+            let total = 0;
+            for (let request of this._requests) {
+                if (!request._loaded || !request._total) {
+                    return;
+                }
+
+                loaded += request._loaded;
+                total += request._total;
+            }
+
+            this._progressCallback(new ProgressEvent("GLTFLoaderProgress", {
+                lengthComputable: true,
+                loaded: loaded,
+                total: total
+            }));
         }
 
         public _executeWhenRenderReady(func: () => void): void {
@@ -276,6 +298,10 @@ module BABYLON.GLTF2 {
                 }
             }
 
+            if (this._parent.onMeshLoaded) {
+                this._parent.onMeshLoaded(this._rootNode.babylonMesh);
+            }
+
             let nodeIndices = scene.nodes;
 
             this._traverseNodes(context, nodeIndices, (node, parentNode) => {
@@ -361,6 +387,10 @@ module BABYLON.GLTF2 {
                     this._loadNode("#/nodes/" + index, childNode);
                 }
             }
+
+            if (this._parent.onMeshLoaded) {
+                this._parent.onMeshLoaded(node.babylonMesh);
+            }
         }
 
         private _loadMesh(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
@@ -417,17 +447,7 @@ module BABYLON.GLTF2 {
                             this._parent.onMaterialLoaded(babylonMaterial);
                         }
 
-                        if (this._parent.onBeforeMaterialReadyAsync) {
-                            this._addLoaderPendingData(material);
-                            this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, subMaterials[index] != null, () => {
-                                this._tryCatchOnError(() => {
-                                    subMaterials[index] = babylonMaterial;
-                                    this._removeLoaderPendingData(material);
-                                });
-                            });
-                        } else {
-                            subMaterials[index] = babylonMaterial;
-                        }
+                        subMaterials[index] = babylonMaterial;
                     });
                 }
             };
@@ -1078,26 +1098,17 @@ module BABYLON.GLTF2 {
                     throw new Error(context + ": Uri is missing");
                 }
 
-                if (GLTFUtils.IsBase64(buffer.uri)) {
-                    const data = GLTFUtils.DecodeBase64(buffer.uri);
-                    buffer.loadedData = new Uint8Array(data);
-                    onSuccess(buffer.loadedData);
+                buffer.loadedObservable = new Observable<IGLTFBuffer>();
+                buffer.loadedObservable.add(buffer => {
+                    onSuccess(buffer.loadedData!);
                     this._removePendingData(buffer);
-                }
-                else {
-
-                    buffer.loadedObservable = new Observable<IGLTFBuffer>();
-                    buffer.loadedObservable.add(buffer => {
-                        onSuccess(buffer.loadedData!);
-                        this._removePendingData(buffer);
-                    });
+                });
 
-                    this._loadUri(context, buffer.uri, data => {
-                        buffer.loadedData = data;
-                        buffer.loadedObservable!.notifyObservers(buffer);
-                        buffer.loadedObservable = undefined;
-                    });
-                }
+                this._loadUriAsync(context, buffer.uri, data => {
+                    buffer.loadedData = data;
+                    buffer.loadedObservable!.notifyObservers(buffer);
+                    buffer.loadedObservable = undefined;
+                });
             }
         }
 
@@ -1217,8 +1228,14 @@ module BABYLON.GLTF2 {
         public _removePendingData(data: any): void {
             if (!this._renderReady) {
                 if (--this._renderPendingCount === 0) {
-                    this._renderReady = true;
-                    this._onRenderReady();
+                    this._addLoaderPendingData(this);
+                    this._compileMaterialsAsync(() => {
+                        this._compileShadowGeneratorsAsync(() => {
+                            this._removeLoaderPendingData(this);
+                            this._renderReady = true;
+                            this._onRenderReady();
+                        });
+                    });
                 }
             }
 
@@ -1252,7 +1269,7 @@ module BABYLON.GLTF2 {
             this._loaderTrackers.push(tracker);
 
             this._addLoaderPendingData(tracker);
-
+            
             action();
 
             this._removeLoaderPendingData(tracker);
@@ -1469,7 +1486,7 @@ module BABYLON.GLTF2 {
                     throw new Error(context + ": Failed to find source " + texture.source);
                 }
 
-                this._loadImage("#/images/" + image.index, image, data => {
+                this._loadImageAsync("#/images/" + image.index, image, data => {
                     texture.url = URL.createObjectURL(new Blob([data], { type: image.mimeType }));
                     texture.dataReadyObservable!.notifyObservers(texture);
                     texture.dataReadyObservable = undefined;
@@ -1488,14 +1505,9 @@ module BABYLON.GLTF2 {
             return babylonTexture;
         }
 
-        private _loadImage(context: string, image: IGLTFImage, onSuccess: (data: ArrayBufferView) => void): void {
+        private _loadImageAsync(context: string, image: IGLTFImage, onSuccess: (data: ArrayBufferView) => void): void {
             if (image.uri) {
-                if (GLTFUtils.IsBase64(image.uri)) {
-                    onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(image.uri)));
-                }
-                else {
-                    this._loadUri(context, image.uri, onSuccess);
-                }
+                this._loadUriAsync(context, image.uri, onSuccess);
             }
             else {
                 const bufferView = GLTFLoader._GetProperty(this._gltf.bufferViews, image.bufferView);
@@ -1507,7 +1519,12 @@ module BABYLON.GLTF2 {
             }
         }
 
-        public _loadUri(context: string, uri: string, onSuccess: (data: ArrayBufferView) => void): void {
+        public _loadUriAsync(context: string, uri: string, onSuccess: (data: ArrayBufferView) => void): void {
+            if (GLTFUtils.IsBase64(uri)) {
+                onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(uri)));
+                return;
+            }
+
             if (!GLTFUtils.ValidateUri(uri)) {
                 throw new Error(context + ": Uri '" + uri + "' is invalid");
             }
@@ -1518,15 +1535,21 @@ module BABYLON.GLTF2 {
                 });
             }, event => {
                 this._tryCatchOnError(() => {
-                    this._onProgress(event);
+                    if (request && !this._renderReady) {
+                        request._loaded = event.loaded;
+                        request._total = event.total;
+                        this._onProgress();
+                    }
                 });
             }, this._babylonScene.database, true, request => {
                 this._tryCatchOnError(() => {
                     throw new Error(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""));
                 });
-            });
+            }) as GLTFLoaderRequest;
 
             if (request) {
+                request._loaded = null;
+                request._total = null;
                 this._requests.push(request);
             }
         }
@@ -1630,6 +1653,98 @@ module BABYLON.GLTF2 {
 
             return 0;
         }
+
+        private _compileMaterialAsync(babylonMaterial: Material, babylonMesh: AbstractMesh, onSuccess: () => void): void {
+            if (!this._parent.compileMaterials) {
+                onSuccess();
+                return;
+            }
+
+            if (this._parent.useClipPlane) {
+                babylonMaterial.forceCompilation(babylonMesh, () => {
+                    babylonMaterial.forceCompilation(babylonMesh, () => {
+                        this._tryCatchOnError(onSuccess);
+                    }, { clipPlane: true });
+                });
+            }
+            else {
+                babylonMaterial.forceCompilation(babylonMesh, () => {
+                    this._tryCatchOnError(onSuccess);
+                });
+            }
+        }
+
+        private _compileMaterialsAsync(onSuccess: () => void): void {
+            if (!this._parent.compileMaterials || !this._gltf.materials) {
+                onSuccess();
+                return;
+            }
+
+            let meshes = this._getMeshes();
+
+            let remaining = 0;
+            for (let mesh of meshes) {
+                if (mesh.material instanceof MultiMaterial) {
+                    for (let subMaterial of mesh.material.subMaterials) {
+                        if (subMaterial) {
+                            remaining++;
+                        }
+                    }
+                }
+            }
+
+            if (remaining === 0) {
+                onSuccess();
+                return;
+            }
+
+            for (let mesh of meshes) {
+                if (mesh.material instanceof MultiMaterial) {
+                    for (let subMaterial of mesh.material.subMaterials) {
+                        if (subMaterial) {
+                            this._compileMaterialAsync(subMaterial, mesh, () => {
+                                if (--remaining === 0) {
+                                    onSuccess();
+                                }
+                            });
+                        }
+                    }
+                }
+            }
+        }
+
+        private _compileShadowGeneratorsAsync(onSuccess: () => void): void {
+            if (!this._parent.compileShadowGenerators) {
+                onSuccess();
+                return;
+            }
+
+            let lights = this._babylonScene.lights;
+
+            let remaining = 0;
+            for (let light of lights) {
+                let generator = light.getShadowGenerator();
+                if (generator) {
+                    remaining++;
+                }
+            }
+
+            if (remaining === 0) {
+                onSuccess();
+                return;
+            }
+
+            for (let light of lights) {
+                let generator = light.getShadowGenerator();
+                if (generator) {
+                    generator.forceCompilation(() => {
+                        if (--remaining === 0) {
+                            this._tryCatchOnError(onSuccess);
+                        }
+                    });
+                }
+            }
+        }
     }
 
     BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = parent => new GLTFLoader(parent);

+ 7 - 8
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -31,20 +31,19 @@ module BABYLON {
         public onParsed: (data: IGLTFLoaderData) => void;
 
         // V1 options
-        public static HomogeneousCoordinates: boolean = false;
-        public static IncrementalLoading: boolean = true;
+        public static HomogeneousCoordinates = false;
+        public static IncrementalLoading = true;
 
         // V2 options
-        public coordinateSystemMode: GLTFLoaderCoordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+        public coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+        public compileMaterials = false;
+        public compileShadowGenerators = false;
+        public useClipPlane = false;
+        public onMeshLoaded: (mesh: AbstractMesh) => void;
         public onTextureLoaded: (texture: BaseTexture) => void;
         public onMaterialLoaded: (material: Material) => void;
 
         /**
-         * Let the user decides if he needs to process the material (like precompilation) before affecting it to meshes
-         */
-        public onBeforeMaterialReadyAsync: (material: Material, targetMesh: AbstractMesh, isLOD: boolean, callback: () => void) => void;
-
-        /**
          * Raised when the asset is completely loaded, just before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete just after onSuccess.

+ 2 - 7
sandbox/index.js

@@ -38,13 +38,8 @@ if (BABYLON.Engine.isSupported()) {
         if (plugin.name !== "gltf") {
             return;
         }
-        plugin.onBeforeMaterialReadyAsync = function(material, mesh, isLOD, callback) {
-            if (!isLOD) {
-                callback();
-                return;
-            }
-            material.forceCompilation(mesh, callback);
-        }
+
+        plugin.compileMaterials = true;
     });
 
     // Resize

+ 24 - 10
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -14,7 +14,7 @@
 
         recreateShadowMap(): void;
 
-        forceCompilation(onCompiled: (generator: ShadowGenerator) => void, options?: { useInstances: boolean }): void;
+        forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void;
 
         serialize(): any;
         dispose(): void;
@@ -602,31 +602,47 @@
         /**
          * Force shader compilation including textures ready check
          */
-        public forceCompilation(onCompiled: (generator: ShadowGenerator) => void, options?: { useInstances: boolean }): void {
+        public forceCompilation(onCompiled?: (generator: ShadowGenerator) => void, options?: Partial<{ useInstances: boolean }>): void {
+            let localOptions = {
+                useInstances: false,
+                ...options
+            };
+
             let shadowMap = this.getShadowMap();
             if (!shadowMap) {
+                if (onCompiled) {
+                    onCompiled(this);
+                }
                 return;
             }
 
-            var subMeshes = new Array<SubMesh>();
-            var currentIndex = 0;
-
             let renderList = shadowMap.renderList;
-
             if (!renderList) {
+                if (onCompiled) {
+                    onCompiled(this);
+                }
                 return;
             }
 
+            var subMeshes = new Array<SubMesh>();
             for (var mesh of renderList) {
                 subMeshes.push(...mesh.subMeshes);
             }
+            if (subMeshes.length === 0) {
+                if (onCompiled) {
+                    onCompiled(this);
+                }
+                return;
+            }
+
+            var currentIndex = 0;
 
             var checkReady = () => {
                 if (!this._scene || !this._scene.getEngine()) {
                     return;
                 }
 
-                while (this.isReady(subMeshes[currentIndex], options ? options.useInstances : false)) {
+                while (this.isReady(subMeshes[currentIndex], localOptions.useInstances)) {
                     currentIndex++;
                     if (currentIndex >= subMeshes.length) {
                         if (onCompiled) {
@@ -638,9 +654,7 @@
                 setTimeout(checkReady, 16);
             };
 
-            if (subMeshes.length > 0) {
-                checkReady();
-            }
+            checkReady();
         }
 
         /**

+ 9 - 3
src/Materials/babylon.material.ts

@@ -565,7 +565,13 @@
         /**
          * Force shader compilation including textures ready check
          */
-        public forceCompilation(mesh: AbstractMesh, onCompiled: (material: Material) => void, options?: { alphaTest: boolean, clipPlane: boolean }): void {
+        public forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial<{ alphaTest: Nullable<boolean>, clipPlane: boolean }>): void {
+            let localOptions = {
+                alphaTest: null,
+                clipPlane: false,
+                ...options
+            };
+
             var subMesh = new BaseSubMesh();
             var scene = this.getScene();
             var engine = scene.getEngine();
@@ -582,9 +588,9 @@
                 var alphaTestState = engine.getAlphaTesting();
                 var clipPlaneState = scene.clipPlane;
 
-                engine.setAlphaTesting(options ? options.alphaTest : this.needAlphaTesting());
+                engine.setAlphaTesting(localOptions.alphaTest || (!this.needAlphaBlendingForMesh(mesh) && this.needAlphaTesting()));
 
-                if (options && options.clipPlane) {
+                if (localOptions.clipPlane) {
                     scene.clipPlane = new Plane(0, 0, 0, 1);
                 }