瀏覽代碼

Merge... need to rebuild loader Yeaaaahhhhhhh me

sebastien 6 年之前
父節點
當前提交
73d87a529e
共有 33 個文件被更改,包括 15229 次插入65887 次删除
  1. 12352 12333
      Playground/babylon.d.txt
  2. 8 8
      Playground/js/index.js
  3. 0 50831
      dist/preview release/babylon.d.ts
  4. 0 1
      dist/preview release/babylon.js
  5. 8 4
      dist/preview release/babylon.max.js
  6. 8 4
      dist/preview release/babylon.no-module.max.js
  7. 0 1
      dist/preview release/babylon.worker.js
  8. 8 4
      dist/preview release/es6.js
  9. 5 0
      dist/preview release/gui/babylon.gui.min.js
  10. 4 0
      dist/preview release/gui/babylon.gui.min.js.map
  11. 15 1
      dist/preview release/viewer/babylon.viewer.d.ts
  12. 1 1
      dist/preview release/viewer/babylon.viewer.js
  13. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  14. 18 1
      dist/preview release/viewer/babylon.viewer.module.d.ts
  15. 10 0
      dist/preview release/what's new.md
  16. 1 3
      gui/src/2D/controls/control.ts
  17. 93 95
      loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts
  18. 257 258
      loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts
  19. 219 218
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  20. 1714 1661
      loaders/src/glTF/2.0/glTFLoader.ts
  21. 95 92
      loaders/src/glTF/2.0/glTFLoaderExtension.ts
  22. 208 205
      loaders/src/glTF/2.0/glTFLoaderInterfaces.ts
  23. 23 23
      src/Audio/sound.ts
  24. 1 1
      src/Bones/bone.ts
  25. 88 25
      src/Mesh/abstractMesh.ts
  26. 3 0
      src/Mesh/geometry.ts
  27. 9 11
      src/Mesh/instancedMesh.ts
  28. 4 76
      src/Mesh/mesh.ts
  29. 25 0
      src/Mesh/transformNode.ts
  30. 11 1
      src/Tools/tools.ts
  31. 6 3
      src/node.ts
  32. 24 14
      src/scene.ts
  33. 10 11
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

文件差異過大導致無法顯示
+ 12352 - 12333
Playground/babylon.d.txt


+ 8 - 8
Playground/js/index.js

@@ -582,25 +582,25 @@ function showError(errorMessage, errorEvent) {
                         //create scene
                         eval("scene = " + createSceneFunction + "()");
 
-                        // if scene returns a promise avoid checks
-                        if (scene.then) {
-                            checkCamera = false
-                            checkSceneCount = false
-                        }
-
                         if (!scene) {
                             showError(createSceneFunction + " function must return a scene.", null);
                             return;
                         }
 
+                        // if scene returns a promise avoid checks
+                        if (scene.then) {
+                            checkCamera = false;
+                            checkSceneCount = false;
+                        }
+
                         var createEngineZip = (createEngineFunction === "createEngine")
                             ? "createEngine()"
-                            : defaultEngineZip
+                            : defaultEngineZip;
 
                         zipCode =
                             code + "\r\n\r\n" +
                             "var engine = " + createEngineZip + ";\r\n" +
-                            "var scene = " + createSceneFunction + "();"
+                            "var scene = " + createSceneFunction + "();";
 
                     }
 

文件差異過大導致無法顯示
+ 0 - 50831
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 0 - 1
dist/preview release/babylon.js


+ 8 - 4
dist/preview release/babylon.max.js

@@ -18670,9 +18670,11 @@ var BABYLON;
          * Creates a new Node
          * @param name the name and id to be given to this node
          * @param scene the scene this node will be added to
+         * @param addToRootNodes the node will be added to scene.rootNodes
          */
-        function Node(name, scene) {
+        function Node(name, scene, addToRootNodes) {
             if (scene === void 0) { scene = null; }
+            if (addToRootNodes === void 0) { addToRootNodes = true; }
             /**
              * Gets or sets a string used to store user defined state for the node
              */
@@ -18717,7 +18719,9 @@ var BABYLON;
             this._scene = (scene || BABYLON.Engine.LastCreatedScene);
             this.uniqueId = this._scene.getUniqueId();
             this._initCache();
-            this.addToSceneRootNodes();
+            if (addToRootNodes) {
+                this.addToSceneRootNodes();
+            }
         }
         /**
          * Add a new node constructor
@@ -84932,7 +84936,7 @@ var BABYLON;
                 var cameraName = camera.name;
                 var postProcesses = this._postProcesses[this._singleInstance ? 0 : cameraName];
                 if (postProcesses) {
-                    this._postProcesses[this._singleInstance ? 0 : cameraName].forEach(function (postProcess) {
+                    postProcesses.forEach(function (postProcess) {
                         camera.detachPostProcess(postProcess);
                     });
                 }
@@ -92231,7 +92235,7 @@ var BABYLON;
             if (restPose === void 0) { restPose = null; }
             if (baseMatrix === void 0) { baseMatrix = null; }
             if (index === void 0) { index = null; }
-            var _this = _super.call(this, name, skeleton.getScene()) || this;
+            var _this = _super.call(this, name, skeleton.getScene(), false) || this;
             _this.name = name;
             /**
              * Gets the list of child bones

+ 8 - 4
dist/preview release/babylon.no-module.max.js

@@ -18637,9 +18637,11 @@ var BABYLON;
          * Creates a new Node
          * @param name the name and id to be given to this node
          * @param scene the scene this node will be added to
+         * @param addToRootNodes the node will be added to scene.rootNodes
          */
-        function Node(name, scene) {
+        function Node(name, scene, addToRootNodes) {
             if (scene === void 0) { scene = null; }
+            if (addToRootNodes === void 0) { addToRootNodes = true; }
             /**
              * Gets or sets a string used to store user defined state for the node
              */
@@ -18684,7 +18686,9 @@ var BABYLON;
             this._scene = (scene || BABYLON.Engine.LastCreatedScene);
             this.uniqueId = this._scene.getUniqueId();
             this._initCache();
-            this.addToSceneRootNodes();
+            if (addToRootNodes) {
+                this.addToSceneRootNodes();
+            }
         }
         /**
          * Add a new node constructor
@@ -84899,7 +84903,7 @@ var BABYLON;
                 var cameraName = camera.name;
                 var postProcesses = this._postProcesses[this._singleInstance ? 0 : cameraName];
                 if (postProcesses) {
-                    this._postProcesses[this._singleInstance ? 0 : cameraName].forEach(function (postProcess) {
+                    postProcesses.forEach(function (postProcess) {
                         camera.detachPostProcess(postProcess);
                     });
                 }
@@ -92198,7 +92202,7 @@ var BABYLON;
             if (restPose === void 0) { restPose = null; }
             if (baseMatrix === void 0) { baseMatrix = null; }
             if (index === void 0) { index = null; }
-            var _this = _super.call(this, name, skeleton.getScene()) || this;
+            var _this = _super.call(this, name, skeleton.getScene(), false) || this;
             _this.name = name;
             /**
              * Gets the list of child bones

文件差異過大導致無法顯示
+ 0 - 1
dist/preview release/babylon.worker.js


+ 8 - 4
dist/preview release/es6.js

@@ -18637,9 +18637,11 @@ var BABYLON;
          * Creates a new Node
          * @param name the name and id to be given to this node
          * @param scene the scene this node will be added to
+         * @param addToRootNodes the node will be added to scene.rootNodes
          */
-        function Node(name, scene) {
+        function Node(name, scene, addToRootNodes) {
             if (scene === void 0) { scene = null; }
+            if (addToRootNodes === void 0) { addToRootNodes = true; }
             /**
              * Gets or sets a string used to store user defined state for the node
              */
@@ -18684,7 +18686,9 @@ var BABYLON;
             this._scene = (scene || BABYLON.Engine.LastCreatedScene);
             this.uniqueId = this._scene.getUniqueId();
             this._initCache();
-            this.addToSceneRootNodes();
+            if (addToRootNodes) {
+                this.addToSceneRootNodes();
+            }
         }
         /**
          * Add a new node constructor
@@ -84899,7 +84903,7 @@ var BABYLON;
                 var cameraName = camera.name;
                 var postProcesses = this._postProcesses[this._singleInstance ? 0 : cameraName];
                 if (postProcesses) {
-                    this._postProcesses[this._singleInstance ? 0 : cameraName].forEach(function (postProcess) {
+                    postProcesses.forEach(function (postProcess) {
                         camera.detachPostProcess(postProcess);
                     });
                 }
@@ -92198,7 +92202,7 @@ var BABYLON;
             if (restPose === void 0) { restPose = null; }
             if (baseMatrix === void 0) { baseMatrix = null; }
             if (index === void 0) { index = null; }
-            var _this = _super.call(this, name, skeleton.getScene()) || this;
+            var _this = _super.call(this, name, skeleton.getScene(), false) || this;
             _this.name = name;
             /**
              * Gets the list of child bones

文件差異過大導致無法顯示
+ 5 - 0
dist/preview release/gui/babylon.gui.min.js


文件差異過大導致無法顯示
+ 4 - 0
dist/preview release/gui/babylon.gui.min.js.map


+ 15 - 1
dist/preview release/viewer/babylon.viewer.d.ts

@@ -924,7 +924,7 @@ declare module BabylonViewer {
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
@@ -1558,6 +1558,20 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 declare module BabylonViewer {
+    /**
+        * A custom upgrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedUpgrade(sceneManager: SceneManager): boolean;
+    /**
+        * A custom degrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedDegrade(sceneManager: SceneManager): boolean;
+}
+declare module BabylonViewer {
 }
 declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 18 - 1
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -986,13 +986,14 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 
 declare module 'babylonjs-viewer/optimizer/custom' {
+    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     /**
       *
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 
@@ -1663,6 +1664,22 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 
+declare module 'babylonjs-viewer/optimizer/custom/extended' {
+    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
+    /**
+        * A custom upgrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedUpgrade(sceneManager: SceneManager): boolean;
+    /**
+        * A custom degrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedDegrade(sceneManager: SceneManager): boolean;
+}
+
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';

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

@@ -51,6 +51,9 @@
 
 ### glTF Loader
 
+- Added support for mesh instancing for improved performance when multiple nodes point to the same mesh ([bghgary](https://github.com/bghgary))
+- Create `TransformNode` objects instead of `Mesh` objects for glTF nodes without geometry ([bghgary](https://github.com/bghgary))
+
 ### glTF Serializer
 
 ### Viewer
@@ -58,9 +61,11 @@
 ### Materials Library
 
 ## Bug fixes
+- Removed bones from rootNodes where they should never have been ([Deltakosh](https://github.com/deltakosh))
 - Refocusing on input gui with pointer events ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo scaling not consistent when camera is parented ([TrevorDev](https://github.com/TrevorDev))
 - Context loss causing unexpected results with dynamic textures ([TrevorDev](https://github.com/TrevorDev))
+- CreateScreenshotUsingRenderTarget stretches mirror textures when setting both width and height ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
@@ -101,3 +106,8 @@
   - `engine.drawCallsPerfCounter`: use SceneInstrumentation class instead
   - `shadowGenerator.useVarianceShadowMap`: use useExponentialShadowMap instead
   - `shadowGenerator.useBlurVarianceShadowMap`: use useBlurExponentialShadowMap instead
+- The glTF loader now creates `InstancedMesh` objects when two nodes point to the same mesh ([bghgary](https://github.com/bghgary))
+- The glTF loader now creates `TransformNode` objects instead of `Mesh` objects for glTF nodes without geometry ([bghgary](https://github.com/bghgary))
+  - _Note: The root node is still a `Mesh` object and is still the first in the returned list of meshes_
+  - `TransformNode` objects are excluded from the returned list of meshes when importing mesh
+  - `TransformNode` objects do not raise `onMeshLoaded` events

+ 1 - 3
gui/src/2D/controls/control.ts

@@ -1274,9 +1274,7 @@ export class Control {
         }
         this._enterCount = 0;
 
-        var canNotify: boolean = this.onPointerOutObservable.notifyObservers(this, -1, target, this);
-
-        if (canNotify && this.parent != null) { this.parent._onPointerOut(target); }
+        this.onPointerOutObservable.notifyObservers(this, -1, target, this);
     }
 
     /** @hidden */

+ 93 - 95
loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts

@@ -1,109 +1,107 @@
-import { Mesh, Nullable, Light, DirectionalLight, Vector3, PointLight, SpotLight, Color3 } from "babylonjs";
-import { IChildRootProperty } from "babylonjs-gltf2interface";
-import { INode } from "../glTFLoaderInterfaces";
-import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
-import { GLTFLoader, ArrayItem } from "../glTFLoader";
-
-const NAME = "KHR_lights_punctual";
-
-enum LightType {
-    DIRECTIONAL = "directional",
-    POINT = "point",
-    SPOT = "spot"
-}
-
-interface ILightReference {
-    light: number;
-}
-
-interface ILight extends IChildRootProperty {
-    type: LightType;
-    color?: number[];
-    intensity?: number;
-    range?: number;
-    spot?: {
-        innerConeAngle?: number;
-        outerConeAngle?: number;
-    };
-}
-
-interface ILights {
-    lights: ILight[];
-}
-
-/**
- * [Specification](https://github.com/KhronosGroup/glTF/blob/1048d162a44dbcb05aefc1874bfd423cf60135a6/extensions/2.0/Khronos/KHR_lights_punctual/README.md) (Experimental)
- */
-export class KHR_lights implements IGLTFLoaderExtension {
-    /** The name of this extension. */
-    public readonly name = NAME;
-
-    /** Defines whether this extension is enabled. */
-    public enabled = true;
-
-    private _loader: GLTFLoader;
-    private _lights?: ILight[];
-
-    /** @hidden */
-    constructor(loader: GLTFLoader) {
-        this._loader = loader;
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Loader.Extensions {
+    const NAME = "KHR_lights_punctual";
+
+    enum LightType {
+        DIRECTIONAL = "directional",
+        POINT = "point",
+        SPOT = "spot"
     }
 
-    /** @hidden */
-    public dispose() {
-        delete this._loader;
-        delete this._lights;
+    interface ILightReference {
+        light: number;
     }
 
-    /** @hidden */
-    public onLoading(): void {
-        const extensions = this._loader.gltf.extensions;
-        if (extensions && extensions[this.name]) {
-            const extension = extensions[this.name] as ILights;
-            this._lights = extension.lights;
-        }
+    interface ILight extends IChildRootProperty {
+        type: LightType;
+        color?: number[];
+        intensity?: number;
+        range?: number;
+        spot?: {
+            innerConeAngle?: number;
+            outerConeAngle?: number;
+        };
+    }
+
+    interface ILights {
+        lights: ILight[];
     }
 
-    /** @hidden */
-    public loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>> {
-        return GLTFLoader.LoadExtensionAsync<ILightReference, Mesh>(context, node, this.name, (extensionContext, extension) => {
-            return this._loader.loadNodeAsync(context, node, (babylonMesh) => {
-                let babylonLight: Light;
+    /**
+     * [Specification](https://github.com/KhronosGroup/glTF/blob/1048d162a44dbcb05aefc1874bfd423cf60135a6/extensions/2.0/Khronos/KHR_lights_punctual/README.md) (Experimental)
+     */
+    export class KHR_lights implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        public readonly name = NAME;
 
-                const light = ArrayItem.Get(extensionContext, this._lights, extension.light);
-                const name = light.name || babylonMesh.name;
+        /** Defines whether this extension is enabled. */
+        public enabled = true;
 
-                switch (light.type) {
-                    case LightType.DIRECTIONAL: {
-                        babylonLight = new DirectionalLight(name, Vector3.Backward(), this._loader.babylonScene);
-                        break;
-                    }
-                    case LightType.POINT: {
-                        babylonLight = new PointLight(name, Vector3.Zero(), this._loader.babylonScene);
-                        break;
-                    }
-                    case LightType.SPOT: {
-                        const babylonSpotLight = new SpotLight(name, Vector3.Zero(), Vector3.Backward(), 0, 1, this._loader.babylonScene);
-                        babylonSpotLight.angle = ((light.spot && light.spot.outerConeAngle) || Math.PI / 4) * 2;
-                        babylonSpotLight.innerAngle = ((light.spot && light.spot.innerConeAngle) || 0) * 2;
-                        babylonLight = babylonSpotLight;
-                        break;
-                    }
-                    default: {
-                        throw new Error(`${extensionContext}: Invalid light type (${light.type})`);
+        private _loader: GLTFLoader;
+        private _lights?: ILight[];
+
+        /** @hidden */
+        constructor(loader: GLTFLoader) {
+            this._loader = loader;
+        }
+
+        /** @hidden */
+        public dispose() {
+            delete this._loader;
+            delete this._lights;
+        }
+
+        /** @hidden */
+        public onLoading(): void {
+            const extensions = this._loader.gltf.extensions;
+            if (extensions && extensions[this.name]) {
+                const extension = extensions[this.name] as ILights;
+                this._lights = extension.lights;
+            }
+        }
+
+        /** @hidden */
+        public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
+            return GLTFLoader.LoadExtensionAsync<ILightReference, TransformNode>(context, node, this.name, (extensionContext, extension) => {
+                return this._loader.loadNodeAsync(context, node, (babylonMesh) => {
+                    let babylonLight: Light;
+
+                    const light = ArrayItem.Get(extensionContext, this._lights, extension.light);
+                    const name = light.name || babylonMesh.name;
+
+                    switch (light.type) {
+                        case LightType.DIRECTIONAL: {
+                            babylonLight = new DirectionalLight(name, Vector3.Backward(), this._loader.babylonScene);
+                            break;
+                        }
+                        case LightType.POINT: {
+                            babylonLight = new PointLight(name, Vector3.Zero(), this._loader.babylonScene);
+                            break;
+                        }
+                        case LightType.SPOT: {
+                            const babylonSpotLight = new SpotLight(name, Vector3.Zero(), Vector3.Backward(), 0, 1, this._loader.babylonScene);
+                            babylonSpotLight.angle = ((light.spot && light.spot.outerConeAngle) || Math.PI / 4) * 2;
+                            babylonSpotLight.innerAngle = ((light.spot && light.spot.innerConeAngle) || 0) * 2;
+                            babylonLight = babylonSpotLight;
+                            break;
+                        }
+                        default: {
+                            throw new Error(`${extensionContext}: Invalid light type (${light.type})`);
+                        }
                     }
-                }
 
-                babylonLight.falloffType = Light.FALLOFF_GLTF;
-                babylonLight.diffuse = light.color ? Color3.FromArray(light.color) : Color3.White();
-                babylonLight.intensity = light.intensity == undefined ? 1 : light.intensity;
-                babylonLight.range = light.range == undefined ? Number.MAX_VALUE : light.range;
-                babylonLight.parent = babylonMesh;
+                    babylonLight.falloffType = Light.FALLOFF_GLTF;
+                    babylonLight.diffuse = light.color ? Color3.FromArray(light.color) : Color3.White();
+                    babylonLight.intensity = light.intensity == undefined ? 1 : light.intensity;
+                    babylonLight.range = light.range == undefined ? Number.MAX_VALUE : light.range;
+                    babylonLight.parent = babylonMesh;
 
-                assign(babylonMesh);
+                    assign(babylonMesh);
+                });
             });
-        });
+        }
     }
-}
 
-GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_lights(loader));
+    GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_lights(loader));
+}

+ 257 - 258
loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts

@@ -1,303 +1,302 @@
-import { WeightedSound, Sound, Nullable, Mesh, Vector3, Tools, AnimationGroup, AnimationEvent } from "babylonjs";
-import { IArrayItem, IScene, INode, IAnimation } from "../glTFLoaderInterfaces";
-import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
-import { GLTFLoader, ArrayItem } from "../glTFLoader";
-
-const NAME = "MSFT_audio_emitter";
-
-interface IClipReference {
-    clip: number;
-    weight?: number;
-}
-
-interface IEmittersReference {
-    emitters: number[];
-}
-
-const enum DistanceModel {
-    linear = "linear",
-    inverse = "inverse",
-    exponential = "exponential",
-}
-
-interface IEmitter {
-    name?: string;
-    distanceModel?: DistanceModel;
-    refDistance?: number;
-    maxDistance?: number;
-    rolloffFactor?: number;
-    innerAngle?: number;
-    outerAngle?: number;
-    loop?: boolean;
-    volume?: number;
-    clips: IClipReference[];
-}
-
-const enum AudioMimeType {
-    WAV = "audio/wav",
-}
-
-interface IClip {
-    uri?: string;
-    bufferView?: number;
-    mimeType?: AudioMimeType;
-}
-
-interface ILoaderClip extends IClip, IArrayItem {
-    _objectURL?: Promise<string>;
-}
-
-interface ILoaderEmitter extends IEmitter, IArrayItem {
-    _babylonData?: {
-        sound?: WeightedSound;
-        loaded: Promise<void>;
-    };
-    _babylonSounds: Sound[];
-}
-
-interface IMSFTAudioEmitter {
-    clips: ILoaderClip[];
-    emitters: ILoaderEmitter[];
-}
-
-const enum AnimationEventAction {
-    play = "play",
-    pause = "pause",
-    stop = "stop",
-}
-
-interface IAnimationEvent {
-    action: AnimationEventAction;
-    emitter: number;
-    time: number;
-    startOffset?: number;
-}
-
-interface ILoaderAnimationEvent extends IAnimationEvent, IArrayItem {
-}
-
-interface ILoaderAnimationEvents {
-    events: ILoaderAnimationEvent[];
-}
-
-/**
- * [Specification](https://github.com/najadojo/glTF/tree/MSFT_audio_emitter/extensions/2.0/Vendor/MSFT_audio_emitter)
- */
-export class MSFT_audio_emitter implements IGLTFLoaderExtension {
-    /** The name of this extension. */
-    public readonly name = NAME;
-
-    /** Defines whether this extension is enabled. */
-    public enabled = true;
-
-    private _loader: GLTFLoader;
-    private _clips: Array<ILoaderClip>;
-    private _emitters: Array<ILoaderEmitter>;
-
-    /** @hidden */
-    constructor(loader: GLTFLoader) {
-        this._loader = loader;
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GLTF2.Loader.Extensions {
+    const NAME = "MSFT_audio_emitter";
+
+    interface IClipReference {
+        clip: number;
+        weight?: number;
     }
 
-    /** @hidden */
-    public dispose() {
-        delete this._loader;
-        delete this._clips;
-        delete this._emitters;
+    interface IEmittersReference {
+        emitters: number[];
     }
 
-    /** @hidden */
-    public onLoading(): void {
-        const extensions = this._loader.gltf.extensions;
-        if (extensions && extensions[this.name]) {
-            const extension = extensions[this.name] as IMSFTAudioEmitter;
+    const enum DistanceModel {
+        linear = "linear",
+        inverse = "inverse",
+        exponential = "exponential",
+    }
 
-            this._clips = extension.clips;
-            this._emitters = extension.emitters;
+    interface IEmitter {
+        name?: string;
+        distanceModel?: DistanceModel;
+        refDistance?: number;
+        maxDistance?: number;
+        rolloffFactor?: number;
+        innerAngle?: number;
+        outerAngle?: number;
+        loop?: boolean;
+        volume?: number;
+        clips: IClipReference[];
+    }
 
-            ArrayItem.Assign(this._clips);
-            ArrayItem.Assign(this._emitters);
-        }
+    const enum AudioMimeType {
+        WAV = "audio/wav",
     }
 
-    /** @hidden */
-    public loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>> {
-        return GLTFLoader.LoadExtensionAsync<IEmittersReference>(context, scene, this.name, (extensionContext, extension) => {
-            const promises = new Array<Promise<any>>();
+    interface IClip {
+        uri?: string;
+        bufferView?: number;
+        mimeType?: AudioMimeType;
+    }
 
-            promises.push(this._loader.loadSceneAsync(context, scene));
+    interface ILoaderClip extends IClip, IArrayItem {
+        _objectURL?: Promise<string>;
+    }
 
-            for (const emitterIndex of extension.emitters) {
-                const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);
-                if (emitter.refDistance != undefined || emitter.maxDistance != undefined || emitter.rolloffFactor != undefined ||
-                    emitter.distanceModel != undefined || emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
-                    throw new Error(`${extensionContext}: Direction or Distance properties are not allowed on emitters attached to a scene`);
-                }
+    interface ILoaderEmitter extends IEmitter, IArrayItem {
+        _babylonData?: {
+            sound?: WeightedSound;
+            loaded: Promise<void>;
+        };
+        _babylonSounds: Sound[];
+    }
 
-                promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter));
-            }
+    interface IMSFTAudioEmitter {
+        clips: ILoaderClip[];
+        emitters: ILoaderEmitter[];
+    }
+
+    const enum AnimationEventAction {
+        play = "play",
+        pause = "pause",
+        stop = "stop",
+    }
+
+    interface IAnimationEvent {
+        action: AnimationEventAction;
+        emitter: number;
+        time: number;
+        startOffset?: number;
+    }
 
-            return Promise.all(promises).then(() => {});
-        });
+    interface ILoaderAnimationEvent extends IAnimationEvent, IArrayItem {
     }
 
-    /** @hidden */
-    public loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>> {
-        return GLTFLoader.LoadExtensionAsync<IEmittersReference, Mesh>(context, node, this.name, (extensionContext, extension) => {
-            const promises = new Array<Promise<any>>();
+    interface ILoaderAnimationEvents {
+        events: ILoaderAnimationEvent[];
+    }
+
+    /**
+     * [Specification](https://github.com/najadojo/glTF/tree/MSFT_audio_emitter/extensions/2.0/Vendor/MSFT_audio_emitter)
+     */
+    export class MSFT_audio_emitter implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        public readonly name = NAME;
+
+        /** Defines whether this extension is enabled. */
+        public enabled = true;
+
+        private _loader: GLTFLoader;
+        private _clips: Array<ILoaderClip>;
+        private _emitters: Array<ILoaderEmitter>;
+
+        /** @hidden */
+        constructor(loader: GLTFLoader) {
+            this._loader = loader;
+        }
+
+        /** @hidden */
+        public dispose() {
+            delete this._loader;
+            delete this._clips;
+            delete this._emitters;
+        }
+
+        /** @hidden */
+        public onLoading(): void {
+            const extensions = this._loader.gltf.extensions;
+            if (extensions && extensions[this.name]) {
+                const extension = extensions[this.name] as IMSFTAudioEmitter;
+
+                this._clips = extension.clips;
+                this._emitters = extension.emitters;
+
+                ArrayItem.Assign(this._clips);
+                ArrayItem.Assign(this._emitters);
+            }
+        }
+
+        /** @hidden */
+        public loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>> {
+            return GLTFLoader.LoadExtensionAsync<IEmittersReference>(context, scene, this.name, (extensionContext, extension) => {
+                const promises = new Array<Promise<any>>();
+
+                promises.push(this._loader.loadSceneAsync(context, scene));
 
-            return this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {
                 for (const emitterIndex of extension.emitters) {
                     const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);
-                    promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter).then(() => {
-                        for (const sound of emitter._babylonSounds) {
-                            sound.attachToMesh(babylonMesh);
-                            if (emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
-                                sound.setLocalDirectionToMesh(Vector3.Forward());
-                                sound.setDirectionalCone(
-                                    2 * Tools.ToDegrees(emitter.innerAngle == undefined ? Math.PI : emitter.innerAngle),
-                                    2 * Tools.ToDegrees(emitter.outerAngle == undefined ? Math.PI : emitter.outerAngle),
-                                    0);
-                            }
-                        }
-                    }));
+                    if (emitter.refDistance != undefined || emitter.maxDistance != undefined || emitter.rolloffFactor != undefined ||
+                        emitter.distanceModel != undefined || emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
+                        throw new Error(`${extensionContext}: Direction or Distance properties are not allowed on emitters attached to a scene`);
+                    }
+
+                    promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter));
                 }
 
-                assign(babylonMesh);
-            }).then((babylonMesh) => {
-                return Promise.all(promises).then(() => {
-                    return babylonMesh;
-                });
+                return Promise.all(promises).then(() => {});
             });
-        });
-    }
+        }
 
-    /** @hidden */
-    public loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>> {
-        return GLTFLoader.LoadExtensionAsync<ILoaderAnimationEvents, AnimationGroup>(context, animation, this.name, (extensionContext, extension) => {
-            return this._loader.loadAnimationAsync(context, animation).then((babylonAnimationGroup) => {
+        /** @hidden */
+        public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
+            return GLTFLoader.LoadExtensionAsync<IEmittersReference, TransformNode>(context, node, this.name, (extensionContext, extension) => {
                 const promises = new Array<Promise<any>>();
 
-                ArrayItem.Assign(extension.events);
-                for (const event of extension.events) {
-                    promises.push(this._loadAnimationEventAsync(`${extensionContext}/events/${event.index}`, context, animation, event, babylonAnimationGroup));
-                }
-
-                return Promise.all(promises).then(() => {
-                    return babylonAnimationGroup;
+                return this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {
+                    for (const emitterIndex of extension.emitters) {
+                        const emitter = ArrayItem.Get(`${extensionContext}/emitters`, this._emitters, emitterIndex);
+                        promises.push(this._loadEmitterAsync(`${extensionContext}/emitters/${emitter.index}`, emitter).then(() => {
+                            for (const sound of emitter._babylonSounds) {
+                                sound.attachToMesh(babylonMesh);
+                                if (emitter.innerAngle != undefined || emitter.outerAngle != undefined) {
+                                    sound.setLocalDirectionToMesh(Vector3.Forward());
+                                    sound.setDirectionalCone(
+                                        2 * Tools.ToDegrees(emitter.innerAngle == undefined ? Math.PI : emitter.innerAngle),
+                                        2 * Tools.ToDegrees(emitter.outerAngle == undefined ? Math.PI : emitter.outerAngle),
+                                        0);
+                                }
+                            }
+                        }));
+                    }
+
+                    assign(babylonMesh);
+                }).then((babylonMesh) => {
+                    return Promise.all(promises).then(() => {
+                        return babylonMesh;
+                    });
                 });
             });
-        });
-    }
-
-    private _loadClipAsync(context: string, clip: ILoaderClip): Promise<string> {
-        if (clip._objectURL) {
-            return clip._objectURL;
         }
 
-        let promise: Promise<ArrayBufferView>;
-        if (clip.uri) {
-            promise = this._loader.loadUriAsync(context, clip.uri);
-        }
-        else {
-            const bufferView = ArrayItem.Get(`${context}/bufferView`, this._loader.gltf.bufferViews, clip.bufferView);
-            promise = this._loader.loadBufferViewAsync(`#/bufferViews/${bufferView.index}`, bufferView);
-        }
+        /** @hidden */
+        public loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>> {
+            return GLTFLoader.LoadExtensionAsync<ILoaderAnimationEvents, AnimationGroup>(context, animation, this.name, (extensionContext, extension) => {
+                return this._loader.loadAnimationAsync(context, animation).then((babylonAnimationGroup) => {
+                    const promises = new Array<Promise<any>>();
 
-        clip._objectURL = promise.then((data) => {
-            return URL.createObjectURL(new Blob([data], { type: clip.mimeType }));
-        });
+                    ArrayItem.Assign(extension.events);
+                    for (const event of extension.events) {
+                        promises.push(this._loadAnimationEventAsync(`${extensionContext}/events/${event.index}`, context, animation, event, babylonAnimationGroup));
+                    }
 
-        return clip._objectURL;
-    }
+                    return Promise.all(promises).then(() => {
+                        return babylonAnimationGroup;
+                    });
+                });
+            });
+        }
 
-    private _loadEmitterAsync(context: string, emitter: ILoaderEmitter): Promise<void> {
-        emitter._babylonSounds = emitter._babylonSounds || [];
-        if (!emitter._babylonData) {
-            const clipPromises = new Array<Promise<any>>();
-            const name = emitter.name || `emitter${emitter.index}`;
-            const options = {
-                loop: false,
-                autoplay: false,
-                volume: emitter.volume == undefined ? 1 : emitter.volume,
-            };
-
-            for (let i = 0; i < emitter.clips.length; i++) {
-                const clipContext = `#/extensions/${this.name}/clips`;
-                const clip = ArrayItem.Get(clipContext, this._clips, emitter.clips[i].clip);
-                clipPromises.push(this._loadClipAsync(`${clipContext}/${emitter.clips[i].clip}`, clip).then((objectURL: string) => {
-                    const sound = emitter._babylonSounds[i] = new Sound(name, objectURL, this._loader.babylonScene, null, options);
-                    sound.refDistance = emitter.refDistance || 1;
-                    sound.maxDistance = emitter.maxDistance || 256;
-                    sound.rolloffFactor = emitter.rolloffFactor || 1;
-                    sound.distanceModel = emitter.distanceModel || 'exponential';
-                    sound._positionInEmitterSpace = true;
-                }));
+        private _loadClipAsync(context: string, clip: ILoaderClip): Promise<string> {
+            if (clip._objectURL) {
+                return clip._objectURL;
             }
 
-            const promise = Promise.all(clipPromises).then(() => {
-                const weights = emitter.clips.map((clip) => { return clip.weight || 1; });
-                const weightedSound = new WeightedSound(emitter.loop || false, emitter._babylonSounds, weights);
-                if (emitter.innerAngle) { weightedSound.directionalConeInnerAngle = 2 * Tools.ToDegrees(emitter.innerAngle); }
-                if (emitter.outerAngle) { weightedSound.directionalConeOuterAngle = 2 * Tools.ToDegrees(emitter.outerAngle); }
-                if (emitter.volume) { weightedSound.volume = emitter.volume; }
-                emitter._babylonData!.sound = weightedSound;
+            let promise: Promise<ArrayBufferView>;
+            if (clip.uri) {
+                promise = this._loader.loadUriAsync(context, clip.uri);
+            }
+            else {
+                const bufferView = ArrayItem.Get(`${context}/bufferView`, this._loader.gltf.bufferViews, clip.bufferView);
+                promise = this._loader.loadBufferViewAsync(`#/bufferViews/${bufferView.index}`, bufferView);
+            }
+
+            clip._objectURL = promise.then((data) => {
+                return URL.createObjectURL(new Blob([data], { type: clip.mimeType }));
             });
 
-            emitter._babylonData = {
-                loaded: promise
-            };
+            return clip._objectURL;
         }
 
-        return emitter._babylonData.loaded;
-    }
-
-    private _getEventAction(context: string, sound: WeightedSound, action: AnimationEventAction, time: number, startOffset?: number): (currentFrame: number) => void {
-        switch (action) {
-            case AnimationEventAction.play: {
-                return (currentFrame: number) => {
-                    const frameOffset = (startOffset || 0) + (currentFrame - time);
-                    sound.play(frameOffset);
-                };
-            }
-            case AnimationEventAction.stop: {
-                return (currentFrame: number) => {
-                    sound.stop();
+        private _loadEmitterAsync(context: string, emitter: ILoaderEmitter): Promise<void> {
+            emitter._babylonSounds = emitter._babylonSounds || [];
+            if (!emitter._babylonData) {
+                const clipPromises = new Array<Promise<any>>();
+                const name = emitter.name || `emitter${emitter.index}`;
+                const options = {
+                    loop: false,
+                    autoplay: false,
+                    volume: emitter.volume == undefined ? 1 : emitter.volume,
                 };
-            }
-            case AnimationEventAction.pause: {
-                return (currentFrame: number) => {
-                    sound.pause();
+
+                for (let i = 0; i < emitter.clips.length; i++) {
+                    const clipContext = `#/extensions/${this.name}/clips`;
+                    const clip = ArrayItem.Get(clipContext, this._clips, emitter.clips[i].clip);
+                    clipPromises.push(this._loadClipAsync(`${clipContext}/${emitter.clips[i].clip}`, clip).then((objectURL: string) => {
+                        const sound = emitter._babylonSounds[i] = new Sound(name, objectURL, this._loader.babylonScene, null, options);
+                        sound.refDistance = emitter.refDistance || 1;
+                        sound.maxDistance = emitter.maxDistance || 256;
+                        sound.rolloffFactor = emitter.rolloffFactor || 1;
+                        sound.distanceModel = emitter.distanceModel || 'exponential';
+                        sound._positionInEmitterSpace = true;
+                    }));
+                }
+
+                const promise = Promise.all(clipPromises).then(() => {
+                    const weights = emitter.clips.map((clip) => { return clip.weight || 1; });
+                    const weightedSound = new WeightedSound(emitter.loop || false, emitter._babylonSounds, weights);
+                    if (emitter.innerAngle) { weightedSound.directionalConeInnerAngle = 2 * Tools.ToDegrees(emitter.innerAngle); }
+                    if (emitter.outerAngle) { weightedSound.directionalConeOuterAngle = 2 * Tools.ToDegrees(emitter.outerAngle); }
+                    if (emitter.volume) { weightedSound.volume = emitter.volume; }
+                    emitter._babylonData!.sound = weightedSound;
+                });
+
+                emitter._babylonData = {
+                    loaded: promise
                 };
             }
-            default: {
-                throw new Error(`${context}: Unsupported action ${action}`);
-            }
+
+            return emitter._babylonData.loaded;
         }
-    }
 
-    private _loadAnimationEventAsync(context: string, animationContext: string, animation: IAnimation, event: ILoaderAnimationEvent, babylonAnimationGroup: AnimationGroup): Promise<void> {
-        if (babylonAnimationGroup.targetedAnimations.length == 0) {
-            return Promise.resolve();
+        private _getEventAction(context: string, sound: WeightedSound, action: AnimationEventAction, time: number, startOffset?: number): (currentFrame: number) => void {
+            switch (action) {
+                case AnimationEventAction.play: {
+                    return (currentFrame: number) => {
+                        const frameOffset = (startOffset || 0) + (currentFrame - time);
+                        sound.play(frameOffset);
+                    };
+                }
+                case AnimationEventAction.stop: {
+                    return (currentFrame: number) => {
+                        sound.stop();
+                    };
+                }
+                case AnimationEventAction.pause: {
+                    return (currentFrame: number) => {
+                        sound.pause();
+                    };
+                }
+                default: {
+                    throw new Error(`${context}: Unsupported action ${action}`);
+                }
+            }
         }
-        const babylonAnimation = babylonAnimationGroup.targetedAnimations[0];
-        const emitterIndex = event.emitter;
-        const emitter = ArrayItem.Get(`#/extensions/${this.name}/emitters`, this._emitters, emitterIndex);
-        return this._loadEmitterAsync(context, emitter).then(() => {
-            const sound = emitter._babylonData!.sound;
-            if (sound) {
-                var babylonAnimationEvent = new AnimationEvent(event.time, this._getEventAction(context, sound, event.action, event.time, event.startOffset));
-                babylonAnimation.animation.addEvent(babylonAnimationEvent);
-                // Make sure all started audio stops when this animation is terminated.
-                babylonAnimationGroup.onAnimationGroupEndObservable.add(() => {
-                    sound.stop();
-                });
-                babylonAnimationGroup.onAnimationGroupPauseObservable.add(() => {
-                    sound.pause();
-                });
+
+        private _loadAnimationEventAsync(context: string, animationContext: string, animation: IAnimation, event: ILoaderAnimationEvent, babylonAnimationGroup: AnimationGroup): Promise<void> {
+            if (babylonAnimationGroup.targetedAnimations.length == 0) {
+                return Promise.resolve();
             }
-        });
+            const babylonAnimation = babylonAnimationGroup.targetedAnimations[0];
+            const emitterIndex = event.emitter;
+            const emitter = ArrayItem.Get(`#/extensions/${this.name}/emitters`, this._emitters, emitterIndex);
+            return this._loadEmitterAsync(context, emitter).then(() => {
+                const sound = emitter._babylonData!.sound;
+                if (sound) {
+                    var babylonAnimationEvent = new AnimationEvent(event.time, this._getEventAction(context, sound, event.action, event.time, event.startOffset));
+                    babylonAnimation.animation.addEvent(babylonAnimationEvent);
+                    // Make sure all started audio stops when this animation is terminated.
+                    babylonAnimationGroup.onAnimationGroupEndObservable.add(() => {
+                        sound.stop();
+                    });
+                    babylonAnimationGroup.onAnimationGroupPauseObservable.add(() => {
+                        sound.pause();
+                    });
+                }
+            });
+        }
     }
-}
 
-GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_audio_emitter(loader));
+    GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_audio_emitter(loader));
+}

+ 219 - 218
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -1,279 +1,280 @@
-import { Observable, Nullable, Deferred, Mesh, Material } from "babylonjs";
-import { INode, IMaterial } from "../glTFLoaderInterfaces";
-import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
-import { GLTFLoader, ArrayItem } from "../glTFLoader";
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
 
-const NAME = "MSFT_lod";
+module BABYLON.GLTF2.Loader.Extensions {
+    const NAME = "MSFT_lod";
 
-interface IMSFTLOD {
-    ids: number[];
-}
-
-/**
- * [Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod)
- */
-export class MSFT_lod implements IGLTFLoaderExtension {
-    /** The name of this extension. */
-    public readonly name = NAME;
-
-    /** Defines whether this extension is enabled. */
-    public enabled = true;
-
-    /**
-     * Maximum number of LODs to load, starting from the lowest LOD.
-     */
-    public maxLODsToLoad = Number.MAX_VALUE;
-
-    /**
-     * Observable raised when all node LODs of one level are loaded.
-     * The event data is the index of the loaded LOD starting from zero.
-     * Dispose the loader to cancel the loading of the next level of LODs.
-     */
-    public onNodeLODsLoadedObservable = new Observable<number>();
+    interface IMSFTLOD {
+        ids: number[];
+    }
 
     /**
-     * Observable raised when all material LODs of one level are loaded.
-     * The event data is the index of the loaded LOD starting from zero.
-     * Dispose the loader to cancel the loading of the next level of LODs.
+     * [Specification](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_lod)
      */
-    public onMaterialLODsLoadedObservable = new Observable<number>();
-
-    private _loader: GLTFLoader;
-
-    private _nodeIndexLOD: Nullable<number> = null;
-    private _nodeSignalLODs = new Array<Deferred<void>>();
-    private _nodePromiseLODs = new Array<Array<Promise<any>>>();
-
-    private _materialIndexLOD: Nullable<number> = null;
-    private _materialSignalLODs = new Array<Deferred<void>>();
-    private _materialPromiseLODs = new Array<Array<Promise<any>>>();
-
-    /** @hidden */
-    constructor(loader: GLTFLoader) {
-        this._loader = loader;
-    }
+    export class MSFT_lod implements IGLTFLoaderExtension {
+        /** The name of this extension. */
+        public readonly name = NAME;
+
+        /** Defines whether this extension is enabled. */
+        public enabled = true;
+
+        /**
+         * Maximum number of LODs to load, starting from the lowest LOD.
+         */
+        public maxLODsToLoad = Number.MAX_VALUE;
+
+        /**
+         * Observable raised when all node LODs of one level are loaded.
+         * The event data is the index of the loaded LOD starting from zero.
+         * Dispose the loader to cancel the loading of the next level of LODs.
+         */
+        public onNodeLODsLoadedObservable = new Observable<number>();
+
+        /**
+         * Observable raised when all material LODs of one level are loaded.
+         * The event data is the index of the loaded LOD starting from zero.
+         * Dispose the loader to cancel the loading of the next level of LODs.
+         */
+        public onMaterialLODsLoadedObservable = new Observable<number>();
+
+        private _loader: GLTFLoader;
+
+        private _nodeIndexLOD: Nullable<number> = null;
+        private _nodeSignalLODs = new Array<Deferred<void>>();
+        private _nodePromiseLODs = new Array<Array<Promise<any>>>();
+
+        private _materialIndexLOD: Nullable<number> = null;
+        private _materialSignalLODs = new Array<Deferred<void>>();
+        private _materialPromiseLODs = new Array<Array<Promise<any>>>();
+
+        /** @hidden */
+        constructor(loader: GLTFLoader) {
+            this._loader = loader;
+        }
 
-    /** @hidden */
-    public dispose() {
-        delete this._loader;
+        /** @hidden */
+        public dispose() {
+            delete this._loader;
 
-        this._nodeIndexLOD = null;
-        this._nodeSignalLODs.length = 0;
-        this._nodePromiseLODs.length = 0;
+            this._nodeIndexLOD = null;
+            this._nodeSignalLODs.length = 0;
+            this._nodePromiseLODs.length = 0;
 
-        this._materialIndexLOD = null;
-        this._materialSignalLODs.length = 0;
-        this._materialPromiseLODs.length = 0;
+            this._materialIndexLOD = null;
+            this._materialSignalLODs.length = 0;
+            this._materialPromiseLODs.length = 0;
 
-        this.onMaterialLODsLoadedObservable.clear();
-        this.onNodeLODsLoadedObservable.clear();
-    }
+            this.onMaterialLODsLoadedObservable.clear();
+            this.onNodeLODsLoadedObservable.clear();
+        }
 
-    /** @hidden */
-    public onReady(): void {
-        for (let indexLOD = 0; indexLOD < this._nodePromiseLODs.length; indexLOD++) {
-            const promise = Promise.all(this._nodePromiseLODs[indexLOD]).then(() => {
-                if (indexLOD !== 0) {
-                    this._loader.endPerformanceCounter(`Node LOD ${indexLOD}`);
-                }
+        /** @hidden */
+        public onReady(): void {
+            for (let indexLOD = 0; indexLOD < this._nodePromiseLODs.length; indexLOD++) {
+                const promise = Promise.all(this._nodePromiseLODs[indexLOD]).then(() => {
+                    if (indexLOD !== 0) {
+                        this._loader.endPerformanceCounter(`Node LOD ${indexLOD}`);
+                    }
 
-                this._loader.log(`Loaded node LOD ${indexLOD}`);
-                this.onNodeLODsLoadedObservable.notifyObservers(indexLOD);
+                    this._loader.log(`Loaded node LOD ${indexLOD}`);
+                    this.onNodeLODsLoadedObservable.notifyObservers(indexLOD);
 
-                if (indexLOD !== this._nodePromiseLODs.length - 1) {
-                    this._loader.startPerformanceCounter(`Node LOD ${indexLOD + 1}`);
-                    if (this._nodeSignalLODs[indexLOD]) {
-                        this._nodeSignalLODs[indexLOD].resolve();
+                    if (indexLOD !== this._nodePromiseLODs.length - 1) {
+                        this._loader.startPerformanceCounter(`Node LOD ${indexLOD + 1}`);
+                        if (this._nodeSignalLODs[indexLOD]) {
+                            this._nodeSignalLODs[indexLOD].resolve();
+                        }
                     }
-                }
-            });
+                });
 
-            this._loader._completePromises.push(promise);
-        }
+                this._loader._completePromises.push(promise);
+            }
 
-        for (let indexLOD = 0; indexLOD < this._materialPromiseLODs.length; indexLOD++) {
-            const promise = Promise.all(this._materialPromiseLODs[indexLOD]).then(() => {
-                if (indexLOD !== 0) {
-                    this._loader.endPerformanceCounter(`Material LOD ${indexLOD}`);
-                }
+            for (let indexLOD = 0; indexLOD < this._materialPromiseLODs.length; indexLOD++) {
+                const promise = Promise.all(this._materialPromiseLODs[indexLOD]).then(() => {
+                    if (indexLOD !== 0) {
+                        this._loader.endPerformanceCounter(`Material LOD ${indexLOD}`);
+                    }
 
-                this._loader.log(`Loaded material LOD ${indexLOD}`);
-                this.onMaterialLODsLoadedObservable.notifyObservers(indexLOD);
+                    this._loader.log(`Loaded material LOD ${indexLOD}`);
+                    this.onMaterialLODsLoadedObservable.notifyObservers(indexLOD);
 
-                if (indexLOD !== this._materialPromiseLODs.length - 1) {
-                    this._loader.startPerformanceCounter(`Material LOD ${indexLOD + 1}`);
-                    if (this._materialSignalLODs[indexLOD]) {
-                        this._materialSignalLODs[indexLOD].resolve();
+                    if (indexLOD !== this._materialPromiseLODs.length - 1) {
+                        this._loader.startPerformanceCounter(`Material LOD ${indexLOD + 1}`);
+                        if (this._materialSignalLODs[indexLOD]) {
+                            this._materialSignalLODs[indexLOD].resolve();
+                        }
                     }
-                }
-            });
+                });
 
-            this._loader._completePromises.push(promise);
+                this._loader._completePromises.push(promise);
+            }
         }
-    }
 
-    /** @hidden */
-    public loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>> {
-        return GLTFLoader.LoadExtensionAsync<IMSFTLOD, Mesh>(context, node, this.name, (extensionContext, extension) => {
-            let firstPromise: Promise<Mesh>;
+        /** @hidden */
+        public loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
+            return GLTFLoader.LoadExtensionAsync<IMSFTLOD, TransformNode>(context, node, this.name, (extensionContext, extension) => {
+                let firstPromise: Promise<TransformNode>;
 
-            const nodeLODs = this._getLODs(extensionContext, node, this._loader.gltf.nodes, extension.ids);
-            this._loader.logOpen(`${extensionContext}`);
+                const nodeLODs = this._getLODs(extensionContext, node, this._loader.gltf.nodes, extension.ids);
+                this._loader.logOpen(`${extensionContext}`);
 
-            for (let indexLOD = 0; indexLOD < nodeLODs.length; indexLOD++) {
-                const nodeLOD = nodeLODs[indexLOD];
-
-                if (indexLOD !== 0) {
-                    this._nodeIndexLOD = indexLOD;
-                    this._nodeSignalLODs[indexLOD] = this._nodeSignalLODs[indexLOD] || new Deferred();
-                }
+                for (let indexLOD = 0; indexLOD < nodeLODs.length; indexLOD++) {
+                    const nodeLOD = nodeLODs[indexLOD];
 
-                const promise = this._loader.loadNodeAsync(`#/nodes/${nodeLOD.index}`, nodeLOD).then((babylonMesh) => {
                     if (indexLOD !== 0) {
-                        // TODO: should not rely on _babylonMesh
-                        const previousNodeLOD = nodeLODs[indexLOD - 1];
-                        if (previousNodeLOD._babylonMesh) {
-                            previousNodeLOD._babylonMesh.dispose();
-                            delete previousNodeLOD._babylonMesh;
-                            this._disposeUnusedMaterials();
-                        }
+                        this._nodeIndexLOD = indexLOD;
+                        this._nodeSignalLODs[indexLOD] = this._nodeSignalLODs[indexLOD] || new Deferred();
                     }
 
-                    return babylonMesh;
-                });
+                    const assign = (babylonTransformNode: TransformNode) => { babylonTransformNode.setEnabled(false); };
+                    const promise = this._loader.loadNodeAsync(`#/nodes/${nodeLOD.index}`, nodeLOD, assign).then((babylonMesh) => {
+                        if (indexLOD !== 0) {
+                            // TODO: should not rely on _babylonMesh
+                            const previousNodeLOD = nodeLODs[indexLOD - 1];
+                            if (previousNodeLOD._babylonTransformNode) {
+                                previousNodeLOD._babylonTransformNode.dispose();
+                                delete previousNodeLOD._babylonTransformNode;
+                                this._disposeUnusedMaterials();
+                            }
+                        }
 
-                if (indexLOD === 0) {
-                    firstPromise = promise;
-                }
-                else {
-                    this._nodeIndexLOD = null;
-                }
+                        babylonMesh.setEnabled(true);
+                        return babylonMesh;
+                    });
 
-                this._nodePromiseLODs[indexLOD] = this._nodePromiseLODs[indexLOD] || [];
-                this._nodePromiseLODs[indexLOD].push(promise);
-            }
+                    if (indexLOD === 0) {
+                        firstPromise = promise;
+                    }
+                    else {
+                        this._nodeIndexLOD = null;
+                    }
 
-            this._loader.logClose();
-            return firstPromise!;
-        });
-    }
+                    this._nodePromiseLODs[indexLOD] = this._nodePromiseLODs[indexLOD] || [];
+                    this._nodePromiseLODs[indexLOD].push(promise);
+                }
 
-    /** @hidden */
-    public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
-        // Don't load material LODs if already loading a node LOD.
-        if (this._nodeIndexLOD) {
-            return null;
+                this._loader.logClose();
+                return firstPromise!;
+            });
         }
 
-        return GLTFLoader.LoadExtensionAsync<IMSFTLOD, Material>(context, material, this.name, (extensionContext, extension) => {
-            let firstPromise: Promise<Material>;
+        /** @hidden */
+        public _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>> {
+            // Don't load material LODs if already loading a node LOD.
+            if (this._nodeIndexLOD) {
+                return null;
+            }
 
-            const materialLODs = this._getLODs(extensionContext, material, this._loader.gltf.materials, extension.ids);
-            this._loader.logOpen(`${extensionContext}`);
+            return GLTFLoader.LoadExtensionAsync<IMSFTLOD, Material>(context, material, this.name, (extensionContext, extension) => {
+                let firstPromise: Promise<Material>;
 
-            for (let indexLOD = 0; indexLOD < materialLODs.length; indexLOD++) {
-                const materialLOD = materialLODs[indexLOD];
+                const materialLODs = this._getLODs(extensionContext, material, this._loader.gltf.materials, extension.ids);
+                this._loader.logOpen(`${extensionContext}`);
 
-                if (indexLOD !== 0) {
-                    this._materialIndexLOD = indexLOD;
-                }
+                for (let indexLOD = 0; indexLOD < materialLODs.length; indexLOD++) {
+                    const materialLOD = materialLODs[indexLOD];
 
-                const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD.index}`, materialLOD, babylonMesh, babylonDrawMode, (babylonMaterial) => {
-                    if (indexLOD === 0) {
-                        assign(babylonMaterial);
-                    }
-                }).then((babylonMaterial) => {
                     if (indexLOD !== 0) {
-                        assign(babylonMaterial);
-
-                        // TODO: should not rely on _babylonData
-                        const previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData!;
-                        if (previousBabylonDataLOD[babylonDrawMode]) {
-                            previousBabylonDataLOD[babylonDrawMode].material.dispose();
-                            delete previousBabylonDataLOD[babylonDrawMode];
-                        }
+                        this._materialIndexLOD = indexLOD;
                     }
 
-                    return babylonMaterial;
-                });
+                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD.index}`, materialLOD, babylonMesh, babylonDrawMode, (babylonMaterial) => {
+                        if (indexLOD === 0) {
+                            assign(babylonMaterial);
+                        }
+                    }).then((babylonMaterial) => {
+                        if (indexLOD !== 0) {
+                            assign(babylonMaterial);
+
+                            // TODO: should not rely on _data
+                            const previousDataLOD = materialLODs[indexLOD - 1]._data!;
+                            if (previousDataLOD[babylonDrawMode]) {
+                                previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
+                                delete previousDataLOD[babylonDrawMode];
+                            }
+                        }
 
-                if (indexLOD === 0) {
-                    firstPromise = promise;
-                }
-                else {
-                    this._materialIndexLOD = null;
-                }
+                        return babylonMaterial;
+                    });
 
-                this._materialPromiseLODs[indexLOD] = this._materialPromiseLODs[indexLOD] || [];
-                this._materialPromiseLODs[indexLOD].push(promise);
-            }
+                    if (indexLOD === 0) {
+                        firstPromise = promise;
+                    }
+                    else {
+                        this._materialIndexLOD = null;
+                    }
 
-            this._loader.logClose();
-            return firstPromise!;
-        });
-    }
+                    this._materialPromiseLODs[indexLOD] = this._materialPromiseLODs[indexLOD] || [];
+                    this._materialPromiseLODs[indexLOD].push(promise);
+                }
 
-    /** @hidden */
-    public _loadUriAsync(context: string, 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);
-            });
-        }
-        else if (this._nodeIndexLOD !== null) {
-            this._loader.log(`deferred`);
-            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);
+                this._loader.logClose();
+                return firstPromise!;
             });
         }
 
-        return null;
-    }
+        /** @hidden */
+        public _loadUriAsync(context: string, 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);
+                });
+            }
+            else if (this._nodeIndexLOD !== null) {
+                this._loader.log(`deferred`);
+                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);
+                });
+            }
 
-    /**
-     * Gets an array of LOD properties from lowest to highest.
-     */
-    private _getLODs<T>(context: string, property: T, array: ArrayLike<T> | undefined, ids: number[]): T[] {
-        if (this.maxLODsToLoad <= 0) {
-            throw new Error("maxLODsToLoad must be greater than zero");
+            return null;
         }
 
-        const properties = new Array<T>();
+        /**
+         * Gets an array of LOD properties from lowest to highest.
+         */
+        private _getLODs<T>(context: string, property: T, array: ArrayLike<T> | undefined, ids: number[]): T[] {
+            if (this.maxLODsToLoad <= 0) {
+                throw new Error("maxLODsToLoad must be greater than zero");
+            }
+
+            const properties = new Array<T>();
 
-        for (let i = ids.length - 1; i >= 0; i--) {
-            properties.push(ArrayItem.Get(`${context}/ids/${ids[i]}`, array, ids[i]));
-            if (properties.length === this.maxLODsToLoad) {
-                return properties;
+            for (let i = ids.length - 1; i >= 0; i--) {
+                properties.push(ArrayItem.Get(`${context}/ids/${ids[i]}`, array, ids[i]));
+                if (properties.length === this.maxLODsToLoad) {
+                    return properties;
+                }
             }
-        }
 
-        properties.push(property);
-        return properties;
-    }
+            properties.push(property);
+            return properties;
+        }
 
-    private _disposeUnusedMaterials(): void {
-        // TODO: should not rely on _babylonData
-        const materials = this._loader.gltf.materials;
-        if (materials) {
-            for (const material of materials) {
-                if (material._babylonData) {
-                    for (const drawMode in material._babylonData) {
-                        const babylonData = material._babylonData[drawMode];
-                        if (babylonData.meshes.length === 0) {
-                            babylonData.material.dispose(false, true);
-                            delete material._babylonData[drawMode];
+        private _disposeUnusedMaterials(): void {
+            // TODO: should not rely on _data
+            const materials = this._loader.gltf.materials;
+            if (materials) {
+                for (const material of materials) {
+                    if (material._data) {
+                        for (const drawMode in material._data) {
+                            const data = material._data[drawMode];
+                            if (data.babylonMeshes.length === 0) {
+                                data.babylonMaterial.dispose(false, true);
+                                delete material._data[drawMode];
+                            }
                         }
                     }
                 }
             }
         }
     }
-}
 
-GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_lod(loader));
+    GLTFLoader.RegisterExtension(NAME, (loader) => new MSFT_lod(loader));
+}

文件差異過大導致無法顯示
+ 1714 - 1661
loaders/src/glTF/2.0/glTFLoader.ts


+ 95 - 92
loaders/src/glTF/2.0/glTFLoaderExtension.ts

@@ -1,107 +1,110 @@
-import { IDisposable, Nullable, Mesh, Camera, Geometry, Material, BaseTexture, AnimationGroup } from "babylonjs";
-import { IScene, INode, ICamera, IMeshPrimitive, IMaterial, ITextureInfo, IAnimation } from "./glTFLoaderInterfaces";
-import { IGLTFLoaderExtension as IGLTFBaseLoaderExtension } from "../glTFFileLoader";
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-/** @hidden */
-export var __IGLTFLoaderExtension = 0; // I am here to allow dts to be created
-
-/**
- * Interface for a glTF loader extension.
- */
-export interface IGLTFLoaderExtension extends IGLTFBaseLoaderExtension, IDisposable {
+module BABYLON.GLTF2 {
     /**
-     * Called after the loader state changes to LOADING.
+     * Interface for a glTF loader extension.
      */
-    onLoading?(): void;
+    export interface IGLTFLoaderExtension extends BABYLON.IGLTFLoaderExtension, IDisposable {
+        /**
+         * Called after the loader state changes to LOADING.
+         */
+        onLoading?(): void;
 
-    /**
-     * Called after the loader state changes to READY.
-     */
-    onReady?(): void;
+        /**
+         * Called after the loader state changes to READY.
+         */
+        onReady?(): void;
 
-    /**
-     * Define this method to modify the default behavior when loading scenes.
-     * @param context The context when loading the asset
-     * @param scene The glTF scene property
-     * @returns A promise that resolves when the load is complete or null if not handled
-     */
-    loadSceneAsync?(context: string, scene: IScene): Nullable<Promise<void>>;
+        /**
+         * Define this method to modify the default behavior when loading scenes.
+         * @param context The context when loading the asset
+         * @param scene The glTF scene property
+         * @returns A promise that resolves when the load is complete or null if not handled
+         */
+        loadSceneAsync?(context: string, scene: Loader.IScene): Nullable<Promise<void>>;
 
-    /**
-     * Define this method to modify the default behavior when loading nodes.
-     * @param context The context when loading the asset
-     * @param node The glTF node property
-     * @param assign A function called synchronously after parsing the glTF properties
-     * @returns A promise that resolves with the loaded Babylon mesh when the load is complete or null if not handled
-     */
-    loadNodeAsync?(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        /**
+         * Define this method to modify the default behavior when loading nodes.
+         * @param context The context when loading the asset
+         * @param node The glTF node property
+         * @param assign A function called synchronously after parsing the glTF properties
+         * @returns A promise that resolves with the loaded Babylon transform node when the load is complete or null if not handled
+         */
+        loadNodeAsync?(context: string, node: Loader.INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
 
-    /**
-     * Define this method to modify the default behavior when loading cameras.
-     * @param context The context when loading the asset
-     * @param camera The glTF camera property
-     * @param assign A function called synchronously after parsing the glTF properties
-     * @returns A promise that resolves with the loaded Babylon camera when the load is complete or null if not handled
-     */
-    loadCameraAsync?(context: string, camera: ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
+        /**
+         * Define this method to modify the default behavior when loading cameras.
+         * @param context The context when loading the asset
+         * @param camera The glTF camera property
+         * @param assign A function called synchronously after parsing the glTF properties
+         * @returns A promise that resolves with the loaded Babylon camera when the load is complete or null if not handled
+         */
+        loadCameraAsync?(context: string, camera: Loader.ICamera, assign: (babylonCamera: Camera) => void): Nullable<Promise<Camera>>;
 
-    /**
-     * @hidden Define this method to modify the default behavior when loading vertex data for mesh primitives.
-     * @param context The context when loading the asset
-     * @param primitive The glTF mesh primitive property
-     * @returns A promise that resolves with the loaded geometry when the load is complete or null if not handled
-     */
-    _loadVertexDataAsync?(context: string, primitive: IMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>>;
+        /**
+         * @hidden Define this method to modify the default behavior when loading vertex data for mesh primitives.
+         * @param context The context when loading the asset
+         * @param primitive The glTF mesh primitive property
+         * @returns A promise that resolves with the loaded geometry when the load is complete or null if not handled
+         */
+        _loadVertexDataAsync?(context: string, primitive: Loader.IMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<Geometry>>;
 
-    /**
-     * @hidden Define this method to modify the default behavior when loading materials. Load material creates the material and then loads material properties.
-     * @param context The context when loading the asset
-     * @param material The glTF material property
-     * @param assign A function called synchronously after parsing the glTF properties
-     * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
-     */
-    _loadMaterialAsync?(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
+        /**
+         * @hidden Define this method to modify the default behavior when loading materials. Load material creates the material and then loads material properties.
+         * @param context The context when loading the asset
+         * @param material The glTF material property
+         * @param assign A function called synchronously after parsing the glTF properties
+         * @returns A promise that resolves with the loaded Babylon material when the load is complete or null if not handled
+         */
+        _loadMaterialAsync?(context: string, material: Loader.IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
 
-    /**
-     * Define this method to modify the default behavior when creating materials.
-     * @param context The context when loading the asset
-     * @param material The glTF material property
-     * @param babylonDrawMode The draw mode for the Babylon material
-     * @returns The Babylon material or null if not handled
-     */
-    createMaterial?(context: string, material: IMaterial, babylonDrawMode: number): Nullable<Material>;
+        /**
+         * Define this method to modify the default behavior when creating materials.
+         * @param context The context when loading the asset
+         * @param material The glTF material property
+         * @param babylonDrawMode The draw mode for the Babylon material
+         * @returns The Babylon material or null if not handled
+         */
+        createMaterial?(context: string, material: Loader.IMaterial, babylonDrawMode: number): Nullable<Material>;
 
-    /**
-     * Define this method to modify the default behavior when loading material properties.
-     * @param context The context when loading the asset
-     * @param material The glTF material property
-     * @param babylonMaterial The Babylon material
-     * @returns A promise that resolves when the load is complete or null if not handled
-     */
-    loadMaterialPropertiesAsync?(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
+        /**
+         * Define this method to modify the default behavior when loading material properties.
+         * @param context The context when loading the asset
+         * @param material The glTF material property
+         * @param babylonMaterial The Babylon material
+         * @returns A promise that resolves when the load is complete or null if not handled
+         */
+        loadMaterialPropertiesAsync?(context: string, material: Loader.IMaterial, babylonMaterial: Material): Nullable<Promise<void>>;
 
-    /**
-     * Define this method to modify the default behavior when loading texture infos.
-     * @param context The context when loading the asset
-     * @param textureInfo The glTF texture info property
-     * @param assign A function called synchronously after parsing the glTF properties
-     * @returns A promise that resolves with the loaded Babylon texture when the load is complete or null if not handled
-     */
-    loadTextureInfoAsync?(context: string, textureInfo: ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>>;
+        /**
+         * Define this method to modify the default behavior when loading texture infos.
+         * @param context The context when loading the asset
+         * @param textureInfo The glTF texture info property
+         * @param assign A function called synchronously after parsing the glTF properties
+         * @returns A promise that resolves with the loaded Babylon texture when the load is complete or null if not handled
+         */
+        loadTextureInfoAsync?(context: string, textureInfo: Loader.ITextureInfo, assign: (babylonTexture: BaseTexture) => void): Nullable<Promise<BaseTexture>>;
 
-    /**
-     * Define this method to modify the default behavior when loading animations.
-     * @param context The context when loading the asset
-     * @param animation The glTF animation property
-     * @returns A promise that resolves with the loaded Babylon animation group when the load is complete or null if not handled
-     */
-    loadAnimationAsync?(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
+        /**
+         * Define this method to modify the default behavior when loading animations.
+         * @param context The context when loading the asset
+         * @param animation The glTF animation property
+         * @returns A promise that resolves with the loaded Babylon animation group when the load is complete or null if not handled
+         */
+        loadAnimationAsync?(context: string, animation: Loader.IAnimation): Nullable<Promise<AnimationGroup>>;
 
-    /**
-     * Define this method to modify the default behavior when loading uris.
-     * @param context The context when loading the asset
-     * @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>>;
+        /**
+         * Define this method to modify the default behavior when loading uris.
+         * @param context The context when loading the asset
+         * @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>>;
+    }
+}
+
+/**
+ * Defines the module for the built-in glTF 2.0 loader extensions.
+ */
+module BABYLON.GLTF2.Loader.Extensions {
 }

+ 208 - 205
loaders/src/glTF/2.0/glTFLoaderInterfaces.ts

@@ -1,229 +1,232 @@
-import { VertexBuffer, Buffer, AnimationGroup, Material, AbstractMesh, Mesh, Bone, Skeleton } from "babylonjs";
-import * as IGLTF2 from "babylonjs-gltf2interface";
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
-/** @hidden */
-export var __IGLTFLoaderInterfacesV2 = 0; // I am here to allow dts to be created
+module BABYLON.GLTF2.Loader {
+    /**
+     * Loader interface with an index field.
+     */
+    export interface IArrayItem {
+        /**
+         * The index of this item in the array.
+         */
+        index: number;
+    }
 
-/**
- * Loader interface with an index field.
- */
-export interface IArrayItem {
     /**
-     * The index of this item in the array.
+     * Loader interface with additional members.
      */
-    index: number;
-}
+    export interface IAccessor extends GLTF2.IAccessor, IArrayItem {
+        /** @hidden */
+        _data?: Promise<ArrayBufferView>;
 
-/**
- * Loader interface with additional members.
- */
-export interface IAccessor extends IGLTF2.IAccessor, IArrayItem {
-    /** @hidden */
-    _data?: Promise<ArrayBufferView>;
+        /** @hidden */
+        _babylonVertexBuffer?: Promise<VertexBuffer>;
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IAnimationChannel extends GLTF2.IAnimationChannel, IArrayItem {
+    }
 
     /** @hidden */
-    _babylonVertexBuffer?: Promise<VertexBuffer>;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IAnimationChannel extends IGLTF2.IAnimationChannel, IArrayItem {
-}
-
-/** @hidden */
-export interface _IAnimationSamplerData {
-    input: Float32Array;
-    interpolation: IGLTF2.AnimationSamplerInterpolation;
-    output: Float32Array;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IAnimationSampler extends IGLTF2.IAnimationSampler, IArrayItem {
-    /** @hidden */
-    _data?: Promise<_IAnimationSamplerData>;
-}
+    export interface _IAnimationSamplerData {
+        input: Float32Array;
+        interpolation: AnimationSamplerInterpolation;
+        output: Float32Array;
+    }
 
-/**
- * Loader interface with additional members.
- */
-export interface IAnimation extends IGLTF2.IAnimation, IArrayItem {
-    channels: IAnimationChannel[];
-    samplers: IAnimationSampler[];
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IAnimationSampler extends GLTF2.IAnimationSampler, IArrayItem {
+        /** @hidden */
+        _data?: Promise<_IAnimationSamplerData>;
+    }
 
-    /** @hidden */
-    _babylonAnimationGroup?: AnimationGroup;
-}
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IAnimation extends GLTF2.IAnimation, IArrayItem {
+        channels: IAnimationChannel[];
+        samplers: IAnimationSampler[];
 
-/**
- * Loader interface with additional members.
- */
-export interface IBuffer extends IGLTF2.IBuffer, IArrayItem {
-    /** @hidden */
-    _data?: Promise<ArrayBufferView>;
-}
+        /** @hidden */
+        _babylonAnimationGroup?: AnimationGroup;
+    }
 
-/**
- * Loader interface with additional members.
- */
-export interface IBufferView extends IGLTF2.IBufferView, IArrayItem {
-    /** @hidden */
-    _data?: Promise<ArrayBufferView>;
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IBuffer extends GLTF2.IBuffer, IArrayItem {
+        /** @hidden */
+        _data?: Promise<ArrayBufferView>;
+    }
 
-    /** @hidden */
-    _babylonBuffer?: Promise<Buffer>;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface ICamera extends IGLTF2.ICamera, IArrayItem {
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IImage extends IGLTF2.IImage, IArrayItem {
-    /** @hidden */
-    _data?: Promise<ArrayBufferView>;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IMaterialNormalTextureInfo extends IGLTF2.IMaterialNormalTextureInfo, IGLTF2.ITextureInfo {
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IMaterialOcclusionTextureInfo extends IGLTF2.IMaterialOcclusionTextureInfo, IGLTF2.ITextureInfo {
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IMaterialPbrMetallicRoughness extends IGLTF2.IMaterialPbrMetallicRoughness {
-    baseColorTexture?: ITextureInfo;
-    metallicRoughnessTexture?: ITextureInfo;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IMaterial extends IGLTF2.IMaterial, IArrayItem {
-    pbrMetallicRoughness?: IMaterialPbrMetallicRoughness;
-    normalTexture?: IMaterialNormalTextureInfo;
-    occlusionTexture?: IMaterialOcclusionTextureInfo;
-    emissiveTexture?: ITextureInfo;
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IBufferView extends GLTF2.IBufferView, IArrayItem {
+        /** @hidden */
+        _data?: Promise<ArrayBufferView>;
 
-    /** @hidden */
-    _babylonData?: {
-        [drawMode: number]: {
-            material: Material;
-            meshes: AbstractMesh[];
-            promise: Promise<void>;
-        }
-    };
-}
+        /** @hidden */
+        _babylonBuffer?: Promise<Buffer>;
+    }
 
-/**
- * Loader interface with additional members.
- */
-export interface IMesh extends IGLTF2.IMesh, IArrayItem {
-    primitives: IMeshPrimitive[];
-}
+    /**
+     * Loader interface with additional members.
+     */
+    export interface ICamera extends GLTF2.ICamera, IArrayItem {
+    }
 
-/**
- * Loader interface with additional members.
- */
-export interface IMeshPrimitive extends IGLTF2.IMeshPrimitive, IArrayItem {
-}
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IImage extends GLTF2.IImage, IArrayItem {
+        /** @hidden */
+        _data?: Promise<ArrayBufferView>;
+    }
 
-/**
- * Loader interface with additional members.
- */
-export interface INode extends IGLTF2.INode, IArrayItem {
     /**
-     * The parent glTF node.
+     * Loader interface with additional members.
      */
-    parent?: INode;
+    export interface IMaterialNormalTextureInfo extends GLTF2.IMaterialNormalTextureInfo, ITextureInfo {
+    }
 
-    /** @hidden */
-    _babylonMesh?: Mesh;
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IMaterialOcclusionTextureInfo extends GLTF2.IMaterialOcclusionTextureInfo, ITextureInfo {
+    }
 
-    /** @hidden */
-    _primitiveBabylonMeshes?: Mesh[];
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IMaterialPbrMetallicRoughness extends GLTF2.IMaterialPbrMetallicRoughness {
+        baseColorTexture?: ITextureInfo;
+        metallicRoughnessTexture?: ITextureInfo;
+    }
 
-    /** @hidden */
-    _babylonBones?: Bone[];
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IMaterial extends GLTF2.IMaterial, IArrayItem {
+        pbrMetallicRoughness?: IMaterialPbrMetallicRoughness;
+        normalTexture?: IMaterialNormalTextureInfo;
+        occlusionTexture?: IMaterialOcclusionTextureInfo;
+        emissiveTexture?: ITextureInfo;
+
+        /** @hidden */
+        _data?: {
+            [babylonDrawMode: number]: {
+                babylonMaterial: Material;
+                babylonMeshes: AbstractMesh[];
+                promise: Promise<void>;
+            }
+        };
+    }
 
-    /** @hidden */
-    _numMorphTargets?: number;
-}
-
-/** @hidden */
-export interface _ISamplerData {
-    noMipMaps: boolean;
-    samplingMode: number;
-    wrapU: number;
-    wrapV: number;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface ISampler extends IGLTF2.ISampler, IArrayItem {
-    /** @hidden */
-    _data?: _ISamplerData;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IScene extends IGLTF2.IScene, IArrayItem {
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface ISkin extends IGLTF2.ISkin, IArrayItem {
-    /** @hidden */
-    _babylonSkeleton?: Skeleton;
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IMesh extends GLTF2.IMesh, IArrayItem {
+        primitives: IMeshPrimitive[];
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
+        /** @hidden */
+        _instanceData?: {
+            babylonSourceMesh: Mesh;
+            promise: Promise<any>;
+        };
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface INode extends GLTF2.INode, IArrayItem {
+        /**
+         * The parent glTF node.
+         */
+        parent?: INode;
+
+        /** @hidden */
+        _babylonTransformNode?: TransformNode;
+
+        /** @hidden */
+        _primitiveBabylonMeshes?: AbstractMesh[];
+
+        /** @hidden */
+        _babylonBones?: Bone[];
+
+        /** @hidden */
+        _numMorphTargets?: number;
+    }
 
     /** @hidden */
-    _promise?: Promise<void>;
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface ITexture extends IGLTF2.ITexture, IArrayItem {
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface ITextureInfo extends IGLTF2.ITextureInfo {
-}
-
-/**
- * Loader interface with additional members.
- */
-export interface IGLTF extends IGLTF2.IGLTF {
-    accessors?: IAccessor[];
-    animations?: IAnimation[];
-    buffers?: IBuffer[];
-    bufferViews?: IBufferView[];
-    cameras?: ICamera[];
-    images?: IImage[];
-    materials?: IMaterial[];
-    meshes?: IMesh[];
-    nodes?: INode[];
-    samplers?: ISampler[];
-    scenes?: IScene[];
-    skins?: ISkin[];
-    textures?: ITexture[];
-}
+    export interface _ISamplerData {
+        noMipMaps: boolean;
+        samplingMode: number;
+        wrapU: number;
+        wrapV: number;
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface ISampler extends GLTF2.ISampler, IArrayItem {
+        /** @hidden */
+        _data?: _ISamplerData;
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IScene extends GLTF2.IScene, IArrayItem {
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface ISkin extends GLTF2.ISkin, IArrayItem {
+        /** @hidden */
+        _data?: {
+            babylonSkeleton: Skeleton;
+            promise: Promise<void>;
+        };
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface ITexture extends GLTF2.ITexture, IArrayItem {
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface ITextureInfo extends GLTF2.ITextureInfo {
+    }
+
+    /**
+     * Loader interface with additional members.
+     */
+    export interface IGLTF extends GLTF2.IGLTF {
+        accessors?: IAccessor[];
+        animations?: IAnimation[];
+        buffers?: IBuffer[];
+        bufferViews?: IBufferView[];
+        cameras?: ICamera[];
+        images?: IImage[];
+        materials?: IMaterial[];
+        meshes?: IMesh[];
+        nodes?: INode[];
+        samplers?: ISampler[];
+        scenes?: IScene[];
+        skins?: ISkin[];
+        textures?: ITexture[];
+    }
+}

+ 23 - 23
src/Audio/sound.ts

@@ -105,7 +105,7 @@ import { SceneComponentConstants } from "sceneComponent";
         private _coneOuterAngle: number = 360;
         private _coneOuterGain: number = 0;
         private _scene: Scene;
-        private _connectedMesh: Nullable<AbstractMesh>;
+        private _connectedTransformNode: Nullable<TransformNode>;
         private _customAttenuationFunction: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number;
         private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _isOutputConnected = false;
@@ -342,9 +342,9 @@ import { SceneComponentConstants } from "sceneComponent";
                     this._streamingSource.disconnect();
                 }
 
-                if (this._connectedMesh && this._registerFunc) {
-                    this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
-                    this._connectedMesh = null;
+                if (this._connectedTransformNode && this._registerFunc) {
+                    this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc);
+                    this._connectedTransformNode = null;
                 }
             }
         }
@@ -568,17 +568,17 @@ import { SceneComponentConstants } from "sceneComponent";
         public setLocalDirectionToMesh(newLocalDirection: Vector3): void {
             this._localDirection = newLocalDirection;
 
-            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.isPlaying) {
+            if (Engine.audioEngine.canUseWebAudio && this._connectedTransformNode && this.isPlaying) {
                 this._updateDirection();
             }
         }
 
         private _updateDirection() {
-            if (!this._connectedMesh || !this._soundPanner) {
+            if (!this._connectedTransformNode || !this._soundPanner) {
                 return;
             }
 
-            var mat = this._connectedMesh.getWorldMatrix();
+            var mat = this._connectedTransformNode.getWorldMatrix();
             var direction = Vector3.TransformNormal(this._localDirection, mat);
             direction.normalize();
             this._soundPanner.setOrientation(direction.x, direction.y, direction.z);
@@ -586,8 +586,8 @@ import { SceneComponentConstants } from "sceneComponent";
 
         /** @hidden */
         public updateDistanceFromListener() {
-            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.useCustomAttenuation && this._soundGain && this._scene.activeCamera) {
-                var distance = this._connectedMesh.getDistanceToCamera(this._scene.activeCamera);
+            if (Engine.audioEngine.canUseWebAudio && this._connectedTransformNode && this.useCustomAttenuation && this._soundGain && this._scene.activeCamera) {
+                var distance = this._connectedTransformNode.getDistanceToCamera(this._scene.activeCamera);
                 this._soundGain.gain.value = this._customAttenuationFunction(this._volume, distance, this.maxDistance, this.refDistance, this.rolloffFactor);
             }
         }
@@ -623,7 +623,7 @@ import { SceneComponentConstants } from "sceneComponent";
                                 this._soundPanner.coneInnerAngle = this._coneInnerAngle;
                                 this._soundPanner.coneOuterAngle = this._coneOuterAngle;
                                 this._soundPanner.coneOuterGain = this._coneOuterGain;
-                                if (this._connectedMesh) {
+                                if (this._connectedTransformNode) {
                                     this._updateDirection();
                                 }
                                 else {
@@ -790,15 +790,15 @@ import { SceneComponentConstants } from "sceneComponent";
 
         /**
          * Attach the sound to a dedicated mesh
-         * @param meshToConnectTo The mesh to connect the sound with
+         * @param transformNode The transform node to connect the sound with
          * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#attaching-a-sound-to-a-mesh
          */
-        public attachToMesh(meshToConnectTo: AbstractMesh): void {
-            if (this._connectedMesh && this._registerFunc) {
-                this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
+        public attachToMesh(transformNode: TransformNode): void {
+            if (this._connectedTransformNode && this._registerFunc) {
+                this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                 this._registerFunc = null;
             }
-            this._connectedMesh = meshToConnectTo;
+            this._connectedTransformNode = transformNode;
             if (!this.spatialSound) {
                 this.spatialSound = true;
                 this._createSpatialParameters();
@@ -807,9 +807,9 @@ import { SceneComponentConstants } from "sceneComponent";
                     this.play();
                 }
             }
-            this._onRegisterAfterWorldMatrixUpdate(this._connectedMesh);
-            this._registerFunc = (connectedMesh: TransformNode) => this._onRegisterAfterWorldMatrixUpdate(connectedMesh);
-            meshToConnectTo.registerAfterWorldMatrixUpdate(this._registerFunc);
+            this._onRegisterAfterWorldMatrixUpdate(this._connectedTransformNode);
+            this._registerFunc = (transformNode: TransformNode) => this._onRegisterAfterWorldMatrixUpdate(transformNode);
+            this._connectedTransformNode.registerAfterWorldMatrixUpdate(this._registerFunc);
         }
 
         /**
@@ -817,10 +817,10 @@ import { SceneComponentConstants } from "sceneComponent";
          * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#attaching-a-sound-to-a-mesh
          */
         public detachFromMesh() {
-            if (this._connectedMesh && this._registerFunc) {
-                this._connectedMesh.unregisterAfterWorldMatrixUpdate(this._registerFunc);
+            if (this._connectedTransformNode && this._registerFunc) {
+                this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc);
                 this._registerFunc = null;
-                this._connectedMesh = null;
+                this._connectedTransformNode = null;
             }
         }
 
@@ -913,8 +913,8 @@ import { SceneComponentConstants } from "sceneComponent";
             };
 
             if (this.spatialSound) {
-                if (this._connectedMesh) {
-                    serializationObject.connectedMeshId = this._connectedMesh.id;
+                if (this._connectedTransformNode) {
+                    serializationObject.connectedMeshId = this._connectedTransformNode.id;
                 }
 
                 serializationObject.position = this._position.asArray();

+ 1 - 1
src/Bones/bone.ts

@@ -80,7 +80,7 @@ import { Node } from "Node";
              */
             public name: string, skeleton: Skeleton, parentBone: Nullable<Bone> = null, localMatrix: Nullable<Matrix> = null,
             restPose: Nullable<Matrix> = null, baseMatrix: Nullable<Matrix> = null, index: Nullable<number> = null) {
-            super(name, skeleton.getScene());
+            super(name, skeleton.getScene(), false);
             this._skeleton = skeleton;
             this._localMatrix = localMatrix ? localMatrix.clone() : Matrix.Identity();
             this._restPose = restPose ? restPose : this._localMatrix.clone();

+ 88 - 25
src/Mesh/abstractMesh.ts

@@ -1054,6 +1054,94 @@ import { SolidParticle } from "Particles";
             };
         }
 
+        /**
+         * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
+         * This means the mesh underlying bounding box and sphere are recomputed.
+         * @param applySkeleton defines whether to apply the skeleton before computing the bounding info
+         * @returns the current mesh
+         */
+        public refreshBoundingInfo(applySkeleton: boolean = false): AbstractMesh {
+            if (this._boundingInfo && this._boundingInfo.isLocked) {
+                return this;
+            }
+
+            this._refreshBoundingInfo(this._getPositionData(applySkeleton), null);
+            return this;
+        }
+
+        /** @hidden */
+        public _refreshBoundingInfo(data: Nullable<FloatArray>, bias: Nullable<Vector2>): void {
+            if (data) {
+                var extend = Tools.ExtractMinAndMax(data, 0, this.getTotalVertices(), bias);
+                if (this._boundingInfo) {
+                    this._boundingInfo.reConstruct(extend.minimum, extend.maximum);
+                }
+                else {
+                    this._boundingInfo = new BoundingInfo(extend.minimum, extend.maximum);
+                }
+            }
+
+            if (this.subMeshes) {
+                for (var index = 0; index < this.subMeshes.length; index++) {
+                    this.subMeshes[index].refreshBoundingInfo();
+                }
+            }
+
+            this._updateBoundingInfo();
+        }
+
+        /** @hidden */
+        public _getPositionData(applySkeleton: boolean): Nullable<FloatArray> {
+            var data = this.getVerticesData(VertexBuffer.PositionKind);
+
+            if (data && applySkeleton && this.skeleton) {
+                data = Tools.Slice(data);
+
+                var matricesIndicesData = this.getVerticesData(VertexBuffer.MatricesIndicesKind);
+                var matricesWeightsData = this.getVerticesData(VertexBuffer.MatricesWeightsKind);
+                if (matricesWeightsData && matricesIndicesData) {
+                    var needExtras = this.numBoneInfluencers > 4;
+                    var matricesIndicesExtraData = needExtras ? this.getVerticesData(VertexBuffer.MatricesIndicesExtraKind) : null;
+                    var matricesWeightsExtraData = needExtras ? this.getVerticesData(VertexBuffer.MatricesWeightsExtraKind) : null;
+
+                    var skeletonMatrices = this.skeleton.getTransformMatrices(this);
+
+                    var tempVector = Tmp.Vector3[0];
+                    var finalMatrix = Tmp.Matrix[0];
+                    var tempMatrix = Tmp.Matrix[1];
+
+                    var matWeightIdx = 0;
+                    for (var index = 0; index < data.length; index += 3, matWeightIdx += 4) {
+                        finalMatrix.reset();
+
+                        var inf: number;
+                        var weight: number;
+                        for (inf = 0; inf < 4; inf++) {
+                            weight = matricesWeightsData[matWeightIdx + inf];
+                            if (weight > 0) {
+                                Matrix.FromFloat32ArrayToRefScaled(skeletonMatrices, Math.floor(matricesIndicesData[matWeightIdx + inf] * 16), weight, tempMatrix);
+                                finalMatrix.addToSelf(tempMatrix);
+                            }
+                        }
+                        if (needExtras) {
+                            for (inf = 0; inf < 4; inf++) {
+                                weight = matricesWeightsExtraData![matWeightIdx + inf];
+                                if (weight > 0) {
+                                    Matrix.FromFloat32ArrayToRefScaled(skeletonMatrices, Math.floor(matricesIndicesExtraData![matWeightIdx + inf] * 16), weight, tempMatrix);
+                                    finalMatrix.addToSelf(tempMatrix);
+                                }
+                            }
+                        }
+
+                        Vector3.TransformCoordinatesFromFloatsToRef(data[index], data[index + 1], data[index + 2], finalMatrix, tempVector);
+                        tempVector.toArray(data, index);
+                    }
+                }
+            }
+
+            return data;
+        }
+
         /** @hidden */
         public _updateBoundingInfo(): AbstractMesh {
             if (this._boundingInfo) {
@@ -1147,31 +1235,6 @@ import { SolidParticle } from "Particles";
             return this._boundingInfo.intersectsPoint(point);
         }
 
-        /**
-         * Gets the position of the current mesh in camera space
-         * @param camera defines the camera to use
-         * @returns a position
-         */
-        public getPositionInCameraSpace(camera: Nullable<Camera> = null): Vector3 {
-            if (!camera) {
-                camera = (<Camera>this.getScene().activeCamera);
-            }
-
-            return Vector3.TransformCoordinates(this.absolutePosition, camera.getViewMatrix());
-        }
-
-        /**
-         * Returns the distance from the mesh to the active camera
-         * @param camera defines the camera to use
-         * @returns the distance
-         */
-        public getDistanceToCamera(camera: Nullable<Camera> = null): number {
-            if (!camera) {
-                camera = (<Camera>this.getScene().activeCamera);
-            }
-            return this.absolutePosition.subtract(camera.position).length();
-        }
-
         // Collisions
 
         /**

+ 3 - 0
src/Mesh/geometry.ts

@@ -710,6 +710,9 @@ import { BoundingInfo } from "index";
 
             // morphTargets
             mesh._syncGeometryWithMorphTargetManager();
+
+            // instances
+            mesh.synchronizeInstances();
         }
 
         private notifyUpdate(kind?: string) {

+ 9 - 11
src/Mesh/instancedMesh.ts

@@ -221,20 +221,18 @@ import { BoundingInfo } from "Culling";
         }
 
         /**
-         * reconstructs and updates the BoundingInfo of the mesh.
-         * @returns the mesh.
+         * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
+         * This means the mesh underlying bounding box and sphere are recomputed.
+         * @param applySkeleton defines whether to apply the skeleton before computing the bounding info
+         * @returns the current mesh
          */
-        public refreshBoundingInfo(): InstancedMesh {
-            var meshBB = this._sourceMesh.getBoundingInfo();
-
-            if (this._boundingInfo) {
-                this._boundingInfo.reConstruct(meshBB.minimum, meshBB.maximum);
-            }
-            else {
-                this._boundingInfo = new BoundingInfo(meshBB.minimum, meshBB.maximum);
+        public refreshBoundingInfo(applySkeleton: boolean = false): InstancedMesh {
+            if (this._boundingInfo && this._boundingInfo.isLocked) {
+                return this;
             }
 
-            this._updateBoundingInfo();
+            const bias = this._sourceMesh.geometry ? this._sourceMesh.geometry.boundingBias : null;
+            this._refreshBoundingInfo(this._sourceMesh._getPositionData(applySkeleton), bias);
             return this;
         }
 

+ 4 - 76
src/Mesh/mesh.ts

@@ -901,91 +901,19 @@ import { PhysicsImpostor } from "Physics";
         /**
          * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
          * This means the mesh underlying bounding box and sphere are recomputed.
+         * @param applySkeleton defines whether to apply the skeleton before computing the bounding info
          * @returns the current mesh
          */
-        public refreshBoundingInfo(): Mesh {
-            return this._refreshBoundingInfo(false);
-        }
-
-        /** @hidden */
-        public _refreshBoundingInfo(applySkeleton: boolean): Mesh {
+        public refreshBoundingInfo(applySkeleton: boolean = false): Mesh {
             if (this._boundingInfo && this._boundingInfo.isLocked) {
                 return this;
             }
 
-            var data = this._getPositionData(applySkeleton);
-            if (data) {
-                const bias = this.geometry ? this.geometry.boundingBias : null;
-                var extend = Tools.ExtractMinAndMax(data, 0, this.getTotalVertices(), bias);
-                if (this._boundingInfo) {
-                    this._boundingInfo.reConstruct(extend.minimum, extend.maximum);
-                }
-                else {
-                    this._boundingInfo = new BoundingInfo(extend.minimum, extend.maximum);
-                }
-            }
-
-            if (this.subMeshes) {
-                for (var index = 0; index < this.subMeshes.length; index++) {
-                    this.subMeshes[index].refreshBoundingInfo();
-                }
-            }
-
-            this._updateBoundingInfo();
+            const bias = this.geometry ? this.geometry.boundingBias : null;
+            this._refreshBoundingInfo(this._getPositionData(applySkeleton), bias);
             return this;
         }
 
-        private _getPositionData(applySkeleton: boolean): Nullable<FloatArray> {
-            var data = this.getVerticesData(VertexBuffer.PositionKind);
-
-            if (data && applySkeleton && this.skeleton) {
-                data = Tools.Slice(data);
-
-                var matricesIndicesData = this.getVerticesData(VertexBuffer.MatricesIndicesKind);
-                var matricesWeightsData = this.getVerticesData(VertexBuffer.MatricesWeightsKind);
-                if (matricesWeightsData && matricesIndicesData) {
-                    var needExtras = this.numBoneInfluencers > 4;
-                    var matricesIndicesExtraData = needExtras ? this.getVerticesData(VertexBuffer.MatricesIndicesExtraKind) : null;
-                    var matricesWeightsExtraData = needExtras ? this.getVerticesData(VertexBuffer.MatricesWeightsExtraKind) : null;
-
-                    var skeletonMatrices = this.skeleton.getTransformMatrices(this);
-
-                    var tempVector = Tmp.Vector3[0];
-                    var finalMatrix = Tmp.Matrix[0];
-                    var tempMatrix = Tmp.Matrix[1];
-
-                    var matWeightIdx = 0;
-                    for (var index = 0; index < data.length; index += 3, matWeightIdx += 4) {
-                        finalMatrix.reset();
-
-                        var inf: number;
-                        var weight: number;
-                        for (inf = 0; inf < 4; inf++) {
-                            weight = matricesWeightsData[matWeightIdx + inf];
-                            if (weight > 0) {
-                                Matrix.FromFloat32ArrayToRefScaled(skeletonMatrices, Math.floor(matricesIndicesData[matWeightIdx + inf] * 16), weight, tempMatrix);
-                                finalMatrix.addToSelf(tempMatrix);
-                            }
-                        }
-                        if (needExtras) {
-                            for (inf = 0; inf < 4; inf++) {
-                                weight = matricesWeightsExtraData![matWeightIdx + inf];
-                                if (weight > 0) {
-                                    Matrix.FromFloat32ArrayToRefScaled(skeletonMatrices, Math.floor(matricesIndicesExtraData![matWeightIdx + inf] * 16), weight, tempMatrix);
-                                    finalMatrix.addToSelf(tempMatrix);
-                                }
-                            }
-                        }
-
-                        Vector3.TransformCoordinatesFromFloatsToRef(data[index], data[index + 1], data[index + 2], finalMatrix, tempVector);
-                        tempVector.toArray(data, index);
-                    }
-                }
-            }
-
-            return data;
-        }
-
         /** @hidden */
         public _createGlobalSubMesh(force: boolean): Nullable<SubMesh> {
             var totalVertices = this.getTotalVertices();

+ 25 - 0
src/Mesh/transformNode.ts

@@ -999,6 +999,31 @@ import { Bone } from "Bones";
         }
 
         /**
+         * Gets the position of the current mesh in camera space
+         * @param camera defines the camera to use
+         * @returns a position
+         */
+        public getPositionInCameraSpace(camera: Nullable<Camera> = null): Vector3 {
+            if (!camera) {
+                camera = (<Camera>this.getScene().activeCamera);
+            }
+
+            return Vector3.TransformCoordinates(this.absolutePosition, camera.getViewMatrix());
+        }
+
+        /**
+         * Returns the distance from the mesh to the active camera
+         * @param camera defines the camera to use
+         * @returns the distance
+         */
+        public getDistanceToCamera(camera: Nullable<Camera> = null): number {
+            if (!camera) {
+                camera = (<Camera>this.getScene().activeCamera);
+            }
+            return this.absolutePosition.subtract(camera.position).length();
+        }
+
+        /**
          * Clone the current transform node
          * @param name Name of the new clone
          * @param newParent New parent for the clone

+ 11 - 1
src/Tools/tools.ts

@@ -1522,6 +1522,16 @@ import { FloatArray, IndicesArray, Nullable } from "types";
                 scene.activeCamera = camera;
             }
 
+            var renderCanvas = engine.getRenderingCanvas();
+            if (!renderCanvas) {
+                Tools.Error("No rendering canvas found !");
+                return;
+            }
+
+            var originalSize = {width: renderCanvas.width, height: renderCanvas.height};
+            engine.setSize(width, height);
+            scene.render();
+
             // At this point size can be a number, or an object (according to engine.prototype.createRenderTargetTexture method)
             var texture = new RenderTargetTexture("screenShot", size, scene, false, false, Engine.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE);
             texture.renderList = null;
@@ -1541,7 +1551,7 @@ import { FloatArray, IndicesArray, Nullable } from "types";
             if (previousCamera) {
                 scene.activeCamera = previousCamera;
             }
-
+            engine.setSize(originalSize.width, originalSize.height);
             camera.getProjectionMatrix(true); // Force cache refresh;
         }
 

+ 6 - 3
src/node.ts

@@ -16,7 +16,7 @@ import { Observable, Observer, serialize } from "Tools";
      * Node is the basic class for all scene objects (Mesh, Light, Camera.)
      */
     export class Node implements IBehaviorAware<Node> {
-        private static _NodeConstructors: {[key: string]: any} = {};
+        private static _NodeConstructors: { [key: string]: any } = {};
 
         /**
          * Add a new node constructor
@@ -235,15 +235,18 @@ import { Observable, Observer, serialize } from "Tools";
          * Creates a new Node
          * @param name the name and id to be given to this node
          * @param scene the scene this node will be added to
+         * @param addToRootNodes the node will be added to scene.rootNodes
          */
-        constructor(name: string, scene: Nullable<Scene> = null) {
+        constructor(name: string, scene: Nullable<Scene> = null, addToRootNodes = true) {
             this.name = name;
             this.id = name;
             this._scene = <Scene>(scene || Engine.LastCreatedScene);
             this.uniqueId = this._scene.getUniqueId();
             this._initCache();
 
-            this.addToSceneRootNodes();
+            if (addToRootNodes) {
+                this.addToSceneRootNodes();
+            }
         }
 
         /**

+ 24 - 14
src/scene.ts

@@ -3800,27 +3800,32 @@ import { Node } from "node";
          * @return the found node or null if not found at all
          */
         public getNodeByID(id: string): Nullable<Node> {
-            var mesh = this.getMeshByID(id);
-
+            const mesh = this.getMeshByID(id);
             if (mesh) {
                 return mesh;
             }
 
-            var light = this.getLightByID(id);
+            const transformNode = this.getTransformNodeByID(id);
+            if (transformNode) {
+                return transformNode;
+            }
 
+            const light = this.getLightByID(id);
             if (light) {
                 return light;
             }
 
-            var camera = this.getCameraByID(id);
-
+            const camera = this.getCameraByID(id);
             if (camera) {
                 return camera;
             }
 
-            var bone = this.getBoneByID(id);
+            const bone = this.getBoneByID(id);
+            if (bone) {
+                return bone;
+            }
 
-            return bone;
+            return null;
         }
 
         /**
@@ -3829,27 +3834,32 @@ import { Node } from "node";
          * @return the found node or null if not found at all.
          */
         public getNodeByName(name: string): Nullable<Node> {
-            var mesh = this.getMeshByName(name);
-
+            const mesh = this.getMeshByName(name);
             if (mesh) {
                 return mesh;
             }
 
-            var light = this.getLightByName(name);
+            const transformNode = this.getTransformNodeByName(name);
+            if (transformNode) {
+                return transformNode;
+            }
 
+            const light = this.getLightByName(name);
             if (light) {
                 return light;
             }
 
-            var camera = this.getCameraByName(name);
-
+            const camera = this.getCameraByName(name);
             if (camera) {
                 return camera;
             }
 
-            var bone = this.getBoneByName(name);
+            const bone = this.getBoneByName(name);
+            if (bone) {
+                return bone;
+            }
 
-            return bone;
+            return null;
         }
 
         /**

+ 10 - 11
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -416,19 +416,15 @@ describe('Babylon Scene Loader', function() {
 
         it('Load MultiPrimitive', () => {
             const scene = new BABYLON.Scene(subject);
-            return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then((result) => {
-                expect(result.meshes, "meshes").to.have.lengthOf(4);
+            return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then(result => {
+                expect(result.meshes, "meshes").to.have.lengthOf(3);
 
-                const node = scene.getMeshByName("node");
+                const node = scene.getNodeByName("node");
                 expect(node, "node").to.exist;
-                expect(node, "node").to.be.an.instanceof(BABYLON.Mesh);
+                expect(node, "node").to.be.an.instanceof(BABYLON.TransformNode);
 
-                const mesh = node as BABYLON.Mesh;
-                expect(mesh.geometry).to.not.exist;
-                expect(mesh.material).to.not.exist;
-
-                expect(mesh.getChildren(), "mesh children").to.have.lengthOf(2);
-                for (const childNode of mesh.getChildren()) {
+                expect(node.getChildren(), "node children").to.have.lengthOf(2);
+                for (const childNode of node.getChildren()) {
                     expect(childNode, "child node").to.be.an.instanceof(BABYLON.Mesh);
                     const childMesh = childNode as BABYLON.Mesh;
                     expect(childMesh.geometry).to.exist;
@@ -442,7 +438,10 @@ describe('Babylon Scene Loader', function() {
             return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/BrainStem/", "BrainStem.gltf", scene).then((result) => {
                 expect(result.skeletons, "skeletons").to.have.lengthOf(1);
 
-                const node1 = scene.getMeshByName("node1");
+                const node1 = scene.getNodeByName("node1");
+                expect(node1, "node1").to.exist;
+                expect(node1, "node1").to.be.an.instanceof(BABYLON.TransformNode);
+
                 for (const childMesh of node1.getChildMeshes()) {
                     expect(childMesh.skeleton, "mesh skeleton").to.exist;
                     expect(childMesh.skeleton.name, "mesh skeleton name").to.equal(result.skeletons[0].name);