Browse Source

Merge branch 'master' into clipboardEvents

ssaket 6 years ago
parent
commit
f0686deee0
92 changed files with 20345 additions and 30632 deletions
  1. 7231 7046
      Playground/babylon.d.txt
  2. 8 8
      Playground/js/index.js
  3. BIN
      Playground/scenes/miniBar2.glb
  4. BIN
      Playground/scenes/pumpkinBucketCarved.glb
  5. 2 1
      Tools/Gulp/gulpfile.js
  6. 1 0
      Tools/Gulp/package.json
  7. 1 12317
      dist/preview release/Oimo.js
  8. 8835 8654
      dist/preview release/babylon.d.ts
  9. 1 1
      dist/preview release/babylon.js
  10. 594 262
      dist/preview release/babylon.max.js
  11. 594 262
      dist/preview release/babylon.no-module.max.js
  12. 1 1
      dist/preview release/babylon.worker.js
  13. 596 264
      dist/preview release/es6.js
  14. 1 1
      dist/preview release/glTF2Interface/package.json
  15. 5 1
      dist/preview release/gui/babylon.gui.d.ts
  16. 1 1
      dist/preview release/gui/babylon.gui.js
  17. 1 1
      dist/preview release/gui/babylon.gui.min.js
  18. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  19. 10 2
      dist/preview release/gui/babylon.gui.module.d.ts
  20. 2 2
      dist/preview release/gui/package.json
  21. 5 5
      dist/preview release/inspector/package.json
  22. 21 15
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  23. 178 139
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  24. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  25. 21 15
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  26. 178 139
      dist/preview release/loaders/babylon.glTFFileLoader.js
  27. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  28. 21 15
      dist/preview release/loaders/babylonjs.loaders.d.ts
  29. 178 139
      dist/preview release/loaders/babylonjs.loaders.js
  30. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  31. 21 15
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  32. 3 3
      dist/preview release/loaders/package.json
  33. 2 2
      dist/preview release/materialsLibrary/package.json
  34. 2 2
      dist/preview release/postProcessesLibrary/package.json
  35. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  36. 3 3
      dist/preview release/serializers/package.json
  37. 1 1
      dist/preview release/viewer/babylon.viewer.js
  38. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  39. 22 3
      dist/preview release/what's new.md
  40. 1 2
      gui/src/2D/advancedDynamicTexture.ts
  41. 14 3
      gui/src/2D/controls/control.ts
  42. 2 2
      loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts
  43. 2 2
      loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts
  44. 21 19
      loaders/src/glTF/2.0/Extensions/MSFT_lod.ts
  45. 181 129
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  46. 2 2
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  47. 15 10
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  48. 1 1
      package.json
  49. 5 5
      src/Animations/babylon.runtimeAnimation.ts
  50. 23 23
      src/Audio/babylon.sound.ts
  51. 1 1
      src/Bones/babylon.bone.ts
  52. 19 6
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  53. 2 0
      src/Cameras/XR/babylon.webXRCamera.ts
  54. 12 1
      src/Cameras/XR/babylon.webXREnterExitUI.ts
  55. 29 0
      src/Cameras/XR/babylon.webXRExperienceHelper.ts
  56. 2 1
      src/Cameras/XR/babylon.webXRSessionManager.ts
  57. 16 4
      src/Cameras/babylon.targetCamera.ts
  58. 14 14
      src/Culling/babylon.boundingBox.ts
  59. 10 10
      src/Culling/babylon.boundingInfo.ts
  60. 8 8
      src/Culling/babylon.boundingSphere.ts
  61. 14 14
      src/Culling/babylon.ray.ts
  62. 15 3
      src/Debug/babylon.skeletonViewer.ts
  63. 16 9
      src/Engine/babylon.engine.ts
  64. 1 1
      src/Gamepad/Controllers/babylon.poseEnabledController.ts
  65. 9 9
      src/Gamepad/Controllers/babylon.windowsMotionController.ts
  66. 1 1
      src/Gizmos/babylon.gizmo.ts
  67. 7 0
      src/Materials/Textures/babylon.baseTexture.ts
  68. 1 0
      src/Materials/Textures/babylon.internalTexture.ts
  69. 8 0
      src/Materials/babylon.multiMaterial.ts
  70. 338 229
      src/Math/babylon.math.ts
  71. 95 30
      src/Mesh/babylon.abstractMesh.ts
  72. 3 0
      src/Mesh/babylon.geometry.ts
  73. 9 11
      src/Mesh/babylon.instancedMesh.ts
  74. 36 17
      src/Mesh/babylon.linesMesh.ts
  75. 10 79
      src/Mesh/babylon.mesh.ts
  76. 2 4
      src/Mesh/babylon.subMesh.ts
  77. 33 8
      src/Mesh/babylon.transformNode.ts
  78. 8 1
      src/Physics/Plugins/babylon.cannonJSPlugin.ts
  79. 0 8
      src/Physics/Plugins/babylon.oimoJSPlugin.ts
  80. 1 1
      src/Physics/babylon.physicsImpostor.ts
  81. 7 4
      src/PostProcess/RenderPipeline/babylon.postProcessRenderEffect.ts
  82. 5 10
      src/Rendering/babylon.edgesRenderer.ts
  83. 0 4
      src/Shaders/background.vertex.fx
  84. 11 1
      src/Tools/babylon.tools.ts
  85. 10 6
      src/babylon.node.ts
  86. 72 17
      src/babylon.scene.ts
  87. 33 0
      src/babylon.types.ts
  88. 9 10
      tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts
  89. 72 0
      tests/unit/babylon/src/Mesh/babylon.positionAndRotation.tests.ts
  90. 1 0
      tests/unit/karma.conf.js
  91. BIN
      tests/validation/ReferenceImages/xrCameraContainerRotation.png
  92. 590 584
      tests/validation/config.json

File diff suppressed because it is too large
+ 7231 - 7046
Playground/babylon.d.txt


+ 8 - 8
Playground/js/index.js

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

BIN
Playground/scenes/miniBar2.glb


BIN
Playground/scenes/pumpkinBucketCarved.glb


+ 2 - 1
Tools/Gulp/gulpfile.js

@@ -33,6 +33,7 @@ var karmaServer = require('karma').Server;
 var gulpTslint = require("gulp-tslint");
 var gulpTslint = require("gulp-tslint");
 var tslint = require("tslint");
 var tslint = require("tslint");
 const filter = require('gulp-filter');
 const filter = require('gulp-filter');
+var cors = require('cors');
 
 
 //viewer declaration
 //viewer declaration
 var processDeclaration = require('./processViewerDeclaration');
 var processDeclaration = require('./processViewerDeclaration');
@@ -843,7 +844,7 @@ gulp.task("webserver", function() {
     var options = {
     var options = {
         port: 1338,
         port: 1338,
         livereload: false,
         livereload: false,
-
+        middleware: [cors()]
     };
     };
 
 
     if (commandLineOptions.public) {
     if (commandLineOptions.public) {

+ 1 - 0
Tools/Gulp/package.json

@@ -29,6 +29,7 @@
         "gulp-typescript": "4.0.2",
         "gulp-typescript": "4.0.2",
         "gulp-uglify": "^3.0.1",
         "gulp-uglify": "^3.0.1",
         "gulp-webserver": "^0.9.1",
         "gulp-webserver": "^0.9.1",
+        "cors": "^2.8.4",
         "karma": "^2.0.5",
         "karma": "^2.0.5",
         "karma-browserstack-launcher": "^1.3.0",
         "karma-browserstack-launcher": "^1.3.0",
         "karma-chai": "^0.1.0",
         "karma-chai": "^0.1.0",

File diff suppressed because it is too large
+ 1 - 12317
dist/preview release/Oimo.js


File diff suppressed because it is too large
+ 8835 - 8654
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 594 - 262
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 594 - 262
dist/preview release/babylon.no-module.max.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 596 - 264
dist/preview release/es6.js


+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
 {
     "name": "babylonjs-gltf2interface",
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 5 - 1
dist/preview release/gui/babylon.gui.d.ts

@@ -819,6 +819,10 @@ declare module BABYLON.GUI {
     export class Control {
     export class Control {
             /** defines the name of the control */
             /** defines the name of the control */
             name?: string | undefined;
             name?: string | undefined;
+            /**
+                * Gets or sets a boolean indicating if alpha must be an inherited value (false by default)
+                */
+            static AllowAlphaInheritance: boolean;
             /** @hidden */
             /** @hidden */
             _root: BABYLON.Nullable<Container>;
             _root: BABYLON.Nullable<Container>;
             /** @hidden */
             /** @hidden */
@@ -1484,7 +1488,7 @@ declare module BABYLON.GUI {
                 */
                 */
             keepsFocusWith(): BABYLON.Nullable<Control[]>;
             keepsFocusWith(): BABYLON.Nullable<Control[]>;
             /** @hidden */
             /** @hidden */
-            processKey(keyCode: number, key?: string): void;
+            processKey(keyCode: number, key?: string, evt?: KeyboardEvent): void;
             /** @hidden */
             /** @hidden */
             processKeyboard(evt: KeyboardEvent): void;
             processKeyboard(evt: KeyboardEvent): void;
             _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js.map


+ 10 - 2
dist/preview release/gui/babylon.gui.module.d.ts

@@ -926,6 +926,10 @@ declare module 'babylonjs-gui/2D/controls/control' {
     export class Control {
     export class Control {
             /** defines the name of the control */
             /** defines the name of the control */
             name?: string | undefined;
             name?: string | undefined;
+            /**
+                * Gets or sets a boolean indicating if alpha must be an inherited value (false by default)
+                */
+            static AllowAlphaInheritance: boolean;
             /** @hidden */
             /** @hidden */
             _root: Nullable<Container>;
             _root: Nullable<Container>;
             /** @hidden */
             /** @hidden */
@@ -1609,7 +1613,7 @@ declare module 'babylonjs-gui/2D/controls/inputText' {
                 */
                 */
             keepsFocusWith(): Nullable<Control[]>;
             keepsFocusWith(): Nullable<Control[]>;
             /** @hidden */
             /** @hidden */
-            processKey(keyCode: number, key?: string): void;
+            processKey(keyCode: number, key?: string, evt?: KeyboardEvent): void;
             /** @hidden */
             /** @hidden */
             processKeyboard(evt: KeyboardEvent): void;
             processKeyboard(evt: KeyboardEvent): void;
             _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
@@ -3763,6 +3767,10 @@ declare module BABYLON.GUI {
     export class Control {
     export class Control {
             /** defines the name of the control */
             /** defines the name of the control */
             name?: string | undefined;
             name?: string | undefined;
+            /**
+                * Gets or sets a boolean indicating if alpha must be an inherited value (false by default)
+                */
+            static AllowAlphaInheritance: boolean;
             /** @hidden */
             /** @hidden */
             _root: BABYLON.Nullable<Container>;
             _root: BABYLON.Nullable<Container>;
             /** @hidden */
             /** @hidden */
@@ -4428,7 +4436,7 @@ declare module BABYLON.GUI {
                 */
                 */
             keepsFocusWith(): BABYLON.Nullable<Control[]>;
             keepsFocusWith(): BABYLON.Nullable<Control[]>;
             /** @hidden */
             /** @hidden */
-            processKey(keyCode: number, key?: string): void;
+            processKey(keyCode: number, key?: string, evt?: KeyboardEvent): void;
             /** @hidden */
             /** @hidden */
             processKeyboard(evt: KeyboardEvent): void;
             processKeyboard(evt: KeyboardEvent): void;
             _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;

+ 2 - 2
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-gui",
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.4"
+        "babylonjs": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 5 - 5
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-inspector",
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.4",
-        "babylonjs-gui": "4.0.0-alpha.4",
-        "babylonjs-loaders": "4.0.0-alpha.4",
-        "babylonjs-serializers": "4.0.0-alpha.4"
+        "babylonjs": "4.0.0-alpha.6",
+        "babylonjs-gui": "4.0.0-alpha.6",
+        "babylonjs-loaders": "4.0.0-alpha.6",
+        "babylonjs-serializers": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 21 - 15
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -451,10 +451,10 @@ declare module BABYLON.GLTF2.Loader {
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         emissiveTexture?: ITextureInfo;
         emissiveTexture?: ITextureInfo;
         /** @hidden */
         /** @hidden */
-        _babylonData?: {
-            [drawMode: number]: {
-                material: Material;
-                meshes: AbstractMesh[];
+        _data?: {
+            [babylonDrawMode: number]: {
+                babylonMaterial: Material;
+                babylonMeshes: AbstractMesh[];
                 promise: Promise<void>;
                 promise: Promise<void>;
             };
             };
         };
         };
@@ -469,6 +469,11 @@ declare module BABYLON.GLTF2.Loader {
      * Loader interface with additional members.
      * Loader interface with additional members.
      */
      */
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
+        /** @hidden */
+        _instanceData?: {
+            babylonSourceMesh: Mesh;
+            promise: Promise<any>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -479,9 +484,9 @@ declare module BABYLON.GLTF2.Loader {
          */
          */
         parent?: INode;
         parent?: INode;
         /** @hidden */
         /** @hidden */
-        _babylonMesh?: Mesh;
+        _babylonTransformNode?: TransformNode;
         /** @hidden */
         /** @hidden */
-        _primitiveBabylonMeshes?: Mesh[];
+        _primitiveBabylonMeshes?: AbstractMesh[];
         /** @hidden */
         /** @hidden */
         _babylonBones?: Bone[];
         _babylonBones?: Bone[];
         /** @hidden */
         /** @hidden */
@@ -511,9 +516,10 @@ declare module BABYLON.GLTF2.Loader {
      */
      */
     interface ISkin extends GLTF2.ISkin, IArrayItem {
     interface ISkin extends GLTF2.ISkin, IArrayItem {
         /** @hidden */
         /** @hidden */
-        _babylonSkeleton?: Skeleton;
-        /** @hidden */
-        _promise?: Promise<void>;
+        _data?: {
+            babylonSkeleton: Skeleton;
+            promise: Promise<void>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -647,7 +653,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
          * @returns A promise that resolves with the loaded Babylon mesh when the load is complete
          */
          */
-        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonMesh: Mesh) => void): Promise<Mesh>;
+        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonTransformNode: TransformNode) => void): Promise<TransformNode>;
         private _loadMeshAsync;
         private _loadMeshAsync;
         private _loadMeshPrimitiveAsync;
         private _loadMeshPrimitiveAsync;
         private _loadVertexDataAsync;
         private _loadVertexDataAsync;
@@ -845,9 +851,9 @@ declare module BABYLON.GLTF2 {
          * @param context The context when loading the asset
          * @param context The context when loading the asset
          * @param node The glTF node property
          * @param node The glTF node property
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
+         * @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: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync?(context: string, node: Loader.INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /**
         /**
          * Define this method to modify the default behavior when loading cameras.
          * Define this method to modify the default behavior when loading cameras.
          * @param context The context when loading the asset
          * @param context The context when loading the asset
@@ -957,7 +963,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onReady(): void;
         onReady(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         /** @hidden */
@@ -1018,7 +1024,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         private _loadClipAsync;
         private _loadClipAsync;
@@ -1110,7 +1116,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onLoading(): void;
         onLoading(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
     }
     }
 }
 }
 
 

+ 178 - 139
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -954,6 +954,9 @@ var BABYLON;
                         promises.push(_this._compileShadowGeneratorsAsync());
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
                     }
                     var resultPromise = Promise.all(promises).then(function () {
                     var resultPromise = Promise.all(promises).then(function () {
+                        if (_this._rootBabylonMesh) {
+                            _this._rootBabylonMesh.setEnabled(true);
+                        }
                         _this._setState(BABYLON.GLTFLoaderState.READY);
                         _this._setState(BABYLON.GLTFLoaderState.READY);
                         _this._extensionsOnReady();
                         _this._extensionsOnReady();
                         _this._startAnimations();
                         _this._startAnimations();
@@ -1063,8 +1066,9 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._createRootNode = function () {
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this.babylonScene);
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this.babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = {
                 var rootNode = {
-                    _babylonMesh: this._rootBabylonMesh,
+                    _babylonTransformNode: this._rootBabylonMesh,
                     index: -1
                     index: -1
                 };
                 };
                 switch (this._parent.coordinateSystemMode) {
                 switch (this._parent.coordinateSystemMode) {
@@ -1121,8 +1125,8 @@ var BABYLON;
                         callback(babylonMesh);
                         callback(babylonMesh);
                     }
                     }
                 }
                 }
-                else {
-                    callback(node._babylonMesh);
+                else if (node._babylonTransformNode instanceof BABYLON.AbstractMesh) {
+                    callback(node._babylonTransformNode);
                 }
                 }
             };
             };
             GLTFLoader.prototype._getMeshes = function () {
             GLTFLoader.prototype._getMeshes = function () {
@@ -1133,15 +1137,9 @@ var BABYLON;
                 if (nodes) {
                 if (nodes) {
                     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
                     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
                         var node = nodes_1[_i];
                         var node = nodes_1[_i];
-                        if (node._babylonMesh) {
-                            meshes.push(node._babylonMesh);
-                        }
-                        if (node._primitiveBabylonMeshes) {
-                            for (var _a = 0, _b = node._primitiveBabylonMeshes; _a < _b.length; _a++) {
-                                var babylonMesh = _b[_a];
-                                meshes.push(babylonMesh);
-                            }
-                        }
+                        this._forEachPrimitive(node, function (babylonMesh) {
+                            meshes.push(babylonMesh);
+                        });
                     }
                     }
                 }
                 }
                 return meshes;
                 return meshes;
@@ -1152,8 +1150,8 @@ var BABYLON;
                 if (skins) {
                 if (skins) {
                     for (var _i = 0, skins_1 = skins; _i < skins_1.length; _i++) {
                     for (var _i = 0, skins_1 = skins; _i < skins_1.length; _i++) {
                         var skin = skins_1[_i];
                         var skin = skins_1[_i];
-                        if (skin._babylonSkeleton) {
-                            skeletons.push(skin._babylonSkeleton);
+                        if (skin._data) {
+                            skeletons.push(skin._data.babylonSkeleton);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1213,113 +1211,145 @@ var BABYLON;
                 if (extensionPromise) {
                 if (extensionPromise) {
                     return extensionPromise;
                     return extensionPromise;
                 }
                 }
-                if (node._babylonMesh) {
+                if (node._babylonTransformNode) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
                     throw new Error(context + ": Invalid recursive node hierarchy");
                 }
                 }
                 var promises = new Array();
                 var promises = new Array();
                 this.logOpen(context + " " + (node.name || ""));
                 this.logOpen(context + " " + (node.name || ""));
-                var babylonMesh = new BABYLON.Mesh(node.name || "node" + node.index, this.babylonScene);
-                node._babylonMesh = babylonMesh;
-                babylonMesh.setEnabled(false);
-                GLTFLoader._LoadTransform(node, babylonMesh);
-                if (node.mesh != undefined) {
-                    var mesh = ArrayItem.Get(context + "/mesh", this.gltf.meshes, node.mesh);
-                    promises.push(this._loadMeshAsync("#/meshes/" + mesh.index, node, mesh, babylonMesh));
-                }
-                if (node.camera != undefined) {
-                    var camera = ArrayItem.Get(context + "/camera", this.gltf.cameras, node.camera);
-                    promises.push(this.loadCameraAsync("#/cameras/" + camera.index, camera, function (babylonCamera) {
-                        babylonCamera.parent = babylonMesh;
-                    }));
-                }
-                if (node.children) {
-                    var _loop_1 = function (index) {
-                        var childNode = ArrayItem.Get(context + "/children/" + index, this_1.gltf.nodes, index);
-                        promises.push(this_1.loadNodeAsync("#/nodes/" + node.index, childNode, function (childBabylonMesh) {
-                            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                            if (childNode.skin != undefined) {
-                                childBabylonMesh.parent = _this._rootBabylonMesh;
-                                return;
-                            }
-                            childBabylonMesh.parent = babylonMesh;
+                var loadNode = function (babylonTransformNode) {
+                    GLTFLoader._LoadTransform(node, babylonTransformNode);
+                    if (node.camera != undefined) {
+                        var camera = ArrayItem.Get(context + "/camera", _this.gltf.cameras, node.camera);
+                        promises.push(_this.loadCameraAsync("#/cameras/" + camera.index, camera, function (babylonCamera) {
+                            babylonCamera.parent = babylonTransformNode;
                         }));
                         }));
-                    };
-                    var this_1 = this;
-                    for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
-                        var index = _a[_i];
-                        _loop_1(index);
                     }
                     }
+                    if (node.children) {
+                        var _loop_1 = function (index) {
+                            var childNode = ArrayItem.Get(context + "/children/" + index, _this.gltf.nodes, index);
+                            promises.push(_this.loadNodeAsync("#/nodes/" + node.index, childNode, function (childBabylonMesh) {
+                                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                                if (childNode.skin != undefined) {
+                                    childBabylonMesh.parent = _this._rootBabylonMesh;
+                                    return;
+                                }
+                                childBabylonMesh.parent = babylonTransformNode;
+                            }));
+                        };
+                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
+                            var index = _a[_i];
+                            _loop_1(index);
+                        }
+                    }
+                    assign(babylonTransformNode);
+                };
+                if (node.mesh == undefined) {
+                    var nodeName = node.name || "node" + node.index;
+                    node._babylonTransformNode = new BABYLON.TransformNode(nodeName, this.babylonScene);
+                    loadNode(node._babylonTransformNode);
+                }
+                else {
+                    var mesh = ArrayItem.Get(context + "/mesh", this.gltf.meshes, node.mesh);
+                    promises.push(this._loadMeshAsync("#/meshes/" + mesh.index, node, mesh, loadNode));
                 }
                 }
-                assign(babylonMesh);
-                this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 this.logClose();
                 this.logClose();
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                    return babylonMesh;
+                    _this._forEachPrimitive(node, function (babylonMesh) {
+                        babylonMesh.refreshBoundingInfo(true);
+                    });
+                    return node._babylonTransformNode;
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, babylonMesh) {
-                var _this = this;
-                var promises = new Array();
-                this.logOpen(context + " " + (mesh.name || ""));
+            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, assign) {
                 var primitives = mesh.primitives;
                 var primitives = mesh.primitives;
-                if (!primitives || primitives.length === 0) {
+                if (!primitives || !primitives.length) {
                     throw new Error(context + ": Primitives are missing");
                     throw new Error(context + ": Primitives are missing");
                 }
                 }
-                ArrayItem.Assign(primitives);
+                if (primitives[0].index == undefined) {
+                    ArrayItem.Assign(primitives);
+                }
+                var promises = new Array();
+                this.logOpen(context + " " + (mesh.name || ""));
+                var name = node.name || "node" + node.index;
                 if (primitives.length === 1) {
                 if (primitives.length === 1) {
-                    var primitive = primitives[0];
-                    promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, node, mesh, primitive, babylonMesh));
+                    var primitive = mesh.primitives[0];
+                    promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index + "}", name, node, mesh, primitive, function (babylonMesh) {
+                        node._babylonTransformNode = babylonMesh;
+                    }));
                 }
                 }
                 else {
                 else {
-                    node._primitiveBabylonMeshes = [];
+                    var babylonTransformNode_1 = new BABYLON.TransformNode(name, this.babylonScene);
+                    node._babylonTransformNode = babylonTransformNode_1;
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                         var primitive = primitives_1[_i];
                         var primitive = primitives_1[_i];
-                        var primitiveBabylonMesh = new BABYLON.Mesh((mesh.name || babylonMesh.name) + "_" + primitive.index, this.babylonScene, babylonMesh);
-                        node._primitiveBabylonMeshes.push(primitiveBabylonMesh);
-                        promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, node, mesh, primitive, primitiveBabylonMesh));
-                        this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                        promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index + "}", name + "_primitive" + primitive.index, node, mesh, primitive, function (babylonMesh) {
+                            babylonMesh.parent = babylonTransformNode_1;
+                            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                            node._primitiveBabylonMeshes.push(babylonMesh);
+                        }));
                     }
                     }
                 }
                 }
                 if (node.skin != undefined) {
                 if (node.skin != undefined) {
                     var skin = ArrayItem.Get(context + "/skin", this.gltf.skins, node.skin);
                     var skin = ArrayItem.Get(context + "/skin", this.gltf.skins, node.skin);
                     promises.push(this._loadSkinAsync("#/skins/" + skin.index, node, skin));
                     promises.push(this._loadSkinAsync("#/skins/" + skin.index, node, skin));
                 }
                 }
+                assign(node._babylonTransformNode);
                 this.logClose();
                 this.logClose();
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    _this._forEachPrimitive(node, function (babylonMesh) {
-                        babylonMesh._refreshBoundingInfo(true);
-                    });
+                    return node._babylonTransformNode;
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadMeshPrimitiveAsync = function (context, node, mesh, primitive, babylonMesh) {
+            GLTFLoader.prototype._loadMeshPrimitiveAsync = function (context, name, node, mesh, primitive, assign) {
                 var _this = this;
                 var _this = this;
-                var promises = new Array();
                 this.logOpen("" + context);
                 this.logOpen("" + context);
-                this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
-                promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonGeometry) {
-                    return _this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(function () {
-                        babylonGeometry.applyToMesh(babylonMesh);
-                    });
-                }));
-                var babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
-                if (primitive.material == undefined) {
-                    var babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
-                    if (!babylonMaterial) {
-                        babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
-                        this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
-                    }
-                    babylonMesh.material = babylonMaterial;
+                var canInstance = (node.skin == undefined && !mesh.primitives[0].targets);
+                var babylonAbstractMesh;
+                var promise;
+                var instanceData = primitive._instanceData;
+                if (canInstance && instanceData) {
+                    babylonAbstractMesh = instanceData.babylonSourceMesh.createInstance(name);
+                    promise = instanceData.promise;
                 }
                 }
                 else {
                 else {
-                    var material = ArrayItem.Get(context + "/material", this.gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material.index, material, babylonMesh, babylonDrawMode, function (babylonMaterial) {
-                        babylonMesh.material = babylonMaterial;
+                    var promises = new Array();
+                    var babylonMesh_1 = new BABYLON.Mesh(name, this.babylonScene);
+                    this._createMorphTargets(context, node, mesh, primitive, babylonMesh_1);
+                    promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh_1).then(function (babylonGeometry) {
+                        return _this._loadMorphTargetsAsync(context, primitive, babylonMesh_1, babylonGeometry).then(function () {
+                            babylonGeometry.applyToMesh(babylonMesh_1);
+                        });
                     }));
                     }));
+                    var babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
+                    if (primitive.material == undefined) {
+                        var babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
+                        if (!babylonMaterial) {
+                            babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
+                            this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
+                        }
+                        babylonMesh_1.material = babylonMaterial;
+                    }
+                    else {
+                        var material = ArrayItem.Get(context + "/material", this.gltf.materials, primitive.material);
+                        promises.push(this._loadMaterialAsync("#/materials/" + material.index, material, babylonMesh_1, babylonDrawMode, function (babylonMaterial) {
+                            babylonMesh_1.material = babylonMaterial;
+                        }));
+                    }
+                    promise = Promise.all(promises);
+                    if (canInstance) {
+                        primitive._instanceData = {
+                            babylonSourceMesh: babylonMesh_1,
+                            promise: promise
+                        };
+                    }
+                    babylonAbstractMesh = babylonMesh_1;
                 }
                 }
+                this._parent.onMeshLoadedObservable.notifyObservers(babylonAbstractMesh);
+                assign(babylonAbstractMesh);
                 this.logClose();
                 this.logClose();
-                return Promise.all(promises).then(function () { });
+                return promise.then(function () {
+                    return babylonAbstractMesh;
+                });
             };
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
                 var _this = this;
@@ -1446,6 +1476,11 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
             GLTFLoader._LoadTransform = function (node, babylonNode) {
             GLTFLoader._LoadTransform = function (node, babylonNode) {
+                // Ignore the TRS of skinned nodes.
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                if (node.skin != undefined) {
+                    return;
+                }
                 var position = BABYLON.Vector3.Zero();
                 var position = BABYLON.Vector3.Zero();
                 var rotation = BABYLON.Quaternion.Identity();
                 var rotation = BABYLON.Quaternion.Identity();
                 var scaling = BABYLON.Vector3.One();
                 var scaling = BABYLON.Vector3.One();
@@ -1474,45 +1509,45 @@ var BABYLON;
                     _this._forEachPrimitive(node, function (babylonMesh) {
                     _this._forEachPrimitive(node, function (babylonMesh) {
                         babylonMesh.skeleton = skeleton;
                         babylonMesh.skeleton = skeleton;
                     });
                     });
-                    // Ignore the TRS of skinned nodes.
-                    // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                    node._babylonMesh.position = BABYLON.Vector3.Zero();
-                    node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
-                    node._babylonMesh.scaling = BABYLON.Vector3.One();
                 };
                 };
-                if (skin._promise) {
-                    return skin._promise.then(function () {
-                        assignSkeleton(skin._babylonSkeleton);
+                if (skin._data) {
+                    var data_1 = skin._data;
+                    return data_1.promise.then(function () {
+                        assignSkeleton(data_1.babylonSkeleton);
                     });
                     });
                 }
                 }
                 var skeletonId = "skeleton" + skin.index;
                 var skeletonId = "skeleton" + skin.index;
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
-                skin._babylonSkeleton = babylonSkeleton;
-                this._loadBones(context, skin);
+                this._loadBones(context, skin, babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
-                return (skin._promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
+                var promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
                     _this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
                     _this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
-                }));
+                });
+                skin._data = {
+                    babylonSkeleton: babylonSkeleton,
+                    promise: promise
+                };
+                return promise;
             };
             };
-            GLTFLoader.prototype._loadBones = function (context, skin) {
+            GLTFLoader.prototype._loadBones = function (context, skin, babylonSkeleton) {
                 var babylonBones = {};
                 var babylonBones = {};
                 for (var _i = 0, _a = skin.joints; _i < _a.length; _i++) {
                 for (var _i = 0, _a = skin.joints; _i < _a.length; _i++) {
                     var index = _a[_i];
                     var index = _a[_i];
                     var node = ArrayItem.Get(context + "/joints/" + index, this.gltf.nodes, index);
                     var node = ArrayItem.Get(context + "/joints/" + index, this.gltf.nodes, index);
-                    this._loadBone(node, skin, babylonBones);
+                    this._loadBone(node, skin, babylonSkeleton, babylonBones);
                 }
                 }
             };
             };
-            GLTFLoader.prototype._loadBone = function (node, skin, babylonBones) {
+            GLTFLoader.prototype._loadBone = function (node, skin, babylonSkeleton, babylonBones) {
                 var babylonBone = babylonBones[node.index];
                 var babylonBone = babylonBones[node.index];
                 if (babylonBone) {
                 if (babylonBone) {
                     return babylonBone;
                     return babylonBone;
                 }
                 }
                 var babylonParentBone = null;
                 var babylonParentBone = null;
-                if (node.parent && node.parent._babylonMesh !== this._rootBabylonMesh) {
-                    babylonParentBone = this._loadBone(node.parent, skin, babylonBones);
+                if (node.parent && node.parent._babylonTransformNode !== this._rootBabylonMesh) {
+                    babylonParentBone = this._loadBone(node.parent, skin, babylonSkeleton, babylonBones);
                 }
                 }
                 var boneIndex = skin.joints.indexOf(node.index);
                 var boneIndex = skin.joints.indexOf(node.index);
-                babylonBone = new BABYLON.Bone(node.name || "joint" + node.index, skin._babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
+                babylonBone = new BABYLON.Bone(node.name || "joint" + node.index, babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
                 babylonBones[node.index] = babylonBone;
                 babylonBones[node.index] = babylonBone;
                 node._babylonBones = node._babylonBones || [];
                 node._babylonBones = node._babylonBones || [];
                 node._babylonBones.push(babylonBone);
                 node._babylonBones.push(babylonBone);
@@ -1643,7 +1678,7 @@ var BABYLON;
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 // Ignore animations that have no animation targets.
                 // Ignore animations that have no animation targets.
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
-                    (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonMesh)) {
+                    (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
                     return Promise.resolve();
                     return Promise.resolve();
                 }
                 }
                 // Ignore animations targeting TRS of skinned nodes.
                 // Ignore animations targeting TRS of skinned nodes.
@@ -1774,8 +1809,10 @@ var BABYLON;
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
                         babylonAnimation.setKeys(keys);
-                        if (targetNode._babylonBones) {
-                            var babylonAnimationTargets = [targetNode._babylonMesh].concat(targetNode._babylonBones);
+                        var babylonTransformNode = targetNode._babylonTransformNode;
+                        var babylonBones = targetNode._babylonBones;
+                        if (babylonBones) {
+                            var babylonAnimationTargets = [babylonTransformNode].concat(babylonBones);
                             for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
                             for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
                                 var babylonAnimationTarget = babylonAnimationTargets_1[_i];
                                 var babylonAnimationTarget = babylonAnimationTargets_1[_i];
                                 babylonAnimationTarget.animations.push(babylonAnimation);
                                 babylonAnimationTarget.animations.push(babylonAnimation);
@@ -1783,8 +1820,8 @@ var BABYLON;
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                         }
                         }
                         else {
                         else {
-                            targetNode._babylonMesh.animations.push(babylonAnimation);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonMesh);
+                            babylonTransformNode.animations.push(babylonAnimation);
+                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
                         }
                         }
                     }
                     }
                 });
                 });
@@ -1987,30 +2024,30 @@ var BABYLON;
                 if (extensionPromise) {
                 if (extensionPromise) {
                     return extensionPromise;
                     return extensionPromise;
                 }
                 }
-                material._babylonData = material._babylonData || {};
-                var babylonData = material._babylonData[babylonDrawMode];
+                material._data = material._data || {};
+                var babylonData = material._data[babylonDrawMode];
                 if (!babylonData) {
                 if (!babylonData) {
                     this.logOpen(context + " " + (material.name || ""));
                     this.logOpen(context + " " + (material.name || ""));
                     var babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                     var babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                     babylonData = {
                     babylonData = {
-                        material: babylonMaterial,
-                        meshes: [],
+                        babylonMaterial: babylonMaterial,
+                        babylonMeshes: [],
                         promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                         promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                     };
                     };
-                    material._babylonData[babylonDrawMode] = babylonData;
+                    material._data[babylonDrawMode] = babylonData;
                     this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this.logClose();
                     this.logClose();
                 }
                 }
-                babylonData.meshes.push(babylonMesh);
+                babylonData.babylonMeshes.push(babylonMesh);
                 babylonMesh.onDisposeObservable.addOnce(function () {
                 babylonMesh.onDisposeObservable.addOnce(function () {
-                    var index = babylonData.meshes.indexOf(babylonMesh);
+                    var index = babylonData.babylonMeshes.indexOf(babylonMesh);
                     if (index !== -1) {
                     if (index !== -1) {
-                        babylonData.meshes.splice(index, 1);
+                        babylonData.babylonMeshes.splice(index, 1);
                     }
                     }
                 });
                 });
-                assign(babylonData.material);
+                assign(babylonData.babylonMaterial);
                 return babylonData.promise.then(function () {
                 return babylonData.promise.then(function () {
-                    return babylonData.material;
+                    return babylonData.babylonMaterial;
                 });
                 });
             };
             };
             GLTFLoader.prototype._createDefaultMaterial = function (name, babylonDrawMode) {
             GLTFLoader.prototype._createDefaultMaterial = function (name, babylonDrawMode) {
@@ -2403,14 +2440,14 @@ var BABYLON;
                 if (this.gltf.materials) {
                 if (this.gltf.materials) {
                     for (var _i = 0, _a = this.gltf.materials; _i < _a.length; _i++) {
                     for (var _i = 0, _a = this.gltf.materials; _i < _a.length; _i++) {
                         var material = _a[_i];
                         var material = _a[_i];
-                        if (material._babylonData) {
-                            for (var babylonDrawMode in material._babylonData) {
-                                var babylonData = material._babylonData[babylonDrawMode];
-                                for (var _b = 0, _c = babylonData.meshes; _b < _c.length; _b++) {
+                        if (material._data) {
+                            for (var babylonDrawMode in material._data) {
+                                var babylonData = material._data[babylonDrawMode];
+                                for (var _b = 0, _c = babylonData.babylonMeshes; _b < _c.length; _b++) {
                                     var babylonMesh = _c[_b];
                                     var babylonMesh = _c[_b];
                                     // Ensure nonUniformScaling is set if necessary.
                                     // Ensure nonUniformScaling is set if necessary.
                                     babylonMesh.computeWorldMatrix(true);
                                     babylonMesh.computeWorldMatrix(true);
-                                    var babylonMaterial = babylonData.material;
+                                    var babylonMaterial = babylonData.babylonMaterial;
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                     if (this._parent.useClipPlane) {
                                     if (this._parent.useClipPlane) {
                                         promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                         promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -2710,16 +2747,18 @@ var BABYLON;
                                     _this._nodeIndexLOD = indexLOD;
                                     _this._nodeIndexLOD = indexLOD;
                                     _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                                     _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                                 }
                                 }
-                                var promise = _this._loader.loadNodeAsync("#/nodes/" + nodeLOD.index, nodeLOD).then(function (babylonMesh) {
+                                var assign_1 = function (babylonTransformNode) { babylonTransformNode.setEnabled(false); };
+                                var promise = _this._loader.loadNodeAsync("#/nodes/" + nodeLOD.index, nodeLOD, assign_1).then(function (babylonMesh) {
                                     if (indexLOD !== 0) {
                                     if (indexLOD !== 0) {
                                         // TODO: should not rely on _babylonMesh
                                         // TODO: should not rely on _babylonMesh
                                         var previousNodeLOD = nodeLODs[indexLOD - 1];
                                         var previousNodeLOD = nodeLODs[indexLOD - 1];
-                                        if (previousNodeLOD._babylonMesh) {
-                                            previousNodeLOD._babylonMesh.dispose();
-                                            delete previousNodeLOD._babylonMesh;
+                                        if (previousNodeLOD._babylonTransformNode) {
+                                            previousNodeLOD._babylonTransformNode.dispose();
+                                            delete previousNodeLOD._babylonTransformNode;
                                             _this._disposeUnusedMaterials();
                                             _this._disposeUnusedMaterials();
                                         }
                                         }
                                     }
                                     }
+                                    babylonMesh.setEnabled(true);
                                     return babylonMesh;
                                     return babylonMesh;
                                 });
                                 });
                                 if (indexLOD === 0) {
                                 if (indexLOD === 0) {
@@ -2761,11 +2800,11 @@ var BABYLON;
                                 }).then(function (babylonMaterial) {
                                 }).then(function (babylonMaterial) {
                                     if (indexLOD !== 0) {
                                     if (indexLOD !== 0) {
                                         assign(babylonMaterial);
                                         assign(babylonMaterial);
-                                        // TODO: should not rely on _babylonData
-                                        var previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData;
-                                        if (previousBabylonDataLOD[babylonDrawMode]) {
-                                            previousBabylonDataLOD[babylonDrawMode].material.dispose();
-                                            delete previousBabylonDataLOD[babylonDrawMode];
+                                        // TODO: should not rely on _data
+                                        var previousDataLOD = materialLODs[indexLOD - 1]._data;
+                                        if (previousDataLOD[babylonDrawMode]) {
+                                            previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
+                                            delete previousDataLOD[babylonDrawMode];
                                         }
                                         }
                                     }
                                     }
                                     return babylonMaterial;
                                     return babylonMaterial;
@@ -2826,17 +2865,17 @@ var BABYLON;
                         return properties;
                         return properties;
                     };
                     };
                     MSFT_lod.prototype._disposeUnusedMaterials = function () {
                     MSFT_lod.prototype._disposeUnusedMaterials = function () {
-                        // TODO: should not rely on _babylonData
+                        // TODO: should not rely on _data
                         var materials = this._loader.gltf.materials;
                         var materials = this._loader.gltf.materials;
                         if (materials) {
                         if (materials) {
                             for (var _i = 0, materials_1 = materials; _i < materials_1.length; _i++) {
                             for (var _i = 0, materials_1 = materials; _i < materials_1.length; _i++) {
                                 var material = materials_1[_i];
                                 var material = materials_1[_i];
-                                if (material._babylonData) {
-                                    for (var drawMode in material._babylonData) {
-                                        var babylonData = material._babylonData[drawMode];
-                                        if (babylonData.meshes.length === 0) {
-                                            babylonData.material.dispose(false, true);
-                                            delete material._babylonData[drawMode];
+                                if (material._data) {
+                                    for (var drawMode in material._data) {
+                                        var data = material._data[drawMode];
+                                        if (data.babylonMeshes.length === 0) {
+                                            data.babylonMaterial.dispose(false, true);
+                                            delete material._data[drawMode];
                                         }
                                         }
                                     }
                                     }
                                 }
                                 }

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 21 - 15
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -1013,10 +1013,10 @@ declare module BABYLON.GLTF2.Loader {
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         emissiveTexture?: ITextureInfo;
         emissiveTexture?: ITextureInfo;
         /** @hidden */
         /** @hidden */
-        _babylonData?: {
-            [drawMode: number]: {
-                material: Material;
-                meshes: AbstractMesh[];
+        _data?: {
+            [babylonDrawMode: number]: {
+                babylonMaterial: Material;
+                babylonMeshes: AbstractMesh[];
                 promise: Promise<void>;
                 promise: Promise<void>;
             };
             };
         };
         };
@@ -1031,6 +1031,11 @@ declare module BABYLON.GLTF2.Loader {
      * Loader interface with additional members.
      * Loader interface with additional members.
      */
      */
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
+        /** @hidden */
+        _instanceData?: {
+            babylonSourceMesh: Mesh;
+            promise: Promise<any>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -1041,9 +1046,9 @@ declare module BABYLON.GLTF2.Loader {
          */
          */
         parent?: INode;
         parent?: INode;
         /** @hidden */
         /** @hidden */
-        _babylonMesh?: Mesh;
+        _babylonTransformNode?: TransformNode;
         /** @hidden */
         /** @hidden */
-        _primitiveBabylonMeshes?: Mesh[];
+        _primitiveBabylonMeshes?: AbstractMesh[];
         /** @hidden */
         /** @hidden */
         _babylonBones?: Bone[];
         _babylonBones?: Bone[];
         /** @hidden */
         /** @hidden */
@@ -1073,9 +1078,10 @@ declare module BABYLON.GLTF2.Loader {
      */
      */
     interface ISkin extends GLTF2.ISkin, IArrayItem {
     interface ISkin extends GLTF2.ISkin, IArrayItem {
         /** @hidden */
         /** @hidden */
-        _babylonSkeleton?: Skeleton;
-        /** @hidden */
-        _promise?: Promise<void>;
+        _data?: {
+            babylonSkeleton: Skeleton;
+            promise: Promise<void>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -1209,7 +1215,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
          * @returns A promise that resolves with the loaded Babylon mesh when the load is complete
          */
          */
-        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonMesh: Mesh) => void): Promise<Mesh>;
+        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonTransformNode: TransformNode) => void): Promise<TransformNode>;
         private _loadMeshAsync;
         private _loadMeshAsync;
         private _loadMeshPrimitiveAsync;
         private _loadMeshPrimitiveAsync;
         private _loadVertexDataAsync;
         private _loadVertexDataAsync;
@@ -1407,9 +1413,9 @@ declare module BABYLON.GLTF2 {
          * @param context The context when loading the asset
          * @param context The context when loading the asset
          * @param node The glTF node property
          * @param node The glTF node property
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
+         * @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: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync?(context: string, node: Loader.INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /**
         /**
          * Define this method to modify the default behavior when loading cameras.
          * Define this method to modify the default behavior when loading cameras.
          * @param context The context when loading the asset
          * @param context The context when loading the asset
@@ -1519,7 +1525,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onReady(): void;
         onReady(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         /** @hidden */
@@ -1580,7 +1586,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         private _loadClipAsync;
         private _loadClipAsync;
@@ -1672,7 +1678,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onLoading(): void;
         onLoading(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
     }
     }
 }
 }
 
 

+ 178 - 139
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3162,6 +3162,9 @@ var BABYLON;
                         promises.push(_this._compileShadowGeneratorsAsync());
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
                     }
                     var resultPromise = Promise.all(promises).then(function () {
                     var resultPromise = Promise.all(promises).then(function () {
+                        if (_this._rootBabylonMesh) {
+                            _this._rootBabylonMesh.setEnabled(true);
+                        }
                         _this._setState(BABYLON.GLTFLoaderState.READY);
                         _this._setState(BABYLON.GLTFLoaderState.READY);
                         _this._extensionsOnReady();
                         _this._extensionsOnReady();
                         _this._startAnimations();
                         _this._startAnimations();
@@ -3271,8 +3274,9 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._createRootNode = function () {
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this.babylonScene);
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this.babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = {
                 var rootNode = {
-                    _babylonMesh: this._rootBabylonMesh,
+                    _babylonTransformNode: this._rootBabylonMesh,
                     index: -1
                     index: -1
                 };
                 };
                 switch (this._parent.coordinateSystemMode) {
                 switch (this._parent.coordinateSystemMode) {
@@ -3329,8 +3333,8 @@ var BABYLON;
                         callback(babylonMesh);
                         callback(babylonMesh);
                     }
                     }
                 }
                 }
-                else {
-                    callback(node._babylonMesh);
+                else if (node._babylonTransformNode instanceof BABYLON.AbstractMesh) {
+                    callback(node._babylonTransformNode);
                 }
                 }
             };
             };
             GLTFLoader.prototype._getMeshes = function () {
             GLTFLoader.prototype._getMeshes = function () {
@@ -3341,15 +3345,9 @@ var BABYLON;
                 if (nodes) {
                 if (nodes) {
                     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
                     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
                         var node = nodes_1[_i];
                         var node = nodes_1[_i];
-                        if (node._babylonMesh) {
-                            meshes.push(node._babylonMesh);
-                        }
-                        if (node._primitiveBabylonMeshes) {
-                            for (var _a = 0, _b = node._primitiveBabylonMeshes; _a < _b.length; _a++) {
-                                var babylonMesh = _b[_a];
-                                meshes.push(babylonMesh);
-                            }
-                        }
+                        this._forEachPrimitive(node, function (babylonMesh) {
+                            meshes.push(babylonMesh);
+                        });
                     }
                     }
                 }
                 }
                 return meshes;
                 return meshes;
@@ -3360,8 +3358,8 @@ var BABYLON;
                 if (skins) {
                 if (skins) {
                     for (var _i = 0, skins_1 = skins; _i < skins_1.length; _i++) {
                     for (var _i = 0, skins_1 = skins; _i < skins_1.length; _i++) {
                         var skin = skins_1[_i];
                         var skin = skins_1[_i];
-                        if (skin._babylonSkeleton) {
-                            skeletons.push(skin._babylonSkeleton);
+                        if (skin._data) {
+                            skeletons.push(skin._data.babylonSkeleton);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -3421,113 +3419,145 @@ var BABYLON;
                 if (extensionPromise) {
                 if (extensionPromise) {
                     return extensionPromise;
                     return extensionPromise;
                 }
                 }
-                if (node._babylonMesh) {
+                if (node._babylonTransformNode) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
                     throw new Error(context + ": Invalid recursive node hierarchy");
                 }
                 }
                 var promises = new Array();
                 var promises = new Array();
                 this.logOpen(context + " " + (node.name || ""));
                 this.logOpen(context + " " + (node.name || ""));
-                var babylonMesh = new BABYLON.Mesh(node.name || "node" + node.index, this.babylonScene);
-                node._babylonMesh = babylonMesh;
-                babylonMesh.setEnabled(false);
-                GLTFLoader._LoadTransform(node, babylonMesh);
-                if (node.mesh != undefined) {
-                    var mesh = ArrayItem.Get(context + "/mesh", this.gltf.meshes, node.mesh);
-                    promises.push(this._loadMeshAsync("#/meshes/" + mesh.index, node, mesh, babylonMesh));
-                }
-                if (node.camera != undefined) {
-                    var camera = ArrayItem.Get(context + "/camera", this.gltf.cameras, node.camera);
-                    promises.push(this.loadCameraAsync("#/cameras/" + camera.index, camera, function (babylonCamera) {
-                        babylonCamera.parent = babylonMesh;
-                    }));
-                }
-                if (node.children) {
-                    var _loop_1 = function (index) {
-                        var childNode = ArrayItem.Get(context + "/children/" + index, this_1.gltf.nodes, index);
-                        promises.push(this_1.loadNodeAsync("#/nodes/" + node.index, childNode, function (childBabylonMesh) {
-                            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                            if (childNode.skin != undefined) {
-                                childBabylonMesh.parent = _this._rootBabylonMesh;
-                                return;
-                            }
-                            childBabylonMesh.parent = babylonMesh;
+                var loadNode = function (babylonTransformNode) {
+                    GLTFLoader._LoadTransform(node, babylonTransformNode);
+                    if (node.camera != undefined) {
+                        var camera = ArrayItem.Get(context + "/camera", _this.gltf.cameras, node.camera);
+                        promises.push(_this.loadCameraAsync("#/cameras/" + camera.index, camera, function (babylonCamera) {
+                            babylonCamera.parent = babylonTransformNode;
                         }));
                         }));
-                    };
-                    var this_1 = this;
-                    for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
-                        var index = _a[_i];
-                        _loop_1(index);
                     }
                     }
+                    if (node.children) {
+                        var _loop_1 = function (index) {
+                            var childNode = ArrayItem.Get(context + "/children/" + index, _this.gltf.nodes, index);
+                            promises.push(_this.loadNodeAsync("#/nodes/" + node.index, childNode, function (childBabylonMesh) {
+                                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                                if (childNode.skin != undefined) {
+                                    childBabylonMesh.parent = _this._rootBabylonMesh;
+                                    return;
+                                }
+                                childBabylonMesh.parent = babylonTransformNode;
+                            }));
+                        };
+                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
+                            var index = _a[_i];
+                            _loop_1(index);
+                        }
+                    }
+                    assign(babylonTransformNode);
+                };
+                if (node.mesh == undefined) {
+                    var nodeName = node.name || "node" + node.index;
+                    node._babylonTransformNode = new BABYLON.TransformNode(nodeName, this.babylonScene);
+                    loadNode(node._babylonTransformNode);
+                }
+                else {
+                    var mesh = ArrayItem.Get(context + "/mesh", this.gltf.meshes, node.mesh);
+                    promises.push(this._loadMeshAsync("#/meshes/" + mesh.index, node, mesh, loadNode));
                 }
                 }
-                assign(babylonMesh);
-                this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 this.logClose();
                 this.logClose();
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                    return babylonMesh;
+                    _this._forEachPrimitive(node, function (babylonMesh) {
+                        babylonMesh.refreshBoundingInfo(true);
+                    });
+                    return node._babylonTransformNode;
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, babylonMesh) {
-                var _this = this;
-                var promises = new Array();
-                this.logOpen(context + " " + (mesh.name || ""));
+            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, assign) {
                 var primitives = mesh.primitives;
                 var primitives = mesh.primitives;
-                if (!primitives || primitives.length === 0) {
+                if (!primitives || !primitives.length) {
                     throw new Error(context + ": Primitives are missing");
                     throw new Error(context + ": Primitives are missing");
                 }
                 }
-                ArrayItem.Assign(primitives);
+                if (primitives[0].index == undefined) {
+                    ArrayItem.Assign(primitives);
+                }
+                var promises = new Array();
+                this.logOpen(context + " " + (mesh.name || ""));
+                var name = node.name || "node" + node.index;
                 if (primitives.length === 1) {
                 if (primitives.length === 1) {
-                    var primitive = primitives[0];
-                    promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, node, mesh, primitive, babylonMesh));
+                    var primitive = mesh.primitives[0];
+                    promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index + "}", name, node, mesh, primitive, function (babylonMesh) {
+                        node._babylonTransformNode = babylonMesh;
+                    }));
                 }
                 }
                 else {
                 else {
-                    node._primitiveBabylonMeshes = [];
+                    var babylonTransformNode_1 = new BABYLON.TransformNode(name, this.babylonScene);
+                    node._babylonTransformNode = babylonTransformNode_1;
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                         var primitive = primitives_1[_i];
                         var primitive = primitives_1[_i];
-                        var primitiveBabylonMesh = new BABYLON.Mesh((mesh.name || babylonMesh.name) + "_" + primitive.index, this.babylonScene, babylonMesh);
-                        node._primitiveBabylonMeshes.push(primitiveBabylonMesh);
-                        promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, node, mesh, primitive, primitiveBabylonMesh));
-                        this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                        promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index + "}", name + "_primitive" + primitive.index, node, mesh, primitive, function (babylonMesh) {
+                            babylonMesh.parent = babylonTransformNode_1;
+                            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                            node._primitiveBabylonMeshes.push(babylonMesh);
+                        }));
                     }
                     }
                 }
                 }
                 if (node.skin != undefined) {
                 if (node.skin != undefined) {
                     var skin = ArrayItem.Get(context + "/skin", this.gltf.skins, node.skin);
                     var skin = ArrayItem.Get(context + "/skin", this.gltf.skins, node.skin);
                     promises.push(this._loadSkinAsync("#/skins/" + skin.index, node, skin));
                     promises.push(this._loadSkinAsync("#/skins/" + skin.index, node, skin));
                 }
                 }
+                assign(node._babylonTransformNode);
                 this.logClose();
                 this.logClose();
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    _this._forEachPrimitive(node, function (babylonMesh) {
-                        babylonMesh._refreshBoundingInfo(true);
-                    });
+                    return node._babylonTransformNode;
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadMeshPrimitiveAsync = function (context, node, mesh, primitive, babylonMesh) {
+            GLTFLoader.prototype._loadMeshPrimitiveAsync = function (context, name, node, mesh, primitive, assign) {
                 var _this = this;
                 var _this = this;
-                var promises = new Array();
                 this.logOpen("" + context);
                 this.logOpen("" + context);
-                this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
-                promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonGeometry) {
-                    return _this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(function () {
-                        babylonGeometry.applyToMesh(babylonMesh);
-                    });
-                }));
-                var babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
-                if (primitive.material == undefined) {
-                    var babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
-                    if (!babylonMaterial) {
-                        babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
-                        this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
-                    }
-                    babylonMesh.material = babylonMaterial;
+                var canInstance = (node.skin == undefined && !mesh.primitives[0].targets);
+                var babylonAbstractMesh;
+                var promise;
+                var instanceData = primitive._instanceData;
+                if (canInstance && instanceData) {
+                    babylonAbstractMesh = instanceData.babylonSourceMesh.createInstance(name);
+                    promise = instanceData.promise;
                 }
                 }
                 else {
                 else {
-                    var material = ArrayItem.Get(context + "/material", this.gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material.index, material, babylonMesh, babylonDrawMode, function (babylonMaterial) {
-                        babylonMesh.material = babylonMaterial;
+                    var promises = new Array();
+                    var babylonMesh_1 = new BABYLON.Mesh(name, this.babylonScene);
+                    this._createMorphTargets(context, node, mesh, primitive, babylonMesh_1);
+                    promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh_1).then(function (babylonGeometry) {
+                        return _this._loadMorphTargetsAsync(context, primitive, babylonMesh_1, babylonGeometry).then(function () {
+                            babylonGeometry.applyToMesh(babylonMesh_1);
+                        });
                     }));
                     }));
+                    var babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
+                    if (primitive.material == undefined) {
+                        var babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
+                        if (!babylonMaterial) {
+                            babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
+                            this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
+                        }
+                        babylonMesh_1.material = babylonMaterial;
+                    }
+                    else {
+                        var material = ArrayItem.Get(context + "/material", this.gltf.materials, primitive.material);
+                        promises.push(this._loadMaterialAsync("#/materials/" + material.index, material, babylonMesh_1, babylonDrawMode, function (babylonMaterial) {
+                            babylonMesh_1.material = babylonMaterial;
+                        }));
+                    }
+                    promise = Promise.all(promises);
+                    if (canInstance) {
+                        primitive._instanceData = {
+                            babylonSourceMesh: babylonMesh_1,
+                            promise: promise
+                        };
+                    }
+                    babylonAbstractMesh = babylonMesh_1;
                 }
                 }
+                this._parent.onMeshLoadedObservable.notifyObservers(babylonAbstractMesh);
+                assign(babylonAbstractMesh);
                 this.logClose();
                 this.logClose();
-                return Promise.all(promises).then(function () { });
+                return promise.then(function () {
+                    return babylonAbstractMesh;
+                });
             };
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
                 var _this = this;
@@ -3654,6 +3684,11 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
             GLTFLoader._LoadTransform = function (node, babylonNode) {
             GLTFLoader._LoadTransform = function (node, babylonNode) {
+                // Ignore the TRS of skinned nodes.
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                if (node.skin != undefined) {
+                    return;
+                }
                 var position = BABYLON.Vector3.Zero();
                 var position = BABYLON.Vector3.Zero();
                 var rotation = BABYLON.Quaternion.Identity();
                 var rotation = BABYLON.Quaternion.Identity();
                 var scaling = BABYLON.Vector3.One();
                 var scaling = BABYLON.Vector3.One();
@@ -3682,45 +3717,45 @@ var BABYLON;
                     _this._forEachPrimitive(node, function (babylonMesh) {
                     _this._forEachPrimitive(node, function (babylonMesh) {
                         babylonMesh.skeleton = skeleton;
                         babylonMesh.skeleton = skeleton;
                     });
                     });
-                    // Ignore the TRS of skinned nodes.
-                    // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                    node._babylonMesh.position = BABYLON.Vector3.Zero();
-                    node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
-                    node._babylonMesh.scaling = BABYLON.Vector3.One();
                 };
                 };
-                if (skin._promise) {
-                    return skin._promise.then(function () {
-                        assignSkeleton(skin._babylonSkeleton);
+                if (skin._data) {
+                    var data_1 = skin._data;
+                    return data_1.promise.then(function () {
+                        assignSkeleton(data_1.babylonSkeleton);
                     });
                     });
                 }
                 }
                 var skeletonId = "skeleton" + skin.index;
                 var skeletonId = "skeleton" + skin.index;
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
-                skin._babylonSkeleton = babylonSkeleton;
-                this._loadBones(context, skin);
+                this._loadBones(context, skin, babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
-                return (skin._promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
+                var promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
                     _this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
                     _this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
-                }));
+                });
+                skin._data = {
+                    babylonSkeleton: babylonSkeleton,
+                    promise: promise
+                };
+                return promise;
             };
             };
-            GLTFLoader.prototype._loadBones = function (context, skin) {
+            GLTFLoader.prototype._loadBones = function (context, skin, babylonSkeleton) {
                 var babylonBones = {};
                 var babylonBones = {};
                 for (var _i = 0, _a = skin.joints; _i < _a.length; _i++) {
                 for (var _i = 0, _a = skin.joints; _i < _a.length; _i++) {
                     var index = _a[_i];
                     var index = _a[_i];
                     var node = ArrayItem.Get(context + "/joints/" + index, this.gltf.nodes, index);
                     var node = ArrayItem.Get(context + "/joints/" + index, this.gltf.nodes, index);
-                    this._loadBone(node, skin, babylonBones);
+                    this._loadBone(node, skin, babylonSkeleton, babylonBones);
                 }
                 }
             };
             };
-            GLTFLoader.prototype._loadBone = function (node, skin, babylonBones) {
+            GLTFLoader.prototype._loadBone = function (node, skin, babylonSkeleton, babylonBones) {
                 var babylonBone = babylonBones[node.index];
                 var babylonBone = babylonBones[node.index];
                 if (babylonBone) {
                 if (babylonBone) {
                     return babylonBone;
                     return babylonBone;
                 }
                 }
                 var babylonParentBone = null;
                 var babylonParentBone = null;
-                if (node.parent && node.parent._babylonMesh !== this._rootBabylonMesh) {
-                    babylonParentBone = this._loadBone(node.parent, skin, babylonBones);
+                if (node.parent && node.parent._babylonTransformNode !== this._rootBabylonMesh) {
+                    babylonParentBone = this._loadBone(node.parent, skin, babylonSkeleton, babylonBones);
                 }
                 }
                 var boneIndex = skin.joints.indexOf(node.index);
                 var boneIndex = skin.joints.indexOf(node.index);
-                babylonBone = new BABYLON.Bone(node.name || "joint" + node.index, skin._babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
+                babylonBone = new BABYLON.Bone(node.name || "joint" + node.index, babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
                 babylonBones[node.index] = babylonBone;
                 babylonBones[node.index] = babylonBone;
                 node._babylonBones = node._babylonBones || [];
                 node._babylonBones = node._babylonBones || [];
                 node._babylonBones.push(babylonBone);
                 node._babylonBones.push(babylonBone);
@@ -3851,7 +3886,7 @@ var BABYLON;
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 // Ignore animations that have no animation targets.
                 // Ignore animations that have no animation targets.
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
-                    (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonMesh)) {
+                    (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
                     return Promise.resolve();
                     return Promise.resolve();
                 }
                 }
                 // Ignore animations targeting TRS of skinned nodes.
                 // Ignore animations targeting TRS of skinned nodes.
@@ -3982,8 +4017,10 @@ var BABYLON;
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
                         babylonAnimation.setKeys(keys);
-                        if (targetNode._babylonBones) {
-                            var babylonAnimationTargets = [targetNode._babylonMesh].concat(targetNode._babylonBones);
+                        var babylonTransformNode = targetNode._babylonTransformNode;
+                        var babylonBones = targetNode._babylonBones;
+                        if (babylonBones) {
+                            var babylonAnimationTargets = [babylonTransformNode].concat(babylonBones);
                             for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
                             for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
                                 var babylonAnimationTarget = babylonAnimationTargets_1[_i];
                                 var babylonAnimationTarget = babylonAnimationTargets_1[_i];
                                 babylonAnimationTarget.animations.push(babylonAnimation);
                                 babylonAnimationTarget.animations.push(babylonAnimation);
@@ -3991,8 +4028,8 @@ var BABYLON;
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                         }
                         }
                         else {
                         else {
-                            targetNode._babylonMesh.animations.push(babylonAnimation);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonMesh);
+                            babylonTransformNode.animations.push(babylonAnimation);
+                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
                         }
                         }
                     }
                     }
                 });
                 });
@@ -4195,30 +4232,30 @@ var BABYLON;
                 if (extensionPromise) {
                 if (extensionPromise) {
                     return extensionPromise;
                     return extensionPromise;
                 }
                 }
-                material._babylonData = material._babylonData || {};
-                var babylonData = material._babylonData[babylonDrawMode];
+                material._data = material._data || {};
+                var babylonData = material._data[babylonDrawMode];
                 if (!babylonData) {
                 if (!babylonData) {
                     this.logOpen(context + " " + (material.name || ""));
                     this.logOpen(context + " " + (material.name || ""));
                     var babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                     var babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                     babylonData = {
                     babylonData = {
-                        material: babylonMaterial,
-                        meshes: [],
+                        babylonMaterial: babylonMaterial,
+                        babylonMeshes: [],
                         promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                         promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                     };
                     };
-                    material._babylonData[babylonDrawMode] = babylonData;
+                    material._data[babylonDrawMode] = babylonData;
                     this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this.logClose();
                     this.logClose();
                 }
                 }
-                babylonData.meshes.push(babylonMesh);
+                babylonData.babylonMeshes.push(babylonMesh);
                 babylonMesh.onDisposeObservable.addOnce(function () {
                 babylonMesh.onDisposeObservable.addOnce(function () {
-                    var index = babylonData.meshes.indexOf(babylonMesh);
+                    var index = babylonData.babylonMeshes.indexOf(babylonMesh);
                     if (index !== -1) {
                     if (index !== -1) {
-                        babylonData.meshes.splice(index, 1);
+                        babylonData.babylonMeshes.splice(index, 1);
                     }
                     }
                 });
                 });
-                assign(babylonData.material);
+                assign(babylonData.babylonMaterial);
                 return babylonData.promise.then(function () {
                 return babylonData.promise.then(function () {
-                    return babylonData.material;
+                    return babylonData.babylonMaterial;
                 });
                 });
             };
             };
             GLTFLoader.prototype._createDefaultMaterial = function (name, babylonDrawMode) {
             GLTFLoader.prototype._createDefaultMaterial = function (name, babylonDrawMode) {
@@ -4611,14 +4648,14 @@ var BABYLON;
                 if (this.gltf.materials) {
                 if (this.gltf.materials) {
                     for (var _i = 0, _a = this.gltf.materials; _i < _a.length; _i++) {
                     for (var _i = 0, _a = this.gltf.materials; _i < _a.length; _i++) {
                         var material = _a[_i];
                         var material = _a[_i];
-                        if (material._babylonData) {
-                            for (var babylonDrawMode in material._babylonData) {
-                                var babylonData = material._babylonData[babylonDrawMode];
-                                for (var _b = 0, _c = babylonData.meshes; _b < _c.length; _b++) {
+                        if (material._data) {
+                            for (var babylonDrawMode in material._data) {
+                                var babylonData = material._data[babylonDrawMode];
+                                for (var _b = 0, _c = babylonData.babylonMeshes; _b < _c.length; _b++) {
                                     var babylonMesh = _c[_b];
                                     var babylonMesh = _c[_b];
                                     // Ensure nonUniformScaling is set if necessary.
                                     // Ensure nonUniformScaling is set if necessary.
                                     babylonMesh.computeWorldMatrix(true);
                                     babylonMesh.computeWorldMatrix(true);
-                                    var babylonMaterial = babylonData.material;
+                                    var babylonMaterial = babylonData.babylonMaterial;
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                     if (this._parent.useClipPlane) {
                                     if (this._parent.useClipPlane) {
                                         promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                         promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -4918,16 +4955,18 @@ var BABYLON;
                                     _this._nodeIndexLOD = indexLOD;
                                     _this._nodeIndexLOD = indexLOD;
                                     _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                                     _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                                 }
                                 }
-                                var promise = _this._loader.loadNodeAsync("#/nodes/" + nodeLOD.index, nodeLOD).then(function (babylonMesh) {
+                                var assign_1 = function (babylonTransformNode) { babylonTransformNode.setEnabled(false); };
+                                var promise = _this._loader.loadNodeAsync("#/nodes/" + nodeLOD.index, nodeLOD, assign_1).then(function (babylonMesh) {
                                     if (indexLOD !== 0) {
                                     if (indexLOD !== 0) {
                                         // TODO: should not rely on _babylonMesh
                                         // TODO: should not rely on _babylonMesh
                                         var previousNodeLOD = nodeLODs[indexLOD - 1];
                                         var previousNodeLOD = nodeLODs[indexLOD - 1];
-                                        if (previousNodeLOD._babylonMesh) {
-                                            previousNodeLOD._babylonMesh.dispose();
-                                            delete previousNodeLOD._babylonMesh;
+                                        if (previousNodeLOD._babylonTransformNode) {
+                                            previousNodeLOD._babylonTransformNode.dispose();
+                                            delete previousNodeLOD._babylonTransformNode;
                                             _this._disposeUnusedMaterials();
                                             _this._disposeUnusedMaterials();
                                         }
                                         }
                                     }
                                     }
+                                    babylonMesh.setEnabled(true);
                                     return babylonMesh;
                                     return babylonMesh;
                                 });
                                 });
                                 if (indexLOD === 0) {
                                 if (indexLOD === 0) {
@@ -4969,11 +5008,11 @@ var BABYLON;
                                 }).then(function (babylonMaterial) {
                                 }).then(function (babylonMaterial) {
                                     if (indexLOD !== 0) {
                                     if (indexLOD !== 0) {
                                         assign(babylonMaterial);
                                         assign(babylonMaterial);
-                                        // TODO: should not rely on _babylonData
-                                        var previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData;
-                                        if (previousBabylonDataLOD[babylonDrawMode]) {
-                                            previousBabylonDataLOD[babylonDrawMode].material.dispose();
-                                            delete previousBabylonDataLOD[babylonDrawMode];
+                                        // TODO: should not rely on _data
+                                        var previousDataLOD = materialLODs[indexLOD - 1]._data;
+                                        if (previousDataLOD[babylonDrawMode]) {
+                                            previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
+                                            delete previousDataLOD[babylonDrawMode];
                                         }
                                         }
                                     }
                                     }
                                     return babylonMaterial;
                                     return babylonMaterial;
@@ -5034,17 +5073,17 @@ var BABYLON;
                         return properties;
                         return properties;
                     };
                     };
                     MSFT_lod.prototype._disposeUnusedMaterials = function () {
                     MSFT_lod.prototype._disposeUnusedMaterials = function () {
-                        // TODO: should not rely on _babylonData
+                        // TODO: should not rely on _data
                         var materials = this._loader.gltf.materials;
                         var materials = this._loader.gltf.materials;
                         if (materials) {
                         if (materials) {
                             for (var _i = 0, materials_1 = materials; _i < materials_1.length; _i++) {
                             for (var _i = 0, materials_1 = materials; _i < materials_1.length; _i++) {
                                 var material = materials_1[_i];
                                 var material = materials_1[_i];
-                                if (material._babylonData) {
-                                    for (var drawMode in material._babylonData) {
-                                        var babylonData = material._babylonData[drawMode];
-                                        if (babylonData.meshes.length === 0) {
-                                            babylonData.material.dispose(false, true);
-                                            delete material._babylonData[drawMode];
+                                if (material._data) {
+                                    for (var drawMode in material._data) {
+                                        var data = material._data[drawMode];
+                                        if (data.babylonMeshes.length === 0) {
+                                            data.babylonMaterial.dispose(false, true);
+                                            delete material._data[drawMode];
                                         }
                                         }
                                     }
                                     }
                                 }
                                 }

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 21 - 15
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -1143,10 +1143,10 @@ declare module BABYLON.GLTF2.Loader {
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         emissiveTexture?: ITextureInfo;
         emissiveTexture?: ITextureInfo;
         /** @hidden */
         /** @hidden */
-        _babylonData?: {
-            [drawMode: number]: {
-                material: Material;
-                meshes: AbstractMesh[];
+        _data?: {
+            [babylonDrawMode: number]: {
+                babylonMaterial: Material;
+                babylonMeshes: AbstractMesh[];
                 promise: Promise<void>;
                 promise: Promise<void>;
             };
             };
         };
         };
@@ -1161,6 +1161,11 @@ declare module BABYLON.GLTF2.Loader {
      * Loader interface with additional members.
      * Loader interface with additional members.
      */
      */
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
+        /** @hidden */
+        _instanceData?: {
+            babylonSourceMesh: Mesh;
+            promise: Promise<any>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -1171,9 +1176,9 @@ declare module BABYLON.GLTF2.Loader {
          */
          */
         parent?: INode;
         parent?: INode;
         /** @hidden */
         /** @hidden */
-        _babylonMesh?: Mesh;
+        _babylonTransformNode?: TransformNode;
         /** @hidden */
         /** @hidden */
-        _primitiveBabylonMeshes?: Mesh[];
+        _primitiveBabylonMeshes?: AbstractMesh[];
         /** @hidden */
         /** @hidden */
         _babylonBones?: Bone[];
         _babylonBones?: Bone[];
         /** @hidden */
         /** @hidden */
@@ -1203,9 +1208,10 @@ declare module BABYLON.GLTF2.Loader {
      */
      */
     interface ISkin extends GLTF2.ISkin, IArrayItem {
     interface ISkin extends GLTF2.ISkin, IArrayItem {
         /** @hidden */
         /** @hidden */
-        _babylonSkeleton?: Skeleton;
-        /** @hidden */
-        _promise?: Promise<void>;
+        _data?: {
+            babylonSkeleton: Skeleton;
+            promise: Promise<void>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -1339,7 +1345,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
          * @returns A promise that resolves with the loaded Babylon mesh when the load is complete
          */
          */
-        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonMesh: Mesh) => void): Promise<Mesh>;
+        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonTransformNode: TransformNode) => void): Promise<TransformNode>;
         private _loadMeshAsync;
         private _loadMeshAsync;
         private _loadMeshPrimitiveAsync;
         private _loadMeshPrimitiveAsync;
         private _loadVertexDataAsync;
         private _loadVertexDataAsync;
@@ -1537,9 +1543,9 @@ declare module BABYLON.GLTF2 {
          * @param context The context when loading the asset
          * @param context The context when loading the asset
          * @param node The glTF node property
          * @param node The glTF node property
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
+         * @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: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync?(context: string, node: Loader.INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /**
         /**
          * Define this method to modify the default behavior when loading cameras.
          * Define this method to modify the default behavior when loading cameras.
          * @param context The context when loading the asset
          * @param context The context when loading the asset
@@ -1649,7 +1655,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onReady(): void;
         onReady(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         /** @hidden */
@@ -1710,7 +1716,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         private _loadClipAsync;
         private _loadClipAsync;
@@ -1802,7 +1808,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onLoading(): void;
         onLoading(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
     }
     }
 }
 }
 
 

+ 178 - 139
dist/preview release/loaders/babylonjs.loaders.js

@@ -4224,6 +4224,9 @@ var BABYLON;
                         promises.push(_this._compileShadowGeneratorsAsync());
                         promises.push(_this._compileShadowGeneratorsAsync());
                     }
                     }
                     var resultPromise = Promise.all(promises).then(function () {
                     var resultPromise = Promise.all(promises).then(function () {
+                        if (_this._rootBabylonMesh) {
+                            _this._rootBabylonMesh.setEnabled(true);
+                        }
                         _this._setState(BABYLON.GLTFLoaderState.READY);
                         _this._setState(BABYLON.GLTFLoaderState.READY);
                         _this._extensionsOnReady();
                         _this._extensionsOnReady();
                         _this._startAnimations();
                         _this._startAnimations();
@@ -4333,8 +4336,9 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._createRootNode = function () {
             GLTFLoader.prototype._createRootNode = function () {
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this.babylonScene);
                 this._rootBabylonMesh = new BABYLON.Mesh("__root__", this.babylonScene);
+                this._rootBabylonMesh.setEnabled(false);
                 var rootNode = {
                 var rootNode = {
-                    _babylonMesh: this._rootBabylonMesh,
+                    _babylonTransformNode: this._rootBabylonMesh,
                     index: -1
                     index: -1
                 };
                 };
                 switch (this._parent.coordinateSystemMode) {
                 switch (this._parent.coordinateSystemMode) {
@@ -4391,8 +4395,8 @@ var BABYLON;
                         callback(babylonMesh);
                         callback(babylonMesh);
                     }
                     }
                 }
                 }
-                else {
-                    callback(node._babylonMesh);
+                else if (node._babylonTransformNode instanceof BABYLON.AbstractMesh) {
+                    callback(node._babylonTransformNode);
                 }
                 }
             };
             };
             GLTFLoader.prototype._getMeshes = function () {
             GLTFLoader.prototype._getMeshes = function () {
@@ -4403,15 +4407,9 @@ var BABYLON;
                 if (nodes) {
                 if (nodes) {
                     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
                     for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
                         var node = nodes_1[_i];
                         var node = nodes_1[_i];
-                        if (node._babylonMesh) {
-                            meshes.push(node._babylonMesh);
-                        }
-                        if (node._primitiveBabylonMeshes) {
-                            for (var _a = 0, _b = node._primitiveBabylonMeshes; _a < _b.length; _a++) {
-                                var babylonMesh = _b[_a];
-                                meshes.push(babylonMesh);
-                            }
-                        }
+                        this._forEachPrimitive(node, function (babylonMesh) {
+                            meshes.push(babylonMesh);
+                        });
                     }
                     }
                 }
                 }
                 return meshes;
                 return meshes;
@@ -4422,8 +4420,8 @@ var BABYLON;
                 if (skins) {
                 if (skins) {
                     for (var _i = 0, skins_1 = skins; _i < skins_1.length; _i++) {
                     for (var _i = 0, skins_1 = skins; _i < skins_1.length; _i++) {
                         var skin = skins_1[_i];
                         var skin = skins_1[_i];
-                        if (skin._babylonSkeleton) {
-                            skeletons.push(skin._babylonSkeleton);
+                        if (skin._data) {
+                            skeletons.push(skin._data.babylonSkeleton);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -4483,113 +4481,145 @@ var BABYLON;
                 if (extensionPromise) {
                 if (extensionPromise) {
                     return extensionPromise;
                     return extensionPromise;
                 }
                 }
-                if (node._babylonMesh) {
+                if (node._babylonTransformNode) {
                     throw new Error(context + ": Invalid recursive node hierarchy");
                     throw new Error(context + ": Invalid recursive node hierarchy");
                 }
                 }
                 var promises = new Array();
                 var promises = new Array();
                 this.logOpen(context + " " + (node.name || ""));
                 this.logOpen(context + " " + (node.name || ""));
-                var babylonMesh = new BABYLON.Mesh(node.name || "node" + node.index, this.babylonScene);
-                node._babylonMesh = babylonMesh;
-                babylonMesh.setEnabled(false);
-                GLTFLoader._LoadTransform(node, babylonMesh);
-                if (node.mesh != undefined) {
-                    var mesh = ArrayItem.Get(context + "/mesh", this.gltf.meshes, node.mesh);
-                    promises.push(this._loadMeshAsync("#/meshes/" + mesh.index, node, mesh, babylonMesh));
-                }
-                if (node.camera != undefined) {
-                    var camera = ArrayItem.Get(context + "/camera", this.gltf.cameras, node.camera);
-                    promises.push(this.loadCameraAsync("#/cameras/" + camera.index, camera, function (babylonCamera) {
-                        babylonCamera.parent = babylonMesh;
-                    }));
-                }
-                if (node.children) {
-                    var _loop_1 = function (index) {
-                        var childNode = ArrayItem.Get(context + "/children/" + index, this_1.gltf.nodes, index);
-                        promises.push(this_1.loadNodeAsync("#/nodes/" + node.index, childNode, function (childBabylonMesh) {
-                            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                            if (childNode.skin != undefined) {
-                                childBabylonMesh.parent = _this._rootBabylonMesh;
-                                return;
-                            }
-                            childBabylonMesh.parent = babylonMesh;
+                var loadNode = function (babylonTransformNode) {
+                    GLTFLoader._LoadTransform(node, babylonTransformNode);
+                    if (node.camera != undefined) {
+                        var camera = ArrayItem.Get(context + "/camera", _this.gltf.cameras, node.camera);
+                        promises.push(_this.loadCameraAsync("#/cameras/" + camera.index, camera, function (babylonCamera) {
+                            babylonCamera.parent = babylonTransformNode;
                         }));
                         }));
-                    };
-                    var this_1 = this;
-                    for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
-                        var index = _a[_i];
-                        _loop_1(index);
                     }
                     }
+                    if (node.children) {
+                        var _loop_1 = function (index) {
+                            var childNode = ArrayItem.Get(context + "/children/" + index, _this.gltf.nodes, index);
+                            promises.push(_this.loadNodeAsync("#/nodes/" + node.index, childNode, function (childBabylonMesh) {
+                                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                                if (childNode.skin != undefined) {
+                                    childBabylonMesh.parent = _this._rootBabylonMesh;
+                                    return;
+                                }
+                                childBabylonMesh.parent = babylonTransformNode;
+                            }));
+                        };
+                        for (var _i = 0, _a = node.children; _i < _a.length; _i++) {
+                            var index = _a[_i];
+                            _loop_1(index);
+                        }
+                    }
+                    assign(babylonTransformNode);
+                };
+                if (node.mesh == undefined) {
+                    var nodeName = node.name || "node" + node.index;
+                    node._babylonTransformNode = new BABYLON.TransformNode(nodeName, this.babylonScene);
+                    loadNode(node._babylonTransformNode);
+                }
+                else {
+                    var mesh = ArrayItem.Get(context + "/mesh", this.gltf.meshes, node.mesh);
+                    promises.push(this._loadMeshAsync("#/meshes/" + mesh.index, node, mesh, loadNode));
                 }
                 }
-                assign(babylonMesh);
-                this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
                 this.logClose();
                 this.logClose();
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    babylonMesh.setEnabled(true);
-                    return babylonMesh;
+                    _this._forEachPrimitive(node, function (babylonMesh) {
+                        babylonMesh.refreshBoundingInfo(true);
+                    });
+                    return node._babylonTransformNode;
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, babylonMesh) {
-                var _this = this;
-                var promises = new Array();
-                this.logOpen(context + " " + (mesh.name || ""));
+            GLTFLoader.prototype._loadMeshAsync = function (context, node, mesh, assign) {
                 var primitives = mesh.primitives;
                 var primitives = mesh.primitives;
-                if (!primitives || primitives.length === 0) {
+                if (!primitives || !primitives.length) {
                     throw new Error(context + ": Primitives are missing");
                     throw new Error(context + ": Primitives are missing");
                 }
                 }
-                ArrayItem.Assign(primitives);
+                if (primitives[0].index == undefined) {
+                    ArrayItem.Assign(primitives);
+                }
+                var promises = new Array();
+                this.logOpen(context + " " + (mesh.name || ""));
+                var name = node.name || "node" + node.index;
                 if (primitives.length === 1) {
                 if (primitives.length === 1) {
-                    var primitive = primitives[0];
-                    promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, node, mesh, primitive, babylonMesh));
+                    var primitive = mesh.primitives[0];
+                    promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index + "}", name, node, mesh, primitive, function (babylonMesh) {
+                        node._babylonTransformNode = babylonMesh;
+                    }));
                 }
                 }
                 else {
                 else {
-                    node._primitiveBabylonMeshes = [];
+                    var babylonTransformNode_1 = new BABYLON.TransformNode(name, this.babylonScene);
+                    node._babylonTransformNode = babylonTransformNode_1;
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                     for (var _i = 0, primitives_1 = primitives; _i < primitives_1.length; _i++) {
                         var primitive = primitives_1[_i];
                         var primitive = primitives_1[_i];
-                        var primitiveBabylonMesh = new BABYLON.Mesh((mesh.name || babylonMesh.name) + "_" + primitive.index, this.babylonScene, babylonMesh);
-                        node._primitiveBabylonMeshes.push(primitiveBabylonMesh);
-                        promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index, node, mesh, primitive, primitiveBabylonMesh));
-                        this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                        promises.push(this._loadMeshPrimitiveAsync(context + "/primitives/" + primitive.index + "}", name + "_primitive" + primitive.index, node, mesh, primitive, function (babylonMesh) {
+                            babylonMesh.parent = babylonTransformNode_1;
+                            node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                            node._primitiveBabylonMeshes.push(babylonMesh);
+                        }));
                     }
                     }
                 }
                 }
                 if (node.skin != undefined) {
                 if (node.skin != undefined) {
                     var skin = ArrayItem.Get(context + "/skin", this.gltf.skins, node.skin);
                     var skin = ArrayItem.Get(context + "/skin", this.gltf.skins, node.skin);
                     promises.push(this._loadSkinAsync("#/skins/" + skin.index, node, skin));
                     promises.push(this._loadSkinAsync("#/skins/" + skin.index, node, skin));
                 }
                 }
+                assign(node._babylonTransformNode);
                 this.logClose();
                 this.logClose();
                 return Promise.all(promises).then(function () {
                 return Promise.all(promises).then(function () {
-                    _this._forEachPrimitive(node, function (babylonMesh) {
-                        babylonMesh._refreshBoundingInfo(true);
-                    });
+                    return node._babylonTransformNode;
                 });
                 });
             };
             };
-            GLTFLoader.prototype._loadMeshPrimitiveAsync = function (context, node, mesh, primitive, babylonMesh) {
+            GLTFLoader.prototype._loadMeshPrimitiveAsync = function (context, name, node, mesh, primitive, assign) {
                 var _this = this;
                 var _this = this;
-                var promises = new Array();
                 this.logOpen("" + context);
                 this.logOpen("" + context);
-                this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
-                promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then(function (babylonGeometry) {
-                    return _this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(function () {
-                        babylonGeometry.applyToMesh(babylonMesh);
-                    });
-                }));
-                var babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
-                if (primitive.material == undefined) {
-                    var babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
-                    if (!babylonMaterial) {
-                        babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
-                        this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                        this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
-                    }
-                    babylonMesh.material = babylonMaterial;
+                var canInstance = (node.skin == undefined && !mesh.primitives[0].targets);
+                var babylonAbstractMesh;
+                var promise;
+                var instanceData = primitive._instanceData;
+                if (canInstance && instanceData) {
+                    babylonAbstractMesh = instanceData.babylonSourceMesh.createInstance(name);
+                    promise = instanceData.promise;
                 }
                 }
                 else {
                 else {
-                    var material = ArrayItem.Get(context + "/material", this.gltf.materials, primitive.material);
-                    promises.push(this._loadMaterialAsync("#/materials/" + material.index, material, babylonMesh, babylonDrawMode, function (babylonMaterial) {
-                        babylonMesh.material = babylonMaterial;
+                    var promises = new Array();
+                    var babylonMesh_1 = new BABYLON.Mesh(name, this.babylonScene);
+                    this._createMorphTargets(context, node, mesh, primitive, babylonMesh_1);
+                    promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh_1).then(function (babylonGeometry) {
+                        return _this._loadMorphTargetsAsync(context, primitive, babylonMesh_1, babylonGeometry).then(function () {
+                            babylonGeometry.applyToMesh(babylonMesh_1);
+                        });
                     }));
                     }));
+                    var babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
+                    if (primitive.material == undefined) {
+                        var babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
+                        if (!babylonMaterial) {
+                            babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
+                            this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                            this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
+                        }
+                        babylonMesh_1.material = babylonMaterial;
+                    }
+                    else {
+                        var material = ArrayItem.Get(context + "/material", this.gltf.materials, primitive.material);
+                        promises.push(this._loadMaterialAsync("#/materials/" + material.index, material, babylonMesh_1, babylonDrawMode, function (babylonMaterial) {
+                            babylonMesh_1.material = babylonMaterial;
+                        }));
+                    }
+                    promise = Promise.all(promises);
+                    if (canInstance) {
+                        primitive._instanceData = {
+                            babylonSourceMesh: babylonMesh_1,
+                            promise: promise
+                        };
+                    }
+                    babylonAbstractMesh = babylonMesh_1;
                 }
                 }
+                this._parent.onMeshLoadedObservable.notifyObservers(babylonAbstractMesh);
+                assign(babylonAbstractMesh);
                 this.logClose();
                 this.logClose();
-                return Promise.all(promises).then(function () { });
+                return promise.then(function () {
+                    return babylonAbstractMesh;
+                });
             };
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
                 var _this = this;
@@ -4716,6 +4746,11 @@ var BABYLON;
                 return Promise.all(promises).then(function () { });
                 return Promise.all(promises).then(function () { });
             };
             };
             GLTFLoader._LoadTransform = function (node, babylonNode) {
             GLTFLoader._LoadTransform = function (node, babylonNode) {
+                // Ignore the TRS of skinned nodes.
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                if (node.skin != undefined) {
+                    return;
+                }
                 var position = BABYLON.Vector3.Zero();
                 var position = BABYLON.Vector3.Zero();
                 var rotation = BABYLON.Quaternion.Identity();
                 var rotation = BABYLON.Quaternion.Identity();
                 var scaling = BABYLON.Vector3.One();
                 var scaling = BABYLON.Vector3.One();
@@ -4744,45 +4779,45 @@ var BABYLON;
                     _this._forEachPrimitive(node, function (babylonMesh) {
                     _this._forEachPrimitive(node, function (babylonMesh) {
                         babylonMesh.skeleton = skeleton;
                         babylonMesh.skeleton = skeleton;
                     });
                     });
-                    // Ignore the TRS of skinned nodes.
-                    // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                    node._babylonMesh.position = BABYLON.Vector3.Zero();
-                    node._babylonMesh.rotationQuaternion = BABYLON.Quaternion.Identity();
-                    node._babylonMesh.scaling = BABYLON.Vector3.One();
                 };
                 };
-                if (skin._promise) {
-                    return skin._promise.then(function () {
-                        assignSkeleton(skin._babylonSkeleton);
+                if (skin._data) {
+                    var data_1 = skin._data;
+                    return data_1.promise.then(function () {
+                        assignSkeleton(data_1.babylonSkeleton);
                     });
                     });
                 }
                 }
                 var skeletonId = "skeleton" + skin.index;
                 var skeletonId = "skeleton" + skin.index;
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
                 var babylonSkeleton = new BABYLON.Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
-                skin._babylonSkeleton = babylonSkeleton;
-                this._loadBones(context, skin);
+                this._loadBones(context, skin, babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
                 assignSkeleton(babylonSkeleton);
-                return (skin._promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
+                var promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then(function (inverseBindMatricesData) {
                     _this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
                     _this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
-                }));
+                });
+                skin._data = {
+                    babylonSkeleton: babylonSkeleton,
+                    promise: promise
+                };
+                return promise;
             };
             };
-            GLTFLoader.prototype._loadBones = function (context, skin) {
+            GLTFLoader.prototype._loadBones = function (context, skin, babylonSkeleton) {
                 var babylonBones = {};
                 var babylonBones = {};
                 for (var _i = 0, _a = skin.joints; _i < _a.length; _i++) {
                 for (var _i = 0, _a = skin.joints; _i < _a.length; _i++) {
                     var index = _a[_i];
                     var index = _a[_i];
                     var node = ArrayItem.Get(context + "/joints/" + index, this.gltf.nodes, index);
                     var node = ArrayItem.Get(context + "/joints/" + index, this.gltf.nodes, index);
-                    this._loadBone(node, skin, babylonBones);
+                    this._loadBone(node, skin, babylonSkeleton, babylonBones);
                 }
                 }
             };
             };
-            GLTFLoader.prototype._loadBone = function (node, skin, babylonBones) {
+            GLTFLoader.prototype._loadBone = function (node, skin, babylonSkeleton, babylonBones) {
                 var babylonBone = babylonBones[node.index];
                 var babylonBone = babylonBones[node.index];
                 if (babylonBone) {
                 if (babylonBone) {
                     return babylonBone;
                     return babylonBone;
                 }
                 }
                 var babylonParentBone = null;
                 var babylonParentBone = null;
-                if (node.parent && node.parent._babylonMesh !== this._rootBabylonMesh) {
-                    babylonParentBone = this._loadBone(node.parent, skin, babylonBones);
+                if (node.parent && node.parent._babylonTransformNode !== this._rootBabylonMesh) {
+                    babylonParentBone = this._loadBone(node.parent, skin, babylonSkeleton, babylonBones);
                 }
                 }
                 var boneIndex = skin.joints.indexOf(node.index);
                 var boneIndex = skin.joints.indexOf(node.index);
-                babylonBone = new BABYLON.Bone(node.name || "joint" + node.index, skin._babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
+                babylonBone = new BABYLON.Bone(node.name || "joint" + node.index, babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
                 babylonBones[node.index] = babylonBone;
                 babylonBones[node.index] = babylonBone;
                 node._babylonBones = node._babylonBones || [];
                 node._babylonBones = node._babylonBones || [];
                 node._babylonBones.push(babylonBone);
                 node._babylonBones.push(babylonBone);
@@ -4913,7 +4948,7 @@ var BABYLON;
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 var targetNode = ArrayItem.Get(context + "/target/node", this.gltf.nodes, channel.target.node);
                 // Ignore animations that have no animation targets.
                 // Ignore animations that have no animation targets.
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
                 if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
-                    (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonMesh)) {
+                    (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
                     return Promise.resolve();
                     return Promise.resolve();
                 }
                 }
                 // Ignore animations targeting TRS of skinned nodes.
                 // Ignore animations targeting TRS of skinned nodes.
@@ -5044,8 +5079,10 @@ var BABYLON;
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var animationName = babylonAnimationGroup.name + "_channel" + babylonAnimationGroup.targetedAnimations.length;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
                         babylonAnimation.setKeys(keys);
-                        if (targetNode._babylonBones) {
-                            var babylonAnimationTargets = [targetNode._babylonMesh].concat(targetNode._babylonBones);
+                        var babylonTransformNode = targetNode._babylonTransformNode;
+                        var babylonBones = targetNode._babylonBones;
+                        if (babylonBones) {
+                            var babylonAnimationTargets = [babylonTransformNode].concat(babylonBones);
                             for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
                             for (var _i = 0, babylonAnimationTargets_1 = babylonAnimationTargets; _i < babylonAnimationTargets_1.length; _i++) {
                                 var babylonAnimationTarget = babylonAnimationTargets_1[_i];
                                 var babylonAnimationTarget = babylonAnimationTargets_1[_i];
                                 babylonAnimationTarget.animations.push(babylonAnimation);
                                 babylonAnimationTarget.animations.push(babylonAnimation);
@@ -5053,8 +5090,8 @@ var BABYLON;
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                             babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                         }
                         }
                         else {
                         else {
-                            targetNode._babylonMesh.animations.push(babylonAnimation);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonMesh);
+                            babylonTransformNode.animations.push(babylonAnimation);
+                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
                         }
                         }
                     }
                     }
                 });
                 });
@@ -5257,30 +5294,30 @@ var BABYLON;
                 if (extensionPromise) {
                 if (extensionPromise) {
                     return extensionPromise;
                     return extensionPromise;
                 }
                 }
-                material._babylonData = material._babylonData || {};
-                var babylonData = material._babylonData[babylonDrawMode];
+                material._data = material._data || {};
+                var babylonData = material._data[babylonDrawMode];
                 if (!babylonData) {
                 if (!babylonData) {
                     this.logOpen(context + " " + (material.name || ""));
                     this.logOpen(context + " " + (material.name || ""));
                     var babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                     var babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                     babylonData = {
                     babylonData = {
-                        material: babylonMaterial,
-                        meshes: [],
+                        babylonMaterial: babylonMaterial,
+                        babylonMeshes: [],
                         promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                         promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                     };
                     };
-                    material._babylonData[babylonDrawMode] = babylonData;
+                    material._data[babylonDrawMode] = babylonData;
                     this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this.logClose();
                     this.logClose();
                 }
                 }
-                babylonData.meshes.push(babylonMesh);
+                babylonData.babylonMeshes.push(babylonMesh);
                 babylonMesh.onDisposeObservable.addOnce(function () {
                 babylonMesh.onDisposeObservable.addOnce(function () {
-                    var index = babylonData.meshes.indexOf(babylonMesh);
+                    var index = babylonData.babylonMeshes.indexOf(babylonMesh);
                     if (index !== -1) {
                     if (index !== -1) {
-                        babylonData.meshes.splice(index, 1);
+                        babylonData.babylonMeshes.splice(index, 1);
                     }
                     }
                 });
                 });
-                assign(babylonData.material);
+                assign(babylonData.babylonMaterial);
                 return babylonData.promise.then(function () {
                 return babylonData.promise.then(function () {
-                    return babylonData.material;
+                    return babylonData.babylonMaterial;
                 });
                 });
             };
             };
             GLTFLoader.prototype._createDefaultMaterial = function (name, babylonDrawMode) {
             GLTFLoader.prototype._createDefaultMaterial = function (name, babylonDrawMode) {
@@ -5673,14 +5710,14 @@ var BABYLON;
                 if (this.gltf.materials) {
                 if (this.gltf.materials) {
                     for (var _i = 0, _a = this.gltf.materials; _i < _a.length; _i++) {
                     for (var _i = 0, _a = this.gltf.materials; _i < _a.length; _i++) {
                         var material = _a[_i];
                         var material = _a[_i];
-                        if (material._babylonData) {
-                            for (var babylonDrawMode in material._babylonData) {
-                                var babylonData = material._babylonData[babylonDrawMode];
-                                for (var _b = 0, _c = babylonData.meshes; _b < _c.length; _b++) {
+                        if (material._data) {
+                            for (var babylonDrawMode in material._data) {
+                                var babylonData = material._data[babylonDrawMode];
+                                for (var _b = 0, _c = babylonData.babylonMeshes; _b < _c.length; _b++) {
                                     var babylonMesh = _c[_b];
                                     var babylonMesh = _c[_b];
                                     // Ensure nonUniformScaling is set if necessary.
                                     // Ensure nonUniformScaling is set if necessary.
                                     babylonMesh.computeWorldMatrix(true);
                                     babylonMesh.computeWorldMatrix(true);
-                                    var babylonMaterial = babylonData.material;
+                                    var babylonMaterial = babylonData.babylonMaterial;
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                     if (this._parent.useClipPlane) {
                                     if (this._parent.useClipPlane) {
                                         promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                         promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -5980,16 +6017,18 @@ var BABYLON;
                                     _this._nodeIndexLOD = indexLOD;
                                     _this._nodeIndexLOD = indexLOD;
                                     _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                                     _this._nodeSignalLODs[indexLOD] = _this._nodeSignalLODs[indexLOD] || new BABYLON.Deferred();
                                 }
                                 }
-                                var promise = _this._loader.loadNodeAsync("#/nodes/" + nodeLOD.index, nodeLOD).then(function (babylonMesh) {
+                                var assign_1 = function (babylonTransformNode) { babylonTransformNode.setEnabled(false); };
+                                var promise = _this._loader.loadNodeAsync("#/nodes/" + nodeLOD.index, nodeLOD, assign_1).then(function (babylonMesh) {
                                     if (indexLOD !== 0) {
                                     if (indexLOD !== 0) {
                                         // TODO: should not rely on _babylonMesh
                                         // TODO: should not rely on _babylonMesh
                                         var previousNodeLOD = nodeLODs[indexLOD - 1];
                                         var previousNodeLOD = nodeLODs[indexLOD - 1];
-                                        if (previousNodeLOD._babylonMesh) {
-                                            previousNodeLOD._babylonMesh.dispose();
-                                            delete previousNodeLOD._babylonMesh;
+                                        if (previousNodeLOD._babylonTransformNode) {
+                                            previousNodeLOD._babylonTransformNode.dispose();
+                                            delete previousNodeLOD._babylonTransformNode;
                                             _this._disposeUnusedMaterials();
                                             _this._disposeUnusedMaterials();
                                         }
                                         }
                                     }
                                     }
+                                    babylonMesh.setEnabled(true);
                                     return babylonMesh;
                                     return babylonMesh;
                                 });
                                 });
                                 if (indexLOD === 0) {
                                 if (indexLOD === 0) {
@@ -6031,11 +6070,11 @@ var BABYLON;
                                 }).then(function (babylonMaterial) {
                                 }).then(function (babylonMaterial) {
                                     if (indexLOD !== 0) {
                                     if (indexLOD !== 0) {
                                         assign(babylonMaterial);
                                         assign(babylonMaterial);
-                                        // TODO: should not rely on _babylonData
-                                        var previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData;
-                                        if (previousBabylonDataLOD[babylonDrawMode]) {
-                                            previousBabylonDataLOD[babylonDrawMode].material.dispose();
-                                            delete previousBabylonDataLOD[babylonDrawMode];
+                                        // TODO: should not rely on _data
+                                        var previousDataLOD = materialLODs[indexLOD - 1]._data;
+                                        if (previousDataLOD[babylonDrawMode]) {
+                                            previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
+                                            delete previousDataLOD[babylonDrawMode];
                                         }
                                         }
                                     }
                                     }
                                     return babylonMaterial;
                                     return babylonMaterial;
@@ -6096,17 +6135,17 @@ var BABYLON;
                         return properties;
                         return properties;
                     };
                     };
                     MSFT_lod.prototype._disposeUnusedMaterials = function () {
                     MSFT_lod.prototype._disposeUnusedMaterials = function () {
-                        // TODO: should not rely on _babylonData
+                        // TODO: should not rely on _data
                         var materials = this._loader.gltf.materials;
                         var materials = this._loader.gltf.materials;
                         if (materials) {
                         if (materials) {
                             for (var _i = 0, materials_1 = materials; _i < materials_1.length; _i++) {
                             for (var _i = 0, materials_1 = materials; _i < materials_1.length; _i++) {
                                 var material = materials_1[_i];
                                 var material = materials_1[_i];
-                                if (material._babylonData) {
-                                    for (var drawMode in material._babylonData) {
-                                        var babylonData = material._babylonData[drawMode];
-                                        if (babylonData.meshes.length === 0) {
-                                            babylonData.material.dispose(false, true);
-                                            delete material._babylonData[drawMode];
+                                if (material._data) {
+                                    for (var drawMode in material._data) {
+                                        var data = material._data[drawMode];
+                                        if (data.babylonMeshes.length === 0) {
+                                            data.babylonMaterial.dispose(false, true);
+                                            delete material._data[drawMode];
                                         }
                                         }
                                     }
                                     }
                                 }
                                 }

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.min.js


+ 21 - 15
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -1150,10 +1150,10 @@ declare module BABYLON.GLTF2.Loader {
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         occlusionTexture?: IMaterialOcclusionTextureInfo;
         emissiveTexture?: ITextureInfo;
         emissiveTexture?: ITextureInfo;
         /** @hidden */
         /** @hidden */
-        _babylonData?: {
-            [drawMode: number]: {
-                material: Material;
-                meshes: AbstractMesh[];
+        _data?: {
+            [babylonDrawMode: number]: {
+                babylonMaterial: Material;
+                babylonMeshes: AbstractMesh[];
                 promise: Promise<void>;
                 promise: Promise<void>;
             };
             };
         };
         };
@@ -1168,6 +1168,11 @@ declare module BABYLON.GLTF2.Loader {
      * Loader interface with additional members.
      * Loader interface with additional members.
      */
      */
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
     interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
+        /** @hidden */
+        _instanceData?: {
+            babylonSourceMesh: Mesh;
+            promise: Promise<any>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -1178,9 +1183,9 @@ declare module BABYLON.GLTF2.Loader {
          */
          */
         parent?: INode;
         parent?: INode;
         /** @hidden */
         /** @hidden */
-        _babylonMesh?: Mesh;
+        _babylonTransformNode?: TransformNode;
         /** @hidden */
         /** @hidden */
-        _primitiveBabylonMeshes?: Mesh[];
+        _primitiveBabylonMeshes?: AbstractMesh[];
         /** @hidden */
         /** @hidden */
         _babylonBones?: Bone[];
         _babylonBones?: Bone[];
         /** @hidden */
         /** @hidden */
@@ -1210,9 +1215,10 @@ declare module BABYLON.GLTF2.Loader {
      */
      */
     interface ISkin extends GLTF2.ISkin, IArrayItem {
     interface ISkin extends GLTF2.ISkin, IArrayItem {
         /** @hidden */
         /** @hidden */
-        _babylonSkeleton?: Skeleton;
-        /** @hidden */
-        _promise?: Promise<void>;
+        _data?: {
+            babylonSkeleton: Skeleton;
+            promise: Promise<void>;
+        };
     }
     }
     /**
     /**
      * Loader interface with additional members.
      * Loader interface with additional members.
@@ -1346,7 +1352,7 @@ declare module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
          * @returns A promise that resolves with the loaded Babylon mesh when the load is complete
          */
          */
-        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonMesh: Mesh) => void): Promise<Mesh>;
+        loadNodeAsync(context: string, node: Loader.INode, assign?: (babylonTransformNode: TransformNode) => void): Promise<TransformNode>;
         private _loadMeshAsync;
         private _loadMeshAsync;
         private _loadMeshPrimitiveAsync;
         private _loadMeshPrimitiveAsync;
         private _loadVertexDataAsync;
         private _loadVertexDataAsync;
@@ -1544,9 +1550,9 @@ declare module BABYLON.GLTF2 {
          * @param context The context when loading the asset
          * @param context The context when loading the asset
          * @param node The glTF node property
          * @param node The glTF node property
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
+         * @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: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync?(context: string, node: Loader.INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /**
         /**
          * Define this method to modify the default behavior when loading cameras.
          * Define this method to modify the default behavior when loading cameras.
          * @param context The context when loading the asset
          * @param context The context when loading the asset
@@ -1656,7 +1662,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onReady(): void;
         onReady(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         _loadMaterialAsync(context: string, material: IMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<Material>>;
         /** @hidden */
         /** @hidden */
@@ -1717,7 +1723,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         loadSceneAsync(context: string, scene: IScene): Nullable<Promise<void>>;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
         /** @hidden */
         /** @hidden */
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         loadAnimationAsync(context: string, animation: IAnimation): Nullable<Promise<AnimationGroup>>;
         private _loadClipAsync;
         private _loadClipAsync;
@@ -1809,7 +1815,7 @@ declare module BABYLON.GLTF2.Loader.Extensions {
         /** @hidden */
         /** @hidden */
         onLoading(): void;
         onLoading(): void;
         /** @hidden */
         /** @hidden */
-        loadNodeAsync(context: string, node: INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync(context: string, node: INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>>;
     }
     }
 }
 }
 
 

+ 3 - 3
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-loaders",
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.4",
-        "babylonjs": "4.0.0-alpha.4"
+        "babylonjs-gltf2interface": "4.0.0-alpha.6",
+        "babylonjs": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 2 - 2
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-materials",
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.4"
+        "babylonjs": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 2 - 2
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-post-process",
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.4"
+        "babylonjs": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 2 - 2
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-procedural-textures",
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.4"
+        "babylonjs": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

+ 3 - 3
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-serializers",
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     ],
     "license": "Apache-2.0",
     "license": "Apache-2.0",
     "dependencies": {
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.4",
-        "babylonjs-gltf2interface": "4.0.0-alpha.4"
+        "babylonjs": "4.0.0-alpha.6",
+        "babylonjs-gltf2interface": "4.0.0-alpha.6"
     },
     },
     "engines": {
     "engines": {
         "node": "*"
         "node": "*"

File diff suppressed because it is too large
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


+ 22 - 3
dist/preview release/what's new.md

@@ -13,6 +13,7 @@
   - webXRExperienceHelper to setup a default XR experience ([TrevorDev](https://github.com/TrevorDev))
   - webXRExperienceHelper to setup a default XR experience ([TrevorDev](https://github.com/TrevorDev))
   - WebXREnterExitUI and WebXRManagedOutputCanvas classes to configure the XR experience ([TrevorDev](https://github.com/TrevorDev))
   - WebXREnterExitUI and WebXRManagedOutputCanvas classes to configure the XR experience ([TrevorDev](https://github.com/TrevorDev))
   - WebXRInput manage controllers for the XR experience ([TrevorDev](https://github.com/TrevorDev))
   - WebXRInput manage controllers for the XR experience ([TrevorDev](https://github.com/TrevorDev))
+  - WebXR camera rotation using parent container ([TrevorDev](https://github.com/TrevorDev))
 - GUI:
 - GUI:
   - Added new [ImageBasedSlider](http://doc.babylonjs.com/how_to/gui#imagebasedslider) to let users customize sliders using images ([Deltakosh](https://github.com/deltakosh))
   - Added new [ImageBasedSlider](http://doc.babylonjs.com/how_to/gui#imagebasedslider) to let users customize sliders using images ([Deltakosh](https://github.com/deltakosh))
 
 
@@ -23,9 +24,11 @@
 - Added `button.image` and `button.textBlock` to simplify access to button internal parts ([Deltakosh](https://github.com/deltakosh))
 - Added `button.image` and `button.textBlock` to simplify access to button internal parts ([Deltakosh](https://github.com/deltakosh))
 - Added `sldier.displayThumb` to show/hide slider's thumb ([Deltakosh](https://github.com/deltakosh))
 - Added `sldier.displayThumb` to show/hide slider's thumb ([Deltakosh](https://github.com/deltakosh))
 - Added `grid.rowCount`, `grid.columnCount` and `grid.getChildrenAt()` ([Deltakosh](https://github.com/deltakosh))
 - Added `grid.rowCount`, `grid.columnCount` and `grid.getChildrenAt()` ([Deltakosh](https://github.com/deltakosh))
+- Added `Control.AllowAlphaInheritance` to let users control the way alpha is used (inherited or not) ([Deltakosh](https://github.com/deltakosh))
 
 
 ### Core Engine
 ### Core Engine
 
 
+- Added support for utility layer for SkeletonViewer ([Deltakosh](https://github.com/deltakosh))
 - Improved shader precision detection ([Deltakosh](https://github.com/deltakosh))
 - Improved shader precision detection ([Deltakosh](https://github.com/deltakosh))
 - Added support for bone matrix texture. Now skeletons will use a texture instead of uniforms when possible ([Deltakosh](https://github.com/deltakosh))
 - Added support for bone matrix texture. Now skeletons will use a texture instead of uniforms when possible ([Deltakosh](https://github.com/deltakosh))
 - Refactored of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
 - Refactored of the SolidParticleSystem code for performance and code quality improvement ([barroij](https://github.com/barroij))
@@ -46,9 +49,13 @@
   - Added an `Vector3.UnprojectRayToRef` static function to avoid computing and inverting the projection matrix twice when updating a Ray.
   - Added an `Vector3.UnprojectRayToRef` static function to avoid computing and inverting the projection matrix twice when updating a Ray.
 - Align `BoundingBox` and `BoundingSphere` API and behavior for clarity and simplicity. As a consequence, the `BoundingBox`'s method `setWorldMatrix` has been removed and the underlying world matrix cannot be modified but by calling `reConstruct` or `update`. ([barroij](https://github.com/barroij))
 - Align `BoundingBox` and `BoundingSphere` API and behavior for clarity and simplicity. As a consequence, the `BoundingBox`'s method `setWorldMatrix` has been removed and the underlying world matrix cannot be modified but by calling `reConstruct` or `update`. ([barroij](https://github.com/barroij))
 - Make sure that `Material.markAsDirty` and all the `markXXXDirty` methods early out when `scene.blockMaterialDirtyMechanism` is true. ([barroij](https://github.com/barroij))
 - Make sure that `Material.markAsDirty` and all the `markXXXDirty` methods early out when `scene.blockMaterialDirtyMechanism` is true. ([barroij](https://github.com/barroij))
+- Add updateUpVectorFromRotation to target camera to allow the up vector to be computed from rotation ([TrevorDev](https://github.com/TrevorDev))
 
 
 ### glTF Loader
 ### 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
 ### glTF Serializer
 
 
 ### Viewer
 ### Viewer
@@ -56,17 +63,24 @@
 ### Materials Library
 ### Materials Library
 
 
 ## Bug fixes
 ## 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))
 - 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))
+- VR helper only updating vr cameras position when entering vr, rotation was missing ([TrevorDev](https://github.com/TrevorDev))
+- Fix VR controllers after gltfLoader transformNode change ([TrevorDev](https://github.com/TrevorDev))
 
 
 ### Core Engine
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
 - Fixed a bug with pointer up being fire twice ([Deltakosh](https://github.com/deltakosh))
 - Fixed a bug with pointer up being fire twice ([Deltakosh](https://github.com/deltakosh))
 - Fixed a bug with particle systems being update once per camera instead of once per frame ([Deltakosh](https://github.com/deltakosh))
 - Fixed a bug with particle systems being update once per camera instead of once per frame ([Deltakosh](https://github.com/deltakosh))
-- Handle properly the `LinesMesh` `intersectionThreshold` by using its value directly when the intersection against a `Ray` is checked, instead of extending the `BoundingInfo` accordingly ([barroij](https://github.com/barroij))
+- Handle properly the `LinesMesh` `intersectionThreshold` by using its value directly when the intersection against a `Ray` is checked, instead of extending the `BoundingInfo` accordingly + Addded an `InstancesLinesMesh` class used to create instance of `LinesMesh` so that each instance can have its own `intersectionThreshold` value ([barroij](https://github.com/barroij))
 - Fixed the `LineEdgesRenderer` used for edge rendering of `LinesMesh` handle properly LinesMesh made of disconnected lines + Make it work for instance of `LinesMesh` ([barroij](https://github.com/barroij))
 - Fixed the `LineEdgesRenderer` used for edge rendering of `LinesMesh` handle properly LinesMesh made of disconnected lines + Make it work for instance of `LinesMesh` ([barroij](https://github.com/barroij))
 - Fixed `Matrix.toNormalMatrix`function ([barroij](https://github.com/barroij))
 - Fixed `Matrix.toNormalMatrix`function ([barroij](https://github.com/barroij))
 - Add missing effect layer to asset container ([TrevorDev](https://github.com/TrevorDev))
 - Add missing effect layer to asset container ([TrevorDev](https://github.com/TrevorDev))
 - Fixed effect layer compatibility with multi materials ([Sebavan](https://github.com/Sebavan))
 - Fixed effect layer compatibility with multi materials ([Sebavan](https://github.com/Sebavan))
+- Added a `DeepImmutable<T>` type to specifiy that a referenced object should be considered recursively immutable, meaning that all its properties are `readonly` and that if a property is a reference to an object, this object is also recursively immutable. ([barroij](https://github.com/barroij))
 
 
 ### Viewer
 ### Viewer
 
 
@@ -77,8 +91,8 @@
 - `Database.IDBStorageEnabled` is now false by default ([Deltakosh](https://github.com/deltakosh))
 - `Database.IDBStorageEnabled` is now false by default ([Deltakosh](https://github.com/deltakosh))
 - `Database.openAsync` was renamed by `Database.open` ([Deltakosh](https://github.com/deltakosh))
 - `Database.openAsync` was renamed by `Database.open` ([Deltakosh](https://github.com/deltakosh))
 - `scene.database` was renamed to `scene.offlineProvider` ([Deltakosh](https://github.com/deltakosh))
 - `scene.database` was renamed to `scene.offlineProvider` ([Deltakosh](https://github.com/deltakosh))
-- `BoundingBox.setWorldMatrix` was removed. `BoundingBox.getWorldMatrix` now returns a `Readonly<Matrix>` ([barroij](https://github.com/barroij))
-- `Matrix`'s accessor `m` and method `toArray` and `asArray` now returns a `Readonly<Float32Array>` as the matrix underlying array is not supposed to be modified manually from the outside of the class ([barroij](https://github.com/barroij))
+- `BoundingBox.setWorldMatrix` was removed. `BoundingBox.getWorldMatrix` now returns a `DeepImmutable<Matrix>` ([barroij](https://github.com/barroij))
+- `Matrix`'s accessor `m` and method `toArray` and `asArray` now returns a `DeepImmutable<Float32Array>` as the matrix underlying array is not supposed to be modified manually from the outside of the class ([barroij](https://github.com/barroij))
 - Removed some deprecated (flagged since 3.0) properties and functions ([Deltakosh](https://github.com/deltakosh))
 - Removed some deprecated (flagged since 3.0) properties and functions ([Deltakosh](https://github.com/deltakosh))
   - `scene.getInterFramePerfCounter()`: use SceneInstrumentation class instead
   - `scene.getInterFramePerfCounter()`: use SceneInstrumentation class instead
   - `scene.interFramePerfCounter`: use SceneInstrumentation class instead
   - `scene.interFramePerfCounter`: use SceneInstrumentation class instead
@@ -97,3 +111,8 @@
   - `engine.drawCallsPerfCounter`: use SceneInstrumentation class instead
   - `engine.drawCallsPerfCounter`: use SceneInstrumentation class instead
   - `shadowGenerator.useVarianceShadowMap`: use useExponentialShadowMap instead
   - `shadowGenerator.useVarianceShadowMap`: use useExponentialShadowMap instead
   - `shadowGenerator.useBlurVarianceShadowMap`: use useBlurExponentialShadowMap 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 - 2
gui/src/2D/advancedDynamicTexture.ts

@@ -574,9 +574,8 @@ export class AdvancedDynamicTexture extends DynamicTexture {
             if (type === PointerEventTypes.POINTERMOVE) {
             if (type === PointerEventTypes.POINTERMOVE) {
                 if (this._lastControlOver[pointerId]) {
                 if (this._lastControlOver[pointerId]) {
                     this._lastControlOver[pointerId]._onPointerOut(this._lastControlOver[pointerId]);
                     this._lastControlOver[pointerId]._onPointerOut(this._lastControlOver[pointerId]);
+                    delete this._lastControlOver[pointerId];
                 }
                 }
-
-                delete this._lastControlOver[pointerId];
             }
             }
         }
         }
 
 

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

@@ -11,6 +11,11 @@ import { Matrix2D, Vector2WithInfo } from "../math2D";
  * @see http://doc.babylonjs.com/how_to/gui#controls
  * @see http://doc.babylonjs.com/how_to/gui#controls
  */
  */
 export class Control {
 export class Control {
+    /**
+     * Gets or sets a boolean indicating if alpha must be an inherited value (false by default)
+     */
+    public static AllowAlphaInheritance = false;
+
     private _alpha = 1;
     private _alpha = 1;
     private _alphaSet = false;
     private _alphaSet = false;
     private _zIndex = 0;
     private _zIndex = 0;
@@ -985,7 +990,9 @@ export class Control {
             context.fillStyle = this._color;
             context.fillStyle = this._color;
         }
         }
 
 
-        if (this._alphaSet) {
+        if (Control.AllowAlphaInheritance) {
+            context.globalAlpha *= this._alpha;
+        } else if (this._alphaSet) {
             context.globalAlpha = this.parent ? this.parent.alpha * this._alpha : this._alpha;
             context.globalAlpha = this.parent ? this.parent.alpha * this._alpha : this._alpha;
         }
         }
     }
     }
@@ -1269,12 +1276,16 @@ export class Control {
 
 
     /** @hidden */
     /** @hidden */
     public _onPointerOut(target: Control): void {
     public _onPointerOut(target: Control): void {
-        if (!this._isEnabled) {
+        if (!this._isEnabled || target === this) {
             return;
             return;
         }
         }
         this._enterCount = 0;
         this._enterCount = 0;
 
 
-        var canNotify: boolean = this.onPointerOutObservable.notifyObservers(this, -1, target, this);
+        var canNotify: boolean = true;
+
+        if (!target.isAscendant(this)) {
+            canNotify = this.onPointerOutObservable.notifyObservers(this, -1, target, this);
+        }
 
 
         if (canNotify && this.parent != null) { this.parent._onPointerOut(target); }
         if (canNotify && this.parent != null) { this.parent._onPointerOut(target); }
     }
     }

+ 2 - 2
loaders/src/glTF/2.0/Extensions/KHR_lights_punctual.ts

@@ -62,8 +62,8 @@ module BABYLON.GLTF2.Loader.Extensions {
         }
         }
 
 
         /** @hidden */
         /** @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) => {
+        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) => {
                 return this._loader.loadNodeAsync(context, node, (babylonMesh) => {
                     let babylonLight: Light;
                     let babylonLight: Light;
 
 

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

@@ -140,8 +140,8 @@ module BABYLON.GLTF2.Loader.Extensions {
         }
         }
 
 
         /** @hidden */
         /** @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) => {
+        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>>();
                 const promises = new Array<Promise<any>>();
 
 
                 return this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {
                 return this._loader.loadNodeAsync(extensionContext, node, (babylonMesh) => {

+ 21 - 19
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -111,9 +111,9 @@ module BABYLON.GLTF2.Loader.Extensions {
         }
         }
 
 
         /** @hidden */
         /** @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>;
+        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);
                 const nodeLODs = this._getLODs(extensionContext, node, this._loader.gltf.nodes, extension.ids);
                 this._loader.logOpen(`${extensionContext}`);
                 this._loader.logOpen(`${extensionContext}`);
@@ -126,17 +126,19 @@ module BABYLON.GLTF2.Loader.Extensions {
                         this._nodeSignalLODs[indexLOD] = this._nodeSignalLODs[indexLOD] || new Deferred();
                         this._nodeSignalLODs[indexLOD] = this._nodeSignalLODs[indexLOD] || new Deferred();
                     }
                     }
 
 
-                    const promise = this._loader.loadNodeAsync(`#/nodes/${nodeLOD.index}`, nodeLOD).then((babylonMesh) => {
+                    const assign = (babylonTransformNode: TransformNode) => { babylonTransformNode.setEnabled(false); };
+                    const promise = this._loader.loadNodeAsync(`#/nodes/${nodeLOD.index}`, nodeLOD, assign).then((babylonMesh) => {
                         if (indexLOD !== 0) {
                         if (indexLOD !== 0) {
                             // TODO: should not rely on _babylonMesh
                             // TODO: should not rely on _babylonMesh
                             const previousNodeLOD = nodeLODs[indexLOD - 1];
                             const previousNodeLOD = nodeLODs[indexLOD - 1];
-                            if (previousNodeLOD._babylonMesh) {
-                                previousNodeLOD._babylonMesh.dispose();
-                                delete previousNodeLOD._babylonMesh;
+                            if (previousNodeLOD._babylonTransformNode) {
+                                previousNodeLOD._babylonTransformNode.dispose();
+                                delete previousNodeLOD._babylonTransformNode;
                                 this._disposeUnusedMaterials();
                                 this._disposeUnusedMaterials();
                             }
                             }
                         }
                         }
 
 
+                        babylonMesh.setEnabled(true);
                         return babylonMesh;
                         return babylonMesh;
                     });
                     });
 
 
@@ -184,11 +186,11 @@ module BABYLON.GLTF2.Loader.Extensions {
                         if (indexLOD !== 0) {
                         if (indexLOD !== 0) {
                             assign(babylonMaterial);
                             assign(babylonMaterial);
 
 
-                            // TODO: should not rely on _babylonData
-                            const previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData!;
-                            if (previousBabylonDataLOD[babylonDrawMode]) {
-                                previousBabylonDataLOD[babylonDrawMode].material.dispose();
-                                delete previousBabylonDataLOD[babylonDrawMode];
+                            // TODO: should not rely on _data
+                            const previousDataLOD = materialLODs[indexLOD - 1]._data!;
+                            if (previousDataLOD[babylonDrawMode]) {
+                                previousDataLOD[babylonDrawMode].babylonMaterial.dispose();
+                                delete previousDataLOD[babylonDrawMode];
                             }
                             }
                         }
                         }
 
 
@@ -256,16 +258,16 @@ module BABYLON.GLTF2.Loader.Extensions {
         }
         }
 
 
         private _disposeUnusedMaterials(): void {
         private _disposeUnusedMaterials(): void {
-            // TODO: should not rely on _babylonData
+            // TODO: should not rely on _data
             const materials = this._loader.gltf.materials;
             const materials = this._loader.gltf.materials;
             if (materials) {
             if (materials) {
                 for (const material of 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];
+                    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];
                             }
                             }
                         }
                         }
                     }
                     }

+ 181 - 129
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -247,6 +247,10 @@ module BABYLON.GLTF2 {
                 }
                 }
 
 
                 const resultPromise = Promise.all(promises).then(() => {
                 const resultPromise = Promise.all(promises).then(() => {
+                    if (this._rootBabylonMesh) {
+                        this._rootBabylonMesh.setEnabled(true);
+                    }
+
                     this._setState(GLTFLoaderState.READY);
                     this._setState(GLTFLoaderState.READY);
                     this._extensionsOnReady();
                     this._extensionsOnReady();
 
 
@@ -374,9 +378,10 @@ module BABYLON.GLTF2 {
 
 
         private _createRootNode(): Loader.INode {
         private _createRootNode(): Loader.INode {
             this._rootBabylonMesh = new Mesh("__root__", this.babylonScene);
             this._rootBabylonMesh = new Mesh("__root__", this.babylonScene);
+            this._rootBabylonMesh.setEnabled(false);
 
 
             const rootNode: Loader.INode = {
             const rootNode: Loader.INode = {
-                _babylonMesh: this._rootBabylonMesh,
+                _babylonTransformNode: this._rootBabylonMesh,
                 index: -1
                 index: -1
             };
             };
 
 
@@ -434,19 +439,19 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => { });
             return Promise.all(promises).then(() => { });
         }
         }
 
 
-        private _forEachPrimitive(node: Loader.INode, callback: (babylonMesh: Mesh) => void): void {
+        private _forEachPrimitive(node: Loader.INode, callback: (babylonMesh: AbstractMesh) => void): void {
             if (node._primitiveBabylonMeshes) {
             if (node._primitiveBabylonMeshes) {
                 for (const babylonMesh of node._primitiveBabylonMeshes) {
                 for (const babylonMesh of node._primitiveBabylonMeshes) {
                     callback(babylonMesh);
                     callback(babylonMesh);
                 }
                 }
             }
             }
-            else {
-                callback(node._babylonMesh!);
+            else if (node._babylonTransformNode instanceof AbstractMesh) {
+                callback(node._babylonTransformNode);
             }
             }
         }
         }
 
 
-        private _getMeshes(): Mesh[] {
-            const meshes = new Array<Mesh>();
+        private _getMeshes(): AbstractMesh[] {
+            const meshes = new Array<AbstractMesh>();
 
 
             // Root mesh is always first.
             // Root mesh is always first.
             meshes.push(this._rootBabylonMesh);
             meshes.push(this._rootBabylonMesh);
@@ -454,15 +459,9 @@ module BABYLON.GLTF2 {
             const nodes = this.gltf.nodes;
             const nodes = this.gltf.nodes;
             if (nodes) {
             if (nodes) {
                 for (const node of nodes) {
                 for (const node of nodes) {
-                    if (node._babylonMesh) {
-                        meshes.push(node._babylonMesh);
-                    }
-
-                    if (node._primitiveBabylonMeshes) {
-                        for (const babylonMesh of node._primitiveBabylonMeshes) {
-                            meshes.push(babylonMesh);
-                        }
-                    }
+                    this._forEachPrimitive(node, (babylonMesh) => {
+                        meshes.push(babylonMesh);
+                    });
                 }
                 }
             }
             }
 
 
@@ -475,8 +474,8 @@ module BABYLON.GLTF2 {
             const skins = this.gltf.skins;
             const skins = this.gltf.skins;
             if (skins) {
             if (skins) {
                 for (const skin of skins) {
                 for (const skin of skins) {
-                    if (skin._babylonSkeleton) {
-                        skeletons.push(skin._babylonSkeleton);
+                    if (skin._data) {
+                        skeletons.push(skin._data.babylonSkeleton);
                     }
                     }
                 }
                 }
             }
             }
@@ -533,13 +532,13 @@ module BABYLON.GLTF2 {
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
          * @returns A promise that resolves with the loaded Babylon mesh when the load is complete
          */
          */
-        public loadNodeAsync(context: string, node: Loader.INode, assign: (babylonMesh: Mesh) => void = () => { }): Promise<Mesh> {
+        public loadNodeAsync(context: string, node: Loader.INode, assign: (babylonTransformNode: TransformNode) => void = () => { }): Promise<TransformNode> {
             const extensionPromise = this._extensionsLoadNodeAsync(context, node, assign);
             const extensionPromise = this._extensionsLoadNodeAsync(context, node, assign);
             if (extensionPromise) {
             if (extensionPromise) {
                 return extensionPromise;
                 return extensionPromise;
             }
             }
 
 
-            if (node._babylonMesh) {
+            if (node._babylonTransformNode) {
                 throw new Error(`${context}: Invalid recursive node hierarchy`);
                 throw new Error(`${context}: Invalid recursive node hierarchy`);
             }
             }
 
 
@@ -547,72 +546,86 @@ module BABYLON.GLTF2 {
 
 
             this.logOpen(`${context} ${node.name || ""}`);
             this.logOpen(`${context} ${node.name || ""}`);
 
 
-            const babylonMesh = new Mesh(node.name || `node${node.index}`, this.babylonScene);
-            node._babylonMesh = babylonMesh;
+            const loadNode = (babylonTransformNode: TransformNode) => {
+                GLTFLoader._LoadTransform(node, babylonTransformNode);
 
 
-            babylonMesh.setEnabled(false);
-            GLTFLoader._LoadTransform(node, babylonMesh);
+                if (node.camera != undefined) {
+                    const camera = ArrayItem.Get(`${context}/camera`, this.gltf.cameras, node.camera);
+                    promises.push(this.loadCameraAsync(`#/cameras/${camera.index}`, camera, (babylonCamera) => {
+                        babylonCamera.parent = babylonTransformNode;
+                    }));
+                }
 
 
-            if (node.mesh != undefined) {
-                const mesh = ArrayItem.Get(`${context}/mesh`, this.gltf.meshes, node.mesh);
-                promises.push(this._loadMeshAsync(`#/meshes/${mesh.index}`, node, mesh, babylonMesh));
-            }
+                if (node.children) {
+                    for (const index of node.children) {
+                        const childNode = ArrayItem.Get(`${context}/children/${index}`, this.gltf.nodes, index);
+                        promises.push(this.loadNodeAsync(`#/nodes/${node.index}`, childNode, (childBabylonMesh) => {
+                            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+                            if (childNode.skin != undefined) {
+                                childBabylonMesh.parent = this._rootBabylonMesh;
+                                return;
+                            }
 
 
-            if (node.camera != undefined) {
-                const camera = ArrayItem.Get(`${context}/camera`, this.gltf.cameras, node.camera);
-                promises.push(this.loadCameraAsync(`#/cameras/${camera.index}`, camera, (babylonCamera) => {
-                    babylonCamera.parent = babylonMesh;
-                }));
-            }
+                            childBabylonMesh.parent = babylonTransformNode;
+                        }));
+                    }
+                }
 
 
-            if (node.children) {
-                for (const index of node.children) {
-                    const childNode = ArrayItem.Get(`${context}/children/${index}`, this.gltf.nodes, index);
-                    promises.push(this.loadNodeAsync(`#/nodes/${node.index}`, childNode, (childBabylonMesh) => {
-                        // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                        if (childNode.skin != undefined) {
-                            childBabylonMesh.parent = this._rootBabylonMesh;
-                            return;
-                        }
+                assign(babylonTransformNode);
+            };
 
 
-                        childBabylonMesh.parent = babylonMesh;
-                    }));
-                }
+            if (node.mesh == undefined) {
+                const nodeName = node.name || `node${node.index}`;
+                node._babylonTransformNode = new TransformNode(nodeName, this.babylonScene);
+                loadNode(node._babylonTransformNode);
+            }
+            else {
+                const mesh = ArrayItem.Get(`${context}/mesh`, this.gltf.meshes, node.mesh);
+                promises.push(this._loadMeshAsync(`#/meshes/${mesh.index}`, node, mesh, loadNode));
             }
             }
-
-            assign(babylonMesh);
-            this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
 
 
             this.logClose();
             this.logClose();
 
 
             return Promise.all(promises).then(() => {
             return Promise.all(promises).then(() => {
-                babylonMesh.setEnabled(true);
-                return babylonMesh;
+                this._forEachPrimitive(node, (babylonMesh) => {
+                    babylonMesh.refreshBoundingInfo(true);
+                });
+
+                return node._babylonTransformNode!;
             });
             });
         }
         }
 
 
-        private _loadMeshAsync(context: string, node: Loader.INode, mesh: Loader.IMesh, babylonMesh: Mesh): Promise<void> {
+        private _loadMeshAsync(context: string, node: Loader.INode, mesh: Loader.IMesh, assign: (babylonTransformNode: TransformNode) => void): Promise<TransformNode> {
+            const primitives = mesh.primitives;
+            if (!primitives || !primitives.length) {
+                throw new Error(`${context}: Primitives are missing`);
+            }
+
+            if (primitives[0].index == undefined) {
+                ArrayItem.Assign(primitives);
+            }
+
             const promises = new Array<Promise<any>>();
             const promises = new Array<Promise<any>>();
 
 
             this.logOpen(`${context} ${mesh.name || ""}`);
             this.logOpen(`${context} ${mesh.name || ""}`);
 
 
-            const primitives = mesh.primitives;
-            if (!primitives || primitives.length === 0) {
-                throw new Error(`${context}: Primitives are missing`);
-            }
+            const name = node.name || `node${node.index}`;
 
 
-            ArrayItem.Assign(primitives);
             if (primitives.length === 1) {
             if (primitives.length === 1) {
-                const primitive = primitives[0];
-                promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, node, mesh, primitive, babylonMesh));
+                const primitive = mesh.primitives[0];
+                promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}}`, name, node, mesh, primitive, (babylonMesh) => {
+                    node._babylonTransformNode = babylonMesh;
+                }));
             }
             }
             else {
             else {
-                node._primitiveBabylonMeshes = [];
+                const babylonTransformNode = new TransformNode(name, this.babylonScene);
+                node._babylonTransformNode = babylonTransformNode;
                 for (const primitive of primitives) {
                 for (const primitive of primitives) {
-                    const primitiveBabylonMesh = new Mesh(`${mesh.name || babylonMesh.name}_${primitive.index}`, this.babylonScene, babylonMesh);
-                    node._primitiveBabylonMeshes.push(primitiveBabylonMesh);
-                    promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, node, mesh, primitive, primitiveBabylonMesh));
-                    this._parent.onMeshLoadedObservable.notifyObservers(babylonMesh);
+                    promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}}`, `${name}_primitive${primitive.index}`, node, mesh, primitive, (babylonMesh) => {
+                        babylonMesh.parent = babylonTransformNode;
+                        node._primitiveBabylonMeshes = node._primitiveBabylonMeshes || [];
+                        node._primitiveBabylonMeshes.push(babylonMesh);
+                    }));
                 }
                 }
             }
             }
 
 
@@ -621,47 +634,77 @@ module BABYLON.GLTF2 {
                 promises.push(this._loadSkinAsync(`#/skins/${skin.index}`, node, skin));
                 promises.push(this._loadSkinAsync(`#/skins/${skin.index}`, node, skin));
             }
             }
 
 
+            assign(node._babylonTransformNode!);
+
             this.logClose();
             this.logClose();
 
 
             return Promise.all(promises).then(() => {
             return Promise.all(promises).then(() => {
-                this._forEachPrimitive(node, (babylonMesh) => {
-                    babylonMesh._refreshBoundingInfo(true);
-                });
+                return node._babylonTransformNode!;
             });
             });
         }
         }
 
 
-        private _loadMeshPrimitiveAsync(context: string, node: Loader.INode, mesh: Loader.IMesh, primitive: Loader.IMeshPrimitive, babylonMesh: Mesh): Promise<void> {
-            const promises = new Array<Promise<any>>();
-
+        private _loadMeshPrimitiveAsync(context: string, name: string, node: Loader.INode, mesh: Loader.IMesh, primitive: Loader.IMeshPrimitive, assign: (babylonMesh: AbstractMesh) => void): Promise<AbstractMesh> {
             this.logOpen(`${context}`);
             this.logOpen(`${context}`);
 
 
-            this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
-            promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then((babylonGeometry) => {
-                return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(() => {
-                    babylonGeometry.applyToMesh(babylonMesh);
-                });
-            }));
+            const canInstance = (node.skin == undefined && !mesh.primitives[0].targets);
 
 
-            const babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
-            if (primitive.material == undefined) {
-                let babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
-                if (!babylonMaterial) {
-                    babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
-                    this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                    this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
-                }
-                babylonMesh.material = babylonMaterial;
+            let babylonAbstractMesh: AbstractMesh;
+            let promise: Promise<any>;
+
+            const instanceData = primitive._instanceData;
+            if (canInstance && instanceData) {
+                babylonAbstractMesh = instanceData.babylonSourceMesh.createInstance(name);
+                promise = instanceData.promise;
             }
             }
             else {
             else {
-                const material = ArrayItem.Get(`${context}/material`, this.gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync(`#/materials/${material.index}`, material, babylonMesh, babylonDrawMode, (babylonMaterial) => {
-                    babylonMesh.material = babylonMaterial;
+                const promises = new Array<Promise<any>>();
+
+                const babylonMesh = new Mesh(name, this.babylonScene);
+
+                this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
+                promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then((babylonGeometry) => {
+                    return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(() => {
+                        babylonGeometry.applyToMesh(babylonMesh);
+                    });
                 }));
                 }));
+
+                const babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
+                if (primitive.material == undefined) {
+                    let babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
+                    if (!babylonMaterial) {
+                        babylonMaterial = this._createDefaultMaterial("__gltf_default", babylonDrawMode);
+                        this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
+                        this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
+                    }
+                    babylonMesh.material = babylonMaterial;
+                }
+                else {
+                    const material = ArrayItem.Get(`${context}/material`, this.gltf.materials, primitive.material);
+                    promises.push(this._loadMaterialAsync(`#/materials/${material.index}`, material, babylonMesh, babylonDrawMode, (babylonMaterial) => {
+                        babylonMesh.material = babylonMaterial;
+                    }));
+                }
+
+                promise = Promise.all(promises);
+
+                if (canInstance) {
+                    primitive._instanceData = {
+                        babylonSourceMesh: babylonMesh,
+                        promise: promise
+                    };
+                }
+
+                babylonAbstractMesh = babylonMesh;
             }
             }
 
 
+            this._parent.onMeshLoadedObservable.notifyObservers(babylonAbstractMesh);
+            assign(babylonAbstractMesh);
+
             this.logClose();
             this.logClose();
 
 
-            return Promise.all(promises).then(() => { });
+            return promise.then(() => {
+                return babylonAbstractMesh;
+            });
         }
         }
 
 
         private _loadVertexDataAsync(context: string, primitive: Loader.IMeshPrimitive, babylonMesh: Mesh): Promise<Geometry> {
         private _loadVertexDataAsync(context: string, primitive: Loader.IMeshPrimitive, babylonMesh: Mesh): Promise<Geometry> {
@@ -815,6 +858,12 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private static _LoadTransform(node: Loader.INode, babylonNode: TransformNode): void {
         private static _LoadTransform(node: Loader.INode, babylonNode: TransformNode): void {
+            // Ignore the TRS of skinned nodes.
+            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+            if (node.skin != undefined) {
+                return;
+            }
+
             let position = Vector3.Zero();
             let position = Vector3.Zero();
             let rotation = Quaternion.Identity();
             let rotation = Quaternion.Identity();
             let scaling = Vector3.One();
             let scaling = Vector3.One();
@@ -839,53 +888,54 @@ module BABYLON.GLTF2 {
                 this._forEachPrimitive(node, (babylonMesh) => {
                 this._forEachPrimitive(node, (babylonMesh) => {
                     babylonMesh.skeleton = skeleton;
                     babylonMesh.skeleton = skeleton;
                 });
                 });
-
-                // Ignore the TRS of skinned nodes.
-                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
-                node._babylonMesh!.position = Vector3.Zero();
-                node._babylonMesh!.rotationQuaternion = Quaternion.Identity();
-                node._babylonMesh!.scaling = Vector3.One();
             };
             };
 
 
-            if (skin._promise) {
-                return skin._promise.then(() => {
-                    assignSkeleton(skin._babylonSkeleton!);
+            if (skin._data) {
+                const data = skin._data;
+                return data.promise.then(() => {
+                    assignSkeleton(data.babylonSkeleton);
                 });
                 });
             }
             }
 
 
             const skeletonId = `skeleton${skin.index}`;
             const skeletonId = `skeleton${skin.index}`;
             const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
             const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this.babylonScene);
-            skin._babylonSkeleton = babylonSkeleton;
-            this._loadBones(context, skin);
+            this._loadBones(context, skin, babylonSkeleton);
             assignSkeleton(babylonSkeleton);
             assignSkeleton(babylonSkeleton);
 
 
-            return (skin._promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then((inverseBindMatricesData) => {
+            const promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then((inverseBindMatricesData) => {
                 this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
                 this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
-            }));
+            });
+
+            skin._data = {
+                babylonSkeleton: babylonSkeleton,
+                promise: promise
+            };
+
+            return promise;
         }
         }
 
 
-        private _loadBones(context: string, skin: Loader.ISkin): void {
+        private _loadBones(context: string, skin: Loader.ISkin, babylonSkeleton: Skeleton): void {
             const babylonBones: { [index: number]: Bone } = {};
             const babylonBones: { [index: number]: Bone } = {};
             for (const index of skin.joints) {
             for (const index of skin.joints) {
                 const node = ArrayItem.Get(`${context}/joints/${index}`, this.gltf.nodes, index);
                 const node = ArrayItem.Get(`${context}/joints/${index}`, this.gltf.nodes, index);
-                this._loadBone(node, skin, babylonBones);
+                this._loadBone(node, skin, babylonSkeleton, babylonBones);
             }
             }
         }
         }
 
 
-        private _loadBone(node: Loader.INode, skin: Loader.ISkin, babylonBones: { [index: number]: Bone }): Bone {
+        private _loadBone(node: Loader.INode, skin: Loader.ISkin, babylonSkeleton: Skeleton, babylonBones: { [index: number]: Bone }): Bone {
             let babylonBone = babylonBones[node.index];
             let babylonBone = babylonBones[node.index];
             if (babylonBone) {
             if (babylonBone) {
                 return babylonBone;
                 return babylonBone;
             }
             }
 
 
             let babylonParentBone: Nullable<Bone> = null;
             let babylonParentBone: Nullable<Bone> = null;
-            if (node.parent && node.parent._babylonMesh !== this._rootBabylonMesh) {
-                babylonParentBone = this._loadBone(node.parent, skin, babylonBones);
+            if (node.parent && node.parent._babylonTransformNode !== this._rootBabylonMesh) {
+                babylonParentBone = this._loadBone(node.parent, skin, babylonSkeleton, babylonBones);
             }
             }
 
 
             const boneIndex = skin.joints.indexOf(node.index);
             const boneIndex = skin.joints.indexOf(node.index);
 
 
-            babylonBone = new Bone(node.name || `joint${node.index}`, skin._babylonSkeleton!, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
+            babylonBone = new Bone(node.name || `joint${node.index}`, babylonSkeleton, babylonParentBone, this._getNodeMatrix(node), null, null, boneIndex);
             babylonBones[node.index] = babylonBone;
             babylonBones[node.index] = babylonBone;
 
 
             node._babylonBones = node._babylonBones || [];
             node._babylonBones = node._babylonBones || [];
@@ -1045,7 +1095,7 @@ module BABYLON.GLTF2 {
 
 
             // Ignore animations that have no animation targets.
             // Ignore animations that have no animation targets.
             if ((channel.target.path === AnimationChannelTargetPath.WEIGHTS && !targetNode._numMorphTargets) ||
             if ((channel.target.path === AnimationChannelTargetPath.WEIGHTS && !targetNode._numMorphTargets) ||
-                (channel.target.path !== AnimationChannelTargetPath.WEIGHTS && !targetNode._babylonMesh)) {
+                (channel.target.path !== AnimationChannelTargetPath.WEIGHTS && !targetNode._babylonTransformNode)) {
                 return Promise.resolve();
                 return Promise.resolve();
             }
             }
 
 
@@ -1168,7 +1218,7 @@ module BABYLON.GLTF2 {
                             outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                             outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                         })));
                         })));
 
 
-                        this._forEachPrimitive(targetNode, (babylonMesh) => {
+                        this._forEachPrimitive(targetNode, (babylonMesh: Mesh) => {
                             const morphTarget = babylonMesh.morphTargetManager!.getTarget(targetIndex);
                             const morphTarget = babylonMesh.morphTargetManager!.getTarget(targetIndex);
                             const babylonAnimationClone = babylonAnimation.clone();
                             const babylonAnimationClone = babylonAnimation.clone();
                             morphTarget.animations.push(babylonAnimationClone);
                             morphTarget.animations.push(babylonAnimationClone);
@@ -1181,16 +1231,18 @@ module BABYLON.GLTF2 {
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
                     babylonAnimation.setKeys(keys);
 
 
-                    if (targetNode._babylonBones) {
-                        const babylonAnimationTargets = [targetNode._babylonMesh!, ...targetNode._babylonBones];
+                    const babylonTransformNode = targetNode._babylonTransformNode!;
+                    const babylonBones = targetNode._babylonBones;
+                    if (babylonBones) {
+                        const babylonAnimationTargets = [babylonTransformNode, ...babylonBones];
                         for (const babylonAnimationTarget of babylonAnimationTargets) {
                         for (const babylonAnimationTarget of babylonAnimationTargets) {
                             babylonAnimationTarget.animations.push(babylonAnimation);
                             babylonAnimationTarget.animations.push(babylonAnimation);
                         }
                         }
                         babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                         babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonAnimationTargets);
                     }
                     }
                     else {
                     else {
-                        targetNode._babylonMesh!.animations.push(babylonAnimation);
-                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, targetNode._babylonMesh);
+                        babylonTransformNode.animations.push(babylonAnimation);
+                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTransformNode);
                     }
                     }
                 }
                 }
             });
             });
@@ -1429,39 +1481,39 @@ module BABYLON.GLTF2 {
                 return extensionPromise;
                 return extensionPromise;
             }
             }
 
 
-            material._babylonData = material._babylonData || {};
-            let babylonData = material._babylonData[babylonDrawMode];
+            material._data = material._data || {};
+            let babylonData = material._data[babylonDrawMode];
             if (!babylonData) {
             if (!babylonData) {
                 this.logOpen(`${context} ${material.name || ""}`);
                 this.logOpen(`${context} ${material.name || ""}`);
 
 
                 const babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
                 const babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
 
 
                 babylonData = {
                 babylonData = {
-                    material: babylonMaterial,
-                    meshes: [],
+                    babylonMaterial: babylonMaterial,
+                    babylonMeshes: [],
                     promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                     promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial)
                 };
                 };
 
 
-                material._babylonData[babylonDrawMode] = babylonData;
+                material._data[babylonDrawMode] = babylonData;
 
 
                 this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                 this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
 
 
                 this.logClose();
                 this.logClose();
             }
             }
 
 
-            babylonData.meshes.push(babylonMesh);
+            babylonData.babylonMeshes.push(babylonMesh);
 
 
             babylonMesh.onDisposeObservable.addOnce(() => {
             babylonMesh.onDisposeObservable.addOnce(() => {
-                const index = babylonData.meshes.indexOf(babylonMesh);
+                const index = babylonData.babylonMeshes.indexOf(babylonMesh);
                 if (index !== -1) {
                 if (index !== -1) {
-                    babylonData.meshes.splice(index, 1);
+                    babylonData.babylonMeshes.splice(index, 1);
                 }
                 }
             });
             });
 
 
-            assign(babylonData.material);
+            assign(babylonData.babylonMaterial);
 
 
             return babylonData.promise.then(() => {
             return babylonData.promise.then(() => {
-                return babylonData.material;
+                return babylonData.babylonMaterial;
             });
             });
         }
         }
 
 
@@ -1914,14 +1966,14 @@ module BABYLON.GLTF2 {
 
 
             if (this.gltf.materials) {
             if (this.gltf.materials) {
                 for (const material of this.gltf.materials) {
                 for (const material of this.gltf.materials) {
-                    if (material._babylonData) {
-                        for (const babylonDrawMode in material._babylonData) {
-                            const babylonData = material._babylonData[babylonDrawMode];
-                            for (const babylonMesh of babylonData.meshes) {
+                    if (material._data) {
+                        for (const babylonDrawMode in material._data) {
+                            const babylonData = material._data[babylonDrawMode];
+                            for (const babylonMesh of babylonData.babylonMeshes) {
                                 // Ensure nonUniformScaling is set if necessary.
                                 // Ensure nonUniformScaling is set if necessary.
                                 babylonMesh.computeWorldMatrix(true);
                                 babylonMesh.computeWorldMatrix(true);
 
 
-                                const babylonMaterial = babylonData.material;
+                                const babylonMaterial = babylonData.babylonMaterial;
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
                                 if (this._parent.useClipPlane) {
                                 if (this._parent.useClipPlane) {
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
                                     promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
@@ -2002,7 +2054,7 @@ module BABYLON.GLTF2 {
             return this._applyExtensions(scene, (extension) => extension.loadSceneAsync && extension.loadSceneAsync(context, scene));
             return this._applyExtensions(scene, (extension) => extension.loadSceneAsync && extension.loadSceneAsync(context, scene));
         }
         }
 
 
-        private _extensionsLoadNodeAsync(context: string, node: Loader.INode, assign: (babylonMesh: Mesh) => void): Nullable<Promise<Mesh>> {
+        private _extensionsLoadNodeAsync(context: string, node: Loader.INode, assign: (babylonTransformNode: TransformNode) => void): Nullable<Promise<TransformNode>> {
             return this._applyExtensions(node, (extension) => extension.loadNodeAsync && extension.loadNodeAsync(context, node, assign));
             return this._applyExtensions(node, (extension) => extension.loadNodeAsync && extension.loadNodeAsync(context, node, assign));
         }
         }
 
 

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

@@ -28,9 +28,9 @@ module BABYLON.GLTF2 {
          * @param context The context when loading the asset
          * @param context The context when loading the asset
          * @param node The glTF node property
          * @param node The glTF node property
          * @param assign A function called synchronously after parsing the glTF properties
          * @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
+         * @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: Mesh) => void): Nullable<Promise<Mesh>>;
+        loadNodeAsync?(context: string, node: Loader.INode, assign: (babylonMesh: TransformNode) => void): Nullable<Promise<TransformNode>>;
 
 
         /**
         /**
          * Define this method to modify the default behavior when loading cameras.
          * Define this method to modify the default behavior when loading cameras.

+ 15 - 10
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -117,10 +117,10 @@ module BABYLON.GLTF2.Loader {
         emissiveTexture?: ITextureInfo;
         emissiveTexture?: ITextureInfo;
 
 
         /** @hidden */
         /** @hidden */
-        _babylonData?: {
-            [drawMode: number]: {
-                material: Material;
-                meshes: AbstractMesh[];
+        _data?: {
+            [babylonDrawMode: number]: {
+                babylonMaterial: Material;
+                babylonMeshes: AbstractMesh[];
                 promise: Promise<void>;
                 promise: Promise<void>;
             }
             }
         };
         };
@@ -137,6 +137,11 @@ module BABYLON.GLTF2.Loader {
      * Loader interface with additional members.
      * Loader interface with additional members.
      */
      */
     export interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
     export interface IMeshPrimitive extends GLTF2.IMeshPrimitive, IArrayItem {
+        /** @hidden */
+        _instanceData?: {
+            babylonSourceMesh: Mesh;
+            promise: Promise<any>;
+        };
     }
     }
 
 
     /**
     /**
@@ -149,10 +154,10 @@ module BABYLON.GLTF2.Loader {
         parent?: INode;
         parent?: INode;
 
 
         /** @hidden */
         /** @hidden */
-        _babylonMesh?: Mesh;
+        _babylonTransformNode?: TransformNode;
 
 
         /** @hidden */
         /** @hidden */
-        _primitiveBabylonMeshes?: Mesh[];
+        _primitiveBabylonMeshes?: AbstractMesh[];
 
 
         /** @hidden */
         /** @hidden */
         _babylonBones?: Bone[];
         _babylonBones?: Bone[];
@@ -188,10 +193,10 @@ module BABYLON.GLTF2.Loader {
      */
      */
     export interface ISkin extends GLTF2.ISkin, IArrayItem {
     export interface ISkin extends GLTF2.ISkin, IArrayItem {
         /** @hidden */
         /** @hidden */
-        _babylonSkeleton?: Skeleton;
-
-        /** @hidden */
-        _promise?: Promise<void>;
+        _data?: {
+            babylonSkeleton: Skeleton;
+            promise: Promise<void>;
+        };
     }
     }
 
 
     /**
     /**

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     ],
     "name": "babylonjs",
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.0.0-alpha.4",
+    "version": "4.0.0-alpha.6",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 5 - 5
src/Animations/babylon.runtimeAnimation.ts

@@ -3,19 +3,19 @@ module BABYLON {
     // Static values to help the garbage collector
     // Static values to help the garbage collector
 
 
     // Quaternion
     // Quaternion
-    const _staticOffsetValueQuaternion: Readonly<Quaternion> = Object.freeze(new Quaternion(0, 0, 0, 0));
+    const _staticOffsetValueQuaternion: DeepImmutable<Quaternion> = Object.freeze(new Quaternion(0, 0, 0, 0));
 
 
     // Vector3
     // Vector3
-    const _staticOffsetValueVector3: Readonly<Vector3> = Object.freeze(Vector3.Zero());
+    const _staticOffsetValueVector3: DeepImmutable<Vector3> = Object.freeze(Vector3.Zero());
 
 
     // Vector2
     // Vector2
-    const _staticOffsetValueVector2: Readonly<Vector2> = Object.freeze(Vector2.Zero());
+    const _staticOffsetValueVector2: DeepImmutable<Vector2> = Object.freeze(Vector2.Zero());
 
 
     // Size
     // Size
-    const _staticOffsetValueSize: Readonly<Size> = Object.freeze(Size.Zero());
+    const _staticOffsetValueSize: DeepImmutable<Size> = Object.freeze(Size.Zero());
 
 
     // Color3
     // Color3
-    const _staticOffsetValueColor3: Readonly<Color3> = Object.freeze(Color3.Black());
+    const _staticOffsetValueColor3: DeepImmutable<Color3> = Object.freeze(Color3.Black());
 
 
     /**
     /**
      * Defines a runtime animation
      * Defines a runtime animation

+ 23 - 23
src/Audio/babylon.sound.ts

@@ -97,7 +97,7 @@ module BABYLON {
         private _coneOuterAngle: number = 360;
         private _coneOuterAngle: number = 360;
         private _coneOuterGain: number = 0;
         private _coneOuterGain: number = 0;
         private _scene: Scene;
         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 _customAttenuationFunction: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number;
         private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
         private _isOutputConnected = false;
         private _isOutputConnected = false;
@@ -334,9 +334,9 @@ module BABYLON {
                     this._streamingSource.disconnect();
                     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;
                 }
                 }
             }
             }
         }
         }
@@ -560,17 +560,17 @@ module BABYLON {
         public setLocalDirectionToMesh(newLocalDirection: Vector3): void {
         public setLocalDirectionToMesh(newLocalDirection: Vector3): void {
             this._localDirection = newLocalDirection;
             this._localDirection = newLocalDirection;
 
 
-            if (Engine.audioEngine.canUseWebAudio && this._connectedMesh && this.isPlaying) {
+            if (Engine.audioEngine.canUseWebAudio && this._connectedTransformNode && this.isPlaying) {
                 this._updateDirection();
                 this._updateDirection();
             }
             }
         }
         }
 
 
         private _updateDirection() {
         private _updateDirection() {
-            if (!this._connectedMesh || !this._soundPanner) {
+            if (!this._connectedTransformNode || !this._soundPanner) {
                 return;
                 return;
             }
             }
 
 
-            var mat = this._connectedMesh.getWorldMatrix();
+            var mat = this._connectedTransformNode.getWorldMatrix();
             var direction = Vector3.TransformNormal(this._localDirection, mat);
             var direction = Vector3.TransformNormal(this._localDirection, mat);
             direction.normalize();
             direction.normalize();
             this._soundPanner.setOrientation(direction.x, direction.y, direction.z);
             this._soundPanner.setOrientation(direction.x, direction.y, direction.z);
@@ -578,8 +578,8 @@ module BABYLON {
 
 
         /** @hidden */
         /** @hidden */
         public updateDistanceFromListener() {
         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);
                 this._soundGain.gain.value = this._customAttenuationFunction(this._volume, distance, this.maxDistance, this.refDistance, this.rolloffFactor);
             }
             }
         }
         }
@@ -615,7 +615,7 @@ module BABYLON {
                                 this._soundPanner.coneInnerAngle = this._coneInnerAngle;
                                 this._soundPanner.coneInnerAngle = this._coneInnerAngle;
                                 this._soundPanner.coneOuterAngle = this._coneOuterAngle;
                                 this._soundPanner.coneOuterAngle = this._coneOuterAngle;
                                 this._soundPanner.coneOuterGain = this._coneOuterGain;
                                 this._soundPanner.coneOuterGain = this._coneOuterGain;
-                                if (this._connectedMesh) {
+                                if (this._connectedTransformNode) {
                                     this._updateDirection();
                                     this._updateDirection();
                                 }
                                 }
                                 else {
                                 else {
@@ -782,15 +782,15 @@ module BABYLON {
 
 
         /**
         /**
          * Attach the sound to a dedicated mesh
          * 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
          * @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._registerFunc = null;
             }
             }
-            this._connectedMesh = meshToConnectTo;
+            this._connectedTransformNode = transformNode;
             if (!this.spatialSound) {
             if (!this.spatialSound) {
                 this.spatialSound = true;
                 this.spatialSound = true;
                 this._createSpatialParameters();
                 this._createSpatialParameters();
@@ -799,9 +799,9 @@ module BABYLON {
                     this.play();
                     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);
         }
         }
 
 
         /**
         /**
@@ -809,10 +809,10 @@ module BABYLON {
          * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#attaching-a-sound-to-a-mesh
          * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#attaching-a-sound-to-a-mesh
          */
          */
         public detachFromMesh() {
         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._registerFunc = null;
-                this._connectedMesh = null;
+                this._connectedTransformNode = null;
             }
             }
         }
         }
 
 
@@ -905,8 +905,8 @@ module BABYLON {
             };
             };
 
 
             if (this.spatialSound) {
             if (this.spatialSound) {
-                if (this._connectedMesh) {
-                    serializationObject.connectedMeshId = this._connectedMesh.id;
+                if (this._connectedTransformNode) {
+                    serializationObject.connectedMeshId = this._connectedTransformNode.id;
                 }
                 }
 
 
                 serializationObject.position = this._position.asArray();
                 serializationObject.position = this._position.asArray();

+ 1 - 1
src/Bones/babylon.bone.ts

@@ -73,7 +73,7 @@ module BABYLON {
              */
              */
             public name: string, skeleton: Skeleton, parentBone: Nullable<Bone> = null, localMatrix: Nullable<Matrix> = null,
             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) {
             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._skeleton = skeleton;
             this._localMatrix = localMatrix ? localMatrix.clone() : Matrix.Identity();
             this._localMatrix = localMatrix ? localMatrix.clone() : Matrix.Identity();
             this._restPose = restPose ? restPose : this._localMatrix.clone();
             this._restPose = restPose ? restPose : this._localMatrix.clone();

+ 19 - 6
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -197,17 +197,18 @@ module BABYLON {
                 });
                 });
             };
             };
             makeNotPick(mesh);
             makeNotPick(mesh);
-            var childMeshes = mesh.getChildMeshes();
+            var meshChildren = mesh.getChildren(undefined, false);
 
 
+            let laserParent: TransformNode = mesh;
             this.webVRController._pointingPoseNode = null;
             this.webVRController._pointingPoseNode = null;
-            for (var i = 0; i < childMeshes.length; i++) {
-                if (childMeshes[i].name && childMeshes[i].name.indexOf(PoseEnabledController.POINTING_POSE) >= 0) {
-                    mesh = childMeshes[i];
-                    this.webVRController._pointingPoseNode = mesh;
+            for (var i = 0; i < meshChildren.length; i++) {
+                if (meshChildren[i].name && meshChildren[i].name.indexOf(PoseEnabledController.POINTING_POSE) >= 0) {
+                    laserParent = <TransformNode>meshChildren[i];
+                    this.webVRController._pointingPoseNode = laserParent;
                     break;
                     break;
                 }
                 }
             }
             }
-            this._laserPointer.parent = mesh;
+            this._laserPointer.parent = laserParent;
         }
         }
 
 
         public _updatePointerDistance(distance: number = 100) {
         public _updatePointerDistance(distance: number = 100) {
@@ -875,6 +876,18 @@ module BABYLON {
 
 
             if (this._scene.activeCamera) {
             if (this._scene.activeCamera) {
                 this._position = this._scene.activeCamera.position.clone();
                 this._position = this._scene.activeCamera.position.clone();
+
+                if (this.vrDeviceOrientationCamera) {
+                    this.vrDeviceOrientationCamera.rotation = BABYLON.Quaternion.FromRotationMatrix(this._scene.activeCamera.getWorldMatrix().getRotationMatrix()).toEulerAngles();
+                }
+                if (this.webVRCamera) {
+                    var currentYRotation = this.webVRCamera.deviceRotationQuaternion.toEulerAngles().y;
+                    var desiredYRotation = BABYLON.Quaternion.FromRotationMatrix(this._scene.activeCamera.getWorldMatrix().getRotationMatrix()).toEulerAngles().y;
+                    var delta = desiredYRotation - currentYRotation;
+                    var currentGlobalRotation = this.webVRCamera.rotationQuaternion.toEulerAngles().y;
+                    this.webVRCamera.rotationQuaternion = Quaternion.FromEulerAngles(0, currentGlobalRotation + delta, 0);
+                }
+
                 // make sure that we return to the last active camera
                 // make sure that we return to the last active camera
                 this._existingCamera = this._scene.activeCamera;
                 this._existingCamera = this._scene.activeCamera;
             }
             }

+ 2 - 0
src/Cameras/XR/babylon.webXRCamera.ts

@@ -18,6 +18,7 @@ module BABYLON {
             this.minZ = 0;
             this.minZ = 0;
             this.rotationQuaternion = new BABYLON.Quaternion();
             this.rotationQuaternion = new BABYLON.Quaternion();
             this.cameraRigMode = BABYLON.Camera.RIG_MODE_CUSTOM;
             this.cameraRigMode = BABYLON.Camera.RIG_MODE_CUSTOM;
+            this.updateUpVectorFromRotation = true;
             this._updateNumberOfRigCameras(1);
             this._updateNumberOfRigCameras(1);
         }
         }
 
 
@@ -26,6 +27,7 @@ module BABYLON {
                 var newCamera = new BABYLON.TargetCamera("view: " + this.rigCameras.length, BABYLON.Vector3.Zero(), this.getScene());
                 var newCamera = new BABYLON.TargetCamera("view: " + this.rigCameras.length, BABYLON.Vector3.Zero(), this.getScene());
                 newCamera.minZ = 0;
                 newCamera.minZ = 0;
                 newCamera.parent = this;
                 newCamera.parent = this;
+                newCamera.rotationQuaternion = new BABYLON.Quaternion();
                 this.rigCameras.push(newCamera);
                 this.rigCameras.push(newCamera);
             }
             }
             while (this.rigCameras.length > viewCount) {
             while (this.rigCameras.length > viewCount) {

+ 12 - 1
src/Cameras/XR/babylon.webXREnterExitUI.ts

@@ -44,13 +44,21 @@ module BABYLON {
         private _buttons: Array<WebXREnterExitUIButton> = [];
         private _buttons: Array<WebXREnterExitUIButton> = [];
         private _activeButton: Nullable<WebXREnterExitUIButton> = null;
         private _activeButton: Nullable<WebXREnterExitUIButton> = null;
         /**
         /**
+         * Fired every time the active button is changed.
+         *
+         * When xr is entered via a button that launches xr that button will be the callback parameter
+         *
+         * When exiting xr the callback parameter will be null)
+         */
+        public activeButtonChangedObservable = new BABYLON.Observable<Nullable<WebXREnterExitUIButton>>();
+        /**
          * Creates UI to allow the user to enter/exit XR mode
          * Creates UI to allow the user to enter/exit XR mode
          * @param scene the scene to add the ui to
          * @param scene the scene to add the ui to
          * @param helper the xr experience helper to enter/exit xr with
          * @param helper the xr experience helper to enter/exit xr with
          * @param options options to configure the UI
          * @param options options to configure the UI
          * @returns the created ui
          * @returns the created ui
          */
          */
-        public static CreateAsync(scene: BABYLON.Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions) {
+        public static CreateAsync(scene: BABYLON.Scene, helper: WebXRExperienceHelper, options: WebXREnterExitUIOptions): Promise<WebXREnterExitUI> {
             var ui = new WebXREnterExitUI(scene, options);
             var ui = new WebXREnterExitUI(scene, options);
             var supportedPromises = ui._buttons.map((btn) => {
             var supportedPromises = ui._buttons.map((btn) => {
                 return helper.supportsSessionAsync(btn.initializationOptions);
                 return helper.supportsSessionAsync(btn.initializationOptions);
@@ -76,6 +84,7 @@ module BABYLON {
                         };
                         };
                     }
                     }
                 });
                 });
+                return ui;
             });
             });
         }
         }
 
 
@@ -120,6 +129,7 @@ module BABYLON {
             this._buttons.forEach((b) => {
             this._buttons.forEach((b) => {
                 b.update(this._activeButton);
                 b.update(this._activeButton);
             });
             });
+            this.activeButtonChangedObservable.notifyObservers(this._activeButton);
         }
         }
 
 
         /**
         /**
@@ -130,6 +140,7 @@ module BABYLON {
             if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
             if (renderCanvas && renderCanvas.parentNode && renderCanvas.parentNode.contains(this._overlay)) {
                 renderCanvas.parentNode.removeChild(this._overlay);
                 renderCanvas.parentNode.removeChild(this._overlay);
             }
             }
+            this.activeButtonChangedObservable.clear();
         }
         }
     }
     }
 }
 }

+ 29 - 0
src/Cameras/XR/babylon.webXRExperienceHelper.ts

@@ -44,6 +44,8 @@ module BABYLON {
             this.onStateChangedObservable.notifyObservers(this.state);
             this.onStateChangedObservable.notifyObservers(this.state);
         }
         }
 
 
+        private static _TmpVector = new BABYLON.Vector3();
+
         /**
         /**
          * Fires when the state of the experience helper has changed
          * Fires when the state of the experience helper has changed
          */
          */
@@ -80,6 +82,7 @@ module BABYLON {
             this.camera = new BABYLON.WebXRCamera("", scene);
             this.camera = new BABYLON.WebXRCamera("", scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this._sessionManager = new BABYLON.WebXRSessionManager(scene);
             this.container = new AbstractMesh("", scene);
             this.container = new AbstractMesh("", scene);
+            this.camera.parent = this.container;
         }
         }
 
 
         /**
         /**
@@ -98,6 +101,9 @@ module BABYLON {
          * @returns promise that resolves after xr mode has entered
          * @returns promise that resolves after xr mode has entered
          */
          */
         public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
         public enterXRAsync(sessionCreationOptions: XRSessionCreationOptions, frameOfReference: string) {
+            if (!this._supported) {
+                throw "XR session not supported by this browser";
+            }
             this._setState(WebXRState.ENTERING_XR);
             this._setState(WebXRState.ENTERING_XR);
 
 
             return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(() => {
             return this._sessionManager.enterXRAsync(sessionCreationOptions, frameOfReference).then(() => {
@@ -140,6 +146,29 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * Updates the global position of the camera by moving the camera's container
+         * This should be used instead of modifying the camera's position as it will be overwritten by an xrSessions's update frame
+         * @param position The desired global position of the camera
+         */
+        public setPositionOfCameraUsingContainer(position: Vector3) {
+            this.camera.globalPosition.subtractToRef(position, WebXRExperienceHelper._TmpVector);
+            this.container.position.subtractInPlace(WebXRExperienceHelper._TmpVector);
+        }
+
+        /**
+         * Rotates the xr camera by rotating the camera's container around the camera's position
+         * This should be used instead of modifying the camera's rotation as it will be overwritten by an xrSessions's update frame
+         * @param rotation the desired quaternion rotation to apply to the camera
+         */
+        public rotateCameraByQuaternionUsingContainer(rotation: Quaternion) {
+            if (!this.container.rotationQuaternion) {
+                this.container.rotationQuaternion = Quaternion.FromEulerVector(this.container.rotation);
+            }
+            this.container.rotationQuaternion.multiplyInPlace(rotation);
+            this.container.position.rotateByQuaternionAroundPointToRef(rotation, this.camera.globalPosition, this.container.position);
+        }
+
+        /**
          * Checks if the creation options are supported by the xr session
          * Checks if the creation options are supported by the xr session
          * @param options creation options
          * @param options creation options
          * @returns true if supported
          * @returns true if supported

+ 2 - 1
src/Cameras/XR/babylon.webXRSessionManager.ts

@@ -39,7 +39,8 @@ module BABYLON {
          * @returns Promise which resolves after it is initialized
          * @returns Promise which resolves after it is initialized
          */
          */
         public initializeAsync(): Promise<void> {
         public initializeAsync(): Promise<void> {
-             // Check if the browser supports webXR
+            Tools.Warn("The WebXR APIs are still under development and are subject to change in the future.");
+            // Check if the browser supports webXR
             this._xrNavigator = navigator;
             this._xrNavigator = navigator;
             if (!this._xrNavigator.xr) {
             if (!this._xrNavigator.xr) {
                 return Promise.reject("webXR not supported by this browser");
                 return Promise.reject("webXR not supported by this browser");

+ 16 - 4
src/Cameras/babylon.targetCamera.ts

@@ -14,6 +14,11 @@ module BABYLON {
          * Define the current rotation the camera is rotating to
          * Define the current rotation the camera is rotating to
          */
          */
         public cameraRotation = new Vector2(0, 0);
         public cameraRotation = new Vector2(0, 0);
+        /**
+         * When set, the up vector of the camera will be updated by the rotation of the camera
+         */
+        public updateUpVectorFromRotation = false;
+        private _tmpQuaternion = new Quaternion();
 
 
         /**
         /**
          * Define the current rotation of the camera
          * Define the current rotation of the camera
@@ -178,7 +183,7 @@ module BABYLON {
 
 
             this._cache.rotation.copyFrom(this.rotation);
             this._cache.rotation.copyFrom(this.rotation);
             if (this.rotationQuaternion) {
             if (this.rotationQuaternion) {
-                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
+                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
             }
             }
         }
         }
 
 
@@ -294,10 +299,10 @@ module BABYLON {
                     var limit = (Math.PI / 2) * 0.95;
                     var limit = (Math.PI / 2) * 0.95;
 
 
                     if (this.rotation.x > limit) {
                     if (this.rotation.x > limit) {
-                        this.rotation.x = limit;
+                        this.rotation.x = limit;
                     }
                     }
                     if (this.rotation.x < -limit) {
                     if (this.rotation.x < -limit) {
-                        this.rotation.x = -limit;
+                        this.rotation.x = -limit;
                     }
                     }
                 }
                 }
             }
             }
@@ -373,7 +378,14 @@ module BABYLON {
 
 
             // Computing target and final matrix
             // Computing target and final matrix
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
             this.position.addToRef(this._transformedReferencePoint, this._currentTarget);
-
+            if (this.updateUpVectorFromRotation) {
+                if (this.rotationQuaternion) {
+                    Axis.Y.rotateByQuaternionToRef(this.rotationQuaternion, this.upVector);
+                } else {
+                    Quaternion.FromEulerVectorToRef(this.rotation, this._tmpQuaternion);
+                    Axis.Y.rotateByQuaternionToRef(this._tmpQuaternion, this.upVector);
+                }
+            }
             this._computeViewMatrix(this.position, this._currentTarget, this.upVector);
             this._computeViewMatrix(this.position, this._currentTarget, this.upVector);
             return this._viewMatrix;
             return this._viewMatrix;
         }
         }

+ 14 - 14
src/Culling/babylon.boundingBox.ts

@@ -48,7 +48,7 @@ module BABYLON {
          */
          */
         public readonly maximum: Vector3 = Vector3.Zero();
         public readonly maximum: Vector3 = Vector3.Zero();
 
 
-        private _worldMatrix: Readonly<Matrix>;
+        private _worldMatrix: DeepImmutable<Matrix>;
         private static readonly TmpVector3 = Tools.BuildArray(3, Vector3.Zero);
         private static readonly TmpVector3 = Tools.BuildArray(3, Vector3.Zero);
 
 
         /**
         /**
@@ -62,7 +62,7 @@ module BABYLON {
          * @param max defines the maximum vector (in local space)
          * @param max defines the maximum vector (in local space)
          * @param worldMatrix defines the new world matrix
          * @param worldMatrix defines the new world matrix
          */
          */
-        constructor(min: Readonly<Vector3>, max: Readonly<Vector3>, worldMatrix?: Readonly<Matrix>) {
+        constructor(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
             this.reConstruct(min, max, worldMatrix);
             this.reConstruct(min, max, worldMatrix);
         }
         }
 
 
@@ -74,7 +74,7 @@ module BABYLON {
          * @param max defines the new maximum vector (in local space)
          * @param max defines the new maximum vector (in local space)
          * @param worldMatrix defines the new world matrix
          * @param worldMatrix defines the new world matrix
          */
          */
-        public reConstruct(min: Readonly<Vector3>, max: Readonly<Vector3>, worldMatrix?: Readonly<Matrix>) {
+        public reConstruct(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
             const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
             const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
             const vectors = this.vectors;
             const vectors = this.vectors;
 
 
@@ -121,12 +121,12 @@ module BABYLON {
          * Gets the world matrix of the bounding box
          * Gets the world matrix of the bounding box
          * @returns a matrix
          * @returns a matrix
          */
          */
-        public getWorldMatrix(): Readonly<Matrix> {
+        public getWorldMatrix(): DeepImmutable<Matrix> {
             return this._worldMatrix;
             return this._worldMatrix;
         }
         }
 
 
         /** @hidden */
         /** @hidden */
-        public _update(world: Readonly<Matrix>): void {
+        public _update(world: DeepImmutable<Matrix>): void {
             const minWorld = this.minimumWorld;
             const minWorld = this.minimumWorld;
             const maxWorld = this.maximumWorld;
             const maxWorld = this.maximumWorld;
             const directions = this.directions;
             const directions = this.directions;
@@ -172,7 +172,7 @@ module BABYLON {
          * @param frustumPlanes defines the frustum planes to test
          * @param frustumPlanes defines the frustum planes to test
          * @returns true if there is an intersection
          * @returns true if there is an intersection
          */
          */
-        public isInFrustum(frustumPlanes: Array<Readonly<Plane>>): boolean {
+        public isInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
             return BoundingBox.IsInFrustum(this.vectorsWorld, frustumPlanes);
             return BoundingBox.IsInFrustum(this.vectorsWorld, frustumPlanes);
         }
         }
 
 
@@ -181,7 +181,7 @@ module BABYLON {
          * @param frustumPlanes defines the frustum planes to test
          * @param frustumPlanes defines the frustum planes to test
          * @returns true if there is an inclusion
          * @returns true if there is an inclusion
          */
          */
-        public isCompletelyInFrustum(frustumPlanes: Array<Readonly<Plane>>): boolean {
+        public isCompletelyInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
             return BoundingBox.IsCompletelyInFrustum(this.vectorsWorld, frustumPlanes);
             return BoundingBox.IsCompletelyInFrustum(this.vectorsWorld, frustumPlanes);
         }
         }
 
 
@@ -190,7 +190,7 @@ module BABYLON {
          * @param point defines the point to test
          * @param point defines the point to test
          * @returns true if the point is inside the bounding box
          * @returns true if the point is inside the bounding box
          */
          */
-        public intersectsPoint(point: Readonly<Vector3>): boolean {
+        public intersectsPoint(point: DeepImmutable<Vector3>): boolean {
             const min = this.minimumWorld;
             const min = this.minimumWorld;
             const max = this.maximumWorld;
             const max = this.maximumWorld;
             const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
             const minX = min.x, minY = min.y, minZ = min.z, maxX = max.x, maxY = max.y, maxZ = max.z;
@@ -217,7 +217,7 @@ module BABYLON {
          * @param sphere defines the sphere to test
          * @param sphere defines the sphere to test
          * @returns true if there is an intersection
          * @returns true if there is an intersection
          */
          */
-        public intersectsSphere(sphere: Readonly<BoundingSphere>): boolean {
+        public intersectsSphere(sphere: DeepImmutable<BoundingSphere>): boolean {
             return BoundingBox.IntersectsSphere(this.minimumWorld, this.maximumWorld, sphere.centerWorld, sphere.radiusWorld);
             return BoundingBox.IntersectsSphere(this.minimumWorld, this.maximumWorld, sphere.centerWorld, sphere.radiusWorld);
         }
         }
 
 
@@ -227,7 +227,7 @@ module BABYLON {
          * @param max defines the max vector to use
          * @param max defines the max vector to use
          * @returns true if there is an intersection
          * @returns true if there is an intersection
          */
          */
-        public intersectsMinMax(min: Readonly<Vector3>, max: Readonly<Vector3>): boolean {
+        public intersectsMinMax(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>): boolean {
             const myMin = this.minimumWorld;
             const myMin = this.minimumWorld;
             const myMax = this.maximumWorld;
             const myMax = this.maximumWorld;
             const myMinX = myMin.x, myMinY = myMin.y, myMinZ = myMin.z, myMaxX = myMax.x, myMaxY = myMax.y, myMaxZ = myMax.z;
             const myMinX = myMin.x, myMinY = myMin.y, myMinZ = myMin.z, myMaxX = myMax.x, myMaxY = myMax.y, myMaxZ = myMax.z;
@@ -255,7 +255,7 @@ module BABYLON {
          * @param box1 defines the second box to test
          * @param box1 defines the second box to test
          * @returns true if there is an intersection
          * @returns true if there is an intersection
          */
          */
-        public static Intersects(box0: BoundingBox, box1: BoundingBox): boolean {
+        public static Intersects(box0: DeepImmutable<BoundingBox>, box1: DeepImmutable<BoundingBox>): boolean {
             return box0.intersectsMinMax(box1.minimumWorld, box1.maximumWorld);
             return box0.intersectsMinMax(box1.minimumWorld, box1.maximumWorld);
         }
         }
 
 
@@ -267,7 +267,7 @@ module BABYLON {
          * @param sphereRadius defines the sphere radius
          * @param sphereRadius defines the sphere radius
          * @returns true if there is an intersection
          * @returns true if there is an intersection
          */
          */
-        public static IntersectsSphere(minPoint: Readonly<Vector3>, maxPoint: Readonly<Vector3>, sphereCenter: Readonly<Vector3>, sphereRadius: number): boolean {
+        public static IntersectsSphere(minPoint: DeepImmutable<Vector3>, maxPoint: DeepImmutable<Vector3>, sphereCenter: DeepImmutable<Vector3>, sphereRadius: number): boolean {
             const vector = BoundingBox.TmpVector3[0];
             const vector = BoundingBox.TmpVector3[0];
             Vector3.ClampToRef(sphereCenter, minPoint, maxPoint, vector);
             Vector3.ClampToRef(sphereCenter, minPoint, maxPoint, vector);
             var num = Vector3.DistanceSquared(sphereCenter, vector);
             var num = Vector3.DistanceSquared(sphereCenter, vector);
@@ -280,7 +280,7 @@ module BABYLON {
          * @param frustumPlanes defines the frustum planes to test
          * @param frustumPlanes defines the frustum planes to test
          * @return true if there is an inclusion
          * @return true if there is an inclusion
          */
          */
-        public static IsCompletelyInFrustum(boundingVectors: Array<Readonly<Vector3>>, frustumPlanes: Array<Readonly<Plane>>): boolean {
+        public static IsCompletelyInFrustum(boundingVectors: Array<DeepImmutable<Vector3>>, frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
             for (var p = 0; p < 6; ++p) {
             for (var p = 0; p < 6; ++p) {
                 const frustumPlane = frustumPlanes[p];
                 const frustumPlane = frustumPlanes[p];
                 for (var i = 0; i < 8; ++i) {
                 for (var i = 0; i < 8; ++i) {
@@ -298,7 +298,7 @@ module BABYLON {
          * @param frustumPlanes defines the frustum planes to test
          * @param frustumPlanes defines the frustum planes to test
          * @return true if there is an intersection
          * @return true if there is an intersection
          */
          */
-        public static IsInFrustum(boundingVectors: Array<Readonly<Vector3>>, frustumPlanes: Array<Readonly<Plane>>): boolean {
+        public static IsInFrustum(boundingVectors: Array<DeepImmutable<Vector3>>, frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
             for (var p = 0; p < 6; ++p) {
             for (var p = 0; p < 6; ++p) {
                 let canReturnFalse = true;
                 let canReturnFalse = true;
                 const frustumPlane = frustumPlanes[p];
                 const frustumPlane = frustumPlanes[p];

+ 10 - 10
src/Culling/babylon.boundingInfo.ts

@@ -1,7 +1,7 @@
 module BABYLON {
 module BABYLON {
     const _result0 = { min: 0, max: 0};
     const _result0 = { min: 0, max: 0};
     const _result1 = { min: 0, max: 0};
     const _result1 = { min: 0, max: 0};
-    const computeBoxExtents = (axis: Readonly<Vector3>, box: Readonly<BoundingBox>, result: {min: number, max: number}) => {
+    const computeBoxExtents = (axis: DeepImmutable<Vector3>, box: DeepImmutable<BoundingBox>, result: {min: number, max: number}) => {
         const p = Vector3.Dot(box.centerWorld, axis);
         const p = Vector3.Dot(box.centerWorld, axis);
 
 
         const r0 = Math.abs(Vector3.Dot(box.directions[0], axis)) * box.extendSize.x;
         const r0 = Math.abs(Vector3.Dot(box.directions[0], axis)) * box.extendSize.x;
@@ -13,7 +13,7 @@ module BABYLON {
         result.max = p + r;
         result.max = p + r;
     };
     };
 
 
-    const axisOverlap = (axis: Vector3, box0: BoundingBox, box1: BoundingBox): boolean => {
+    const axisOverlap = (axis: DeepImmutable<Vector3>, box0: DeepImmutable<BoundingBox>, box1: DeepImmutable<BoundingBox>): boolean => {
         computeBoxExtents(axis, box0, _result0);
         computeBoxExtents(axis, box0, _result0);
         computeBoxExtents(axis, box1, _result1);
         computeBoxExtents(axis, box1, _result1);
         return !(_result0.min > _result1.max || _result1.min > _result0.max);
         return !(_result0.min > _result1.max || _result1.min > _result0.max);
@@ -62,7 +62,7 @@ module BABYLON {
          * @param maximum max vector of the bounding box/sphere
          * @param maximum max vector of the bounding box/sphere
          * @param worldMatrix defines the new world matrix
          * @param worldMatrix defines the new world matrix
          */
          */
-        constructor(minimum: Readonly<Vector3>, maximum: Readonly<Vector3>, worldMatrix?: Readonly<Matrix>) {
+        constructor(minimum: DeepImmutable<Vector3>, maximum: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
             this.boundingBox = new BoundingBox(minimum, maximum, worldMatrix);
             this.boundingBox = new BoundingBox(minimum, maximum, worldMatrix);
             this.boundingSphere = new BoundingSphere(minimum, maximum, worldMatrix);
             this.boundingSphere = new BoundingSphere(minimum, maximum, worldMatrix);
         }
         }
@@ -73,7 +73,7 @@ module BABYLON {
          * @param max defines the new maximum vector (in local space)
          * @param max defines the new maximum vector (in local space)
          * @param worldMatrix defines the new world matrix
          * @param worldMatrix defines the new world matrix
          */
          */
-        public reConstruct(min: Readonly<Vector3>, max: Readonly<Vector3>, worldMatrix?: Readonly<Matrix>) {
+        public reConstruct(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
             this.boundingBox.reConstruct(min, max, worldMatrix);
             this.boundingBox.reConstruct(min, max, worldMatrix);
             this.boundingSphere.reConstruct(min, max, worldMatrix);
             this.boundingSphere.reConstruct(min, max, worldMatrix);
         }
         }
@@ -108,7 +108,7 @@ module BABYLON {
          * Updates the bounding sphere and box
          * Updates the bounding sphere and box
          * @param world world matrix to be used to update
          * @param world world matrix to be used to update
          */
          */
-        public update(world: Readonly<Matrix>) {
+        public update(world: DeepImmutable<Matrix>) {
             if (this._isLocked) {
             if (this._isLocked) {
                 return;
                 return;
             }
             }
@@ -122,7 +122,7 @@ module BABYLON {
          * @param extend New extend of the bounding info
          * @param extend New extend of the bounding info
          * @returns the current bounding info
          * @returns the current bounding info
          */
          */
-        public centerOn(center: Readonly<Vector3>, extend: Readonly<Vector3>): BoundingInfo {
+        public centerOn(center: DeepImmutable<Vector3>, extend: DeepImmutable<Vector3>): BoundingInfo {
 
 
             const minimum = BoundingInfo.TmpVector3[0].copyFrom(center).subtractInPlace(extend);
             const minimum = BoundingInfo.TmpVector3[0].copyFrom(center).subtractInPlace(extend);
             const maximum = BoundingInfo.TmpVector3[1].copyFrom(center).addInPlace(extend);
             const maximum = BoundingInfo.TmpVector3[1].copyFrom(center).addInPlace(extend);
@@ -151,7 +151,7 @@ module BABYLON {
          * @param strategy defines the strategy to use for the culling (default is BABYLON.Scene.CULLINGSTRATEGY_STANDARD)
          * @param strategy defines the strategy to use for the culling (default is BABYLON.Scene.CULLINGSTRATEGY_STANDARD)
          * @returns true if the bounding info is in the frustum planes
          * @returns true if the bounding info is in the frustum planes
          */
          */
-        public isInFrustum(frustumPlanes: Array<Readonly<Plane>>, strategy: number = AbstractMesh.CULLINGSTRATEGY_STANDARD): boolean {
+        public isInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>, strategy: number = AbstractMesh.CULLINGSTRATEGY_STANDARD): boolean {
             if (!this.boundingSphere.isInFrustum(frustumPlanes)) {
             if (!this.boundingSphere.isInFrustum(frustumPlanes)) {
                 return false;
                 return false;
             }
             }
@@ -177,7 +177,7 @@ module BABYLON {
          * @param frustumPlanes Camera near/planes
          * @param frustumPlanes Camera near/planes
          * @returns true if the object is in frustum otherwise false
          * @returns true if the object is in frustum otherwise false
          */
          */
-        public isCompletelyInFrustum(frustumPlanes: Array<Readonly<Plane>>): boolean {
+        public isCompletelyInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
             return this.boundingBox.isCompletelyInFrustum(frustumPlanes);
             return this.boundingBox.isCompletelyInFrustum(frustumPlanes);
         }
         }
         /** @hidden */
         /** @hidden */
@@ -191,7 +191,7 @@ module BABYLON {
          * @param point the point to check intersection with
          * @param point the point to check intersection with
          * @returns if the point intersects
          * @returns if the point intersects
          */
          */
-        public intersectsPoint(point: Readonly<Vector3>): boolean {
+        public intersectsPoint(point: DeepImmutable<Vector3>): boolean {
             if (!this.boundingSphere.centerWorld) {
             if (!this.boundingSphere.centerWorld) {
                 return false;
                 return false;
             }
             }
@@ -214,7 +214,7 @@ module BABYLON {
          * @param precise if the intersection should be done using OBB
          * @param precise if the intersection should be done using OBB
          * @returns if the bounding info intersects
          * @returns if the bounding info intersects
          */
          */
-        public intersects(boundingInfo: Readonly<BoundingInfo>, precise: boolean): boolean {
+        public intersects(boundingInfo: DeepImmutable<BoundingInfo>, precise: boolean): boolean {
             if (!BoundingSphere.Intersects(this.boundingSphere, boundingInfo.boundingSphere)) {
             if (!BoundingSphere.Intersects(this.boundingSphere, boundingInfo.boundingSphere)) {
                 return false;
                 return false;
             }
             }

+ 8 - 8
src/Culling/babylon.boundingSphere.ts

@@ -28,7 +28,7 @@ module BABYLON {
          */
          */
         public readonly maximum = Vector3.Zero();
         public readonly maximum = Vector3.Zero();
 
 
-        private _worldMatrix: Readonly<Matrix>;
+        private _worldMatrix: DeepImmutable<Matrix>;
         private static readonly TmpVector3 = Tools.BuildArray(3, Vector3.Zero);
         private static readonly TmpVector3 = Tools.BuildArray(3, Vector3.Zero);
 
 
         /**
         /**
@@ -37,7 +37,7 @@ module BABYLON {
          * @param max defines the maximum vector (in local space)
          * @param max defines the maximum vector (in local space)
          * @param worldMatrix defines the new world matrix
          * @param worldMatrix defines the new world matrix
          */
          */
-        constructor(min: Readonly<Vector3>, max: Readonly<Vector3>, worldMatrix?: Readonly<Matrix>) {
+        constructor(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
             this.reConstruct(min, max, worldMatrix);
             this.reConstruct(min, max, worldMatrix);
         }
         }
 
 
@@ -47,7 +47,7 @@ module BABYLON {
          * @param max defines the new maximum vector (in local space)
          * @param max defines the new maximum vector (in local space)
          * @param worldMatrix defines the new world matrix
          * @param worldMatrix defines the new world matrix
          */
          */
-        public reConstruct(min: Readonly<Vector3>, max: Readonly<Vector3>, worldMatrix?: Readonly<Matrix>) {
+        public reConstruct(min: DeepImmutable<Vector3>, max: DeepImmutable<Vector3>, worldMatrix?: DeepImmutable<Matrix>) {
             this.minimum.copyFrom(min);
             this.minimum.copyFrom(min);
             this.maximum.copyFrom(max);
             this.maximum.copyFrom(max);
 
 
@@ -80,13 +80,13 @@ module BABYLON {
          * Gets the world matrix of the bounding box
          * Gets the world matrix of the bounding box
          * @returns a matrix
          * @returns a matrix
          */
          */
-        public getWorldMatrix(): Readonly<Matrix> {
+        public getWorldMatrix(): DeepImmutable<Matrix> {
             return this._worldMatrix;
             return this._worldMatrix;
         }
         }
 
 
         // Methods
         // Methods
         /** @hidden */
         /** @hidden */
-        public _update(worldMatrix: Readonly<Matrix>): void {
+        public _update(worldMatrix: DeepImmutable<Matrix>): void {
             if (!worldMatrix.isIdentity()) {
             if (!worldMatrix.isIdentity()) {
                 Vector3.TransformCoordinatesToRef(this.center, worldMatrix, this.centerWorld);
                 Vector3.TransformCoordinatesToRef(this.center, worldMatrix, this.centerWorld);
                 const tempVector = BoundingSphere.TmpVector3[0];
                 const tempVector = BoundingSphere.TmpVector3[0];
@@ -104,7 +104,7 @@ module BABYLON {
          * @param frustumPlanes defines the frustum planes to test
          * @param frustumPlanes defines the frustum planes to test
          * @returns true if there is an intersection
          * @returns true if there is an intersection
          */
          */
-        public isInFrustum(frustumPlanes: Array<Readonly<Plane>>): boolean {
+        public isInFrustum(frustumPlanes: Array<DeepImmutable<Plane>>): boolean {
             for (var i = 0; i < 6; i++) {
             for (var i = 0; i < 6; i++) {
                 if (frustumPlanes[i].dotCoordinate(this.centerWorld) <= -this.radiusWorld) {
                 if (frustumPlanes[i].dotCoordinate(this.centerWorld) <= -this.radiusWorld) {
                     return false;
                     return false;
@@ -119,7 +119,7 @@ module BABYLON {
          * @param point defines the point to test
          * @param point defines the point to test
          * @returns true if the point is inside the bounding sphere
          * @returns true if the point is inside the bounding sphere
          */
          */
-        public intersectsPoint(point: Readonly<Vector3>): boolean {
+        public intersectsPoint(point: DeepImmutable<Vector3>): boolean {
             const squareDistance = Vector3.DistanceSquared(this.centerWorld, point);
             const squareDistance = Vector3.DistanceSquared(this.centerWorld, point);
             if (this.radiusWorld * this.radiusWorld < squareDistance) {
             if (this.radiusWorld * this.radiusWorld < squareDistance) {
                 return false;
                 return false;
@@ -135,7 +135,7 @@ module BABYLON {
          * @param sphere1 sphere 1
          * @param sphere1 sphere 1
          * @returns true if the speres intersect
          * @returns true if the speres intersect
          */
          */
-        public static Intersects(sphere0: Readonly<BoundingSphere>, sphere1: Readonly<BoundingSphere>): boolean {
+        public static Intersects(sphere0: DeepImmutable<BoundingSphere>, sphere1: DeepImmutable<BoundingSphere>): boolean {
             const squareDistance = Vector3.DistanceSquared(sphere0.centerWorld, sphere1.centerWorld);
             const squareDistance = Vector3.DistanceSquared(sphere0.centerWorld, sphere1.centerWorld);
             const radiusSum = sphere0.radiusWorld + sphere1.radiusWorld;
             const radiusSum = sphere0.radiusWorld + sphere1.radiusWorld;
 
 

+ 14 - 14
src/Culling/babylon.ray.ts

@@ -29,7 +29,7 @@ module BABYLON {
          * @param intersectionTreshold extra extend to be added to the box in all direction
          * @param intersectionTreshold extra extend to be added to the box in all direction
          * @returns if the box was hit
          * @returns if the box was hit
          */
          */
-        public intersectsBoxMinMax(minimum: Vector3, maximum: Vector3, intersectionTreshold: number = 0): boolean {
+        public intersectsBoxMinMax(minimum: DeepImmutable<Vector3>, maximum: DeepImmutable<Vector3>, intersectionTreshold: number = 0): boolean {
             const newMinimum = Ray.TmpVector3[0].copyFromFloats(minimum.x - intersectionTreshold, minimum.y - intersectionTreshold, minimum.z - intersectionTreshold);
             const newMinimum = Ray.TmpVector3[0].copyFromFloats(minimum.x - intersectionTreshold, minimum.y - intersectionTreshold, minimum.z - intersectionTreshold);
             const newMaximum = Ray.TmpVector3[1].copyFromFloats(maximum.x + intersectionTreshold, maximum.y + intersectionTreshold, maximum.z + intersectionTreshold);
             const newMaximum = Ray.TmpVector3[1].copyFromFloats(maximum.x + intersectionTreshold, maximum.y + intersectionTreshold, maximum.z + intersectionTreshold);
             var d = 0.0;
             var d = 0.0;
@@ -129,7 +129,7 @@ module BABYLON {
          * @param intersectionTreshold extra extend to be added to the BoundingBox in all direction
          * @param intersectionTreshold extra extend to be added to the BoundingBox in all direction
          * @returns if the box was hit
          * @returns if the box was hit
          */
          */
-        public intersectsBox(box: BoundingBox, intersectionTreshold: number = 0): boolean {
+        public intersectsBox(box: DeepImmutable<BoundingBox>, intersectionTreshold: number = 0): boolean {
             return this.intersectsBoxMinMax(box.minimum, box.maximum, intersectionTreshold);
             return this.intersectsBoxMinMax(box.minimum, box.maximum, intersectionTreshold);
         }
         }
 
 
@@ -139,7 +139,7 @@ module BABYLON {
          * @param intersectionTreshold extra extend to be added to the BoundingSphere in all direction
          * @param intersectionTreshold extra extend to be added to the BoundingSphere in all direction
          * @returns true if it hits the sphere
          * @returns true if it hits the sphere
          */
          */
-        public intersectsSphere(sphere: BoundingSphere, intersectionTreshold: number = 0): boolean {
+        public intersectsSphere(sphere: DeepImmutable<BoundingSphere>, intersectionTreshold: number = 0): boolean {
             var x = sphere.center.x - this.origin.x;
             var x = sphere.center.x - this.origin.x;
             var y = sphere.center.y - this.origin.y;
             var y = sphere.center.y - this.origin.y;
             var z = sphere.center.z - this.origin.z;
             var z = sphere.center.z - this.origin.z;
@@ -168,7 +168,7 @@ module BABYLON {
          * @param vertex2 triangle vertex
          * @param vertex2 triangle vertex
          * @returns intersection information if hit
          * @returns intersection information if hit
          */
          */
-        public intersectsTriangle(vertex0: Vector3, vertex1: Vector3, vertex2: Vector3): Nullable<IntersectionInfo> {
+        public intersectsTriangle(vertex0: DeepImmutable<Vector3>, vertex1: DeepImmutable<Vector3>, vertex2: DeepImmutable<Vector3>): Nullable<IntersectionInfo> {
             const edge1 = Ray.TmpVector3[0];
             const edge1 = Ray.TmpVector3[0];
             const edge2 = Ray.TmpVector3[1];
             const edge2 = Ray.TmpVector3[1];
             const pvec = Ray.TmpVector3[2];
             const pvec = Ray.TmpVector3[2];
@@ -216,7 +216,7 @@ module BABYLON {
          * @param plane the plane to check
          * @param plane the plane to check
          * @returns the distance away it was hit
          * @returns the distance away it was hit
          */
          */
-        public intersectsPlane(plane: Plane): Nullable<number> {
+        public intersectsPlane(plane: DeepImmutable<Plane>): Nullable<number> {
             var distance: number;
             var distance: number;
             var result1 = Vector3.Dot(plane.normal, this.direction);
             var result1 = Vector3.Dot(plane.normal, this.direction);
             if (Math.abs(result1) < 9.99999997475243E-07) {
             if (Math.abs(result1) < 9.99999997475243E-07) {
@@ -243,7 +243,7 @@ module BABYLON {
          * @param fastCheck if only the bounding box should checked
          * @param fastCheck if only the bounding box should checked
          * @returns picking info of the intersecton
          * @returns picking info of the intersecton
          */
          */
-        public intersectsMesh(mesh: AbstractMesh, fastCheck?: boolean): PickingInfo {
+        public intersectsMesh(mesh: DeepImmutable<AbstractMesh>, fastCheck?: boolean): PickingInfo {
 
 
             var tm = Tmp.Matrix[0];
             var tm = Tmp.Matrix[0];
 
 
@@ -266,7 +266,7 @@ module BABYLON {
          * @param results array to store result in
          * @param results array to store result in
          * @returns Array of picking infos
          * @returns Array of picking infos
          */
          */
-        public intersectsMeshes(meshes: Array<AbstractMesh>, fastCheck?: boolean, results?: Array<PickingInfo>): Array<PickingInfo> {
+        public intersectsMeshes(meshes: Array<DeepImmutable<AbstractMesh>>, fastCheck?: boolean, results?: Array<PickingInfo>): Array<PickingInfo> {
 
 
             if (results) {
             if (results) {
                 results.length = 0;
                 results.length = 0;
@@ -288,7 +288,7 @@ module BABYLON {
 
 
         }
         }
 
 
-        private _comparePickingInfo(pickingInfoA: PickingInfo, pickingInfoB: PickingInfo): number {
+        private _comparePickingInfo(pickingInfoA: DeepImmutable<PickingInfo>, pickingInfoB: DeepImmutable<PickingInfo>): number {
 
 
             if (pickingInfoA.distance < pickingInfoB.distance) {
             if (pickingInfoA.distance < pickingInfoB.distance) {
                 return -1;
                 return -1;
@@ -310,7 +310,7 @@ module BABYLON {
          * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
          * @param threshold the tolerance margin, if the ray doesn't intersect the segment but is close to the given threshold, the intersection is successful
          * @return the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
          * @return the distance from the ray origin to the intersection point if there's intersection, or -1 if there's no intersection
          */
          */
-        intersectionSegment(sega: Vector3, segb: Vector3, threshold: number): number {
+        intersectionSegment(sega: DeepImmutable<Vector3>, segb: DeepImmutable<Vector3>, threshold: number): number {
             const o = this.origin;
             const o = this.origin;
             const u =  Tmp.Vector3[0];
             const u =  Tmp.Vector3[0];
             const rsegb  = Tmp.Vector3[1];
             const rsegb  = Tmp.Vector3[1];
@@ -408,7 +408,7 @@ module BABYLON {
          * @param projection projection matrix
          * @param projection projection matrix
          * @returns this ray updated
          * @returns this ray updated
          */
          */
-        public update(x: number, y: number, viewportWidth: number, viewportHeight: number, world: Readonly<Matrix>, view: Readonly<Matrix>, projection: Readonly<Matrix>): Ray {
+        public update(x: number, y: number, viewportWidth: number, viewportHeight: number, world: DeepImmutable<Matrix>, view: DeepImmutable<Matrix>, projection: DeepImmutable<Matrix>): Ray {
             Vector3.UnprojectRayToRef(x, y, viewportWidth, viewportHeight, world, view, projection, this);
             Vector3.UnprojectRayToRef(x, y, viewportWidth, viewportHeight, world, view, projection, this);
             return this;
             return this;
         }
         }
@@ -433,7 +433,7 @@ module BABYLON {
          * @param projection projection matrix
          * @param projection projection matrix
          * @returns new ray
          * @returns new ray
          */
          */
-        public static CreateNew(x: number, y: number, viewportWidth: number, viewportHeight: number, world: Matrix, view: Matrix, projection: Matrix): Ray {
+        public static CreateNew(x: number, y: number, viewportWidth: number, viewportHeight: number, world: DeepImmutable<Matrix>, view: DeepImmutable<Matrix>, projection: DeepImmutable<Matrix>): Ray {
             let result = Ray.Zero();
             let result = Ray.Zero();
 
 
             return result.update(x, y, viewportWidth, viewportHeight, world, view, projection);
             return result.update(x, y, viewportWidth, viewportHeight, world, view, projection);
@@ -447,7 +447,7 @@ module BABYLON {
         * @param world a matrix to transform the ray to. Default is the identity matrix.
         * @param world a matrix to transform the ray to. Default is the identity matrix.
         * @returns the new ray
         * @returns the new ray
         */
         */
-        public static CreateNewFromTo(origin: Vector3, end: Vector3, world: Readonly<Matrix> = Matrix.IdentityReadOnly): Ray {
+        public static CreateNewFromTo(origin: DeepImmutable<Vector3>, end: DeepImmutable<Vector3>, world: DeepImmutable<Matrix> = Matrix.IdentityReadOnly): Ray {
             var direction = end.subtract(origin);
             var direction = end.subtract(origin);
             var length = Math.sqrt((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z));
             var length = Math.sqrt((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z));
             direction.normalize();
             direction.normalize();
@@ -461,7 +461,7 @@ module BABYLON {
          * @param matrix matrix to apply
          * @param matrix matrix to apply
          * @returns the resulting new ray
          * @returns the resulting new ray
          */
          */
-        public static Transform(ray: Ray, matrix: Readonly<Matrix>): Ray {
+        public static Transform(ray: DeepImmutable<Ray>, matrix: DeepImmutable<Matrix>): Ray {
             var result = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
             var result = new Ray(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
             Ray.TransformToRef(ray, matrix, result);
             Ray.TransformToRef(ray, matrix, result);
 
 
@@ -474,7 +474,7 @@ module BABYLON {
          * @param matrix matrix to apply
          * @param matrix matrix to apply
          * @param result ray to store result in
          * @param result ray to store result in
          */
          */
-        public static TransformToRef(ray: Ray, matrix: Readonly<Matrix>, result: Ray): void {
+        public static TransformToRef(ray: DeepImmutable<Ray>, matrix: DeepImmutable<Matrix>, result: Ray): void {
             Vector3.TransformCoordinatesToRef(ray.origin, matrix, result.origin);
             Vector3.TransformCoordinatesToRef(ray.origin, matrix, result.origin);
             Vector3.TransformNormalToRef(ray.direction, matrix, result.direction);
             Vector3.TransformNormalToRef(ray.direction, matrix, result.direction);
             result.length = ray.length;
             result.length = ray.length;

+ 15 - 3
src/Debug/babylon.skeletonViewer.ts

@@ -14,12 +14,20 @@ module BABYLON.Debug {
         private _renderFunction: () => void;
         private _renderFunction: () => void;
 
 
         /**
         /**
+         * Returns the mesh used to render the bones
+         */
+        public get debugMesh(): Nullable<LinesMesh> {
+            return this._debugMesh;
+        }
+
+        /**
          * Creates a new SkeletonViewer
          * Creates a new SkeletonViewer
          * @param skeleton defines the skeleton to render
          * @param skeleton defines the skeleton to render
          * @param mesh defines the mesh attached to the skeleton
          * @param mesh defines the mesh attached to the skeleton
          * @param scene defines the hosting scene
          * @param scene defines the hosting scene
          * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
          * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
          * @param renderingGroupId defines the rendering group id to use with the viewer
          * @param renderingGroupId defines the rendering group id to use with the viewer
+         * @param utilityLayerRenderer defines an optional utility layer to render the helper on
          */
          */
         constructor(
         constructor(
             /** defines the skeleton to render */
             /** defines the skeleton to render */
@@ -30,7 +38,10 @@ module BABYLON.Debug {
             /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)  */
             /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)  */
             public autoUpdateBonesMatrices = true,
             public autoUpdateBonesMatrices = true,
             /** defines the rendering group id to use with the viewer */
             /** defines the rendering group id to use with the viewer */
-            public renderingGroupId = 1) {
+            public renderingGroupId = 1,
+            /** defines an optional utility layer to render the helper on */
+            public utilityLayerRenderer?: UtilityLayerRenderer
+        ) {
             this._scene = scene;
             this._scene = scene;
 
 
             this.update();
             this.update();
@@ -131,12 +142,13 @@ module BABYLON.Debug {
             } else {
             } else {
                 this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
                 this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
             }
             }
+            const targetScene = this.utilityLayerRenderer ? this.utilityLayerRenderer.utilityLayerScene : this._scene;
 
 
             if (!this._debugMesh) {
             if (!this._debugMesh) {
-                this._debugMesh = BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, this._scene);
+                this._debugMesh = BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
                 this._debugMesh.renderingGroupId = this.renderingGroupId;
                 this._debugMesh.renderingGroupId = this.renderingGroupId;
             } else {
             } else {
-                BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: this._debugMesh }, this._scene);
+                BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: this._debugMesh }, targetScene);
             }
             }
             this._debugMesh.position.copyFrom(this.mesh.position);
             this._debugMesh.position.copyFrom(this.mesh.position);
             this._debugMesh.color = this.color;
             this._debugMesh.color = this.color;

+ 16 - 9
src/Engine/babylon.engine.ts

@@ -481,7 +481,7 @@ module BABYLON {
          * Returns the current version of the framework
          * Returns the current version of the framework
          */
          */
         public static get Version(): string {
         public static get Version(): string {
-            return "4.0.0-alpha.4";
+            return "4.0.0-alpha.6";
         }
         }
 
 
         /**
         /**
@@ -1096,8 +1096,10 @@ module BABYLON {
                     this.onCanvasPointerOutObservable.notifyObservers(ev);
                     this.onCanvasPointerOutObservable.notifyObservers(ev);
                 };
                 };
 
 
-                window.addEventListener("blur", this._onBlur);
-                window.addEventListener("focus", this._onFocus);
+                if (Tools.IsWindowObjectExist()) {
+                    window.addEventListener("blur", this._onBlur);
+                    window.addEventListener("focus", this._onFocus);
+                }
 
 
                 canvas.addEventListener("pointerout", this._onCanvasPointerOut);
                 canvas.addEventListener("pointerout", this._onCanvasPointerOut);
 
 
@@ -1155,8 +1157,10 @@ module BABYLON {
             }
             }
 
 
             // Viewport
             // Viewport
-            var limitDeviceRatio = options.limitDeviceRatio || window.devicePixelRatio || 1.0;
-            this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, window.devicePixelRatio || 1.0) : 1.0;
+            const devicePixelRatio = Tools.IsWindowObjectExist() ? (window.devicePixelRatio || 1.0) : 1.0;
+
+            var limitDeviceRatio = options.limitDeviceRatio || devicePixelRatio;
+            this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, devicePixelRatio) : 1.0;
             this.resize();
             this.resize();
 
 
             this._isStencilEnable = options.stencil ? true : false;
             this._isStencilEnable = options.stencil ? true : false;
@@ -1217,8 +1221,10 @@ module BABYLON {
                     document.exitPointerLock();
                     document.exitPointerLock();
                 };
                 };
 
 
-                window.addEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted, false);
-                window.addEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted, false);
+                if (Tools.IsWindowObjectExist()) {
+                    window.addEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted, false);
+                    window.addEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted, false);
+                }
             }
             }
 
 
             // Create Audio Engine if needed.
             // Create Audio Engine if needed.
@@ -4633,13 +4639,14 @@ module BABYLON {
          * @param invertY defines if data must be stored with Y axis inverted
          * @param invertY defines if data must be stored with Y axis inverted
          * @param premulAlpha defines if alpha is stored as premultiplied
          * @param premulAlpha defines if alpha is stored as premultiplied
          * @param format defines the format of the data
          * @param format defines the format of the data
+         * @param forceBindTexture if the texture should be forced to be bound eg. after a graphics context loss (Default: false)
          */
          */
-        public updateDynamicTexture(texture: Nullable<InternalTexture>, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha: boolean = false, format?: number): void {
+        public updateDynamicTexture(texture: Nullable<InternalTexture>, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha: boolean = false, format?: number, forceBindTexture: boolean = false): void {
             if (!texture) {
             if (!texture) {
                 return;
                 return;
             }
             }
 
 
-            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true, forceBindTexture);
             this._unpackFlipY(invertY);
             this._unpackFlipY(invertY);
             if (premulAlpha) {
             if (premulAlpha) {
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);

+ 1 - 1
src/Gamepad/Controllers/babylon.poseEnabledController.ts

@@ -178,7 +178,7 @@ module BABYLON {
          * Node to be used when casting a ray from the controller
          * Node to be used when casting a ray from the controller
          * @hidden
          * @hidden
          */
          */
-        public _pointingPoseNode: Nullable<AbstractMesh> = null;
+        public _pointingPoseNode: Nullable<TransformNode> = null;
         /**
         /**
          * Name of the child mesh that can be used to cast a ray from the controller
          * Name of the child mesh that can be used to cast a ray from the controller
          */
          */

+ 9 - 9
src/Gamepad/Controllers/babylon.windowsMotionController.ts

@@ -10,7 +10,7 @@ module BABYLON {
         /**
         /**
          * Node of the mesh corrisponding to the direction the ray should be cast from the controller
          * Node of the mesh corrisponding to the direction the ray should be cast from the controller
          */
          */
-        public pointingPoseNode: AbstractMesh;
+        public pointingPoseNode: TransformNode;
         /**
         /**
          * Map of the button meshes contained in the controller
          * Map of the button meshes contained in the controller
          */
          */
@@ -32,7 +32,7 @@ module BABYLON {
         /**
         /**
          * The mesh
          * The mesh
          */
          */
-        value: AbstractMesh;
+        value: TransformNode;
     }
     }
 
 
     /**
     /**
@@ -42,11 +42,11 @@ module BABYLON {
         /**
         /**
          * The mesh that should be displayed when pressed
          * The mesh that should be displayed when pressed
          */
          */
-        pressed: AbstractMesh;
+        pressed: TransformNode;
         /**
         /**
          * The mesh that should be displayed when not pressed
          * The mesh that should be displayed when not pressed
          */
          */
-        unpressed: AbstractMesh;
+        unpressed: TransformNode;
     }
     }
 
 
     /**
     /**
@@ -56,11 +56,11 @@ module BABYLON {
         /**
         /**
          * The mesh that should be set when at its min
          * The mesh that should be set when at its min
          */
          */
-        min: AbstractMesh;
+        min: TransformNode;
         /**
         /**
          * The mesh that should be set when at its max
          * The mesh that should be set when at its max
          */
          */
-        max: AbstractMesh;
+        max: TransformNode;
     }
     }
 
 
     /**
     /**
@@ -483,11 +483,11 @@ module BABYLON {
 
 
             // Look through all children recursively. This will return null if no mesh exists with the given name.
             // Look through all children recursively. This will return null if no mesh exists with the given name.
             function getChildByName(node: Node, name: string) {
             function getChildByName(node: Node, name: string) {
-                return node.getChildMeshes(false, (n) => n.name === name)[0];
+                return <TransformNode>node.getChildren((n) => n.name === name, false)[0];
             }
             }
             // Look through only immediate children. This will return null if no mesh exists with the given name.
             // Look through only immediate children. This will return null if no mesh exists with the given name.
-            function getImmediateChildByName(node: Node, name: string): AbstractMesh {
-                return node.getChildMeshes(true, (n) => n.name == name)[0];
+            function getImmediateChildByName(node: Node, name: string): TransformNode {
+                return <TransformNode>node.getChildren((n) => n.name == name, true)[0];
             }
             }
         }
         }
 
 

+ 1 - 1
src/Gizmos/babylon.gizmo.ts

@@ -111,7 +111,7 @@ module BABYLON {
                     this._rootMesh.position.copyFrom(this.attachedMesh.absolutePosition);
                     this._rootMesh.position.copyFrom(this.attachedMesh.absolutePosition);
                 }
                 }
                 if (this._updateScale && this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh) {
                 if (this._updateScale && this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh) {
-                    var cameraPosition = this.gizmoLayer.utilityLayerScene.activeCamera.position;
+                    var cameraPosition = this.gizmoLayer.utilityLayerScene.activeCamera.globalPosition;
                     if ((<WebVRFreeCamera>this.gizmoLayer.utilityLayerScene.activeCamera).devicePosition) {
                     if ((<WebVRFreeCamera>this.gizmoLayer.utilityLayerScene.activeCamera).devicePosition) {
                         cameraPosition = (<WebVRFreeCamera>this.gizmoLayer.utilityLayerScene.activeCamera).devicePosition;
                         cameraPosition = (<WebVRFreeCamera>this.gizmoLayer.utilityLayerScene.activeCamera).devicePosition;
                     }
                     }

+ 7 - 0
src/Materials/Textures/babylon.baseTexture.ts

@@ -12,6 +12,12 @@ module BABYLON {
         public static DEFAULT_ANISOTROPIC_FILTERING_LEVEL = 4;
         public static DEFAULT_ANISOTROPIC_FILTERING_LEVEL = 4;
 
 
         /**
         /**
+         * Gets or sets the unique id of the texture
+         */
+        @serialize()
+        public uniqueId: number;
+
+        /**
          * Define the name of the texture.
          * Define the name of the texture.
          */
          */
         @serialize()
         @serialize()
@@ -276,6 +282,7 @@ module BABYLON {
             if (this._scene) {
             if (this._scene) {
                 this._scene.textures.push(this);
                 this._scene.textures.push(this);
                 this._scene.onNewTextureAddedObservable.notifyObservers(this);
                 this._scene.onNewTextureAddedObservable.notifyObservers(this);
+                this.uniqueId = this._scene.getUniqueId();
             }
             }
             this._uid = null;
             this._uid = null;
         }
         }

+ 1 - 0
src/Materials/Textures/babylon.internalTexture.ts

@@ -311,6 +311,7 @@ module BABYLON {
                 case InternalTexture.DATASOURCE_DYNAMIC:
                 case InternalTexture.DATASOURCE_DYNAMIC:
                     proxy = this._engine.createDynamicTexture(this.baseWidth, this.baseHeight, this.generateMipMaps, this.samplingMode);
                     proxy = this._engine.createDynamicTexture(this.baseWidth, this.baseHeight, this.generateMipMaps, this.samplingMode);
                     proxy._swapAndDie(this);
                     proxy._swapAndDie(this);
+                    this._engine.updateDynamicTexture(this, this._engine.getRenderingCanvas()!, this.invertY, undefined, undefined, true);
 
 
                     // The engine will make sure to update content so no need to flag it as isReady = true
                     // The engine will make sure to update content so no need to flag it as isReady = true
                     return;
                     return;

+ 8 - 0
src/Materials/babylon.multiMaterial.ts

@@ -21,6 +21,14 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * Function used to align with Node.getChildren()
+         * @returns the list of Materials used within the multi material
+         */
+        public getChildren(): Nullable<Material>[] {
+            return this.subMaterials;
+        }
+
+        /**
          * Instantiates a new Multi Material
          * Instantiates a new Multi Material
          * A multi-material is used to apply different materials to different parts of the same object without the need of
          * A multi-material is used to apply different materials to different parts of the same object without the need of
          * separate meshes. This can be use to improve performances.
          * separate meshes. This can be use to improve performances.

File diff suppressed because it is too large
+ 338 - 229
src/Math/babylon.math.ts


+ 95 - 30
src/Mesh/babylon.abstractMesh.ts

@@ -1038,6 +1038,94 @@ module BABYLON {
             };
             };
         }
         }
 
 
+        /**
+         * 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 */
         /** @hidden */
         public _updateBoundingInfo(): AbstractMesh {
         public _updateBoundingInfo(): AbstractMesh {
             if (this._boundingInfo) {
             if (this._boundingInfo) {
@@ -1131,31 +1219,6 @@ module BABYLON {
             return this._boundingInfo.intersectsPoint(point);
             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
         // Collisions
 
 
         /**
         /**
@@ -1298,9 +1361,9 @@ module BABYLON {
          */
          */
         public intersects(ray: Ray, fastCheck?: boolean): PickingInfo {
         public intersects(ray: Ray, fastCheck?: boolean): PickingInfo {
             var pickingInfo = new PickingInfo();
             var pickingInfo = new PickingInfo();
-            const intersectionTreshold = this.getClassName() === "LinesMesh" ? (<LinesMesh>(this as any)).intersectionThreshold : 0;
+            const intersectionThreshold = this.getClassName() === "InstancedLinesMesh" || this.getClassName() === "LinesMesh" ? (this as any).intersectionThreshold : 0;
             const boundingInfo = this._boundingInfo;
             const boundingInfo = this._boundingInfo;
-            if (!this.subMeshes || !boundingInfo || !ray.intersectsSphere(boundingInfo.boundingSphere, intersectionTreshold) || !ray.intersectsBox(boundingInfo.boundingBox, intersectionTreshold)) {
+            if (!this.subMeshes || !boundingInfo || !ray.intersectsSphere(boundingInfo.boundingSphere, intersectionThreshold) || !ray.intersectsBox(boundingInfo.boundingBox, intersectionThreshold)) {
                 return pickingInfo;
                 return pickingInfo;
             }
             }
 
 
@@ -1337,8 +1400,10 @@ module BABYLON {
             if (intersectInfo) {
             if (intersectInfo) {
                 // Get picked point
                 // Get picked point
                 const world = this.getWorldMatrix();
                 const world = this.getWorldMatrix();
-                const worldOrigin = Vector3.TransformCoordinates(ray.origin, world);
-                const direction = ray.direction.scaleToRef(intersectInfo.distance, Tmp.Vector3[0]);
+                const worldOrigin = Tmp.Vector3[0];
+                const direction = Tmp.Vector3[1];
+                Vector3.TransformCoordinatesToRef(ray.origin, world, worldOrigin);
+                ray.direction.scaleToRef(intersectInfo.distance, direction);
                 const worldDirection = Vector3.TransformNormal(direction, world);
                 const worldDirection = Vector3.TransformNormal(direction, world);
                 const pickedPoint = worldDirection.addInPlace(worldOrigin);
                 const pickedPoint = worldDirection.addInPlace(worldOrigin);
 
 
@@ -1446,7 +1511,7 @@ module BABYLON {
             });
             });
 
 
             // SubMeshes
             // SubMeshes
-            if (this.getClassName() !== "InstancedMesh") {
+            if (this.getClassName() !== "InstancedMesh" || this.getClassName() !== "InstancedLinesMesh") {
                 this.releaseSubMeshes();
                 this.releaseSubMeshes();
             }
             }
 
 

+ 3 - 0
src/Mesh/babylon.geometry.ts

@@ -701,6 +701,9 @@ module BABYLON {
 
 
             // morphTargets
             // morphTargets
             mesh._syncGeometryWithMorphTargetManager();
             mesh._syncGeometryWithMorphTargetManager();
+
+            // instances
+            mesh.synchronizeInstances();
         }
         }
 
 
         private notifyUpdate(kind?: string) {
         private notifyUpdate(kind?: string) {

+ 9 - 11
src/Mesh/babylon.instancedMesh.ts

@@ -214,20 +214,18 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
-         * 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;
             return this;
         }
         }
 
 

+ 36 - 17
src/Mesh/babylon.linesMesh.ts

@@ -17,24 +17,9 @@ module BABYLON {
          * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
          * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
          * This margin is expressed in world space coordinates, so its value may vary.
          * This margin is expressed in world space coordinates, so its value may vary.
          * Default value is 0.1
          * Default value is 0.1
-         * @returns the intersection Threshold value.
          */
          */
-        public get intersectionThreshold(): number {
-            return this._intersectionThreshold;
-        }
-
-        /**
-         * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
-         * This margin is expressed in world space coordinates, so its value may vary.
-         */
-        public set intersectionThreshold(value: number) {
-            if (this._intersectionThreshold === value) {
-                return;
-            }
-            this._intersectionThreshold = value;
-        }
+        public intersectionThreshold: number;
 
 
-        private _intersectionThreshold: number;
         private _colorShader: ShaderMaterial;
         private _colorShader: ShaderMaterial;
 
 
         /**
         /**
@@ -73,7 +58,7 @@ module BABYLON {
                 this.useVertexAlpha = source.useVertexAlpha;
                 this.useVertexAlpha = source.useVertexAlpha;
             }
             }
 
 
-            this._intersectionThreshold = 0.1;
+            this.intersectionThreshold = 0.1;
 
 
             var defines: string[] = [];
             var defines: string[] = [];
             var options = {
             var options = {
@@ -169,5 +154,39 @@ module BABYLON {
         public clone(name: string, newParent?: Node, doNotCloneChildren?: boolean): LinesMesh {
         public clone(name: string, newParent?: Node, doNotCloneChildren?: boolean): LinesMesh {
             return new LinesMesh(name, this.getScene(), newParent, this, doNotCloneChildren);
             return new LinesMesh(name, this.getScene(), newParent, this, doNotCloneChildren);
         }
         }
+
+        /**
+         * Creates a new InstancedLinesMesh object from the mesh model.
+         * @see http://doc.babylonjs.com/how_to/how_to_use_instances
+         * @param name defines the name of the new instance
+         * @returns a new InstancedLinesMesh
+         */
+        public createInstance(name: string): InstancedLinesMesh {
+            return new InstancedLinesMesh(name, this);
+        }
+    }
+
+    /**
+     * Creates an instance based on a source LinesMesh
+     */
+    export class InstancedLinesMesh extends InstancedMesh {
+        /**
+         * The intersection Threshold is the margin applied when intersection a segment of the LinesMesh with a Ray.
+         * This margin is expressed in world space coordinates, so its value may vary.
+         * Initilized with the intersectionThreshold value of the source LinesMesh
+         */
+        public intersectionThreshold: number;
+
+        constructor(name: string, source: LinesMesh) {
+            super(name, source);
+            this.intersectionThreshold = source.intersectionThreshold;
+        }
+
+        /**
+         * Returns the string "InstancedLinesMesh".
+         */
+        public getClassName(): string {
+            return "InstancedLinesMesh";
+        }
     }
     }
 }
 }

+ 10 - 79
src/Mesh/babylon.mesh.ts

@@ -216,7 +216,7 @@ module BABYLON {
         // Will be used to save a source mesh reference, If any
         // Will be used to save a source mesh reference, If any
         private _source: Nullable<Mesh> = null;
         private _source: Nullable<Mesh> = null;
         // Will be used to for fast cloned mesh lookup
         // Will be used to for fast cloned mesh lookup
-        private meshMap: Nullable<{[id: string]: Mesh | undefined}>;
+        private meshMap: Nullable<{ [id: string]: Mesh | undefined }>;
 
 
         /**
         /**
          * Gets the source mesh (the one used to clone this one from)
          * Gets the source mesh (the one used to clone this one from)
@@ -888,91 +888,19 @@ module BABYLON {
         /**
         /**
          * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
          * 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.
          * 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
          * @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) {
             if (this._boundingInfo && this._boundingInfo.isLocked) {
                 return this;
                 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;
             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 */
         /** @hidden */
         public _createGlobalSubMesh(force: boolean): Nullable<SubMesh> {
         public _createGlobalSubMesh(force: boolean): Nullable<SubMesh> {
             var totalVertices = this.getTotalVertices();
             var totalVertices = this.getTotalVertices();
@@ -2380,7 +2308,6 @@ module BABYLON {
 
 
         /**
         /**
          * Creates a new InstancedMesh object from the mesh model.
          * Creates a new InstancedMesh object from the mesh model.
-         * Warning : this method is not supported for Line mesh and LineSystem
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
          * @see http://doc.babylonjs.com/how_to/how_to_use_instances
          * @param name defines the name of the new instance
          * @param name defines the name of the new instance
          * @returns a new InstancedMesh
          * @returns a new InstancedMesh
@@ -2471,7 +2398,11 @@ module BABYLON {
             }
             }
 
 
             serializationObject.scaling = this.scaling.asArray();
             serializationObject.scaling = this.scaling.asArray();
-            serializationObject.localMatrix = this.getPivotMatrix().asArray();
+            if (this._postMultiplyPivotMatrix) {
+                serializationObject.pivotMatrix = this.getPivotMatrix().asArray();
+            } else {
+                serializationObject.localMatrix = this.getPivotMatrix().asArray();
+            }
 
 
             serializationObject.isEnabled = this.isEnabled(false);
             serializationObject.isEnabled = this.isEnabled(false);
             serializationObject.isVisible = this.isVisible;
             serializationObject.isVisible = this.isVisible;

+ 2 - 4
src/Mesh/babylon.subMesh.ts

@@ -344,10 +344,8 @@ module BABYLON {
 
 
             // LineMesh first as it's also a Mesh...
             // LineMesh first as it's also a Mesh...
             if (LinesMesh) {
             if (LinesMesh) {
-                const mesh = this._mesh instanceof InstancedMesh ? (<InstancedMesh>this._mesh).sourceMesh : this._mesh;
-                if (mesh instanceof LinesMesh) {
-                    const linesMesh = <LinesMesh>mesh;
-                    return this._intersectLines(ray, positions, indices, linesMesh.intersectionThreshold, fastCheck);
+                if (this._mesh.getClassName() === "InstancedLinesMesh" || this._mesh.getClassName() === "LinesMesh") {
+                    return this._intersectLines(ray, positions, indices, (this._mesh as any).intersectionThreshold, fastCheck);
                 }
                 }
             }
             }
 
 

+ 33 - 8
src/Mesh/babylon.transformNode.ts

@@ -89,7 +89,7 @@ module BABYLON {
         private _absolutePosition = Vector3.Zero();
         private _absolutePosition = Vector3.Zero();
         private _pivotMatrix = Matrix.Identity();
         private _pivotMatrix = Matrix.Identity();
         private _pivotMatrixInverse: Matrix;
         private _pivotMatrixInverse: Matrix;
-        private _postMultiplyPivotMatrix = false;
+        protected _postMultiplyPivotMatrix = false;
 
 
         protected _isWorldMatrixFrozen = false;
         protected _isWorldMatrixFrozen = false;
 
 
@@ -120,7 +120,7 @@ module BABYLON {
         /**
         /**
           * Gets or set the node position (default is (0.0, 0.0, 0.0))
           * Gets or set the node position (default is (0.0, 0.0, 0.0))
           */
           */
-         public get position(): Vector3 {
+        public get position(): Vector3 {
             return this._position;
             return this._position;
         }
         }
 
 
@@ -269,11 +269,11 @@ module BABYLON {
             this._cache.billboardMode = -1;
             this._cache.billboardMode = -1;
         }
         }
 
 
-         /**
-         * Flag the transform node as dirty (Forcing it to update everything)
-         * @param property if set to "rotation" the objects rotationQuaternion will be set to null
-         * @returns this transform node
-         */
+        /**
+        * Flag the transform node as dirty (Forcing it to update everything)
+        * @param property if set to "rotation" the objects rotationQuaternion will be set to null
+        * @returns this transform node
+        */
         public markAsDirty(property: string): TransformNode {
         public markAsDirty(property: string): TransformNode {
             if (property === "rotation") {
             if (property === "rotation") {
                 this.rotationQuaternion = null;
                 this.rotationQuaternion = null;
@@ -306,7 +306,7 @@ module BABYLON {
          * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @param postMultiplyPivotMatrix defines if the pivot matrix must be cancelled in the world matrix. When this parameter is set to true (default), the inverse of the pivot matrix is also applied at the end to cancel the transformation effect
          * @returns the current TransformNode
          * @returns the current TransformNode
         */
         */
-        public setPivotMatrix(matrix: Readonly<Matrix>, postMultiplyPivotMatrix = true): TransformNode {
+        public setPivotMatrix(matrix: DeepImmutable<Matrix>, postMultiplyPivotMatrix = true): TransformNode {
             this._pivotMatrix.copyFrom(matrix);
             this._pivotMatrix.copyFrom(matrix);
             this._cache.pivotMatrixUpdated = true;
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
@@ -993,6 +993,31 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * 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
          * Clone the current transform node
          * @param name Name of the new clone
          * @param name Name of the new clone
          * @param newParent New parent for the clone
          * @param newParent New parent for the clone

+ 8 - 1
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -435,7 +435,14 @@ module BABYLON {
                 //get original center with no rotation
                 //get original center with no rotation
                 var c = center.clone();
                 var c = center.clone();
 
 
-                var oldPivot = mesh.getPivotMatrix() || Matrix.Translation(0, 0, 0);
+                var oldPivot = mesh.getPivotMatrix();
+                if (oldPivot) {
+                    // create a copy the pivot Matrix as it is modified in place
+                    oldPivot = oldPivot.clone();
+                }
+                else {
+                    oldPivot = Matrix.Identity();
+                }
 
 
                 //calculate the new center using a pivot (since this.BJSCANNON.js doesn't center height maps)
                 //calculate the new center using a pivot (since this.BJSCANNON.js doesn't center height maps)
                 var p = Matrix.Translation(boundingInfo.boundingBox.extendSizeWorld.x, 0, -boundingInfo.boundingBox.extendSizeWorld.z);
                 var p = Matrix.Translation(boundingInfo.boundingBox.extendSizeWorld.x, 0, -boundingInfo.boundingBox.extendSizeWorld.z);

+ 0 - 8
src/Physics/Plugins/babylon.oimoJSPlugin.ts

@@ -149,10 +149,6 @@ module BABYLON {
                         bodyConfig.pos.push(center.z);
                         bodyConfig.pos.push(center.z);
                         bodyConfig.posShape.push(0, 0, 0);
                         bodyConfig.posShape.push(0, 0, 0);
 
 
-                        //tmp solution
-                        bodyConfig.rot.push(0);
-                        bodyConfig.rot.push(0);
-                        bodyConfig.rot.push(0);
                         bodyConfig.rotShape.push(0, 0, 0);
                         bodyConfig.rotShape.push(0, 0, 0);
                     } else {
                     } else {
                         let localPosition = i.object.getAbsolutePosition().subtract(impostor.object.getAbsolutePosition());
                         let localPosition = i.object.getAbsolutePosition().subtract(impostor.object.getAbsolutePosition());
@@ -161,10 +157,6 @@ module BABYLON {
                         bodyConfig.posShape.push(localPosition.z);
                         bodyConfig.posShape.push(localPosition.z);
                         bodyConfig.pos.push(0, 0, 0);
                         bodyConfig.pos.push(0, 0, 0);
 
 
-                        //tmp solution until https://github.com/lo-th/OIMO.js/pull/37 is merged
-                        bodyConfig.rot.push(0);
-                        bodyConfig.rot.push(0);
-                        bodyConfig.rot.push(0);
                         bodyConfig.rotShape.push(rot.x * radToDeg);
                         bodyConfig.rotShape.push(rot.x * radToDeg);
                         bodyConfig.rotShape.push(rot.y * radToDeg);
                         bodyConfig.rotShape.push(rot.y * radToDeg);
                         bodyConfig.rotShape.push(rot.z * radToDeg);
                         bodyConfig.rotShape.push(rot.z * radToDeg);

+ 1 - 1
src/Physics/babylon.physicsImpostor.ts

@@ -256,7 +256,7 @@ module BABYLON {
                 return;
                 return;
             }
             }
 
 
-            //legacy support for old syntax.
+            // Legacy support for old syntax.
             if (!this._scene && object.getScene) {
             if (!this._scene && object.getScene) {
                 this._scene = object.getScene();
                 this._scene = object.getScene();
             }
             }

+ 7 - 4
src/PostProcess/RenderPipeline/babylon.postProcessRenderEffect.ts

@@ -151,12 +151,15 @@ module BABYLON {
             for (var i = 0; i < cams.length; i++) {
             for (var i = 0; i < cams.length; i++) {
                 var camera: Camera = cams[i];
                 var camera: Camera = cams[i];
                 var cameraName: string = camera.name;
                 var cameraName: string = camera.name;
-                this._postProcesses[this._singleInstance ? 0 : cameraName].forEach((postProcess: PostProcess) => {
-                    camera.detachPostProcess(postProcess);
-                });
+                const postProcesses = this._postProcesses[this._singleInstance ? 0 : cameraName];
+
+                if (postProcesses) {
+                    postProcesses.forEach((postProcess: PostProcess) => {
+                        camera.detachPostProcess(postProcess);
+                    });
+                }
 
 
                 if (this._cameras[cameraName]) {
                 if (this._cameras[cameraName]) {
-                    //this._indicesForCamera.splice(index, 1);
                     this._cameras[cameraName] = null;
                     this._cameras[cameraName] = null;
                 }
                 }
             }
             }

+ 5 - 10
src/Rendering/babylon.edgesRenderer.ts

@@ -62,25 +62,20 @@ module BABYLON {
         return this;
         return this;
     };
     };
 
 
-    export interface InstancedMesh {
+    export interface InstancedLinesMesh {
         /**
         /**
          * Enables the edge rendering mode on the mesh.
          * Enables the edge rendering mode on the mesh.
          * This mode makes the mesh edges visible
          * This mode makes the mesh edges visible
          * @param epsilon defines the maximal distance between two angles to detect a face
          * @param epsilon defines the maximal distance between two angles to detect a face
          * @param checkVerticesInsteadOfIndices indicates that we should check vertex list directly instead of faces
          * @param checkVerticesInsteadOfIndices indicates that we should check vertex list directly instead of faces
-         * @returns the currentInstancedMesh
+         * @returns the current InstancedLinesMesh
          * @see https://www.babylonjs-playground.com/#19O9TU#0
          * @see https://www.babylonjs-playground.com/#19O9TU#0
          */
          */
-        enableEdgesRendering(epsilon?: number, checkVerticesInsteadOfIndices?: boolean): InstancedMesh;
+        enableEdgesRendering(epsilon?: number, checkVerticesInsteadOfIndices?: boolean): InstancedLinesMesh;
     }
     }
 
 
-    InstancedMesh.prototype.enableEdgesRendering = function(epsilon = 0.95, checkVerticesInsteadOfIndices = false): InstancedMesh {
-        if (this.sourceMesh.getClassName() === 'LinesMesh') {
-            LinesMesh.prototype.enableEdgesRendering.apply(this, arguments);
-        }
-        else {
-            AbstractMesh.prototype.enableEdgesRendering.apply(this, arguments);
-        }
+    InstancedLinesMesh.prototype.enableEdgesRendering = function(epsilon = 0.95, checkVerticesInsteadOfIndices = false): InstancedLinesMesh {
+        LinesMesh.prototype.enableEdgesRendering.apply(this, arguments);
         return this;
         return this;
     };
     };
 
 

+ 0 - 4
src/Shaders/background.vertex.fx

@@ -15,10 +15,6 @@ attribute vec3 normal;
 // Uniforms
 // Uniforms
 #include<instancesDeclaration>
 #include<instancesDeclaration>
 
 
-#ifdef POINTSIZE
-uniform float pointSize;
-#endif
-
 // Output
 // Output
 varying vec3 vPositionW;
 varying vec3 vPositionW;
 #ifdef NORMAL
 #ifdef NORMAL

+ 11 - 1
src/Tools/babylon.tools.ts

@@ -1517,6 +1517,16 @@ module BABYLON {
                 scene.activeCamera = camera;
                 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)
             // 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);
             var texture = new RenderTargetTexture("screenShot", size, scene, false, false, Engine.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE);
             texture.renderList = null;
             texture.renderList = null;
@@ -1536,7 +1546,7 @@ module BABYLON {
             if (previousCamera) {
             if (previousCamera) {
                 scene.activeCamera = previousCamera;
                 scene.activeCamera = previousCamera;
             }
             }
-
+            engine.setSize(originalSize.width, originalSize.height);
             camera.getProjectionMatrix(true); // Force cache refresh;
             camera.getProjectionMatrix(true); // Force cache refresh;
         }
         }
 
 

+ 10 - 6
src/babylon.node.ts

@@ -9,7 +9,7 @@ module BABYLON {
      * Node is the basic class for all scene objects (Mesh, Light, Camera.)
      * Node is the basic class for all scene objects (Mesh, Light, Camera.)
      */
      */
     export class Node implements IBehaviorAware<Node> {
     export class Node implements IBehaviorAware<Node> {
-        private static _NodeConstructors: {[key: string]: any} = {};
+        private static _NodeConstructors: { [key: string]: any } = {};
 
 
         /**
         /**
          * Add a new node constructor
          * Add a new node constructor
@@ -228,15 +228,18 @@ module BABYLON {
          * Creates a new Node
          * Creates a new Node
          * @param name the name and id to be given to this node
          * @param name the name and id to be given to this node
          * @param scene the scene this node will be added to
          * @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.name = name;
             this.id = name;
             this.id = name;
             this._scene = <Scene>(scene || Engine.LastCreatedScene);
             this._scene = <Scene>(scene || Engine.LastCreatedScene);
             this.uniqueId = this._scene.getUniqueId();
             this.uniqueId = this._scene.getUniqueId();
             this._initCache();
             this._initCache();
 
 
-            this.addToSceneRootNodes();
+            if (addToRootNodes) {
+                this.addToSceneRootNodes();
+            }
         }
         }
 
 
         /**
         /**
@@ -364,7 +367,7 @@ module BABYLON {
         /** @hidden */
         /** @hidden */
         public updateCache(force?: boolean): void {
         public updateCache(force?: boolean): void {
             if (!force && this.isSynchronized()) {
             if (!force && this.isSynchronized()) {
-                return;
+                return;
             }
             }
 
 
             this._cache.parent = this.parent;
             this._cache.parent = this.parent;
@@ -547,10 +550,11 @@ module BABYLON {
         /**
         /**
          * Get all direct children of this node
          * Get all direct children of this node
          * @param predicate defines an optional predicate that will be called on every evaluated child, the predicate must return true for a given child to be part of the result, otherwise it will be ignored
          * @param predicate defines an optional predicate that will be called on every evaluated child, the predicate must return true for a given child to be part of the result, otherwise it will be ignored
+         * @param directDescendantsOnly defines if true only direct descendants of 'this' will be considered, if false direct and also indirect (children of children, an so on in a recursive manner) descendants of 'this' will be considered (Default: true)
          * @returns an array of Node
          * @returns an array of Node
          */
          */
-        public getChildren(predicate?: (node: Node) => boolean): Node[] {
-            return this.getDescendants(true, predicate);
+        public getChildren(predicate?: (node: Node) => boolean, directDescendantsOnly = true): Node[] {
+            return this.getDescendants(directDescendantsOnly, predicate);
         }
         }
 
 
         /** @hidden */
         /** @hidden */

+ 72 - 17
src/babylon.scene.ts

@@ -507,6 +507,11 @@ module BABYLON {
         public onAfterStepObservable = new Observable<Scene>();
         public onAfterStepObservable = new Observable<Scene>();
 
 
         /**
         /**
+         * An event triggered when the activeCamera property is updated
+         */
+        public onActiveCameraChanged = new Observable<Scene>();
+
+        /**
          * This Observable will be triggered before rendering each renderingGroup of each rendered camera.
          * This Observable will be triggered before rendering each renderingGroup of each rendered camera.
          * The RenderinGroupInfo class contains all the information about the context in which the observable is called
          * The RenderinGroupInfo class contains all the information about the context in which the observable is called
          * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
          * If you wish to register an Observer only for a given set of renderingGroup, use the mask with a combination of the renderingGroup index elevated to the power of two (1 for renderingGroup 0, 2 for renderingrOup1, 4 for 2 and 8 for 3)
@@ -776,8 +781,21 @@ module BABYLON {
 
 
         /** All of the active cameras added to this scene. */
         /** All of the active cameras added to this scene. */
         public activeCameras = new Array<Camera>();
         public activeCameras = new Array<Camera>();
-        /** The current active camera */
-        public activeCamera: Nullable<Camera>;
+
+        private _activeCamera: Nullable<Camera>;
+        /** Gets or sets the current active camera */
+        public get activeCamera(): Nullable<Camera> {
+            return this._activeCamera;
+        }
+
+        public set activeCamera(value: Nullable<Camera>) {
+            if (value === this._activeCamera) {
+                return;
+            }
+
+            this._activeCamera = value;
+            this.onActiveCameraChanged.notifyObservers(this);
+        }
 
 
         private _defaultMaterial: Material;
         private _defaultMaterial: Material;
 
 
@@ -2213,7 +2231,7 @@ module BABYLON {
                     return false;
                     return false;
                 }
                 }
 
 
-                let hardwareInstancedRendering = mesh.getClassName() === "InstancedMesh" || engine.getCaps().instancedArrays && (<Mesh>mesh).instances.length > 0;
+                let hardwareInstancedRendering = mesh.getClassName() === "InstancedMesh" || mesh.getClassName() === "InstancedLinesMesh" || engine.getCaps().instancedArrays && (<Mesh>mesh).instances.length > 0;
                 // Is Ready For Mesh
                 // Is Ready For Mesh
                 for (let step of this._isReadyForMeshStage) {
                 for (let step of this._isReadyForMeshStage) {
                     if (!step.action(mesh, hardwareInstancedRendering)) {
                     if (!step.action(mesh, hardwareInstancedRendering)) {
@@ -2468,6 +2486,32 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
+         * Will start the animation sequence of a given target and its hierarchy
+         * @param target defines the target
+         * @param directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @param targetMask defines if the target should be animated if animations are present (this is called recursively on descendant animatables regardless of return value)
+         * @returns the list of created animatables
+         */
+        public beginHierarchyAnimation(target: any, directDescendantsOnly: boolean, from: number, to: number, loop?: boolean, speedRatio: number = 1.0, onAnimationEnd?: () => void, animatable?: Animatable, stopCurrent = true, targetMask?: (target: any) => boolean): Animatable[] {
+            let children = target.getDescendants(directDescendantsOnly);
+
+            let result = [];
+            result.push(this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask));
+            for (var child of children) {
+                result.push(this.beginAnimation(child, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask));
+            }
+
+            return result;
+        }
+
+        /**
          * Begin a new animation on a given node
          * Begin a new animation on a given node
          * @param target defines the target where the animation will take place
          * @param target defines the target where the animation will take place
          * @param animations defines the list of animations to start
          * @param animations defines the list of animations to start
@@ -3760,27 +3804,32 @@ module BABYLON {
          * @return the found node or null if not found at all
          * @return the found node or null if not found at all
          */
          */
         public getNodeByID(id: string): Nullable<Node> {
         public getNodeByID(id: string): Nullable<Node> {
-            var mesh = this.getMeshByID(id);
-
+            const mesh = this.getMeshByID(id);
             if (mesh) {
             if (mesh) {
                 return mesh;
                 return mesh;
             }
             }
 
 
-            var light = this.getLightByID(id);
+            const transformNode = this.getTransformNodeByID(id);
+            if (transformNode) {
+                return transformNode;
+            }
 
 
+            const light = this.getLightByID(id);
             if (light) {
             if (light) {
                 return light;
                 return light;
             }
             }
 
 
-            var camera = this.getCameraByID(id);
-
+            const camera = this.getCameraByID(id);
             if (camera) {
             if (camera) {
                 return camera;
                 return camera;
             }
             }
 
 
-            var bone = this.getBoneByID(id);
+            const bone = this.getBoneByID(id);
+            if (bone) {
+                return bone;
+            }
 
 
-            return bone;
+            return null;
         }
         }
 
 
         /**
         /**
@@ -3789,27 +3838,32 @@ module BABYLON {
          * @return the found node or null if not found at all.
          * @return the found node or null if not found at all.
          */
          */
         public getNodeByName(name: string): Nullable<Node> {
         public getNodeByName(name: string): Nullable<Node> {
-            var mesh = this.getMeshByName(name);
-
+            const mesh = this.getMeshByName(name);
             if (mesh) {
             if (mesh) {
                 return mesh;
                 return mesh;
             }
             }
 
 
-            var light = this.getLightByName(name);
+            const transformNode = this.getTransformNodeByName(name);
+            if (transformNode) {
+                return transformNode;
+            }
 
 
+            const light = this.getLightByName(name);
             if (light) {
             if (light) {
                 return light;
                 return light;
             }
             }
 
 
-            var camera = this.getCameraByName(name);
-
+            const camera = this.getCameraByName(name);
             if (camera) {
             if (camera) {
                 return camera;
                 return camera;
             }
             }
 
 
-            var bone = this.getBoneByName(name);
+            const bone = this.getBoneByName(name);
+            if (bone) {
+                return bone;
+            }
 
 
-            return bone;
+            return null;
         }
         }
 
 
         /**
         /**
@@ -4796,6 +4850,7 @@ module BABYLON {
             this.onPointerObservable.clear();
             this.onPointerObservable.clear();
             this.onPreKeyboardObservable.clear();
             this.onPreKeyboardObservable.clear();
             this.onKeyboardObservable.clear();
             this.onKeyboardObservable.clear();
+            this.onActiveCameraChanged.clear();
 
 
             this.detachControl();
             this.detachControl();
 
 

+ 33 - 0
src/babylon.types.ts

@@ -26,4 +26,37 @@ module BABYLON {
      * Alias for types that can be used by a Buffer or VertexBuffer.
      * Alias for types that can be used by a Buffer or VertexBuffer.
      */
      */
     export type DataArray = number[] | ArrayBuffer | ArrayBufferView;
     export type DataArray = number[] | ArrayBuffer | ArrayBufferView;
+
+    /**
+     * Alias type for primitive types
+     * @ignorenaming
+     */
+    type Primitive = undefined | null | boolean | string | number | Function;
+
+    /**
+     * Type modifier to make all the properties of an object Readonly
+     */
+    export type Immutable<T> = T extends Primitive
+      ? T
+      : T extends Array<infer U>
+        ? ReadonlyArray<U>
+        : /* T extends Map<infer K, infer V> ? ReadonlyMap<K, V> : // es2015+ only */
+          DeepImmutable<T>;
+
+    /**
+     * Type modifier to make all the properties of an object Readonly recursively
+     */
+    export type DeepImmutable<T> = T extends Primitive
+      ? T
+      : T extends Array<infer U>
+        ? DeepImmutableArray<U>
+        : /* T extends Map<infer K, infer V> ? DeepImmutableMap<K, V> : // es2015+ only */
+          DeepImmutableObject<T>;
+
+    /** @hidden */
+    interface DeepImmutableArray<T> extends ReadonlyArray<DeepImmutable<T>> {}
+    /** @hidden */
+    /* interface DeepImmutableMap<K, V> extends ReadonlyMap<DeepImmutable<K>, DeepImmutable<V>> {} // es2015+ only */
+    /** @hidden */
+    type DeepImmutableObject<T> = { readonly [K in keyof T]: DeepImmutable<T[K]> };
 }
 }

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

@@ -417,18 +417,14 @@ describe('Babylon Scene Loader', function () {
         it('Load MultiPrimitive', () => {
         it('Load MultiPrimitive', () => {
             const scene = new BABYLON.Scene(subject);
             const scene = new BABYLON.Scene(subject);
             return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then(result => {
             return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then(result => {
-                expect(result.meshes, "meshes").to.have.lengthOf(4);
+                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.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);
                     expect(childNode, "child node").to.be.an.instanceof(BABYLON.Mesh);
                     const childMesh = childNode as BABYLON.Mesh;
                     const childMesh = childNode as BABYLON.Mesh;
                     expect(childMesh.geometry).to.exist;
                     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 => {
             return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/BrainStem/", "BrainStem.gltf", scene).then(result => {
                 expect(result.skeletons, "skeletons").to.have.lengthOf(1);
                 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()) {
                 for (const childMesh of node1.getChildMeshes()) {
                     expect(childMesh.skeleton, "mesh skeleton").to.exist;
                     expect(childMesh.skeleton, "mesh skeleton").to.exist;
                     expect(childMesh.skeleton.name, "mesh skeleton name").to.equal(result.skeletons[0].name);
                     expect(childMesh.skeleton.name, "mesh skeleton name").to.equal(result.skeletons[0].name);

+ 72 - 0
tests/unit/babylon/src/Mesh/babylon.positionAndRotation.tests.ts

@@ -0,0 +1,72 @@
+/**
+ * Describes the test suite.
+ */
+describe('Babylon position and rotation', () => {
+    let subject: BABYLON.Engine;
+
+    /**
+     * Loads the dependencies.
+     */
+    before(function (done) {
+        this.timeout(180000);
+        (BABYLONDEVTOOLS).Loader
+            .useDist()
+            .load(function () {
+                // Force apply promise polyfill for consistent behavior between PhantomJS, IE11, and other browsers.
+                BABYLON.PromisePolyfill.Apply(true);
+                done();
+            });
+    });
+
+    /**
+     * Create a new engine subject before each test.
+     */
+    beforeEach(function () {
+        subject = new BABYLON.NullEngine({
+            renderHeight: 256,
+            renderWidth: 256,
+            textureSize: 256,
+            deterministicLockstep: false,
+            lockstepMaxSteps: 1
+        });
+    });
+
+    describe('#position and rotation:', () => {
+        it('converts between quaternions/euler', () => {
+            // Converting between quaternions/euler
+            var originalRotation = new BABYLON.Vector3(0.1,0.2,0.3);
+            var v = originalRotation.clone()
+            var q = BABYLON.Quaternion.FromEulerVector(v)
+            q.toEulerAnglesToRef(v)
+            expect(v.subtract(originalRotation).length() < 0.00001).to.equal(true)
+        });
+        it('reorders vector in place', () => {
+            var originalRotation = new BABYLON.Vector3(0.1,0.2,0.3);
+            var v = originalRotation.clone()
+            v.reorderInPlace("ZYX")
+            expect(v.subtract(new BABYLON.Vector3(0.3,0.2,0.1)).length() < 0.00001).to.equal(true)
+        });
+        it('handles parenting', () => {
+            // Parent child positions
+            const scene = new BABYLON.Scene(subject);
+            var child = new BABYLON.AbstractMesh("", scene)
+            var parent = new BABYLON.AbstractMesh("", scene)
+            parent.position.set(0,0,1)
+            child.position.set(0,0,-1)
+            child.parent = parent
+            child.computeWorldMatrix()
+            expect(child.absolutePosition.equals(new BABYLON.Vector3(0,0,0))).to.equal(true)
+
+            //Rotate parent around child
+            parent.rotationQuaternion = new BABYLON.Quaternion()
+            var eulerRotation = new BABYLON.Vector3(0,Math.PI/2,0)
+            var rotation = new BABYLON.Quaternion()
+            BABYLON.Quaternion.RotationYawPitchRollToRef(eulerRotation.y, eulerRotation.x, eulerRotation.z, rotation)
+            parent.rotationQuaternion.multiplyInPlace(rotation);
+            parent.position.rotateByQuaternionAroundPointToRef(rotation, child.absolutePosition, parent.position)
+            expect(parent.position.subtract(new BABYLON.Vector3(1,0,0)).length() < 0.00001).to.equal(true)
+            expect(parent.rotationQuaternion.toEulerAngles().subtract(eulerRotation).length() < 0.00001).to.equal(true)
+            expect(child.absolutePosition.subtract(new BABYLON.Vector3(0,0,0)).length() < 0.00001).to.equal(true)
+        });
+    });
+});

+ 1 - 0
tests/unit/karma.conf.js

@@ -15,6 +15,7 @@ module.exports = function (config) {
             '!./**/*.d.ts',
             '!./**/*.d.ts',
             './Tools/DevLoader/BabylonLoader.js',
             './Tools/DevLoader/BabylonLoader.js',
             './tests/unit/babylon/babylon.example.tests.js',
             './tests/unit/babylon/babylon.example.tests.js',
+            './tests/unit/babylon/src/Mesh/babylon.positionAndRotation.tests.js',
             './tests/unit/babylon/serializers/babylon.glTFSerializer.tests.js',
             './tests/unit/babylon/serializers/babylon.glTFSerializer.tests.js',
             './tests/unit/babylon/src/babylon.node.tests.js',
             './tests/unit/babylon/src/babylon.node.tests.js',
             './tests/unit/babylon/src/Animations/babylon.animationGroup.tests.js',
             './tests/unit/babylon/src/Animations/babylon.animationGroup.tests.js',

BIN
tests/validation/ReferenceImages/xrCameraContainerRotation.png


File diff suppressed because it is too large
+ 590 - 584
tests/validation/config.json