소스 검색

Merge pull request #5984 from bghgary/loader-extension-fix

Minor update to glTF loader extensions
David Catuhe 6 년 전
부모
커밋
034876890a

+ 3 - 2
loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts

@@ -10,6 +10,7 @@ import { WeightedSound } from "babylonjs/Audio/weightedsound";
 import { IArrayItem, IScene, INode, IAnimation } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
+import { IProperty } from 'babylonjs-gltf2interface';
 
 const NAME = "MSFT_audio_emitter";
 
@@ -45,7 +46,7 @@ const enum AudioMimeType {
     WAV = "audio/wav",
 }
 
-interface IClip {
+interface IClip extends IProperty {
     uri?: string;
     bufferView?: number;
     mimeType?: AudioMimeType;
@@ -205,7 +206,7 @@ export class MSFT_audio_emitter implements IGLTFLoaderExtension {
 
         let promise: Promise<ArrayBufferView>;
         if (clip.uri) {
-            promise = this._loader.loadUriAsync(context, clip.uri);
+            promise = this._loader.loadUriAsync(context, clip, clip.uri);
         }
         else {
             const bufferView = ArrayItem.Get(`${context}/bufferView`, this._loader.gltf.bufferViews, clip.bufferView);

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

@@ -8,6 +8,7 @@ import { Mesh } from "babylonjs/Meshes/mesh";
 import { INode, IMaterial } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader, ArrayItem } from "../glTFLoader";
+import { IProperty } from 'babylonjs-gltf2interface';
 
 const NAME = "MSFT_lod";
 
@@ -222,14 +223,14 @@ export class MSFT_lod implements IGLTFLoaderExtension {
     }
 
     /** @hidden */
-    public _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> {
+    public _loadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>> {
         // Defer the loading of uris if loading a material or node LOD.
         if (this._materialIndexLOD !== null) {
             this._loader.log(`deferred`);
             const previousIndexLOD = this._materialIndexLOD - 1;
             this._materialSignalLODs[previousIndexLOD] = this._materialSignalLODs[previousIndexLOD] || new Deferred<void>();
             return this._materialSignalLODs[previousIndexLOD].promise.then(() => {
-                return this._loader.loadUriAsync(context, uri);
+                return this._loader.loadUriAsync(context, property, uri);
             });
         }
         else if (this._nodeIndexLOD !== null) {
@@ -237,7 +238,7 @@ export class MSFT_lod implements IGLTFLoaderExtension {
             const previousIndexLOD = this._nodeIndexLOD - 1;
             this._nodeSignalLODs[previousIndexLOD] = this._nodeSignalLODs[previousIndexLOD] || new Deferred<void>();
             return this._nodeSignalLODs[this._nodeIndexLOD - 1].promise.then(() => {
-                return this._loader.loadUriAsync(context, uri);
+                return this._loader.loadUriAsync(context, property, uri);
             });
         }
 

+ 133 - 112
loaders/src/glTF/2.0/glTFLoader.ts

@@ -35,7 +35,9 @@ interface IFileRequestInfo extends IFileRequest {
 }
 
 interface ILoaderProperty extends IProperty {
-    _activeLoaderExtensions: any;
+    _activeLoaderExtensionFunctions: {
+        [id: string]: boolean
+    };
 }
 
 /**
@@ -74,12 +76,6 @@ export class ArrayItem {
  * The glTF 2.0 loader
  */
 export class GLTFLoader implements IGLTFLoader {
-    /** The glTF object parsed from the JSON. */
-    public gltf: IGLTF;
-
-    /** The Babylon scene when loading the asset. */
-    public babylonScene: Scene;
-
     /** @hidden */
     public _completePromises = new Array<Promise<any>>();
 
@@ -90,6 +86,8 @@ export class GLTFLoader implements IGLTFLoader {
     private _rootUrl: string;
     private _fileName: string;
     private _uniqueRootUrl: string;
+    private _gltf: IGLTF;
+    private _babylonScene: Scene;
     private _rootBabylonMesh: Mesh;
     private _defaultBabylonMaterialData: { [drawMode: number]: Material } = {};
     private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
@@ -143,6 +141,27 @@ export class GLTFLoader implements IGLTFLoader {
         return this._state;
     }
 
+    /**
+     * The glTF object parsed from the JSON.
+     */
+    public get gltf(): IGLTF {
+        return this._gltf;
+    }
+
+    /**
+     * The Babylon scene when loading the asset.
+     */
+    public get babylonScene(): Scene {
+        return this._babylonScene;
+    }
+
+    /**
+     * The root Babylon mesh when loading the asset.
+     */
+    public get rootBabylonMesh(): Mesh {
+        return this._rootBabylonMesh;
+    }
+
     /** @hidden */
     constructor(parent: GLTFFileLoader) {
         this._parent = parent;
@@ -162,8 +181,6 @@ export class GLTFLoader implements IGLTFLoader {
 
         this._requests.length = 0;
 
-        delete this.gltf;
-        delete this.babylonScene;
         this._completePromises.length = 0;
 
         for (const name in this._extensions) {
@@ -175,6 +192,8 @@ export class GLTFLoader implements IGLTFLoader {
 
         this._extensions = {};
 
+        delete this._gltf;
+        delete this._babylonScene;
         delete this._rootBabylonMesh;
         delete this._progressCallback;
 
@@ -184,7 +203,7 @@ export class GLTFLoader implements IGLTFLoader {
     /** @hidden */
     public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fileName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
         return Promise.resolve().then(() => {
-            this.babylonScene = scene;
+            this._babylonScene = scene;
             this._rootUrl = rootUrl;
             this._fileName = fileName || "scene";
             this._progressCallback = onProgress;
@@ -194,8 +213,8 @@ export class GLTFLoader implements IGLTFLoader {
 
             if (meshesNames) {
                 const nodeMap: { [name: string]: number } = {};
-                if (this.gltf.nodes) {
-                    for (const node of this.gltf.nodes) {
+                if (this._gltf.nodes) {
+                    for (const node of this._gltf.nodes) {
                         if (node.name) {
                             nodeMap[node.name] = node.index;
                         }
@@ -227,7 +246,7 @@ export class GLTFLoader implements IGLTFLoader {
     /** @hidden */
     public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fileName?: string): Promise<void> {
         return Promise.resolve().then(() => {
-            this.babylonScene = scene;
+            this._babylonScene = scene;
             this._rootUrl = rootUrl;
             this._fileName = fileName || "scene";
             this._progressCallback = onProgress;
@@ -257,8 +276,8 @@ export class GLTFLoader implements IGLTFLoader {
             if (nodes) {
                 promises.push(this.loadSceneAsync("/nodes", { nodes: nodes, index: -1 }));
             }
-            else if (this.gltf.scene != undefined) {
-                const scene = ArrayItem.Get(`/scene`, this.gltf.scenes, this.gltf.scene);
+            else if (this._gltf.scene != undefined) {
+                const scene = ArrayItem.Get(`/scene`, this._gltf.scenes, this._gltf.scene);
                 promises.push(this.loadSceneAsync(`/scenes/${scene.index}`, scene));
             }
 
@@ -321,11 +340,11 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _loadData(data: IGLTFLoaderData): void {
-        this.gltf = data.json as IGLTF;
+        this._gltf = data.json as IGLTF;
         this._setupData();
 
         if (data.bin) {
-            const buffers = this.gltf.buffers;
+            const buffers = this._gltf.buffers;
             if (buffers && buffers[0] && !buffers[0].uri) {
                 const binaryBuffer = buffers[0];
                 if (binaryBuffer.byteLength < data.bin.byteLength - 3 || binaryBuffer.byteLength > data.bin.byteLength) {
@@ -341,23 +360,23 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _setupData(): void {
-        ArrayItem.Assign(this.gltf.accessors);
-        ArrayItem.Assign(this.gltf.animations);
-        ArrayItem.Assign(this.gltf.buffers);
-        ArrayItem.Assign(this.gltf.bufferViews);
-        ArrayItem.Assign(this.gltf.cameras);
-        ArrayItem.Assign(this.gltf.images);
-        ArrayItem.Assign(this.gltf.materials);
-        ArrayItem.Assign(this.gltf.meshes);
-        ArrayItem.Assign(this.gltf.nodes);
-        ArrayItem.Assign(this.gltf.samplers);
-        ArrayItem.Assign(this.gltf.scenes);
-        ArrayItem.Assign(this.gltf.skins);
-        ArrayItem.Assign(this.gltf.textures);
-
-        if (this.gltf.nodes) {
+        ArrayItem.Assign(this._gltf.accessors);
+        ArrayItem.Assign(this._gltf.animations);
+        ArrayItem.Assign(this._gltf.buffers);
+        ArrayItem.Assign(this._gltf.bufferViews);
+        ArrayItem.Assign(this._gltf.cameras);
+        ArrayItem.Assign(this._gltf.images);
+        ArrayItem.Assign(this._gltf.materials);
+        ArrayItem.Assign(this._gltf.meshes);
+        ArrayItem.Assign(this._gltf.nodes);
+        ArrayItem.Assign(this._gltf.samplers);
+        ArrayItem.Assign(this._gltf.scenes);
+        ArrayItem.Assign(this._gltf.skins);
+        ArrayItem.Assign(this._gltf.textures);
+
+        if (this._gltf.nodes) {
             const nodeParents: { [index: number]: number } = {};
-            for (const node of this.gltf.nodes) {
+            for (const node of this._gltf.nodes) {
                 if (node.children) {
                     for (const index of node.children) {
                         nodeParents[index] = node.index;
@@ -366,9 +385,9 @@ export class GLTFLoader implements IGLTFLoader {
             }
 
             const rootNode = this._createRootNode();
-            for (const node of this.gltf.nodes) {
+            for (const node of this._gltf.nodes) {
                 const parentIndex = nodeParents[node.index];
-                node.parent = parentIndex === undefined ? rootNode : this.gltf.nodes[parentIndex];
+                node.parent = parentIndex === undefined ? rootNode : this._gltf.nodes[parentIndex];
             }
         }
     }
@@ -385,8 +404,8 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _checkExtensions(): void {
-        if (this.gltf.extensionsRequired) {
-            for (const name of this.gltf.extensionsRequired) {
+        if (this._gltf.extensionsRequired) {
+            for (const name of this._gltf.extensionsRequired) {
                 const extension = this._extensions[name];
                 if (!extension || !extension.enabled) {
                     throw new Error(`Require extension ${name} is not available`);
@@ -401,7 +420,7 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _createRootNode(): INode {
-        this._rootBabylonMesh = new Mesh("__root__", this.babylonScene);
+        this._rootBabylonMesh = new Mesh("__root__", this._babylonScene);
         this._rootBabylonMesh.setEnabled(false);
 
         const rootNode: INode = {
@@ -411,7 +430,7 @@ export class GLTFLoader implements IGLTFLoader {
 
         switch (this._parent.coordinateSystemMode) {
             case GLTFLoaderCoordinateSystemMode.AUTO: {
-                if (!this.babylonScene.useRightHandedSystem) {
+                if (!this._babylonScene.useRightHandedSystem) {
                     rootNode.rotation = [0, 1, 0, 0];
                     rootNode.scale = [1, 1, -1];
                     GLTFLoader._LoadTransform(rootNode, this._rootBabylonMesh);
@@ -419,7 +438,7 @@ export class GLTFLoader implements IGLTFLoader {
                 break;
             }
             case GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
-                this.babylonScene.useRightHandedSystem = true;
+                this._babylonScene.useRightHandedSystem = true;
                 break;
             }
             default: {
@@ -449,7 +468,7 @@ export class GLTFLoader implements IGLTFLoader {
 
         if (scene.nodes) {
             for (let index of scene.nodes) {
-                const node = ArrayItem.Get(`${context}/nodes/${index}`, this.gltf.nodes, index);
+                const node = ArrayItem.Get(`${context}/nodes/${index}`, this._gltf.nodes, index);
                 promises.push(this.loadNodeAsync(`/nodes/${node.index}`, node, (babylonMesh) => {
                     babylonMesh.parent = this._rootBabylonMesh;
                 }));
@@ -458,8 +477,8 @@ export class GLTFLoader implements IGLTFLoader {
 
         // Link all Babylon bones for each glTF node with the corresponding Babylon transform node.
         // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
-        if (this.gltf.nodes) {
-            for (const node of this.gltf.nodes) {
+        if (this._gltf.nodes) {
+            for (const node of this._gltf.nodes) {
                 if (node._babylonTransformNode && node._babylonBones) {
                     for (const babylonBone of node._babylonBones) {
                         babylonBone.linkTransformNode(node._babylonTransformNode);
@@ -489,7 +508,7 @@ export class GLTFLoader implements IGLTFLoader {
         // Root mesh is always first.
         meshes.push(this._rootBabylonMesh);
 
-        const nodes = this.gltf.nodes;
+        const nodes = this._gltf.nodes;
         if (nodes) {
             for (const node of nodes) {
                 this._forEachPrimitive(node, (babylonMesh) => {
@@ -504,7 +523,7 @@ export class GLTFLoader implements IGLTFLoader {
     private _getSkeletons(): Skeleton[] {
         const skeletons = new Array<Skeleton>();
 
-        const skins = this.gltf.skins;
+        const skins = this._gltf.skins;
         if (skins) {
             for (const skin of skins) {
                 if (skin._data) {
@@ -519,7 +538,7 @@ export class GLTFLoader implements IGLTFLoader {
     private _getAnimationGroups(): AnimationGroup[] {
         const animationGroups = new Array<AnimationGroup>();
 
-        const animations = this.gltf.animations;
+        const animations = this._gltf.animations;
         if (animations) {
             for (const animation of animations) {
                 if (animation._babylonAnimationGroup) {
@@ -584,7 +603,7 @@ export class GLTFLoader implements IGLTFLoader {
             GLTFLoader._LoadTransform(node, babylonTransformNode);
 
             if (node.camera != undefined) {
-                const camera = ArrayItem.Get(`${context}/camera`, this.gltf.cameras, node.camera);
+                const camera = ArrayItem.Get(`${context}/camera`, this._gltf.cameras, node.camera);
                 promises.push(this.loadCameraAsync(`/cameras/${camera.index}`, camera, (babylonCamera) => {
                     babylonCamera.parent = babylonTransformNode;
                 }));
@@ -592,7 +611,7 @@ export class GLTFLoader implements IGLTFLoader {
 
             if (node.children) {
                 for (const index of node.children) {
-                    const childNode = ArrayItem.Get(`${context}/children/${index}`, this.gltf.nodes, index);
+                    const childNode = ArrayItem.Get(`${context}/children/${index}`, this._gltf.nodes, index);
                     promises.push(this.loadNodeAsync(`/nodes/${childNode.index}`, childNode, (childBabylonMesh) => {
                         childBabylonMesh.parent = babylonTransformNode;
                     }));
@@ -604,11 +623,11 @@ export class GLTFLoader implements IGLTFLoader {
 
         if (node.mesh == undefined) {
             const nodeName = node.name || `node${node.index}`;
-            node._babylonTransformNode = new TransformNode(nodeName, this.babylonScene);
+            node._babylonTransformNode = new TransformNode(nodeName, this._babylonScene);
             loadNode(node._babylonTransformNode);
         }
         else {
-            const mesh = ArrayItem.Get(`${context}/mesh`, this.gltf.meshes, node.mesh);
+            const mesh = ArrayItem.Get(`${context}/mesh`, this._gltf.meshes, node.mesh);
             promises.push(this._loadMeshAsync(`/meshes/${mesh.index}`, node, mesh, loadNode));
         }
 
@@ -647,7 +666,7 @@ export class GLTFLoader implements IGLTFLoader {
             }));
         }
         else {
-            node._babylonTransformNode = new TransformNode(name, this.babylonScene);
+            node._babylonTransformNode = new TransformNode(name, this._babylonScene);
             node._primitiveBabylonMeshes = [];
             for (const primitive of primitives) {
                 promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, `${name}_primitive${primitive.index}`, node, mesh, primitive, (babylonMesh) => {
@@ -658,7 +677,7 @@ export class GLTFLoader implements IGLTFLoader {
         }
 
         if (node.skin != undefined) {
-            const skin = ArrayItem.Get(`${context}/skin`, this.gltf.skins, node.skin);
+            const skin = ArrayItem.Get(`${context}/skin`, this._gltf.skins, node.skin);
             promises.push(this._loadSkinAsync(`/skins/${skin.index}`, node, skin));
         }
 
@@ -687,7 +706,7 @@ export class GLTFLoader implements IGLTFLoader {
         else {
             const promises = new Array<Promise<any>>();
 
-            const babylonMesh = new Mesh(name, this.babylonScene);
+            const babylonMesh = new Mesh(name, this._babylonScene);
 
             this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
             promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then((babylonGeometry) => {
@@ -707,7 +726,7 @@ export class GLTFLoader implements IGLTFLoader {
                 babylonMesh.material = babylonMaterial;
             }
             else {
-                const material = ArrayItem.Get(`${context}/material`, this.gltf.materials, primitive.material);
+                const material = ArrayItem.Get(`${context}/material`, this._gltf.materials, primitive.material);
                 promises.push(this._loadMaterialAsync(`/materials/${material.index}`, material, babylonMesh, babylonDrawMode, (babylonMaterial) => {
                     babylonMesh.material = babylonMaterial;
                 }));
@@ -749,13 +768,13 @@ export class GLTFLoader implements IGLTFLoader {
 
         const promises = new Array<Promise<any>>();
 
-        const babylonGeometry = new Geometry(babylonMesh.name, this.babylonScene);
+        const babylonGeometry = new Geometry(babylonMesh.name, this._babylonScene);
 
         if (primitive.indices == undefined) {
             babylonMesh.isUnIndexed = true;
         }
         else {
-            const accessor = ArrayItem.Get(`${context}/indices`, this.gltf.accessors, primitive.indices);
+            const accessor = ArrayItem.Get(`${context}/indices`, this._gltf.accessors, primitive.indices);
             promises.push(this._loadIndicesAccessorAsync(`/accessors/${accessor.index}`, accessor).then((data) => {
                 babylonGeometry.setIndices(data);
             }));
@@ -771,7 +790,7 @@ export class GLTFLoader implements IGLTFLoader {
                 babylonMesh._delayInfo.push(kind);
             }
 
-            const accessor = ArrayItem.Get(`${context}/attributes/${attribute}`, this.gltf.accessors, attributes[attribute]);
+            const accessor = ArrayItem.Get(`${context}/attributes/${attribute}`, this._gltf.accessors, attributes[attribute]);
             promises.push(this._loadVertexAccessorAsync(`/accessors/${accessor.index}`, accessor, kind).then((babylonVertexBuffer) => {
                 babylonGeometry.setVerticesBuffer(babylonVertexBuffer, accessor.count);
             }));
@@ -848,7 +867,7 @@ export class GLTFLoader implements IGLTFLoader {
                 return;
             }
 
-            const accessor = ArrayItem.Get(`${context}/${attribute}`, this.gltf.accessors, attributes[attribute]);
+            const accessor = ArrayItem.Get(`${context}/${attribute}`, this._gltf.accessors, attributes[attribute]);
             promises.push(this._loadFloatAccessorAsync(`/accessors/${accessor.index}`, accessor).then((data) => {
                 setData(babylonVertexBuffer, data);
             }));
@@ -925,7 +944,7 @@ export class GLTFLoader implements IGLTFLoader {
         }
 
         const skeletonId = `skeleton${skin.index}`;
-        const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
+        const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
 
         // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
         babylonSkeleton.overrideMesh = this._rootBabylonMesh;
@@ -948,7 +967,7 @@ export class GLTFLoader implements IGLTFLoader {
     private _loadBones(context: string, skin: ISkin, babylonSkeleton: Skeleton): void {
         const babylonBones: { [index: number]: Bone } = {};
         for (const index of skin.joints) {
-            const node = ArrayItem.Get(`${context}/joints/${index}`, this.gltf.nodes, index);
+            const node = ArrayItem.Get(`${context}/joints/${index}`, this._gltf.nodes, index);
             this._loadBone(node, skin, babylonSkeleton, babylonBones);
         }
     }
@@ -980,7 +999,7 @@ export class GLTFLoader implements IGLTFLoader {
             return Promise.resolve(null);
         }
 
-        const accessor = ArrayItem.Get(`${context}/inverseBindMatrices`, this.gltf.accessors, skin.inverseBindMatrices);
+        const accessor = ArrayItem.Get(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
         return this._loadFloatAccessorAsync(`/accessors/${accessor.index}`, accessor);
     }
 
@@ -1029,7 +1048,7 @@ export class GLTFLoader implements IGLTFLoader {
 
         this.logOpen(`${context} ${camera.name || ""}`);
 
-        const babylonCamera = new FreeCamera(camera.name || `camera${camera.index}`, Vector3.Zero(), this.babylonScene, false);
+        const babylonCamera = new FreeCamera(camera.name || `camera${camera.index}`, Vector3.Zero(), this._babylonScene, false);
         babylonCamera.rotation = new Vector3(0, Math.PI, 0);
 
         switch (camera.type) {
@@ -1073,7 +1092,7 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _loadAnimationsAsync(): Promise<void> {
-        const animations = this.gltf.animations;
+        const animations = this._gltf.animations;
         if (!animations) {
             return Promise.resolve();
         }
@@ -1100,7 +1119,7 @@ export class GLTFLoader implements IGLTFLoader {
             return promise;
         }
 
-        const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this.babylonScene);
+        const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._babylonScene);
         animation._babylonAnimationGroup = babylonAnimationGroup;
 
         const promises = new Array<Promise<any>>();
@@ -1123,7 +1142,7 @@ export class GLTFLoader implements IGLTFLoader {
             return Promise.resolve();
         }
 
-        const targetNode = ArrayItem.Get(`${context}/target/node`, this.gltf.nodes, channel.target.node);
+        const targetNode = ArrayItem.Get(`${context}/target/node`, this._gltf.nodes, channel.target.node);
 
         // Ignore animations that have no animation targets.
         if ((channel.target.path === AnimationChannelTargetPath.WEIGHTS && !targetNode._numMorphTargets) ||
@@ -1281,8 +1300,8 @@ export class GLTFLoader implements IGLTFLoader {
             }
         }
 
-        const inputAccessor = ArrayItem.Get(`${context}/input`, this.gltf.accessors, sampler.input);
-        const outputAccessor = ArrayItem.Get(`${context}/output`, this.gltf.accessors, sampler.output);
+        const inputAccessor = ArrayItem.Get(`${context}/input`, this._gltf.accessors, sampler.input);
+        const outputAccessor = ArrayItem.Get(`${context}/output`, this._gltf.accessors, sampler.output);
         sampler._data = Promise.all([
             this._loadFloatAccessorAsync(`/accessors/${inputAccessor.index}`, inputAccessor),
             this._loadFloatAccessorAsync(`/accessors/${outputAccessor.index}`, outputAccessor)
@@ -1306,7 +1325,7 @@ export class GLTFLoader implements IGLTFLoader {
             throw new Error(`${context}/uri: Value is missing`);
         }
 
-        buffer._data = this.loadUriAsync(`${context}/uri`, buffer.uri);
+        buffer._data = this.loadUriAsync(`${context}/uri`, buffer, buffer.uri);
 
         return buffer._data;
     }
@@ -1322,7 +1341,7 @@ export class GLTFLoader implements IGLTFLoader {
             return bufferView._data;
         }
 
-        const buffer = ArrayItem.Get(`${context}/buffer`, this.gltf.buffers, bufferView.buffer);
+        const buffer = ArrayItem.Get(`${context}/buffer`, this._gltf.buffers, bufferView.buffer);
         bufferView._data = this._loadBufferAsync(`/buffers/${buffer.index}`, buffer).then((data) => {
             try {
                 return new Uint8Array(data.buffer, data.byteOffset + (bufferView.byteOffset || 0), bufferView.byteLength);
@@ -1350,7 +1369,7 @@ export class GLTFLoader implements IGLTFLoader {
             return accessor._data as Promise<IndicesArray>;
         }
 
-        const bufferView = ArrayItem.Get(`${context}/bufferView`, this.gltf.bufferViews, accessor.bufferView);
+        const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
         accessor._data = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView).then((data) => {
             return GLTFLoader._GetTypedArray(context, accessor.componentType, data, accessor.byteOffset, accessor.count);
         });
@@ -1376,7 +1395,7 @@ export class GLTFLoader implements IGLTFLoader {
             accessor._data = Promise.resolve(new Float32Array(length));
         }
         else {
-            const bufferView = ArrayItem.Get(`${context}/bufferView`, this.gltf.bufferViews, accessor.bufferView);
+            const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
             accessor._data = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView).then((data) => {
                 return GLTFLoader._GetTypedArray(context, accessor.componentType, data, accessor.byteOffset, length);
             });
@@ -1386,8 +1405,8 @@ export class GLTFLoader implements IGLTFLoader {
             const sparse = accessor.sparse;
             accessor._data = accessor._data.then((view: ArrayBufferView) => {
                 const data = view as Float32Array;
-                const indicesBufferView = ArrayItem.Get(`${context}/sparse/indices/bufferView`, this.gltf.bufferViews, sparse.indices.bufferView);
-                const valuesBufferView = ArrayItem.Get(`${context}/sparse/values/bufferView`, this.gltf.bufferViews, sparse.values.bufferView);
+                const indicesBufferView = ArrayItem.Get(`${context}/sparse/indices/bufferView`, this._gltf.bufferViews, sparse.indices.bufferView);
+                const valuesBufferView = ArrayItem.Get(`${context}/sparse/values/bufferView`, this._gltf.bufferViews, sparse.values.bufferView);
                 return Promise.all([
                     this.loadBufferViewAsync(`/bufferViews/${indicesBufferView.index}`, indicesBufferView),
                     this.loadBufferViewAsync(`/bufferViews/${valuesBufferView.index}`, valuesBufferView)
@@ -1417,7 +1436,7 @@ export class GLTFLoader implements IGLTFLoader {
         }
 
         bufferView._babylonBuffer = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView).then((data) => {
-            return new Buffer(this.babylonScene.getEngine(), data, false);
+            return new Buffer(this._babylonScene.getEngine(), data, false);
         });
 
         return bufferView._babylonBuffer;
@@ -1430,21 +1449,21 @@ export class GLTFLoader implements IGLTFLoader {
 
         if (accessor.sparse) {
             accessor._babylonVertexBuffer = this._loadFloatAccessorAsync(`/accessors/${accessor.index}`, accessor).then((data) => {
-                return new VertexBuffer(this.babylonScene.getEngine(), data, kind, false);
+                return new VertexBuffer(this._babylonScene.getEngine(), data, kind, false);
             });
         }
         // HACK: If byte offset is not a multiple of component type byte length then load as a float array instead of using Babylon buffers.
         else if (accessor.byteOffset && accessor.byteOffset % VertexBuffer.GetTypeByteLength(accessor.componentType) !== 0) {
             Tools.Warn("Accessor byte offset is not a multiple of component type byte length");
             accessor._babylonVertexBuffer = this._loadFloatAccessorAsync(`/accessors/${accessor.index}`, accessor).then((data) => {
-                return new VertexBuffer(this.babylonScene.getEngine(), data, kind, false);
+                return new VertexBuffer(this._babylonScene.getEngine(), data, kind, false);
             });
         }
         else {
-            const bufferView = ArrayItem.Get(`${context}/bufferView`, this.gltf.bufferViews, accessor.bufferView);
+            const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
             accessor._babylonVertexBuffer = this._loadVertexBufferViewAsync(bufferView, kind).then((babylonBuffer) => {
                 const size = GLTFLoader._GetNumComponents(context, accessor.type);
-                return new VertexBuffer(this.babylonScene.getEngine(), babylonBuffer, kind, false, false, bufferView.byteStride,
+                return new VertexBuffer(this._babylonScene.getEngine(), babylonBuffer, kind, false, false, bufferView.byteStride,
                     false, accessor.byteOffset, size, accessor.componentType, accessor.normalized, true);
             });
         }
@@ -1538,8 +1557,8 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _createDefaultMaterial(name: string, babylonDrawMode: number): Material {
-        const babylonMaterial = new PBRMaterial(name, this.babylonScene);
-        babylonMaterial.sideOrientation = this.babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
+        const babylonMaterial = new PBRMaterial(name, this._babylonScene);
+        babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
         babylonMaterial.fillMode = babylonDrawMode;
         babylonMaterial.enableSpecularAntiAliasing = true;
         babylonMaterial.useRadianceOverAlpha = !this._parent.transparencyAsCoverage;
@@ -1621,8 +1640,8 @@ export class GLTFLoader implements IGLTFLoader {
                 babylonMaterial.bumpTexture = texture;
             }));
 
-            babylonMaterial.invertNormalMapX = !this.babylonScene.useRightHandedSystem;
-            babylonMaterial.invertNormalMapY = this.babylonScene.useRightHandedSystem;
+            babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
+            babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
             if (material.normalTexture.scale != undefined) {
                 babylonMaterial.bumpTexture.level = material.normalTexture.scale;
             }
@@ -1707,7 +1726,7 @@ export class GLTFLoader implements IGLTFLoader {
 
         this.logOpen(`${context}`);
 
-        const texture = ArrayItem.Get(`${context}/index`, this.gltf.textures, textureInfo.index);
+        const texture = ArrayItem.Get(`${context}/index`, this._gltf.textures, textureInfo.index);
         const promise = this._loadTextureAsync(`/textures/${textureInfo.index}`, texture, (babylonTexture) => {
             babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
 
@@ -1726,23 +1745,23 @@ export class GLTFLoader implements IGLTFLoader {
 
         this.logOpen(`${context} ${texture.name || ""}`);
 
-        const sampler = (texture.sampler == undefined ? GLTFLoader._DefaultSampler : ArrayItem.Get(`${context}/sampler`, this.gltf.samplers, texture.sampler));
+        const sampler = (texture.sampler == undefined ? GLTFLoader._DefaultSampler : ArrayItem.Get(`${context}/sampler`, this._gltf.samplers, texture.sampler));
         const samplerData = this._loadSampler(`/samplers/${sampler.index}`, sampler);
 
-        const image = ArrayItem.Get(`${context}/source`, this.gltf.images, texture.source);
+        const image = ArrayItem.Get(`${context}/source`, this._gltf.images, texture.source);
         let url: Nullable<string> = null;
         if (image.uri) {
             if (Tools.IsBase64(image.uri)) {
                 url = image.uri;
             }
-            else if (this.babylonScene.getEngine().textureFormatInUse) {
+            else if (this._babylonScene.getEngine().textureFormatInUse) {
                 // If an image uri and a texture format is set like (eg. KTX) load from url instead of blob to support texture format and fallback
                 url = this._rootUrl + image.uri;
             }
         }
 
         const deferred = new Deferred<void>();
-        const babylonTexture = new Texture(url, this.babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
+        const babylonTexture = new Texture(url, this._babylonScene, samplerData.noMipMaps, false, samplerData.samplingMode, () => {
             if (!this._disposed) {
                 deferred.resolve();
             }
@@ -1796,10 +1815,10 @@ export class GLTFLoader implements IGLTFLoader {
             this.logOpen(`${context} ${image.name || ""}`);
 
             if (image.uri) {
-                image._data = this.loadUriAsync(`${context}/uri`, image.uri);
+                image._data = this.loadUriAsync(`${context}/uri`, image, image.uri);
             }
             else {
-                const bufferView = ArrayItem.Get(`${context}/bufferView`, this.gltf.bufferViews, image.bufferView);
+                const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
                 image._data = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView);
             }
 
@@ -1812,11 +1831,12 @@ export class GLTFLoader implements IGLTFLoader {
     /**
      * Loads a glTF uri.
      * @param context The context when loading the asset
+     * @param property The glTF property associated with the uri
      * @param uri The base64 or relative uri
      * @returns A promise that resolves with the loaded data when the load is complete
      */
-    public loadUriAsync(context: string, uri: string): Promise<ArrayBufferView> {
-        const extensionPromise = this._extensionsLoadUriAsync(context, uri);
+    public loadUriAsync(context: string, property: IProperty, uri: string): Promise<ArrayBufferView> {
+        const extensionPromise = this._extensionsLoadUriAsync(context, property, uri);
         if (extensionPromise) {
             return extensionPromise;
         }
@@ -1859,7 +1879,7 @@ export class GLTFLoader implements IGLTFLoader {
                                 }
                             }
                         }
-                    }, this.babylonScene.offlineProvider, true, (request, exception) => {
+                    }, this._babylonScene.offlineProvider, true, (request, exception) => {
                         if (!this._disposed) {
                             reject(new LoadFileError(`${context}: Failed to load '${uri}'${request ? ": " + request.status + " " + request.statusText : ""}`, request));
                         }
@@ -2016,8 +2036,8 @@ export class GLTFLoader implements IGLTFLoader {
 
         const promises = new Array<Promise<any>>();
 
-        if (this.gltf.materials) {
-            for (const material of this.gltf.materials) {
+        if (this._gltf.materials) {
+            for (const material of this._gltf.materials) {
                 if (material._data) {
                     for (const babylonDrawMode in material._data) {
                         const babylonData = material._data[babylonDrawMode];
@@ -2046,7 +2066,7 @@ export class GLTFLoader implements IGLTFLoader {
 
         const promises = new Array<Promise<any>>();
 
-        const lights = this.babylonScene.lights;
+        const lights = this._babylonScene.lights;
         for (let light of lights) {
             let generator = light.getShadowGenerator();
             if (generator) {
@@ -2068,15 +2088,16 @@ export class GLTFLoader implements IGLTFLoader {
         }
     }
 
-    private _applyExtensions<T>(property: IProperty, actionAsync: (extension: IGLTFLoaderExtension) => Nullable<T> | undefined): Nullable<T> {
+    private _applyExtensions<T>(property: IProperty, functionName: string, actionAsync: (extension: IGLTFLoaderExtension) => Nullable<T> | undefined): Nullable<T> {
         for (const name of GLTFLoader._ExtensionNames) {
             const extension = this._extensions[name];
             if (extension.enabled) {
+                const id = `${name}.${functionName}`;
                 const loaderProperty = property as ILoaderProperty;
-                loaderProperty._activeLoaderExtensions = loaderProperty._activeLoaderExtensions || {};
-                const activeLoaderExtensions = loaderProperty._activeLoaderExtensions;
-                if (!activeLoaderExtensions[name]) {
-                    activeLoaderExtensions[name] = true;
+                loaderProperty._activeLoaderExtensionFunctions = loaderProperty._activeLoaderExtensionFunctions || {};
+                const activeLoaderExtensionFunctions = loaderProperty._activeLoaderExtensionFunctions;
+                if (!activeLoaderExtensionFunctions[id]) {
+                    activeLoaderExtensionFunctions[id] = true;
 
                     try {
                         const result = actionAsync(extension);
@@ -2085,7 +2106,7 @@ export class GLTFLoader implements IGLTFLoader {
                         }
                     }
                     finally {
-                        delete activeLoaderExtensions[name];
+                        delete activeLoaderExtensionFunctions[id];
                     }
                 }
             }
@@ -2103,43 +2124,43 @@ export class GLTFLoader implements IGLTFLoader {
     }
 
     private _extensionsLoadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>> {
-        return this._applyExtensions(scene, (extension) => extension.loadSceneAsync && extension.loadSceneAsync(context, scene));
+        return this._applyExtensions(scene, "loadScene", (extension) => extension.loadSceneAsync && extension.loadSceneAsync(context, scene));
     }
 
     private _extensionsLoadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
-        return this._applyExtensions(node, (extension) => extension.loadNodeAsync && extension.loadNodeAsync(context, node, assign));
+        return this._applyExtensions(node, "loadNode", (extension) => extension.loadNodeAsync && extension.loadNodeAsync(context, node, assign));
     }
 
     private _extensionsLoadCameraAsync(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>> {
-        return this._applyExtensions(camera, (extension) => extension.loadCameraAsync && extension.loadCameraAsync(context, camera, assign));
+        return this._applyExtensions(camera, "loadCamera", (extension) => extension.loadCameraAsync && extension.loadCameraAsync(context, camera, assign));
     }
 
     private _extensionsLoadVertexDataAsync(context: string, primitive: IMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>> {
-        return this._applyExtensions(primitive, (extension) => extension._loadVertexDataAsync && extension._loadVertexDataAsync(context, primitive, babylonMesh));
+        return this._applyExtensions(primitive, "loadVertexData", (extension) => extension._loadVertexDataAsync && extension._loadVertexDataAsync(context, primitive, babylonMesh));
     }
 
     private _extensionsLoadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
-        return this._applyExtensions(material, (extension) => extension._loadMaterialAsync && extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
+        return this._applyExtensions(material, "loadMaterial", (extension) => extension._loadMaterialAsync && extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
     }
 
     private _extensionsCreateMaterial(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material> {
-        return this._applyExtensions({}, (extension) => extension.createMaterial && extension.createMaterial(context, material, babylonDrawMode));
+        return this._applyExtensions(material, "createMaterial", (extension) => extension.createMaterial && extension.createMaterial(context, material, babylonDrawMode));
     }
 
     private _extensionsLoadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
-        return this._applyExtensions(material, (extension) => extension.loadMaterialPropertiesAsync && extension.loadMaterialPropertiesAsync(context, material, babylonMaterial));
+        return this._applyExtensions(material, "loadMaterialProperties", (extension) => extension.loadMaterialPropertiesAsync && extension.loadMaterialPropertiesAsync(context, material, babylonMaterial));
     }
 
     private _extensionsLoadTextureInfoAsync(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>> {
-        return this._applyExtensions(textureInfo, (extension) => extension.loadTextureInfoAsync && extension.loadTextureInfoAsync(context, textureInfo, assign));
+        return this._applyExtensions(textureInfo, "loadTextureInfo", (extension) => extension.loadTextureInfoAsync && extension.loadTextureInfoAsync(context, textureInfo, assign));
     }
 
     private _extensionsLoadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>> {
-        return this._applyExtensions(animation, (extension) => extension.loadAnimationAsync && extension.loadAnimationAsync(context, animation));
+        return this._applyExtensions(animation, "loadAnimation", (extension) => extension.loadAnimationAsync && extension.loadAnimationAsync(context, animation));
     }
 
-    private _extensionsLoadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> {
-        return this._applyExtensions({}, (extension) => extension._loadUriAsync && extension._loadUriAsync(context, uri));
+    private _extensionsLoadUriAsync(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>> {
+        return this._applyExtensions(property, "loadUri", (extension) => extension._loadUriAsync && extension._loadUriAsync(context, property, uri));
     }
 
     /**

+ 3 - 1
loaders/src/glTF/2.0/glTFLoaderExtension.ts

@@ -10,6 +10,7 @@ import { IDisposable } from "babylonjs/scene";
 
 import { IScene, INode, ICamera, IMeshPrimitive, IMaterial, ITextureInfo, IAnimation } from "./glTFLoaderInterfaces";
 import { IGLTFLoaderExtension as IGLTFBaseLoaderExtension } from "../glTFFileLoader";
+import { IProperty } from 'babylonjs-gltf2interface';
 
 /**
  * Interface for a glTF loader extension.
@@ -106,8 +107,9 @@ export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposa
     /**
      * Define this method to modify the default behavior when loading uris.
      * @param context The context when loading the asset
+     * @param property The glTF property associated with the uri
      * @param uri The uri to load
      * @returns A promise that resolves with the loaded data when the load is complete or null if not handled
      */
-    _loadUriAsync?(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
+    _loadUriAsync?(context: string, property: IProperty, uri: string): Nullable<Promise<ArrayBufferView>>;
 }