Explorar el Código

Merge pull request #2394 from bghgary/MSFT_lod

Add support for MSFT_lod extension to glTF loader
David Catuhe hace 8 años
padre
commit
b554f9bd81

+ 4 - 2
Tools/Gulp/config.json

@@ -1333,7 +1333,8 @@
                     "../../loaders/src/glTF/2.0/babylon.glTFLoader.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts",
-                    "../../loaders/src/glTF/2.0/babylon.glTFMaterialsPbrSpecularGlossinessExtension.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/MSFT_lod.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts"
                 ],
                 "output": "babylon.glTF2FileLoader.js"
             },
@@ -1350,7 +1351,8 @@
                     "../../loaders/src/glTF/2.0/babylon.glTFLoader.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts",
                     "../../loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts",
-                    "../../loaders/src/glTF/2.0/babylon.glTFMaterialsPbrSpecularGlossinessExtension.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/MSFT_lod.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts"
                 ],
                 "output": "babylon.glTFFileLoader.js"
             }

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

@@ -0,0 +1,50 @@
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Extensions {
+    interface IKHRMaterialsPbrSpecularGlossiness {
+        diffuseFactor: number[];
+        diffuseTexture: IGLTFTextureInfo;
+        specularFactor: number[];
+        glossinessFactor: number;
+        specularGlossinessTexture: IGLTFTextureInfo;
+    }
+
+    export class KHRMaterialsPbrSpecularGlossiness extends GLTFLoaderExtension {
+        public get name(): string {
+            return "KHR_materials_pbrSpecularGlossiness";
+        }
+
+        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (material: Material) => void): boolean {
+            if (!material.extensions) {
+                return false;
+            }
+
+            var properties = material.extensions[this.name] as IKHRMaterialsPbrSpecularGlossiness;
+            if (!properties) {
+                return false;
+            }
+
+            loader.createPbrMaterial(material);
+            loader.loadMaterialBaseProperties(material);
+
+            material.babylonMaterial.albedoColor = properties.diffuseFactor ? Color3.FromArray(properties.diffuseFactor) : new Color3(1, 1, 1);
+            material.babylonMaterial.reflectivityColor = properties.specularFactor ? Color3.FromArray(properties.specularFactor) : new Color3(1, 1, 1);
+            material.babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
+
+            if (properties.diffuseTexture) {
+                material.babylonMaterial.albedoTexture = loader.loadTexture(properties.diffuseTexture);
+                loader.loadMaterialAlphaProperties(material);
+            }
+
+            if (properties.specularGlossinessTexture) {
+                material.babylonMaterial.reflectivityTexture = loader.loadTexture(properties.specularGlossinessTexture);
+                material.babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
+            }
+
+            assign(material.babylonMaterial);
+            return true;
+        }
+    }
+
+    GLTFLoader.RegisterExtension(new KHRMaterialsPbrSpecularGlossiness());
+}

+ 57 - 0
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -0,0 +1,57 @@
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Extensions {
+    interface IMSFTLOD {
+        ids: number[];
+    }
+
+    export class MSFTLOD extends GLTFLoaderExtension {
+        public get name() {
+            return "MSFT_lod";
+        }
+
+        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (material: Material) => void): boolean {
+            if (!material.extensions) {
+                return false;
+            }
+
+            var properties = material.extensions[this.name] as IMSFTLOD;
+            if (!properties) {
+                return false;
+            }
+
+            // Clear out the extension so that it won't get loaded again.
+            material.extensions[this.name] = undefined;
+
+            // Tell the loader not to clear its state until the highest LOD is loaded.
+            loader.addLoaderPendingData(material);
+
+            // Start with the lowest quality LOD.
+            var materialLODs = [material.index, ...properties.ids];
+            this.loadMaterialLOD(loader, material, materialLODs, materialLODs.length - 1, assign);
+
+            return true;
+        }
+
+        private loadMaterialLOD(loader: GLTFLoader, material: IGLTFMaterial, materialLODs: number[], lod: number, assign: (material: Material) => void): void {
+            loader.loadMaterial(materialLODs[lod], babylonMaterial => {
+                babylonMaterial.name += ".LOD" + lod;
+                assign(babylonMaterial);
+
+                // Loading is complete if this is the highest quality LOD.
+                if (lod === 0) {
+                    loader.removeLoaderPendingData(material);
+                    return;
+                }
+
+                // Load the next LOD when all of the textures are loaded.
+                BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), () => {
+                    this.loadMaterialLOD(loader, material, materialLODs, lod - 1, assign);
+                });
+            });
+
+        }
+    }
+
+    GLTFLoader.RegisterExtension(new MSFTLOD());
+}

+ 198 - 150
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -3,12 +3,20 @@
 module BABYLON.GLTF2 {
     export class GLTFLoader implements IGLTFLoader {
         private _gltf: IGLTF;
-        private _pendingCount: number;
-        private _onLoaded: () => void;
         private _errors: string[];
         private _babylonScene: Scene;
         private _rootUrl: string;
         private _defaultMaterial: PBRMaterial;
+        private _onSuccess: () => void;
+        private _onError: () => void;
+
+        private _renderReady: boolean;
+
+        // Count of pending work that needs to complete before the asset is rendered.
+        private _renderPendingCount: number;
+
+        // Count of pending work that needs to complete before the loader is cleared.
+        private _loaderPendingCount: number;
 
         public static Extensions: { [name: string]: GLTFLoaderExtension } = {};
 
@@ -18,27 +26,18 @@ module BABYLON.GLTF2 {
                 return;
             }
 
-            this.Extensions[extension.name] = extension;
-        }
+            GLTFLoader.Extensions[extension.name] = extension;
 
-        public static LoadMaterial(index: number): IGLTFMaterial {
-            return (<GLTFLoader>BABYLON.GLTFFileLoader.GLTFLoaderV2)._loadMaterial(index);
+            // Keep the order of registration so that extensions registered first are called first.
+            GLTFLoaderExtension._Extensions.push(extension);
         }
 
-        public static LoadCoreMaterial(index: number): Material {
-            return (<GLTFLoader>BABYLON.GLTFFileLoader.GLTFLoaderV2)._loadCoreMaterial(index);
+        public get gltf(): IGLTF {
+            return this._gltf;
         }
 
-        public static LoadCommonMaterialProperties(material: IGLTFMaterial): void {
-            return (<GLTFLoader>BABYLON.GLTFFileLoader.GLTFLoaderV2)._loadCommonMaterialProperties(material);
-        }
-
-        public static LoadAlphaProperties(material: IGLTFMaterial): void {
-            return (<GLTFLoader>BABYLON.GLTFFileLoader.GLTFLoaderV2)._loadAlphaProperties(material);
-        }
-
-        public static LoadTexture(textureInfo: IGLTFTextureInfo): Texture {
-            return (<GLTFLoader>BABYLON.GLTFFileLoader.GLTFLoaderV2)._loadTexture(textureInfo);
+        public get babylonScene(): Scene {
+            return this._babylonScene;
         }
 
         public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onError: () => void): void {
@@ -80,25 +79,33 @@ module BABYLON.GLTF2 {
             this._babylonScene = scene;
             this._rootUrl = rootUrl;
 
-            this._onLoaded = () => {
-                this._showMeshes();
-                this._startAnimations();
-
-                if (this._errors.length === 0) {
-                    onSuccess();
-                }
-                else {
-                    this._errors.forEach(error => Tools.Error(error));
-                    onError();
-                }
+            this._onSuccess = onSuccess;
+            this._onError = onError;
 
-                this._clear();
-            };
-
-            this._addPendingData(this);
+            this.addPendingData(this);
             this._loadScene(nodeNames);
             this._loadAnimations();
-            this._removePendingData(this);
+            this.removePendingData(this);
+        }
+
+        private _onRenderReady(): void {
+            this._showMeshes();
+            this._startAnimations();
+
+            if (this._errors.length === 0) {
+                this._onSuccess();
+            }
+            else {
+                this._errors.forEach(error => Tools.Error(error));
+                this._errors = [];
+                this._onError();
+            }
+        }
+
+        private _onLoaderComplete(): void {
+            this._errors.forEach(error => Tools.Error(error));
+            this._errors = [];
+            this._clear();
         }
 
         private _loadData(data: IGLTFLoaderData): void {
@@ -149,14 +156,6 @@ module BABYLON.GLTF2 {
         }
 
         private _clear(): void {
-            this._gltf = undefined;
-            this._pendingCount = 0;
-            this._onLoaded = undefined;
-            this._errors = [];
-            this._babylonScene = undefined;
-            this._rootUrl = undefined;
-            this._defaultMaterial = undefined;
-
             // Revoke object urls created during load
             if (this._gltf && this._gltf.textures) {
                 for (var i = 0; i < this._gltf.textures.length; i++) {
@@ -166,16 +165,49 @@ module BABYLON.GLTF2 {
                     }
                 }
             }
+
+            this._gltf = undefined;
+            this._errors = [];
+            this._babylonScene = undefined;
+            this._rootUrl = undefined;
+            this._defaultMaterial = undefined;
+            this._onSuccess = undefined;
+            this._onError = undefined;
+            this._renderReady = false;
+            this._renderPendingCount = 0;
+            this._loaderPendingCount = 0;
         }
 
         private _loadScene(nodeNames: any): void {
-            nodeNames = (nodeNames === "") ? null : nodeNames;
-            nodeNames = (nodeNames instanceof Array) ? nodeNames : [nodeNames];
-
             var scene = this._gltf.scenes[this._gltf.scene || 0];
+            var nodeIndices = scene.nodes;
 
-            this._traverseScene(nodeNames, scene, node => this._loadSkin(node));
-            this._traverseScene(nodeNames, scene, (node, parentNode) => this._loadMesh(node, parentNode));
+            this._traverseNodes(nodeIndices, (node, index, parentNode) => {
+                node.index = index;
+                node.parent = parentNode;
+                return true;
+            });
+
+            if (nodeNames) {
+                if (!(nodeNames instanceof Array)) {
+                    nodeNames = [nodeNames];
+                }
+
+                var filteredNodeIndices = new Array<number>();
+                this._traverseNodes(nodeIndices, node => {
+                    if (nodeNames.indexOf(node.name) === -1) {
+                        return true;
+                    }
+
+                    filteredNodeIndices.push(node.index);
+                    return false;
+                });
+
+                nodeIndices = filteredNodeIndices;
+            }
+
+            this._traverseNodes(nodeIndices, node => this._loadSkin(node));
+            this._traverseNodes(nodeIndices, node => this._loadMesh(node));
         }
 
         private _loadSkin(node: IGLTFNode): boolean {
@@ -201,7 +233,7 @@ module BABYLON.GLTF2 {
 
                 var accessor = this._gltf.accessors[skin.inverseBindMatrices];
                 this._loadAccessorAsync(accessor, data => {
-                    this._traverseNode(null, skin.skeleton, (node, parent) => this._updateBone(node, parent, skin, <Float32Array>data));
+                    this._traverseNode(skin.skeleton, (node, index, parent) => this._updateBone(node, parent, skin, <Float32Array>data));
                 });
             }
 
@@ -240,7 +272,7 @@ module BABYLON.GLTF2 {
             return babylonBone;
         }
 
-        private _loadMesh(node: IGLTFNode, parentNode: IGLTFNode): boolean {
+        private _loadMesh(node: IGLTFNode): boolean {
             var babylonMesh = new Mesh(node.name || "mesh" + node.index, this._babylonScene);
             babylonMesh.isVisible = false;
 
@@ -251,7 +283,7 @@ module BABYLON.GLTF2 {
                 this._loadMeshData(node, mesh, babylonMesh);
             }
 
-            babylonMesh.parent = parentNode ? parentNode.babylonMesh : null;
+            babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
             node.babylonMesh = babylonMesh;
 
             node.babylonAnimationTargets = node.babylonAnimationTargets || [];
@@ -271,10 +303,9 @@ module BABYLON.GLTF2 {
 
         private _loadMeshData(node: IGLTFNode, mesh: IGLTFMesh, babylonMesh: Mesh): void {
             babylonMesh.name = mesh.name || babylonMesh.name;
-            babylonMesh.subMeshes = [];
 
-            var multiMaterial = new MultiMaterial(babylonMesh.name, this._babylonScene);
-            babylonMesh.material = multiMaterial;
+            var babylonMultiMaterial = new MultiMaterial(babylonMesh.name, this._babylonScene);
+            babylonMesh.material = babylonMultiMaterial;
 
             var geometry = new Geometry(babylonMesh.name, this._babylonScene, null, false, babylonMesh);
             var vertexData = new VertexData();
@@ -283,9 +314,9 @@ module BABYLON.GLTF2 {
 
             var subMeshInfos: { materialIndex: number, verticesStart: number, verticesCount: number, indicesStart: number, indicesCount: number }[] = [];
 
-            var primitivesLoaded = 0;
-            var numPrimitives = mesh.primitives.length;
-            for (var i = 0; i < numPrimitives; i++) {
+            var loadedPrimitives = 0;
+            var totalPrimitives = mesh.primitives.length;
+            for (let i = 0; i < totalPrimitives; i++) {
                 let primitive = mesh.primitives[i];
                 if (primitive.mode && primitive.mode !== EMeshPrimitiveMode.TRIANGLES) {
                     // TODO: handle other primitive modes
@@ -298,26 +329,38 @@ module BABYLON.GLTF2 {
                     this._loadMorphTargetsData(mesh, primitive, subVertexData, babylonMesh);
 
                     subMeshInfos.push({
-                        materialIndex: multiMaterial.subMaterials.length,
+                        materialIndex: i,
                         verticesStart: vertexData.positions.length,
                         verticesCount: subVertexData.positions.length,
                         indicesStart: vertexData.indices.length,
                         indicesCount: subVertexData.indices.length
                     });
 
-                    var subMaterial = primitive.material === undefined ? this._getDefaultMaterial() : GLTFLoaderExtension.LoadMaterial(primitive.material);
-                    multiMaterial.subMaterials.push(subMaterial);
                     vertexData.merge(subVertexData);
 
-                    if (++primitivesLoaded === numPrimitives) {
+                    if (primitive.material === undefined) {
+                        babylonMultiMaterial.subMaterials[i] = this._getDefaultMaterial();
+                    }
+                    else {
+                        this.loadMaterial(primitive.material, (babylonSubMaterial: Material) => {
+                            if (this._renderReady) {
+                                babylonSubMaterial.forceCompilation(babylonMesh, babylonSubMaterial => {
+                                    babylonMultiMaterial.subMaterials[i] = babylonSubMaterial;
+                                });
+                            }
+                            else {
+                                babylonMultiMaterial.subMaterials[i] = babylonSubMaterial;
+                            }
+                        });
+                    }
+
+                    if (++loadedPrimitives === totalPrimitives) {
                         geometry.setAllVerticesData(vertexData, false);
 
-                        // Sub meshes must be created after setting vertex data because of mesh._createGlobalSubMesh.
-                        for (var i = 0; i < subMeshInfos.length; i++) {
-                            var info = subMeshInfos[i];
-                            new SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, babylonMesh);
-                        }
-                    }
+                        // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
+                        // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
+                        babylonMesh.subMeshes = [];
+                        subMeshInfos.forEach(info => new SubMesh(info.materialIndex, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, babylonMesh));                    }
                 });
             }
         }
@@ -332,7 +375,7 @@ module BABYLON.GLTF2 {
             var vertexData = new VertexData();
 
             var loadedAttributes = 0;
-            var numAttributes = Object.keys(attributes).length;
+            var totalAttributes = Object.keys(attributes).length;
             for (let semantic in attributes) {
                 var accessor = this._gltf.accessors[attributes[semantic]];
                 this._loadAccessorAsync(accessor, data => {
@@ -366,7 +409,7 @@ module BABYLON.GLTF2 {
                             break;
                     }
 
-                    if (++loadedAttributes === numAttributes) {
+                    if (++loadedAttributes === totalAttributes) {
                         var indicesAccessor = this._gltf.accessors[primitive.indices];
                         if (indicesAccessor) {
                             this._loadAccessorAsync(indicesAccessor, data => {
@@ -469,39 +512,22 @@ module BABYLON.GLTF2 {
             babylonMesh.scaling = scaling;
         }
 
-        private _traverseScene(nodeNames: string[], scene: IGLTFScene, action: (node: IGLTFNode, parentNode: IGLTFNode) => boolean): void {
-            var nodes = scene.nodes;
-
-            if (nodes) {
-                for (var i = 0; i < nodes.length; i++) {
-                    this._traverseNode(nodeNames, nodes[i], action);
-                }
+        private _traverseNodes(indices: number[], action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode = null): void {
+            for (var i = 0; i < indices.length; i++) {
+                this._traverseNode(indices[i], action, parentNode);
             }
         }
 
-        private _traverseNode(nodeNames: string[], index: number, action: (node: IGLTFNode, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode = null): void {
+        private _traverseNode(index: number, action: (node: IGLTFNode, index: number, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode = null): void {
             var node = this._gltf.nodes[index];
 
-            if (nodeNames) {
-                if (nodeNames.indexOf(node.name)) {
-                    // load all children
-                    nodeNames = null;
-                }
-                else {
-                    // skip this node tree
-                    return;
-                }
-            }
-
-            node.index = index;
-
-            if (!action(node, parentNode)) {
+            if (!action(node, index, parentNode)) {
                 return;
             }
 
             if (node.children) {
                 for (var i = 0; i < node.children.length; i++) {
-                    this._traverseNode(nodeNames, node.children[i], action, node);
+                    this._traverseNode(node.children[i], action, node);
                 }
             }
         }
@@ -650,12 +676,12 @@ module BABYLON.GLTF2 {
 
         private _loadBufferAsync(index: number, onSuccess: (data: ArrayBufferView) => void): void {
             var buffer = this._gltf.buffers[index];
-            this._addPendingData(buffer);
+            this.addPendingData(buffer);
 
             if (buffer.loadedData) {
                 setTimeout(() => {
                     onSuccess(buffer.loadedData);
-                    this._removePendingData(buffer);
+                    this.removePendingData(buffer);
                 });
             }
             else if (GLTFUtils.IsBase64(buffer.uri)) {
@@ -663,20 +689,20 @@ module BABYLON.GLTF2 {
                 buffer.loadedData = new Uint8Array(data);
                 setTimeout(() => {
                     onSuccess(buffer.loadedData);
-                    this._removePendingData(buffer);
+                    this.removePendingData(buffer);
                 });
             }
             else if (buffer.loadedObservable) {
                 buffer.loadedObservable.add(buffer => {
                     onSuccess(buffer.loadedData);
-                    this._removePendingData(buffer);
+                    this.removePendingData(buffer);
                 });
             }
             else {
                 buffer.loadedObservable = new Observable<IGLTFBuffer>();
                 buffer.loadedObservable.add(buffer => {
                     onSuccess(buffer.loadedData);
-                    this._removePendingData(buffer);
+                    this.removePendingData(buffer);
                 });
 
                 Tools.LoadFile(this._rootUrl + buffer.uri, data => {
@@ -685,7 +711,7 @@ module BABYLON.GLTF2 {
                     buffer.loadedObservable = null;
                 }, null, null, true, request => {
                     this._errors.push("Failed to load file '" + buffer.uri + "': " + request.statusText + "(" + request.status + ")");
-                    this._removePendingData(buffer);
+                    this.removePendingData(buffer);
                 });
             }
         }
@@ -738,13 +764,32 @@ module BABYLON.GLTF2 {
             this._loadBufferViewAsync(bufferView, byteOffset, byteLength, accessor.componentType, onSuccess);
         }
 
-        private _addPendingData(data: any) {
-            this._pendingCount++;
+        public addPendingData(data: any) {
+            if (!this._renderReady) {
+                this._renderPendingCount++;
+            }
+
+            this.addLoaderPendingData(data);
+        }
+
+        public removePendingData(data: any) {
+            if (!this._renderReady) {
+                if (--this._renderPendingCount === 0) {
+                    this._renderReady = true;
+                    this._onRenderReady();
+                }
+            }
+
+            this.removeLoaderPendingData(data);
+        }
+
+        public addLoaderPendingData(data: any) {
+            this._loaderPendingCount++;
         }
 
-        private _removePendingData(data: any) {
-            if (--this._pendingCount === 0) {
-                this._onLoaded();
+        public removeLoaderPendingData(data: any) {
+            if (--this._loaderPendingCount === 0) {
+                this._onLoaderComplete();
             }
         }
 
@@ -765,55 +810,59 @@ module BABYLON.GLTF2 {
             return this._defaultMaterial;
         }
 
-        private _loadMaterial(index: number): IGLTFMaterial {
-            var materials = this._gltf.materials;
-            var material = materials ? materials[index] : null;
-            if (!material) {
-                Tools.Warn("Material index (" + index + ") does not exist");
-                return null;
+        private _loadMaterialMetallicRoughnessProperties(material: IGLTFMaterial): void {
+            // Ensure metallic workflow
+            material.babylonMaterial.metallic = 1;
+            material.babylonMaterial.roughness = 1;
+
+            var properties = material.pbrMetallicRoughness;
+            if (!properties) {
+                return;
             }
 
-            material.babylonMaterial = new PBRMaterial(material.name || "mat" + index, this._babylonScene);
-            material.babylonMaterial.sideOrientation = Material.CounterClockWiseSideOrientation;
-            material.babylonMaterial.useScalarInLinearSpace = true;
-            return material;
-        }
+            material.babylonMaterial.albedoColor = properties.baseColorFactor ? Color3.FromArray(properties.baseColorFactor) : new Color3(1, 1, 1);
+            material.babylonMaterial.metallic = properties.metallicFactor === undefined ? 1 : properties.metallicFactor;
+            material.babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
 
-        private _loadCoreMaterial(index: number): Material {
-            var material = this._loadMaterial(index);
-            if (!material) {
-                return null;
+            if (properties.baseColorTexture) {
+                material.babylonMaterial.albedoTexture = this.loadTexture(properties.baseColorTexture);
+                this.loadMaterialAlphaProperties(material);
             }
 
-            this._loadCommonMaterialProperties(material);
+            if (properties.metallicRoughnessTexture) {
+                material.babylonMaterial.metallicTexture = this.loadTexture(properties.metallicRoughnessTexture);
+                material.babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
+                material.babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
+                material.babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
+            }
+        }
 
-            // Ensure metallic workflow
-            material.babylonMaterial.metallic = 1;
-            material.babylonMaterial.roughness = 1;
+        public loadMaterial(index: number, assign: (material: Material) => void): void {
+            var material = this._gltf.materials[index];
+            material.index = index;
 
-            var properties = material.pbrMetallicRoughness;
-            if (properties) {
-                material.babylonMaterial.albedoColor = properties.baseColorFactor ? Color3.FromArray(properties.baseColorFactor) : new Color3(1, 1, 1);
-                material.babylonMaterial.metallic = properties.metallicFactor === undefined ? 1 : properties.metallicFactor;
-                material.babylonMaterial.roughness = properties.roughnessFactor === undefined ? 1 : properties.roughnessFactor;
-
-                if (properties.baseColorTexture) {
-                    material.babylonMaterial.albedoTexture = this._loadTexture(properties.baseColorTexture);
-                    this._loadAlphaProperties(material);
-                }
+            if (material.babylonMaterial) {
+                assign(material.babylonMaterial);
+                return;
+            }
 
-                if (properties.metallicRoughnessTexture) {
-                    material.babylonMaterial.metallicTexture = this._loadTexture(properties.metallicRoughnessTexture);
-                    material.babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
-                    material.babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
-                    material.babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
-                }
+            if (GLTFLoaderExtension.LoadMaterial(this, material, assign)) {
+                return;
             }
 
-            return material.babylonMaterial;
+            this.createPbrMaterial(material);
+            this.loadMaterialBaseProperties(material);
+            this._loadMaterialMetallicRoughnessProperties(material);
+            assign(material.babylonMaterial);
+        }
+
+        public createPbrMaterial(material: IGLTFMaterial): void {
+            material.babylonMaterial = new PBRMaterial(material.name || "mat" + material.index, this._babylonScene);
+            material.babylonMaterial.sideOrientation = Material.CounterClockWiseSideOrientation;
+            material.babylonMaterial.useScalarInLinearSpace = true;
         }
 
-        private _loadCommonMaterialProperties(material: IGLTFMaterial): void {
+        public loadMaterialBaseProperties(material: IGLTFMaterial): void {
             material.babylonMaterial.useEmissiveAsIllumination = (material.emissiveFactor || material.emissiveTexture) ? true : false;
             material.babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0);
             if (material.doubleSided) {
@@ -822,14 +871,14 @@ module BABYLON.GLTF2 {
             }
 
             if (material.normalTexture) {
-                material.babylonMaterial.bumpTexture = this._loadTexture(material.normalTexture);
+                material.babylonMaterial.bumpTexture = this.loadTexture(material.normalTexture);
                 if (material.normalTexture.scale !== undefined) {
                     material.babylonMaterial.bumpTexture.level = material.normalTexture.scale;
                 }
             }
 
             if (material.occlusionTexture) {
-                material.babylonMaterial.ambientTexture = this._loadTexture(material.occlusionTexture);
+                material.babylonMaterial.ambientTexture = this.loadTexture(material.occlusionTexture);
                 material.babylonMaterial.useAmbientInGrayScale = true;
                 if (material.occlusionTexture.strength !== undefined) {
                     material.babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
@@ -837,11 +886,11 @@ module BABYLON.GLTF2 {
             }
 
             if (material.emissiveTexture) {
-                material.babylonMaterial.emissiveTexture = this._loadTexture(material.emissiveTexture);
+                material.babylonMaterial.emissiveTexture = this.loadTexture(material.emissiveTexture);
             }
         }
 
-        private _loadAlphaProperties(material: IGLTFMaterial): void {
+        public loadMaterialAlphaProperties(material: IGLTFMaterial): void {
             var alphaMode = material.alphaMode || "OPAQUE";
             switch (alphaMode) {
                 case "OPAQUE":
@@ -850,19 +899,18 @@ module BABYLON.GLTF2 {
                 case "MASK":
                     material.babylonMaterial.albedoTexture.hasAlpha = true;
                     material.babylonMaterial.useAlphaFromAlbedoTexture = false;
-                    material.babylonMaterial.alphaMode = Engine.ALPHA_DISABLE;
                     break;
                 case "BLEND":
                     material.babylonMaterial.albedoTexture.hasAlpha = true;
                     material.babylonMaterial.useAlphaFromAlbedoTexture = true;
-                    material.babylonMaterial.alphaMode = Engine.ALPHA_COMBINE;
                     break;
                 default:
-                    Tools.Error("Invalid alpha mode '" + material.alphaMode + "'");
+                    Tools.Warn("Invalid alpha mode '" + material.alphaMode + "'");
+                    break;
             }
         }
 
-        private _loadTexture(textureInfo: IGLTFTextureInfo): Texture {
+        public loadTexture(textureInfo: IGLTFTextureInfo): Texture {
             var texture = this._gltf.textures[textureInfo.index];
             var texCoord = textureInfo.texCoord || 0;
 
@@ -911,12 +959,12 @@ module BABYLON.GLTF2 {
             var noMipMaps = (sampler.minFilter === ETextureMinFilter.NEAREST || sampler.minFilter === ETextureMinFilter.LINEAR);
             var samplingMode = GLTFUtils.GetTextureFilterMode(sampler.minFilter);
 
-            this._addPendingData(texture);
+            this.addPendingData(texture);
             var babylonTexture = new Texture(url, this._babylonScene, noMipMaps, false, samplingMode, () => {
-                this._removePendingData(texture);
+                this.removePendingData(texture);
             }, () => {
                 this._errors.push("Failed to load texture '" + source.uri + "'");
-                this._removePendingData(texture);
+                this.removePendingData(texture);
             });
 
             babylonTexture.coordinatesIndex = texCoord;

+ 21 - 21
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -2,36 +2,36 @@
 
 module BABYLON.GLTF2 {
     export abstract class GLTFLoaderExtension {
-        private _name: string;
-
         public enabled: boolean = true;
 
-        public constructor(name: string) {
-            this._name = name;
-        }
+        public abstract get name(): string;
+
+        protected loadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (material: Material) => void): boolean { return false; }
+
+        //
+        // Utilities
+        //
+
+        public static _Extensions: GLTFLoaderExtension[] = [];
 
-        public get name(): string {
-            return this._name;
+        public static LoadMaterial(loader: GLTFLoader, material: IGLTFMaterial, assign: (material: Material) => void): boolean {
+            return this._ApplyExtensions(extension => extension.loadMaterial(loader, material, assign));
         }
 
-        protected loadMaterial(index: number): Material { return null; }
+        private static _ApplyExtensions(action: (extension: GLTFLoaderExtension) => boolean) {
+            var extensions = GLTFLoaderExtension._Extensions;
+            if (!extensions) {
+                return;
+            }
 
-        // ---------
-        // Utilities
-        // ---------
-
-        public static LoadMaterial(index: number): Material {
-            for (var extensionName in GLTFLoader.Extensions) {
-                var extension = GLTFLoader.Extensions[extensionName];
-                if (extension.enabled) {
-                    var babylonMaterial = extension.loadMaterial(index);
-                    if (babylonMaterial) {
-                        return babylonMaterial;
-                    }
+            for (var i = 0; i < extensions.length; i++) {
+                var extension = extensions[i];
+                if (extension.enabled && action(extension)) {
+                    return true;
                 }
             }
 
-            return GLTFLoader.LoadCoreMaterial(index);
+            return false;
         }
     }
 }

+ 3 - 6
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -108,16 +108,11 @@ module BABYLON.GLTF2 {
         targets?: any[];
     }
 
-    export interface IGLTFAssetProfile extends IGLTFProperty {
-        api?: string;
-        version?: string;
-    }
-
     export interface IGLTFAsset extends IGLTFChildRootProperty {
         copyright?: string;
         generator?: string;
-        profile?: IGLTFAssetProfile;
         version: string;
+        minVersion?: string;
     }
 
     export interface IGLTFBuffer extends IGLTFChildRootProperty {
@@ -189,6 +184,7 @@ module BABYLON.GLTF2 {
         doubleSided?: boolean;
 
         // Runtime values
+        index?: number;
         babylonMaterial?: PBRMaterial;
     }
 
@@ -218,6 +214,7 @@ module BABYLON.GLTF2 {
 
         // Runtime values
         index?: number;
+        parent?: IGLTFNode;
         babylonMesh?: Mesh;
         babylonSkinToBones?: { [skin: number]: Bone };
         babylonAnimationTargets?: Node[];

+ 0 - 53
loaders/src/glTF/2.0/babylon.glTFMaterialsPbrSpecularGlossinessExtension.ts

@@ -1,53 +0,0 @@
-/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
-
-module BABYLON.GLTF2 {
-    interface IGLTFMaterialsPbrSpecularGlossiness {
-        diffuseFactor: number[];
-        diffuseTexture: IGLTFTextureInfo;
-        specularFactor: number[];
-        glossinessFactor: number;
-        specularGlossinessTexture: IGLTFTextureInfo;
-    }
-
-    export class GLTFMaterialsPbrSpecularGlossinessExtension extends GLTFLoaderExtension {
-        constructor() {
-            super("KHR_materials_pbrSpecularGlossiness");
-        }
-
-        protected loadMaterial(index: number): Material {
-            var material = GLTFLoader.LoadMaterial(index);
-            if (!material || !material.extensions) return null;
-
-            var properties: IGLTFMaterialsPbrSpecularGlossiness = material.extensions[this.name];
-            if (!properties) return null;
-
-            GLTFLoader.LoadCommonMaterialProperties(material);
-
-            //
-            // Load Factors
-            //
-
-            material.babylonMaterial.albedoColor = properties.diffuseFactor ? Color3.FromArray(properties.diffuseFactor) : new Color3(1, 1, 1);
-            material.babylonMaterial.reflectivityColor = properties.specularFactor ? Color3.FromArray(properties.specularFactor) : new Color3(1, 1, 1);
-            material.babylonMaterial.microSurface = properties.glossinessFactor === undefined ? 1 : properties.glossinessFactor;
-
-            //
-            // Load Textures
-            //
-
-            if (properties.diffuseTexture) {
-                material.babylonMaterial.albedoTexture = GLTFLoader.LoadTexture(properties.diffuseTexture);
-                GLTFLoader.LoadAlphaProperties(material);
-            }
-
-            if (properties.specularGlossinessTexture) {
-                material.babylonMaterial.reflectivityTexture = GLTFLoader.LoadTexture(properties.specularGlossinessTexture);
-                material.babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
-            }
-
-            return material.babylonMaterial;
-        }
-    }
-
-    GLTFLoader.RegisterExtension(new GLTFMaterialsPbrSpecularGlossinessExtension());
-}