Browse Source

Merge pull request #2011 from bghgary/glTFLoader2

glTF 2 loader with versioning support
David Catuhe 8 years ago
parent
commit
66b7b92e98

+ 22 - 6
Tools/Gulp/config.json

@@ -509,14 +509,30 @@
             },
             {
                 "files": [
-                    "../../loaders/src/glTF/babylon.glTFFileLoaderInterfaces.ts",
-                    "../../loaders/src/glTF/babylon.glTFFileLoader.ts",
-                    "../../loaders/src/glTF/babylon.glTFFileLoaderUtils.ts",
-                    "../../loaders/src/glTF/babylon.glTFFileLoaderExtension.ts",
-                    "../../loaders/src/glTF/babylon.glTFBinaryExtension.ts",
-                    "../../loaders/src/glTF/babylon.glTFMaterialCommonExtension.ts"
+                    "../../loaders/src/glTF/babylon.glTFFileLoader.ts"
                 ],
                 "output": "babylon.glTFFileLoader.js"
+            },
+            {
+                "files": [
+                    "../../loaders/src/glTF/1.0/babylon.glTFLoaderInterfaces.ts",
+                    "../../loaders/src/glTF/1.0/babylon.glTFLoader.ts",
+                    "../../loaders/src/glTF/1.0/babylon.glTFLoaderUtils.ts",
+                    "../../loaders/src/glTF/1.0/babylon.glTFLoaderExtension.ts",
+                    "../../loaders/src/glTF/1.0/babylon.glTFBinaryExtension.ts",
+                    "../../loaders/src/glTF/1.0/babylon.glTFMaterialsCommonExtension.ts"
+                ],
+                "output": "babylon.glTF1Loader.js"
+            },
+            {
+                "files": [
+                    "../../loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts",
+                    "../../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"
+                ],
+                "output": "babylon.glTF2Loader.js"
             }
         ],
         "build": {

+ 1 - 0
dist/preview release/what's new.md

@@ -48,6 +48,7 @@
 - Added `VertexBuffer.TangentKind` to specify tangents in place of shader-calculated tangents ([dewadswo](https://github.com/dewadswo), [bghgary](https://github.com/bghgary))
 - Added `material.twoSidedLighting` to PBRMaterial and StandardMaterial to enable flipping normals when backfaceCulling is false ([BeardedGnome](https://github.com/BeardedGnome), [bghgary](https://github.com/bghgary))
 - Added a [HTML page](https://github.com/BabylonJS/Babylon.js/blob/master/Tools/Gulp/profiling.html) with embedded directions to improve the custom build process. ([jcpalmer](https://github.com/Palmer-JC))
+- Added glTF 2.0 loader with versioning support ([bghgary](https://github.com/bghgary), thanks to [BeardedGnome](https://github.com/BeardedGnome) for animation updates)
  
 ### Bug fixes
 - Fixed a bug with spotlight direction ([deltakosh](https://github.com/deltakosh)) 

+ 11 - 74
loaders/src/glTF/babylon.glTFBinaryExtension.ts

@@ -1,17 +1,12 @@
-/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-module BABYLON {
+module BABYLON.GLTF1 {
     const BinaryExtensionBufferName = "binary_glTF";
 
     enum EContentFormat {
         JSON = 0
     };
 
-    interface IGLTFBinaryExtension {
-        content: Object;
-        body: Uint8Array;
-    };
-
     interface IGLTFBinaryExtensionShader {
         bufferView: string;
     };
@@ -23,35 +18,21 @@ module BABYLON {
         width: number;
     };
 
-    export class GLTFBinaryExtension extends GLTFFileLoaderExtension {
-        private _binary: IGLTFBinaryExtension;
+    export class GLTFBinaryExtension extends GLTFLoaderExtension {
+        private _bin : ArrayBufferView;
 
         public constructor() {
             super("KHR_binary_glTF");
         }
 
-        public loadRuntimeAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
-            if (!(data instanceof ArrayBuffer)) {
+        public loadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
+            var extensionsUsed = (<any>data.json).extensionsUsed;
+            if (!extensionsUsed || extensionsUsed.indexOf(this.name) === -1) {
                 return false;
             }
 
-            setTimeout(() => {
-                this._binary = this._parseBinary(<ArrayBuffer>data);
-                if (!this._binary) {
-                    onError();
-                    return true;
-                }
-
-                var gltfRuntime = GLTFFileLoaderBase.CreateRuntime(this._binary.content, scene, rootUrl);
-
-                if (gltfRuntime.extensionsUsed.indexOf(this.name) === -1) {
-                    Tools.Warn("glTF binary file does not have " + this.name + " specified in extensionsUsed");
-                    gltfRuntime.extensionsUsed.push(this.name);
-                }
-
-                onSuccess(gltfRuntime);
-            });
-
+            this._bin = data.bin;
+            onSuccess(GLTFLoaderBase.CreateRuntime(data.json, scene, rootUrl));
             return true;
         }
 
@@ -64,7 +45,7 @@ module BABYLON {
                 return false;
             }
 
-            onSuccess(this._binary.body);
+            onSuccess(this._bin);
             return true;
         }
 
@@ -99,50 +80,6 @@ module BABYLON {
 
             return true;
         }
-
-        // Parses a glTF binary array buffer into its content and body
-        private _parseBinary(data: ArrayBuffer): IGLTFBinaryExtension {
-            var binaryReader = new BinaryReader(data);
-
-            var magic = GLTFUtils.DecodeBufferToText(binaryReader.getUint8Array(4));
-            if (magic != "glTF") {
-                Tools.Error("Unexpected magic: " + magic);
-                return null;
-            }
-
-            var version = binaryReader.getUint32();
-            if (version != 1) {
-                Tools.Error("Unsupported version: " + version);
-                return null;
-            }
-
-            var length = binaryReader.getUint32();
-            if (length != data.byteLength) {
-                Tools.Error("Length in header does not match actual data length: " + length + " != " + data.byteLength);
-                return null;
-            }
-
-            var contentLength = binaryReader.getUint32();
-            var contentFormat = <EContentFormat>binaryReader.getUint32();
-
-            var content: Object;
-            switch (contentFormat) {
-                case EContentFormat.JSON:
-                    var jsonText = GLTFUtils.DecodeBufferToText(binaryReader.getUint8Array(contentLength));
-                    content = JSON.parse(jsonText);
-                    break;
-                default:
-                    Tools.Error("Unexpected content format: " + contentFormat);
-                    return null;
-            }
-
-            var body = binaryReader.getUint8Array();
-
-            return {
-                content: content,
-                body: body
-            };
-        };
     }
 
     class BinaryReader {
@@ -173,5 +110,5 @@ module BABYLON {
         }
     }
 
-    GLTFFileLoader.RegisterExtension(new GLTFBinaryExtension());
+    GLTFLoader.RegisterExtension(new GLTFBinaryExtension());
 }

File diff suppressed because it is too large
+ 1735 - 0
loaders/src/glTF/1.0/babylon.glTFLoader.ts


+ 23 - 23
loaders/src/glTF/babylon.glTFFileLoaderExtension.ts

@@ -1,7 +1,7 @@
-/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-module BABYLON {
-    export abstract class GLTFFileLoaderExtension {
+module BABYLON.GLTF1 {
+    export abstract class GLTFLoaderExtension {
         private _name: string;
 
         public constructor(name: string) {
@@ -16,7 +16,7 @@ module BABYLON {
         * Defines an override for loading the runtime
         * Return true to stop further extensions from loading the runtime
         */
-        public loadRuntimeAsync(scene: Scene, data: string, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
+        public loadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): boolean {
             return false;
         }
 
@@ -72,18 +72,18 @@ module BABYLON {
         // Utilities
         // ---------
 
-        public static LoadRuntimeAsync(scene: Scene, data: string, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): void {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+        public static LoadRuntimeAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (gltfRuntime: IGLTFRuntime) => void, onError: () => void): void {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadRuntimeAsync(scene, data, rootUrl, onSuccess, onError);
             }, () => {
                 setTimeout(() => {
-                    onSuccess(GLTFFileLoaderBase.CreateRuntime(JSON.parse(data), scene, rootUrl));
+                    onSuccess(GLTFLoaderBase.CreateRuntime(data.json, scene, rootUrl));
                 });
             });
         }
 
         public static LoadRuntimeExtensionsAsync(gltfRuntime: IGLTFRuntime, onSuccess: () => void, onError: () => void): void {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadRuntimeExtensionsAsync(gltfRuntime, onSuccess, onError);
             }, () => {
                 setTimeout(() => {
@@ -93,54 +93,54 @@ module BABYLON {
         }
 
         public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (bufferView: ArrayBufferView) => void, onError: () => void, onProgress?: () => void): void {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadBufferAsync(gltfRuntime, id, onSuccess, onError, onProgress);
             }, () => {
-                GLTFFileLoaderBase.LoadBufferAsync(gltfRuntime, id, onSuccess, onError, onProgress);
+                GLTFLoaderBase.LoadBufferAsync(gltfRuntime, id, onSuccess, onError, onProgress);
             });
         }
 
         public static LoadTextureAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (texture: Texture) => void, onError: () => void): void {
-            GLTFFileLoaderExtension.LoadTextureBufferAsync(gltfRuntime, id,
-                buffer => GLTFFileLoaderExtension.CreateTextureAsync(gltfRuntime, id, buffer, onSuccess, onError),
+            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 {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadShaderStringAsync(gltfRuntime, id, onSuccess, onError);
             }, () => {
-                GLTFFileLoaderBase.LoadShaderStringAsync(gltfRuntime, id, onSuccess, onError);
+                GLTFLoaderBase.LoadShaderStringAsync(gltfRuntime, id, onSuccess, onError);
             });
         }
 
         public static LoadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: () => void): void {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadMaterialAsync(gltfRuntime, id, onSuccess, onError);
             }, () => {
-                GLTFFileLoaderBase.LoadMaterialAsync(gltfRuntime, id, onSuccess, onError);
+                GLTFLoaderBase.LoadMaterialAsync(gltfRuntime, id, onSuccess, onError);
             });
         }
 
         private static LoadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: () => void): void {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.loadTextureBufferAsync(gltfRuntime, id, onSuccess, onError);
             }, () => {
-                GLTFFileLoaderBase.LoadTextureBufferAsync(gltfRuntime, id, onSuccess, onError);
+                GLTFLoaderBase.LoadTextureBufferAsync(gltfRuntime, id, onSuccess, onError);
             });
         }
 
         private static CreateTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: ArrayBufferView, onSuccess: (texture: Texture) => void, onError: () => void): void {
-            GLTFFileLoaderExtension.ApplyExtensions(loaderExtension => {
+            GLTFLoaderExtension.ApplyExtensions(loaderExtension => {
                 return loaderExtension.createTextureAsync(gltfRuntime, id, buffer, onSuccess, onError);
             }, () => {
-                GLTFFileLoaderBase.CreateTextureAsync(gltfRuntime, id, buffer, onSuccess, onError);
+                GLTFLoaderBase.CreateTextureAsync(gltfRuntime, id, buffer, onSuccess, onError);
             });
         }
 
-        private static ApplyExtensions(func: (loaderExtension: GLTFFileLoaderExtension) => boolean, defaultFunc: () => void): void {
-            for (var extensionName in GLTFFileLoader.Extensions) {
-                var loaderExtension = GLTFFileLoader.Extensions[extensionName];
+        private static ApplyExtensions(func: (loaderExtension: GLTFLoaderExtension) => boolean, defaultFunc: () => void): void {
+            for (var extensionName in GLTFLoader.Extensions) {
+                var loaderExtension = GLTFLoader.Extensions[extensionName];
                 if (func(loaderExtension)) {
                     return;
                 }

+ 2 - 2
loaders/src/glTF/babylon.glTFFileLoaderInterfaces.ts

@@ -1,6 +1,6 @@
-/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-module BABYLON {
+module BABYLON.GLTF1 {
     /**
     * Enums
     */

+ 2 - 2
loaders/src/glTF/babylon.glTFFileLoaderUtils.ts

@@ -1,6 +1,6 @@
-/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-module BABYLON {
+module BABYLON.GLTF1 {
     /**
     * Utils functions for GLTF
     */

+ 10 - 10
loaders/src/glTF/babylon.glTFMaterialCommonExtension.ts

@@ -1,7 +1,7 @@
-/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-module BABYLON {
-    interface IGLTFMaterialCommonExtensionValues {
+module BABYLON.GLTF1 {
+    interface IGLTFMaterialsCommonExtensionValues {
         ambient?: number[] | string;
         diffuse?: number[] | string;
         emission?: number[] | string;
@@ -10,11 +10,11 @@ module BABYLON {
         transparency?: number;
     };
 
-    interface IGLTFMaterialCommonExtension {
+    interface IGLTFMaterialsCommonExtension {
         technique: string;
         transparent?: number;
         doubleSided?: boolean;
-        values: IGLTFMaterialCommonExtensionValues;
+        values: IGLTFMaterialsCommonExtensionValues;
     };
 
     interface IGLTFRuntimeCommonExtension {
@@ -55,7 +55,7 @@ module BABYLON {
         quadraticAttenuation: number;
     }
 
-    export class GLTFMaterialCommonExtension extends GLTFFileLoaderExtension {
+    export class GLTFMaterialsCommonExtension extends GLTFLoaderExtension {
 
         constructor() {
             super("KHR_materials_common");
@@ -109,7 +109,7 @@ module BABYLON {
             var material: IGLTFMaterial = gltfRuntime.materials[id];
             if (!material || !material.extensions) return false;
 
-            var extension: IGLTFMaterialCommonExtension = material.extensions[this.name];
+            var extension: IGLTFMaterialsCommonExtension = material.extensions[this.name];
             if (!extension) return false;
 
             var standardMaterial = new StandardMaterial(id, gltfRuntime.scene);
@@ -160,12 +160,12 @@ module BABYLON {
 
         private _loadTexture(gltfRuntime: IGLTFRuntime, id: string, material: StandardMaterial, propertyPath: string, onError: () => void): void {
             // Create buffer from texture url
-            GLTFFileLoaderBase.LoadTextureBufferAsync(gltfRuntime, id, (buffer) => {
+            GLTFLoaderBase.LoadTextureBufferAsync(gltfRuntime, id, (buffer) => {
                 // Create texture from buffer
-                GLTFFileLoaderBase.CreateTextureAsync(gltfRuntime, id, buffer, (texture) => material[propertyPath] = texture, onError);
+                GLTFLoaderBase.CreateTextureAsync(gltfRuntime, id, buffer, (texture) => material[propertyPath] = texture, onError);
             }, onError);
         }
     }
 
-    GLTFFileLoader.RegisterExtension(new GLTFMaterialCommonExtension());
+    GLTFLoader.RegisterExtension(new GLTFMaterialsCommonExtension());
 }

File diff suppressed because it is too large
+ 1200 - 0
loaders/src/glTF/2.0/babylon.glTFLoader.ts


+ 46 - 0
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -0,0 +1,46 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2 {
+    export abstract class GLTFLoaderExtension {
+        private _name: string;
+
+        public constructor(name: string) {
+            this._name = name;
+        }
+
+        public get name(): string {
+            return this._name;
+        }
+
+        protected postCreateRuntime(runtime: IGLTFRuntime): void {}
+
+        // Return true to stop other extensions from loading materials.
+        protected loadMaterial(runtime: IGLTFRuntime, index: number): boolean { return false; }
+
+        // ---------
+        // Utilities
+        // ---------
+
+        public static PostCreateRuntime(runtime: IGLTFRuntime): void {
+            for (var extensionName in GLTFLoader.Extensions) {
+                var extension = GLTFLoader.Extensions[extensionName];
+                extension.postCreateRuntime(runtime);
+            }
+        }
+
+        public static LoadMaterial(runtime: IGLTFRuntime, index: number): void {
+            for (var extensionName in GLTFLoader.Extensions) {
+                var extension = GLTFLoader.Extensions[extensionName];
+                if (extension.loadMaterial(runtime, index)) {
+                    return;
+                }
+            }
+
+            var material = GLTFLoader.LoadMaterial(runtime, index);
+            if (material) {
+                GLTFLoader.LoadMetallicRoughnessMaterialProperties(runtime, material);
+                GLTFLoader.LoadCommonMaterialProperties(runtime, material);
+            }
+        }
+    }
+}

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

@@ -0,0 +1,350 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2 {
+    /**
+    * Enums
+    */
+    export enum EBufferViewTarget {
+        ARRAY_BUFFER = 34962,
+        ELEMENT_ARRAY_BUFFER = 34963
+    }
+
+    export enum EComponentType {
+        BYTE = 5120,
+        UNSIGNED_BYTE = 5121,
+        SHORT = 5122,
+        UNSIGNED_SHORT = 5123,
+        FLOAT = 5126
+    }
+
+    export enum EMeshPrimitiveMode {
+        POINTS = 0,
+        LINES = 1,
+        LINE_LOOP = 2,
+        LINE_STRIP = 3,
+        TRIANGLES = 4,
+        TRIANGLE_STRIP = 5,
+        TRIANGLE_FAN = 6
+    }
+
+    export enum EParameterType {
+        BYTE = 5120,
+        UNSIGNED_BYTE = 5121,
+        SHORT = 5122,
+        UNSIGNED_SHORT = 5123,
+        INT = 5124,
+        UNSIGNED_INT = 5125,
+        FLOAT = 5126,
+        FLOAT_VEC2 = 35664,
+        FLOAT_VEC3 = 35665,
+        FLOAT_VEC4 = 35666,
+        INT_VEC2 = 35667,
+        INT_VEC3 = 35668,
+        INT_VEC4 = 35669,
+        BOOL = 35670,
+        BOOL_VEC2 = 35671,
+        BOOL_VEC3 = 35672,
+        BOOL_VEC4 = 35673,
+        FLOAT_MAT2 = 35674,
+        FLOAT_MAT3 = 35675,
+        FLOAT_MAT4 = 35676,
+        SAMPLER_2D = 35678
+    }
+
+    export enum ETextureMagFilter {
+        NEAREST = 9728,
+        LINEAR = 9728,
+    }
+
+    export enum ETextureMinFilter {
+        NEAREST = 9728,
+        LINEAR = 9728,
+        NEAREST_MIPMAP_NEAREST = 9984,
+        LINEAR_MIPMAP_NEAREST = 9985,
+        NEAREST_MIPMAP_LINEAR = 9986,
+        LINEAR_MIPMAP_LINEAR = 9987
+    }
+
+    export enum ETextureFormat {
+        ALPHA = 6406,
+        RGB = 6407,
+        RGBA = 6408,
+        LUMINANCE = 6409,
+        LUMINANCE_ALPHA = 6410
+    }
+
+    export enum ETextureTarget {
+        TEXTURE_2D = 3553
+    }
+
+    export enum ETextureType {
+        UNSIGNED_BYTE = 5121,
+        UNSIGNED_SHORT_5_6_5 = 33635,
+        UNSIGNED_SHORT_4_4_4_4 = 32819,
+        UNSIGNED_SHORT_5_5_5_1 = 32820
+    }
+
+    export enum ETextureWrapMode {
+        CLAMP_TO_EDGE = 33071,
+        MIRRORED_REPEAT = 33648,
+        REPEAT = 10497
+    }
+
+    /**
+    * Interfaces
+    */
+    export interface IGLTFProperty {
+        extensions?: Object;
+        extras?: any;
+    }
+
+    export interface IGLTFChildRootProperty extends IGLTFProperty {
+        name?: string;
+    }
+
+    export interface IGLTFAccessorSparseIndices extends IGLTFProperty {
+        bufferView: number;
+        byteOffset?: number;
+        componentType: EComponentType;
+    }
+
+    export interface IGLTFAccessorSparseValues extends IGLTFProperty {
+        bufferView: number;
+        byteOffset?: number;
+    }
+
+    export interface IGLTFAccessorSparse extends IGLTFProperty {
+        count: number;
+        indices: IGLTFAccessorSparseIndices;
+        values: IGLTFAccessorSparseValues;
+    }
+
+    export interface IGLTFAccessor extends IGLTFChildRootProperty {
+        bufferView?: number;
+        byteOffset?: number;
+        componentType: EComponentType;
+        normalized?: boolean;
+        count: number;
+        type: string;
+        max: number[];
+        min: number[];
+        sparse?: IGLTFAccessorSparse;
+    }
+
+    export interface IGLTFAnimationChannel extends IGLTFProperty {
+        sampler: number;
+        target: IGLTFAnimationChannelTarget;
+    }
+
+    export interface IGLTFAnimationChannelTarget extends IGLTFProperty {
+        node: number;
+        path: string;
+    }
+
+    export interface IGLTFAnimationSampler extends IGLTFProperty {
+        input: number;
+        interpolation?: string;
+        output: number;
+    }
+
+    export interface IGLTFAnimation extends IGLTFChildRootProperty {
+        channels: IGLTFAnimationChannel[];
+        samplers: IGLTFAnimationSampler[];
+    }
+
+    export interface IGLTFAssetProfile extends IGLTFProperty {
+        api?: string;
+        version?: string;
+    }
+
+    export interface IGLTFAsset extends IGLTFChildRootProperty {
+        copyright?: string;
+        generator?: string;
+        profile?: IGLTFAssetProfile;
+        version: string;
+    }
+
+    export interface IGLTFBuffer extends IGLTFChildRootProperty {
+        uri?: string;
+        byteLength: number;
+
+        // Loaded buffer (optimize)
+        loadedBufferView: ArrayBufferView
+    }
+
+    export interface IGLTFBufferView extends IGLTFChildRootProperty {
+        buffer: number;
+        byteOffset: number;
+        byteLength: number;
+        byteStride?: number;
+        target?: EBufferViewTarget;
+    }
+
+    export interface IGLTFCameraOrthographic extends IGLTFProperty {
+        xmag: number;
+        ymag: number;
+        zfar: number;
+        znear: number;
+    }
+
+    export interface IGLTFCameraPerspective extends IGLTFProperty {
+        aspectRatio: number;
+        yfov: number;
+        zfar: number;
+        znear: number;
+    }
+
+    export interface IGLTFCamera extends IGLTFChildRootProperty {
+        orthographic?: IGLTFCameraOrthographic;
+        perspective?: IGLTFCameraPerspective;
+        type: string;
+    }
+
+    export interface IGLTFImage extends IGLTFChildRootProperty {
+        uri?: string;
+        mimeType?: string;
+        bufferView?: number;
+    }
+
+    export interface IGLTFMaterialNormalTextureInfo extends IGLTFTextureInfo {
+        scale: number;
+    }
+
+    export interface IGLTFMaterialOcclusionTextureInfo extends IGLTFTextureInfo {
+        strength: number;
+    }
+
+    export interface IGLTFMaterialPbrMetallicRoughness {
+        baseColorFactor: number[];
+        baseColorTexture: IGLTFTextureInfo;
+        metallicFactor: number;
+        roughnessFactor: number;
+        metallicRoughnessTexture: IGLTFTextureInfo;
+    }
+
+    export interface IGLTFMaterial extends IGLTFChildRootProperty {
+        pbrMetallicRoughness?: IGLTFMaterialPbrMetallicRoughness;
+        normalTexture?: IGLTFMaterialNormalTextureInfo;
+        occlusionTexture?: IGLTFMaterialOcclusionTextureInfo;
+        emissiveTexture?: IGLTFTextureInfo;
+        emissiveFactor?: number[];
+        alphaMode?: string;
+        alphaCutoff: number;
+        doubleSided?: boolean;
+
+        // Babylon.js values (optimize)
+        babylonMaterial?: PBRMaterial;
+    }
+
+    export interface IGLTFMeshPrimitive extends IGLTFProperty {
+        attributes: { [name: string]: number };
+        indices?: number;
+        material?: number;
+        mode?: EMeshPrimitiveMode;
+        targets?: number[];
+    }
+
+    export interface IGLTFMesh extends IGLTFChildRootProperty {
+        primitives: IGLTFMeshPrimitive[];
+        weights?: number[];
+    }
+
+    export interface IGLTFNode extends IGLTFChildRootProperty {
+        camera?: number;
+        children?: number[];
+        skin?: number;
+        matrix?: number[];
+        mesh?: number;
+        rotation?: number[];
+        scale?: number[];
+        translation?: number[];
+        weights?: number[];
+
+        // Babylon.js values (optimize)
+        babylonNode?: Node;
+    }
+
+    export interface IGLTFSampler extends IGLTFChildRootProperty {
+        magFilter?: ETextureMagFilter;
+        minFilter?: ETextureMinFilter;
+        wrapS?: ETextureWrapMode;
+        wrapT?: ETextureWrapMode;
+    }
+
+    export interface IGLTFScene extends IGLTFChildRootProperty {
+        nodes: number[];
+    }
+
+    export interface IGLTFSkin extends IGLTFChildRootProperty {
+        inverseBindMatrices?: number;
+        skeleton?: number;
+        joints: number[];
+
+        // Babylon.js values (optimize)
+        babylonSkeleton?: Skeleton;
+    }
+
+    export interface IGLTFTexture extends IGLTFChildRootProperty {
+        format?: ETextureFormat;
+        internalFormat?: ETextureFormat;
+        sampler: number;
+        source: number;
+        target?: ETextureTarget;
+        type?: ETextureType;
+
+        // Babylon.js values (optimize)
+        babylonTexture?: Texture;
+    }
+
+    export interface IGLTFTextureInfo {
+        index: number;
+        texCoord?: number;
+    }
+
+    export interface IGLTF extends IGLTFProperty {
+        accessors?: IGLTFAccessor[];
+        animations?: IGLTFAnimation[];
+        asset: IGLTFAsset;
+        buffers?: IGLTFBuffer[];
+        bufferViews?: IGLTFBufferView[];
+        cameras?: IGLTFCamera[];
+        extensionsUsed?: string[];
+        extensionsRequired?: string[];
+        glExtensionsUsed?: string[];
+        images?: IGLTFImage[];
+        materials?: IGLTFMaterial[];
+        meshes?: IGLTFMesh[];
+        nodes?: IGLTFNode[];
+        samplers?: IGLTFSampler[];
+        scene?: number;
+        scenes?: IGLTFScene[];
+        skins?: IGLTFSkin[];
+        textures?: IGLTFTexture[];
+    }
+
+    export interface IGLTFRuntime {
+        gltf: IGLTF;
+
+        babylonScene: Scene;
+        rootUrl: string;
+
+        importOnlyMeshes: boolean;
+        importMeshesNames?: string[];
+
+        defaultMaterial?: PBRMaterial;
+    }
+
+    /**
+    * Bones
+    */
+    export interface INodeToRoot {
+        bone: Bone;
+        node: IGLTFNode;
+        index: number;
+    }
+
+    export interface IJointNode {
+        node: IGLTFNode;
+        index: number;
+    }
+}

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

@@ -0,0 +1,141 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2 {
+    /**
+    * Utils functions for GLTF
+    */
+    export class GLTFUtils {
+        /**
+        * If the uri is a base64 string
+        * @param uri: the uri to test
+        */
+        public static IsBase64(uri: string): boolean {
+            return uri.length < 5 ? false : uri.substr(0, 5) === "data:";
+        }
+
+        /**
+        * Decode the base64 uri
+        * @param uri: the uri to decode
+        */
+        public static DecodeBase64(uri: string): ArrayBuffer {
+            var decodedString = atob(uri.split(",")[1]);
+            var bufferLength = decodedString.length;
+            var bufferView = new Uint8Array(new ArrayBuffer(bufferLength));
+
+            for (var i = 0; i < bufferLength; i++) {
+                bufferView[i] = decodedString.charCodeAt(i);
+            }
+
+            return bufferView.buffer;
+        }
+
+        /**
+        * Returns the wrap mode of the texture
+        * @param mode: the mode value
+        */
+        public static GetWrapMode(mode: number): number {
+            switch (mode) {
+                case ETextureWrapMode.CLAMP_TO_EDGE: return Texture.CLAMP_ADDRESSMODE;
+                case ETextureWrapMode.MIRRORED_REPEAT: return Texture.MIRROR_ADDRESSMODE;
+                case ETextureWrapMode.REPEAT: return Texture.WRAP_ADDRESSMODE;
+                default: return Texture.WRAP_ADDRESSMODE;
+            }
+        }
+
+        /**
+         * 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;
+            }
+        }
+
+        /**
+         * Returns the texture filter mode giving a mode value
+         * @param mode: the filter mode value
+         */
+        public static GetTextureFilterMode(mode: number): ETextureMinFilter {
+            switch (mode) {
+                case ETextureMinFilter.LINEAR:
+                case ETextureMinFilter.LINEAR_MIPMAP_NEAREST:
+                case ETextureMinFilter.LINEAR_MIPMAP_LINEAR: return Texture.TRILINEAR_SAMPLINGMODE;
+                case ETextureMinFilter.NEAREST:
+                case ETextureMinFilter.NEAREST_MIPMAP_NEAREST: return Texture.NEAREST_SAMPLINGMODE;
+                default: return Texture.BILINEAR_SAMPLINGMODE;
+            }
+        }
+
+        public static GetBufferFromBufferView(runtime: IGLTFRuntime, bufferView: IGLTFBufferView, byteOffset: number, byteLength: number, componentType: EComponentType): ArrayBufferView {
+            var byteOffset = bufferView.byteOffset + byteOffset;
+
+            var loadedBufferView = runtime.gltf.buffers[bufferView.buffer].loadedBufferView;
+            if (byteOffset + byteLength > loadedBufferView.byteLength) {
+                throw new Error("Buffer access is out of range");
+            }
+
+            var buffer = loadedBufferView.buffer;
+            byteOffset += loadedBufferView.byteOffset;
+
+            switch (componentType) {
+                case EComponentType.BYTE: return new Int8Array(buffer, byteOffset, byteLength);
+                case EComponentType.UNSIGNED_BYTE: return new Uint8Array(buffer, byteOffset, byteLength);
+                case EComponentType.SHORT: return new Int16Array(buffer, byteOffset, byteLength);
+                case EComponentType.UNSIGNED_SHORT: return new Uint16Array(buffer, byteOffset, byteLength);
+                default: return new Float32Array(buffer, byteOffset, byteLength);
+            }
+        }
+
+        /**
+         * Returns a buffer from its accessor
+         * @param runtime: the GLTF runtime
+         * @param accessor: the GLTF accessor
+         */
+        public static GetBufferFromAccessor(runtime: IGLTFRuntime, accessor: IGLTFAccessor): any {
+            var bufferView = runtime.gltf.bufferViews[accessor.bufferView];
+            var byteLength = accessor.count * GLTFUtils.GetByteStrideFromType(accessor);
+            return GLTFUtils.GetBufferFromBufferView(runtime, bufferView, accessor.byteOffset, byteLength, accessor.componentType);
+        }
+
+        /**
+         * Decodes a buffer view into a string
+         * @param view: the buffer view
+         */
+        public static DecodeBufferToText(view: ArrayBufferView): string {
+            var result = "";
+            var length = view.byteLength;
+
+            for (var i = 0; i < length; ++i) {
+                result += String.fromCharCode(view[i]);
+            }
+
+            return result;
+        }
+
+        /**
+         * Returns the default material of gltf.
+         * @param scene: the Babylon.js scene
+         */
+        public static GetDefaultMaterial(runtime: IGLTFRuntime): PBRMaterial {
+            if (!runtime.defaultMaterial) {
+                var material = new PBRMaterial("gltf_default", runtime.babylonScene);
+                material.sideOrientation = Material.CounterClockWiseSideOrientation;
+                material.metallic = 1;
+                material.roughness = 1;
+                runtime.defaultMaterial = material;
+            }
+
+            return runtime.defaultMaterial;
+        }
+    }
+}

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

@@ -0,0 +1,56 @@
+/// <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(runtime: IGLTFRuntime, index: number): boolean {
+            var material = GLTFLoader.LoadMaterial(runtime, index);
+            if (!material || !material.extensions) return false;
+
+            var properties: IGLTFMaterialsPbrSpecularGlossiness = material.extensions[this.name];
+            if (!properties) return false;
+
+            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) {
+                GLTFLoader.LoadTextureAsync(runtime, properties.diffuseTexture,
+                    texture => {
+                        material.babylonMaterial.albedoTexture = texture;
+                        GLTFLoader.LoadAlphaProperties(runtime, material);
+                    },
+                    () => {
+                        Tools.Warn("Failed to load diffuse texture");
+                    });
+            }
+
+            if (properties.specularGlossinessTexture) {
+                GLTFLoader.LoadTextureAsync(runtime, properties.specularGlossinessTexture,
+                    texture => {
+                        material.babylonMaterial.reflectivityTexture = texture;
+                        material.babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
+                    },
+                    () => {
+                        Tools.Warn("Failed to load metallic roughness texture");
+                    });
+            }
+
+            GLTFLoader.LoadCommonMaterialProperties(runtime, material);
+            return true;
+        }
+    }
+
+    GLTFLoader.RegisterExtension(new GLTFMaterialsPbrSpecularGlossinessExtension());
+}

+ 25 - 14
loaders/src/glTF/README.md

@@ -1,12 +1,28 @@
 # Babylon.js glTF File Loader
 
 # Usage
-The glTF file loader is a SceneLoader plugin.
-Just reference the loader in your HTML file:
+The glTF file loader is a SceneLoader plugin. The loader supports glTF version 1.0 and 2.0 and will use the correct loader based on the glTF version string. Include the version support that you want in any combination.
 
+Both versions:
 ```
-<script src="babylon.2.2.js"></script>
+<script src="babylon.js"></script>
 <script src="babylon.glTFFileLoader.js"></script>
+<script src="babylon.glTF1Loader.js"></script>
+<script src="babylon.glTF2Loader.js"></script>
+```
+
+Version 1 only:
+```
+<script src="babylon.js"></script>
+<script src="babylon.glTFFileLoader.js"></script>
+<script src="babylon.glTF1Loader.js"></script>
+```
+
+Version 2 only:
+```
+<script src="babylon.js"></script>
+<script src="babylon.glTFFileLoader.js"></script>
+<script src="babylon.glTF2Loader.js"></script>
 ```
 
 And then, call the scene loader:
@@ -24,19 +40,13 @@ BABYLON.SceneLoader.ImportMesh(["myMesh1", "myMesh2", "..."], "./", "duck.gltf",
 });
 ```
 
-In order the fix the UP vector (Y with Babylon.js) if you want to play with physics, you can customize the loader:
-```
-BABYLON.GLTFFileLoader.MakeYUP = true; // false by default
-```
-
-If you want to disable incremental loading (which is the default behavior), you can set the property IncrementalLoading to false.
-Then, you'll be able to be called back with all geometries and shaders loaded.
-For example, you can retrieve the real bounding infos of a mesh loaded using the loader.
+If you want to disable incremental loading, you can set the property `IncrementalLoading` to false.
+Then, you'll be able to be called back with all geometries, shaders (V1), PBR materials (V2) loaded. Textures are always loaded asynchronously. For example, you can retrieve the real bounding infos of a mesh loaded when incremental loading is disabled.
 ```
 BABYLON.GLTFFileLoader.IncrementalLoading = false; // true by default
 ```
 
-In order to work with homogeneous coordinates (that can be available with some converters and exporters):
+(V1 only) In order to work with homogeneous coordinates (that can be available with some converters and exporters):
 ```
 BABYLON.GLTFFileLoader.HomogeneousCoordinates = true; // false by default
 ```
@@ -47,9 +57,9 @@ BABYLON.GLTFFileLoader.HomogeneousCoordinates = true; // false by default
 * Import geometries
     * From binary files
     * From base64 buffers
-* Import lights
+* Import lights (V1 only)
 * Import cameras
-* Import and set custom shaders
+* Import and set custom shaders (V1 only)
     * Automatically bind attributes
     * Automatically bind matrices
     * Set uniforms
@@ -59,6 +69,7 @@ BABYLON.GLTFFileLoader.HomogeneousCoordinates = true; // false by default
     * Hardware skinning (shaders support)
     * Bones import
 * Handle dummy nodes (empty nodes)
+* PBR materials (V2 only)
 
 ## To improve
 * Test on more geometries

File diff suppressed because it is too large
+ 181 - 1664
loaders/src/glTF/babylon.glTFFileLoader.ts


+ 8 - 5
loaders/src/tsconfig.json

@@ -1,7 +1,10 @@
 {
-    "compilerOptions": {
-        "experimentalDecorators": true,
-        "module": "commonjs", 
-        "target": "es5"
-    }
+  "compileOnSave": true,
+  "compilerOptions": {
+    "experimentalDecorators": true,
+    "module": "commonjs",
+    "target": "es5",
+    "sourceMap": true,
+    "lib": ["dom", "es2015.promise", "es5"]
+  }
 }

+ 3 - 3
src/Materials/babylon.pbrMaterial.ts

@@ -336,7 +336,7 @@
 
         @serializeAsColor3("ambient")
         public ambientColor = new Color3(0, 0, 0);
-        
+
         /**
          * AKA Diffuse Color in other nomenclature.
          */
@@ -705,7 +705,7 @@
                     if (!this.ambientTexture.isReady()) {
                         return false;
                     }
-                    
+
                     needUVs = true;
                     this._defines.AMBIENT = true;
                     this._defines.AMBIENTINGRAYSCALE = this.useAmbientInGrayScale;
@@ -1297,7 +1297,7 @@
                         if (this.metallicTexture) {
                             this._effect.setTexture("reflectivitySampler", this.metallicTexture);
 
-                            this._effect.setFloat3("vReflectivityInfos", this.metallicTexture.coordinatesIndex, this.reflectivityTexture.level, this.ambientTextureStrength);
+                            this._effect.setFloat3("vReflectivityInfos", this.metallicTexture.coordinatesIndex, this.metallicTexture.level, this.ambientTextureStrength);
                             this._effect.setMatrix("reflectivityMatrix", this.metallicTexture.getTextureMatrix());
                         }
                         else if (this.reflectivityTexture) {

+ 4 - 4
src/Shaders/pbr.fragment.fx

@@ -319,13 +319,13 @@ void main(void) {
 	// Diffuse is used as the base of the reflectivity.
 	vec3 baseColor = surfaceAlbedo.rgb;
 
-	// Drop the surface diffuse by the 1.0 - metalness.
-	surfaceAlbedo.rgb *= (1.0 - metallicRoughness.r);
-	
-	// Default specular reflectance at normal incidence aka F0.
+	// Default specular reflectance at normal incidence.
 	// 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
 	const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
 
+	// Compute the converted diffuse.
+	surfaceAlbedo.rgb = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
+
 	// Compute the converted reflectivity.
 	surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);