Browse Source

Merge remote-tracking branch 'BabylonJS/master' into modules

# Conflicts:
#	src/Debug/babylon.axesViewer.ts
#	src/Engine/babylon.engine.ts
Raanan Weber 7 years ago
parent
commit
1060c8ba49
100 changed files with 19794 additions and 13513 deletions
  1. 16 8
      .travis.yml
  2. 3727 3552
      Playground/babylon.d.txt
  3. 167 72
      Playground/debug.html
  4. 23 21
      Playground/frame.html
  5. 2 0
      Playground/index.html
  6. BIN
      Playground/textures/normal.png
  7. 9 1
      Tools/DevLoader/BabylonLoader.js
  8. 12 1
      Tools/Gulp/config.json
  9. 29 11
      Tools/Gulp/gulpfile.js
  10. 10 1
      Tools/Gulp/package.json
  11. 2459 2279
      dist/preview release/babylon.d.ts
  12. 49 48
      dist/preview release/babylon.js
  13. 1031 321
      dist/preview release/babylon.max.js
  14. 49 48
      dist/preview release/babylon.worker.js
  15. 5948 5599
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  16. 52 51
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  17. 1223 402
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  18. 1 1
      dist/preview release/gui/package.json
  19. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  20. 6 0
      dist/preview release/inspector/babylon.inspector.js
  21. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  22. 1 1
      dist/preview release/inspector/package.json
  23. 48 12
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  24. 138 19
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  25. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  26. 50 15
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  27. 179 78
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  28. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  29. 62 17
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  30. 192 81
      dist/preview release/loaders/babylon.glTFFileLoader.js
  31. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  32. 192 81
      dist/preview release/loaders/babylonjs.loaders.js
  33. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  34. 62 17
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  35. 1 1
      dist/preview release/loaders/package.json
  36. 1 1
      dist/preview release/materialsLibrary/package.json
  37. 1 1
      dist/preview release/postProcessesLibrary/package.json
  38. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  39. 162 0
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  40. 649 0
      dist/preview release/serializers/babylon.glTF2Serializer.js
  41. 1 0
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  42. 650 0
      dist/preview release/serializers/babylonjs.serializers.js
  43. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  44. 163 0
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  45. 1 1
      dist/preview release/serializers/package.json
  46. 53 52
      dist/preview release/viewer/babylon.viewer.js
  47. 1 1
      dist/preview release/viewer/package.json
  48. 6 0
      dist/preview release/what's new.md
  49. 7 0
      inspector/src/tabs/StatsTab.ts
  50. 19 7
      loaders/src/glTF/1.0/babylon.glTFLoader.ts
  51. 62 72
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  52. 112 32
      loaders/src/glTF/babylon.glTFFileLoader.ts
  53. 3 2
      package.json
  54. 0 1
      sandbox/index.js
  55. 728 0
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  56. 72 0
      serializers/src/glTF/2.0/babylon.glTFSerializer.ts
  57. 6 5
      src/Animations/babylon.animatable.ts
  58. 44 0
      src/Animations/babylon.animation.ts
  59. 40 4
      src/Animations/babylon.animationGroup.ts
  60. 1 1
      src/Audio/babylon.sound.ts
  61. 38 10
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  62. 16 8
      src/Cameras/VR/babylon.webVRCamera.ts
  63. 1 0
      src/Cameras/babylon.targetCamera.ts
  64. 323 116
      src/Engine/babylon.engine.ts
  65. 11 7
      src/Helpers/babylon.environmentHelper.ts
  66. 109 101
      src/Instrumentation/babylon.sceneInstrumentation.ts
  67. 64 25
      src/Loading/babylon.sceneLoader.ts
  68. 8 1
      src/Materials/Textures/babylon.colorGradingTexture.ts
  69. 2 1
      src/Materials/Textures/babylon.internalTexture.ts
  70. 26 22
      src/Materials/Textures/babylon.multiRenderTarget.ts
  71. 13 5
      src/Materials/Textures/babylon.renderTargetTexture.ts
  72. 1 1
      src/Materials/Textures/babylon.videoTexture.ts
  73. 22 5
      src/Materials/babylon.effect.ts
  74. 24 23
      src/Materials/babylon.materialHelper.ts
  75. 14 1
      src/Materials/babylon.shaderMaterial.ts
  76. 5 5
      src/Mesh/babylon.abstractMesh.ts
  77. 5 4
      src/Mesh/babylon.geometry.ts
  78. 2 2
      src/Mesh/babylon.mesh.ts
  79. 40 39
      src/Mesh/babylon.mesh.vertexData.ts
  80. 3 5
      src/Mesh/babylon.subMesh.ts
  81. 5 2
      src/Mesh/babylon.transformNode.ts
  82. 54 0
      src/Particles/babylon.boxParticleEmitter.ts
  83. 29 0
      src/Particles/babylon.coneParticleEmitter.ts
  84. 6 0
      src/Particles/babylon.iParticleEmitterType.ts
  85. 60 33
      src/Particles/babylon.particleSystem.ts
  86. 35 0
      src/Particles/babylon.sphereParticleEmitter.ts
  87. 15 5
      src/Rendering/babylon.geometryBufferRenderer.ts
  88. 12 5
      src/Rendering/babylon.renderingGroup.ts
  89. 11 4
      src/Rendering/babylon.renderingManager.ts
  90. 20 14
      src/Tools/babylon.assetsManager.ts
  91. 2 2
      src/Tools/babylon.filesInput.ts
  92. 5 0
      src/Tools/babylon.sceneSerializer.ts
  93. 144 58
      src/Tools/babylon.tools.ts
  94. 14 43
      src/babylon.mixins.ts
  95. 15 7
      src/babylon.node.ts
  96. 103 24
      src/babylon.scene.ts
  97. BIN
      tests/validation/ReferenceImages/Flat2009.png
  98. BIN
      tests/validation/ReferenceImages/GLTF LOD.png
  99. BIN
      tests/validation/ReferenceImages/SpaceDeK.png
  100. 0 0
      tests/validation/ReferenceImages/instancedBones.png

+ 16 - 8
.travis.yml

@@ -1,8 +1,16 @@
-language: node_js
-node_js:
-  - "6"
-before_script:
-  - npm install -g gulp
-  - cd ./Tools/Gulp
-  - npm install
-script: gulp typescript
+language: node_js
+node_js:
+- '6'
+addons:
+  sauce_connect:
+    username: vandenberghe.sebastien@gmail.com
+  jwt:
+    secure: kbaJ3o6cVqpB8Gzfd0yDSgGwH+SQXukevtpjMQ8Gip+N6FOumQxihPDqsqYKxc8svvR024nDmkIhbjPjz/YguvC3WUgYW4xk3Za7P7C9cpj2fYdrFuO7pD4sd/fNdUCqvKQ8jcxlIq4eEdBuoBTOHsP9J5KH7Z1M7e58atkx0o8=
+before_script:
+- npm install -g gulp
+- cd ./Tools/Gulp
+- npm install
+script: gulp
+notifications:
+  slack:
+    secure: TBYDAN8Dlkx3dM+Q5ClAZem7agAhQ1oB/fGT665qn7D+j2YfWChvlfXegvXL4LPDmQgbI0UfazcjWId5a0EwmmPkRb+kMJItPiMt5jiIp2WKoZQ+qob6H9tBCRJbbpWM430wiPeKfBfbcZP/XSlpVMWhgU5ogAFDSUKjvHT7IuE=

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


+ 167 - 72
Playground/debug.html

@@ -6,24 +6,24 @@
     <meta charset='utf-8' />
     <meta charset='utf-8' />
     <meta name="viewport" content="width=device-width, user-scalable=no">
     <meta name="viewport" content="width=device-width, user-scalable=no">
     <link rel="shortcut icon" href="https://www.babylonjs.com/img/favicon/favicon.ico">
     <link rel="shortcut icon" href="https://www.babylonjs.com/img/favicon/favicon.ico">
-	<link rel="apple-touch-icon" sizes="57x57" href="https://www.babylonjs.com/img/favicon/apple-icon-57x57.png">
-	<link rel="apple-touch-icon" sizes="60x60" href="https://www.babylonjs.com/img/favicon/apple-icon-60x60.png">
-	<link rel="apple-touch-icon" sizes="72x72" href="https://www.babylonjs.com/img/favicon/apple-icon-72x72.png">
-	<link rel="apple-touch-icon" sizes="76x76" href="https://www.babylonjs.com/img/favicon/apple-icon-76x76.png">
-	<link rel="apple-touch-icon" sizes="114x114" href="https://www.babylonjs.com/img/favicon/apple-icon-114x114.png">
-	<link rel="apple-touch-icon" sizes="120x120" href="https://www.babylonjs.com/img/favicon/apple-icon-120x120.png">
-	<link rel="apple-touch-icon" sizes="144x144" href="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png">
-	<link rel="apple-touch-icon" sizes="152x152" href="https://www.babylonjs.com/img/favicon/apple-icon-152x152.png">
-	<link rel="apple-touch-icon" sizes="180x180" href="https://www.babylonjs.com/img/favicon/apple-icon-180x180.png">
-	<link rel="icon" type="image/png" sizes="192x192"  href="https://www.babylonjs.com/img/favicon/android-icon-192x192.png">
-	<link rel="icon" type="image/png" sizes="32x32" href="https://www.babylonjs.com/img/favicon/favicon-32x32.png">
-	<link rel="icon" type="image/png" sizes="96x96" href="https://www.babylonjs.com/img/favicon/favicon-96x96.png">
-	<link rel="icon" type="image/png" sizes="16x16" href="https://www.babylonjs.com/img/favicon/favicon-16x16.png">
-	<link rel="manifest" href="https://www.babylonjs.com/img/favicon/manifest.json">
-	<meta name="msapplication-TileColor" content="#ffffff">
-	<meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
-	<meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
-	<meta name="theme-color" content="#ffffff">
+    <link rel="apple-touch-icon" sizes="57x57" href="https://www.babylonjs.com/img/favicon/apple-icon-57x57.png">
+    <link rel="apple-touch-icon" sizes="60x60" href="https://www.babylonjs.com/img/favicon/apple-icon-60x60.png">
+    <link rel="apple-touch-icon" sizes="72x72" href="https://www.babylonjs.com/img/favicon/apple-icon-72x72.png">
+    <link rel="apple-touch-icon" sizes="76x76" href="https://www.babylonjs.com/img/favicon/apple-icon-76x76.png">
+    <link rel="apple-touch-icon" sizes="114x114" href="https://www.babylonjs.com/img/favicon/apple-icon-114x114.png">
+    <link rel="apple-touch-icon" sizes="120x120" href="https://www.babylonjs.com/img/favicon/apple-icon-120x120.png">
+    <link rel="apple-touch-icon" sizes="144x144" href="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png">
+    <link rel="apple-touch-icon" sizes="152x152" href="https://www.babylonjs.com/img/favicon/apple-icon-152x152.png">
+    <link rel="apple-touch-icon" sizes="180x180" href="https://www.babylonjs.com/img/favicon/apple-icon-180x180.png">
+    <link rel="icon" type="image/png" sizes="192x192" href="https://www.babylonjs.com/img/favicon/android-icon-192x192.png">
+    <link rel="icon" type="image/png" sizes="32x32" href="https://www.babylonjs.com/img/favicon/favicon-32x32.png">
+    <link rel="icon" type="image/png" sizes="96x96" href="https://www.babylonjs.com/img/favicon/favicon-96x96.png">
+    <link rel="icon" type="image/png" sizes="16x16" href="https://www.babylonjs.com/img/favicon/favicon-16x16.png">
+    <link rel="manifest" href="https://www.babylonjs.com/img/favicon/manifest.json">
+    <meta name="msapplication-TileColor" content="#ffffff">
+    <meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
+    <meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
+    <meta name="theme-color" content="#ffffff">
 
 
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <!--For canvas/code separator-->
     <!--For canvas/code separator-->
@@ -38,7 +38,7 @@
     <!-- Babylon.js -->
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
-    <script src="https://preview.babylonjs.com/babylon.max.js"></script>    
+    <script src="https://preview.babylonjs.com/babylon.max.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
 
 
     <script src="https://preview.babylonjs.com/materialsLibrary/babylon.fireMaterial.min.js"></script>
     <script src="https://preview.babylonjs.com/materialsLibrary/babylon.fireMaterial.min.js"></script>
@@ -73,6 +73,8 @@
 
 
     <script src="https://preview.babylonjs.com/gui/babylon.gui.js"></script>
     <script src="https://preview.babylonjs.com/gui/babylon.gui.js"></script>
 
 
+    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
+
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/canvas2D/dist/preview%20release/babylon.canvas2d.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/canvas2D/dist/preview%20release/babylon.canvas2d.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js"></script>
@@ -90,30 +92,43 @@
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
-            <div class="button run" id="runButton1600">Run <i class="fa fa-play" aria-hidden="true"></i></div>
+            <div class="button run" id="runButton1600">Run
+                <i class="fa fa-play" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
 
 
         <div class="category">
         <div class="category">
-            <div class="button" id="newButton1600">New<i class="fa fa-file" aria-hidden="true"></i></div>
-            <div class="button removeOnPhone" id="clearButton1600">Clear<i class="fa fa-trash" aria-hidden="true"></i></div>
+            <div class="button" id="newButton1600">New
+                <i class="fa fa-file" aria-hidden="true"></i>
+            </div>
+            <div class="button removeOnPhone" id="clearButton1600">Clear
+                <i class="fa fa-trash" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
-            <div class="button" id="saveButton1600">Save <i class="fa fa-floppy-o" aria-hidden="true"></i></div>
-            <div class="button removeOnPhone" id="zipButton1600">Zip<i class="fa fa-download" aria-hidden="true"></i></div>
+            <div class="button" id="saveButton1600">Save
+                <i class="fa fa-floppy-o" aria-hidden="true"></i>
+            </div>
+            <div class="button removeOnPhone" id="zipButton1600">Zip
+                <i class="fa fa-download" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
             <div class="button select">Settings
             <div class="button select">Settings
                 <div class="toDisplay">
                 <div class="toDisplay">
-                    <div class="option subSelect">Theme <i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">Theme
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" id="darkTheme1600">Dark</div>
                             <div class="option" id="darkTheme1600">Dark</div>
                             <div class="option" id="lightTheme1600">Light</div>
                             <div class="option" id="lightTheme1600">Light</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option subSelect"><span id="currentFontSize1600">Font: 14</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentFontSize1600">Font: 14</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
@@ -123,8 +138,11 @@
                             <div class="option" onclick="setFontSize(22);">22</div>
                             <div class="option" onclick="setFontSize(22);">22</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option" id="safemodeToggle1600">Safe mode <i class="fa fa-square-o" aria-hidden="true"></i></div>
-                    <div class="option checked" id="editorButton1600">Editor <i class="fa fa-check-square" aria-hidden="true"></i>
+                    <div class="option" id="safemodeToggle1600">Safe mode
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
+                    <div class="option checked" id="editorButton1600">Editor
+                        <i class="fa fa-check-square" aria-hidden="true"></i>
                     </div>
                     </div>
                     <div class="option" id="fullscreenButton1600">Fullscreen</div>
                     <div class="option" id="fullscreenButton1600">Fullscreen</div>
                     <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
                     <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
@@ -135,20 +153,24 @@
                 </div>
                 </div>
             </div>
             </div>
 
 
-            <div class="button uncheck" id="debugButton1600">Debug layer <i class="fa fa-square-o" aria-hidden="true"></i></div>
+            <div class="button uncheck" id="debugButton1600">Debug layer
+                <i class="fa fa-square-o" aria-hidden="true"></i>
+            </div>
             <div class="button" id="metadataButton1600">Metadata</div>
             <div class="button" id="metadataButton1600">Metadata</div>
         </div>
         </div>
 
 
 
 
 
 
         <div class="category right">
         <div class="category right">
-            <div class="button select"><span id="currentVersion1600">Version: Latest</span>
+            <div class="button select">
+                <span id="currentVersion1600">Version: Latest</span>
                 <div class="toDisplay">
                 <div class="toDisplay">
                     <div class="option" onclick="setVersion('latest');">Latest</div>
                     <div class="option" onclick="setVersion('latest');">Latest</div>
                     <div class="option" onclick="setVersion('2.5');">2.5</div>
                     <div class="option" onclick="setVersion('2.5');">2.5</div>
                 </div>
                 </div>
             </div>
             </div>
-            <div class="button select"> <span id="currentScript1600">Scenes</span>
+            <div class="button select">
+                <span id="currentScript1600">Scenes</span>
                 <div class="toDisplayBig">
                 <div class="toDisplayBig">
                     <ul id="scriptsList1600">
                     <ul id="scriptsList1600">
                     </ul>
                     </ul>
@@ -169,30 +191,43 @@
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
-            <div class="button run" id="runButton1475">Run <i class="fa fa-play" aria-hidden="true"></i></div>
+            <div class="button run" id="runButton1475">Run
+                <i class="fa fa-play" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
 
 
         <div class="category">
         <div class="category">
-            <div class="button" id="newButton1475">New<i class="fa fa-file" aria-hidden="true"></i></div>
-            <div class="button removeOnPhone" id="clearButton1475">Clear<i class="fa fa-trash" aria-hidden="true"></i></div>
+            <div class="button" id="newButton1475">New
+                <i class="fa fa-file" aria-hidden="true"></i>
+            </div>
+            <div class="button removeOnPhone" id="clearButton1475">Clear
+                <i class="fa fa-trash" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
-            <div class="button" id="saveButton1475">Save <i class="fa fa-floppy-o" aria-hidden="true"></i></div>
-            <div class="button removeOnPhone" id="zipButton1475">Zip<i class="fa fa-download" aria-hidden="true"></i></div>
+            <div class="button" id="saveButton1475">Save
+                <i class="fa fa-floppy-o" aria-hidden="true"></i>
+            </div>
+            <div class="button removeOnPhone" id="zipButton1475">Zip
+                <i class="fa fa-download" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
             <div class="button select">Settings
             <div class="button select">Settings
                 <div class="toDisplay">
                 <div class="toDisplay">
-                    <div class="option subSelect">Theme <i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">Theme
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" id="darkTheme1475">Dark</div>
                             <div class="option" id="darkTheme1475">Dark</div>
                             <div class="option" id="lightTheme1475">Light</div>
                             <div class="option" id="lightTheme1475">Light</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option subSelect"><span id="currentFontSize1475">Font: 14</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentFontSize1475">Font: 14</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
@@ -202,8 +237,11 @@
                             <div class="option" onclick="setFontSize(22);">22</div>
                             <div class="option" onclick="setFontSize(22);">22</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option" id='safemodeToggle1475'>Safe mode <i class="fa fa-square-o" aria-hidden="true"></i></div>
-                    <div class="option checked" id="editorButton1475">Editor <i class="fa fa-check-square" aria-hidden="true"></i>
+                    <div class="option" id='safemodeToggle1475'>Safe mode
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
+                    <div class="option checked" id="editorButton1475">Editor
+                        <i class="fa fa-check-square" aria-hidden="true"></i>
                     </div>
                     </div>
                     <div class="option" id="fullscreenButton1475">Fullscreen</div>
                     <div class="option" id="fullscreenButton1475">Fullscreen</div>
                     <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
                     <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
@@ -211,9 +249,13 @@
                     <div class="option" id="minimapToggle1475">Minimap
                     <div class="option" id="minimapToggle1475">Minimap
                         <i class="fa fa-square-o" aria-hidden="true"></i>
                         <i class="fa fa-square-o" aria-hidden="true"></i>
                     </div>
                     </div>
-                    <div class="option" id="debugButton1475">Debug layer<i class="fa fa-square-o" aria-hidden="true"></i></div>
+                    <div class="option" id="debugButton1475">Debug layer
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
                     <div class="option" id="metadataButton1475">Metadata</div>
                     <div class="option" id="metadataButton1475">Metadata</div>
-                    <div class="option subSelect"><span id="currentVersion1475">Vers. : Latest</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentVersion1475">Vers. : Latest</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setVersion('latest');">Latest</div>
                             <div class="option" onclick="setVersion('latest');">Latest</div>
                             <div class="option" onclick="setVersion('2.5');">2.5</div>
                             <div class="option" onclick="setVersion('2.5');">2.5</div>
@@ -224,7 +266,8 @@
         </div>
         </div>
 
 
         <div class="category right">
         <div class="category right">
-            <div class="button select"> <span id="currentScript1475">Scenes</span>
+            <div class="button select">
+                <span id="currentScript1475">Scenes</span>
                 <div class="toDisplayBig">
                 <div class="toDisplayBig">
                     <ul id="scriptsList1475">
                     <ul id="scriptsList1475">
                     </ul>
                     </ul>
@@ -238,30 +281,43 @@
 
 
     <div class="navbar navBar1030">
     <div class="navbar navBar1030">
         <div class="category">
         <div class="category">
-            <div class="button run" id="runButton1030">Run <i class="fa fa-play" aria-hidden="true"></i></div>
+            <div class="button run" id="runButton1030">Run
+                <i class="fa fa-play" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
 
 
         <div class="category">
         <div class="category">
-            <div class="button" id="newButton1030">New<i class="fa fa-file" aria-hidden="true"></i></div>
-            <div class="button removeOnPhone" id="clearButton1030">Clear<i class="fa fa-trash" aria-hidden="true"></i></div>
+            <div class="button" id="newButton1030">New
+                <i class="fa fa-file" aria-hidden="true"></i>
+            </div>
+            <div class="button removeOnPhone" id="clearButton1030">Clear
+                <i class="fa fa-trash" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
-            <div class="button" id="saveButton1030">Save <i class="fa fa-floppy-o" aria-hidden="true"></i></div>
-            <div class="button removeOnPhone" id="zipButton1030">Zip<i class="fa fa-download" aria-hidden="true"></i></div>
+            <div class="button" id="saveButton1030">Save
+                <i class="fa fa-floppy-o" aria-hidden="true"></i>
+            </div>
+            <div class="button removeOnPhone" id="zipButton1030">Zip
+                <i class="fa fa-download" aria-hidden="true"></i>
+            </div>
         </div>
         </div>
 
 
         <div class="category">
         <div class="category">
             <div class="button select">Settings
             <div class="button select">Settings
                 <div class="toDisplay">
                 <div class="toDisplay">
-                    <div class="option subSelect">Theme <i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">Theme
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" id="darkTheme1030">Dark</div>
                             <div class="option" id="darkTheme1030">Dark</div>
                             <div class="option" id="lightTheme1030">Light</div>
                             <div class="option" id="lightTheme1030">Light</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option subSelect"><span id="currentFontSize1030">Font: 14</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentFontSize1030">Font: 14</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
@@ -271,8 +327,11 @@
                             <div class="option" onclick="setFontSize(22);">22</div>
                             <div class="option" onclick="setFontSize(22);">22</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option" id="safemodeToggle1030">Safe mode <i class="fa fa-square-o" aria-hidden="true"></i></div>
-                    <div class="option checked" id="editorButton1030">Editor <i class="fa fa-check-square" aria-hidden="true"></i>
+                    <div class="option" id="safemodeToggle1030">Safe mode
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
+                    <div class="option checked" id="editorButton1030">Editor
+                        <i class="fa fa-check-square" aria-hidden="true"></i>
                     </div>
                     </div>
                     <div class="option" id="fullscreenButton1030">Fullscreen</div>
                     <div class="option" id="fullscreenButton1030">Fullscreen</div>
                     <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
                     <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
@@ -280,9 +339,13 @@
                     <div class="option" id="minimapToggle1030">Minimap
                     <div class="option" id="minimapToggle1030">Minimap
                         <i class="fa fa-square-o" aria-hidden="true"></i>
                         <i class="fa fa-square-o" aria-hidden="true"></i>
                     </div>
                     </div>
-                    <div class="option" id="debugButton1030">Debug layer<i class="fa fa-square-o" aria-hidden="true"></i></div>
+                    <div class="option" id="debugButton1030">Debug layer
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
                     <div class="option" id="metadataButton1030">Metadata</div>
                     <div class="option" id="metadataButton1030">Metadata</div>
-                    <div class="option subSelect"><span id="currentVersion1030">Vers. : Latest</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentVersion1030">Vers. : Latest</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setVersion('latest');">Latest</div>
                             <div class="option" onclick="setVersion('latest');">Latest</div>
                             <div class="option" onclick="setVersion('2.5');">2.5</div>
                             <div class="option" onclick="setVersion('2.5');">2.5</div>
@@ -293,7 +356,8 @@
         </div>
         </div>
 
 
         <div class="category right">
         <div class="category right">
-            <div class="button select"> <span id="currentScript1030">Scenes</span>
+            <div class="button select">
+                <span id="currentScript1030">Scenes</span>
                 <div class="toDisplayBig">
                 <div class="toDisplayBig">
                     <ul id="scriptsList1030">
                     <ul id="scriptsList1030">
                     </ul>
                     </ul>
@@ -309,11 +373,21 @@
         <div class="category">
         <div class="category">
             <div class="button select">File
             <div class="button select">File
                 <div class="toDisplay">
                 <div class="toDisplay">
-                    <div class="option" id="runButton750">Run <i class="fa fa-play" aria-hidden="true"></i></div>
-                    <div class="option" id="newButton750">New <i class="fa fa-file" aria-hidden="true"></i></div>
-                    <div class="option" id="clearButton750">Clear <i class="fa fa-trash" aria-hidden="true"></i></div>
-                    <div class="option" id="saveButton750">Save <i class="fa fa-floppy-o" aria-hidden="true"></i></div>
-                    <div class="option" id="zipButton750">Zip <i class="fa fa-download" aria-hidden="true"></i></div>
+                    <div class="option" id="runButton750">Run
+                        <i class="fa fa-play" aria-hidden="true"></i>
+                    </div>
+                    <div class="option" id="newButton750">New
+                        <i class="fa fa-file" aria-hidden="true"></i>
+                    </div>
+                    <div class="option" id="clearButton750">Clear
+                        <i class="fa fa-trash" aria-hidden="true"></i>
+                    </div>
+                    <div class="option" id="saveButton750">Save
+                        <i class="fa fa-floppy-o" aria-hidden="true"></i>
+                    </div>
+                    <div class="option" id="zipButton750">Zip
+                        <i class="fa fa-download" aria-hidden="true"></i>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>
@@ -321,13 +395,16 @@
         <div class="category">
         <div class="category">
             <div class="button select">Settings
             <div class="button select">Settings
                 <div class="toDisplay">
                 <div class="toDisplay">
-                    <div class="option subSelect">Theme <i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">Theme
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" id="darkTheme750">Dark</div>
                             <div class="option" id="darkTheme750">Dark</div>
                             <div class="option" id="lightTheme750">Light</div>
                             <div class="option" id="lightTheme750">Light</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option subSelect"><span id="currentFontSize750">Font: 14</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentFontSize750">Font: 14</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(12);">12</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
                             <div class="option" onclick="setFontSize(14);">14</div>
@@ -337,8 +414,11 @@
                             <div class="option" onclick="setFontSize(22);">22</div>
                             <div class="option" onclick="setFontSize(22);">22</div>
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="option" id="safemodeToggle750">Safe mode <i class="fa fa-square-o" aria-hidden="true"></i></div>
-                    <div style="display:none;" class="option checked" id="editorButton750">Editor <i class="fa fa-check-square" aria-hidden="true"></i>
+                    <div class="option" id="safemodeToggle750">Safe mode
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
+                    <div style="display:none;" class="option checked" id="editorButton750">Editor
+                        <i class="fa fa-check-square" aria-hidden="true"></i>
                     </div>
                     </div>
                     <div class="option" id="fullscreenButton750">Fullscreen</div>
                     <div class="option" id="fullscreenButton750">Fullscreen</div>
                     <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
                     <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
@@ -346,9 +426,13 @@
                     <div class="option" id="minimapToggle750">Minimap
                     <div class="option" id="minimapToggle750">Minimap
                         <i class="fa fa-square-o" aria-hidden="true"></i>
                         <i class="fa fa-square-o" aria-hidden="true"></i>
                     </div>
                     </div>
-                    <div class="option" id="debugButton750">Debug layer<i class="fa fa-square-o" aria-hidden="true"></i></div>
+                    <div class="option" id="debugButton750">Debug layer
+                        <i class="fa fa-square-o" aria-hidden="true"></i>
+                    </div>
                     <div class="option" id="metadataButton750">Metadata</div>
                     <div class="option" id="metadataButton750">Metadata</div>
-                    <div class="option subSelect"><span id="currentVersion750">Vers. : Latest</span><i class="fa fa-chevron-right" aria-hidden="true"></i>
+                    <div class="option subSelect">
+                        <span id="currentVersion750">Vers. : Latest</span>
+                        <i class="fa fa-chevron-right" aria-hidden="true"></i>
                         <div class="toDisplaySub">
                         <div class="toDisplaySub">
                             <div class="option" onclick="setVersion('latest');">Latest</div>
                             <div class="option" onclick="setVersion('latest');">Latest</div>
                             <div class="option" onclick="setVersion('2.5');">2.5</div>
                             <div class="option" onclick="setVersion('2.5');">2.5</div>
@@ -359,7 +443,8 @@
         </div>
         </div>
 
 
         <div class="category right">
         <div class="category right">
-            <div class="button select"> <span id="currentScript750">Scenes</span>
+            <div class="button select">
+                <span id="currentScript750">Scenes</span>
                 <div class="toDisplayBig">
                 <div class="toDisplayBig">
                     <ul id="scriptsList750">
                     <ul id="scriptsList750">
                     </ul>
                     </ul>
@@ -386,11 +471,21 @@
     <div class="navbarBottom">
     <div class="navbarBottom">
         <div id="statusBar"></div>
         <div id="statusBar"></div>
         <div class="links">
         <div class="links">
-            <div class='link'><a target='_new' href="https://www.netlify.com/">Deployed by Netlify</a></div>
-            <div class='link'> <a target='_new' href="http://www.html5gamedevs.com/forum/16-babylonjs/">Forum</a></div>
-            <div class='link'><a target='_new' href="https://www.babylonjs.com/sandbox">Sandbox</a></div>
-            <div class='link'><a target='_new' href="https://doc.babylonjs.com">Documentation</a></div>
-            <div class='link'><a target='_new' href="https://doc.babylonjs.com/playground">Search</a></div>
+            <div class='link'>
+                <a target='_new' href="https://www.netlify.com/">Deployed by Netlify</a>
+            </div>
+            <div class='link'>
+                <a target='_new' href="http://www.html5gamedevs.com/forum/16-babylonjs/">Forum</a>
+            </div>
+            <div class='link'>
+                <a target='_new' href="https://www.babylonjs.com/sandbox">Sandbox</a>
+            </div>
+            <div class='link'>
+                <a target='_new' href="https://doc.babylonjs.com">Documentation</a>
+            </div>
+            <div class='link'>
+                <a target='_new' href="https://doc.babylonjs.com/playground">Search</a>
+            </div>
         </div>
         </div>
     </div>
     </div>
 
 

+ 23 - 21
Playground/frame.html

@@ -4,30 +4,30 @@
 <head>
 <head>
     <title>Babylon.js Playground</title>
     <title>Babylon.js Playground</title>
     <link rel="shortcut icon" href="https://www.babylonjs.com/img/favicon/favicon.ico">
     <link rel="shortcut icon" href="https://www.babylonjs.com/img/favicon/favicon.ico">
-	<link rel="apple-touch-icon" sizes="57x57" href="https://www.babylonjs.com/img/favicon/apple-icon-57x57.png">
-	<link rel="apple-touch-icon" sizes="60x60" href="https://www.babylonjs.com/img/favicon/apple-icon-60x60.png">
-	<link rel="apple-touch-icon" sizes="72x72" href="https://www.babylonjs.com/img/favicon/apple-icon-72x72.png">
-	<link rel="apple-touch-icon" sizes="76x76" href="https://www.babylonjs.com/img/favicon/apple-icon-76x76.png">
-	<link rel="apple-touch-icon" sizes="114x114" href="https://www.babylonjs.com/img/favicon/apple-icon-114x114.png">
-	<link rel="apple-touch-icon" sizes="120x120" href="https://www.babylonjs.com/img/favicon/apple-icon-120x120.png">
-	<link rel="apple-touch-icon" sizes="144x144" href="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png">
-	<link rel="apple-touch-icon" sizes="152x152" href="https://www.babylonjs.com/img/favicon/apple-icon-152x152.png">
-	<link rel="apple-touch-icon" sizes="180x180" href="https://www.babylonjs.com/img/favicon/apple-icon-180x180.png">
-	<link rel="icon" type="image/png" sizes="192x192"  href="https://www.babylonjs.com/img/favicon/android-icon-192x192.png">
-	<link rel="icon" type="image/png" sizes="32x32" href="https://www.babylonjs.com/img/favicon/favicon-32x32.png">
-	<link rel="icon" type="image/png" sizes="96x96" href="https://www.babylonjs.com/img/favicon/favicon-96x96.png">
-	<link rel="icon" type="image/png" sizes="16x16" href="https://www.babylonjs.com/img/favicon/favicon-16x16.png">
-	<link rel="manifest" href="https://www.babylonjs.com/img/favicon/manifest.json">
-	<meta name="msapplication-TileColor" content="#ffffff">
-	<meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
-	<meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
-	<meta name="theme-color" content="#ffffff">
+    <link rel="apple-touch-icon" sizes="57x57" href="https://www.babylonjs.com/img/favicon/apple-icon-57x57.png">
+    <link rel="apple-touch-icon" sizes="60x60" href="https://www.babylonjs.com/img/favicon/apple-icon-60x60.png">
+    <link rel="apple-touch-icon" sizes="72x72" href="https://www.babylonjs.com/img/favicon/apple-icon-72x72.png">
+    <link rel="apple-touch-icon" sizes="76x76" href="https://www.babylonjs.com/img/favicon/apple-icon-76x76.png">
+    <link rel="apple-touch-icon" sizes="114x114" href="https://www.babylonjs.com/img/favicon/apple-icon-114x114.png">
+    <link rel="apple-touch-icon" sizes="120x120" href="https://www.babylonjs.com/img/favicon/apple-icon-120x120.png">
+    <link rel="apple-touch-icon" sizes="144x144" href="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png">
+    <link rel="apple-touch-icon" sizes="152x152" href="https://www.babylonjs.com/img/favicon/apple-icon-152x152.png">
+    <link rel="apple-touch-icon" sizes="180x180" href="https://www.babylonjs.com/img/favicon/apple-icon-180x180.png">
+    <link rel="icon" type="image/png" sizes="192x192" href="https://www.babylonjs.com/img/favicon/android-icon-192x192.png">
+    <link rel="icon" type="image/png" sizes="32x32" href="https://www.babylonjs.com/img/favicon/favicon-32x32.png">
+    <link rel="icon" type="image/png" sizes="96x96" href="https://www.babylonjs.com/img/favicon/favicon-96x96.png">
+    <link rel="icon" type="image/png" sizes="16x16" href="https://www.babylonjs.com/img/favicon/favicon-16x16.png">
+    <link rel="manifest" href="https://www.babylonjs.com/img/favicon/manifest.json">
+    <meta name="msapplication-TileColor" content="#ffffff">
+    <meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
+    <meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
+    <meta name="theme-color" content="#ffffff">
 
 
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <!-- Babylon.js -->
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
-    <script src="https://preview.babylonjs.com/babylon.js"></script>    
+    <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
 
 
     <script src="https://preview.babylonjs.com/materialsLibrary/babylon.fireMaterial.min.js"></script>
     <script src="https://preview.babylonjs.com/materialsLibrary/babylon.fireMaterial.min.js"></script>
@@ -60,9 +60,11 @@
     <script src="https://preview.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
     <script src="https://preview.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
     <script src="https://preview.babylonjs.com/loaders/babylon.stlFileLoader.js"></script>
     <script src="https://preview.babylonjs.com/loaders/babylon.stlFileLoader.js"></script>
 
 
+    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
+
     <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
     <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
-    
-    <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>    
+
+    <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/canvas2D/dist/preview%20release/babylon.canvas2d.min.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/canvas2D/dist/preview%20release/babylon.canvas2d.min.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/CompoundShader/src/babylonx.CompoundShader.js"></script>
     <link href="frame.css" rel="stylesheet" />
     <link href="frame.css" rel="stylesheet" />

+ 2 - 0
Playground/index.html

@@ -49,6 +49,8 @@
 
 
     <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
     <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
 
 
+    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
+
     <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
     <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
 
 
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>
     <script src="https://rawgit.com/BabylonJS/Extensions/master/ClonerSystem/src/babylonx.cloner.js"></script>

BIN
Playground/textures/normal.png


+ 9 - 1
Tools/DevLoader/BabylonLoader.js

@@ -39,7 +39,7 @@ var BABYLONDEVTOOLS;
             dependencies = [];
             dependencies = [];
             callback = null;
             callback = null;
             min = (document.location.href.toLowerCase().indexOf('dist=min') > 0);
             min = (document.location.href.toLowerCase().indexOf('dist=min') > 0);
-            useDist = (min || document.location.href.toLowerCase().indexOf('dist=true') > 0);            
+            useDist = (min || useDist || document.location.href.toLowerCase().indexOf('dist=true') > 0);            
             babylonJSPath = '';
             babylonJSPath = '';
         }
         }
 
 
@@ -91,6 +91,11 @@ var BABYLONDEVTOOLS;
             return this;
             return this;
         }
         }
 
 
+        Loader.prototype.useDist = function() {
+            useDist = true;
+            return this;
+        }
+
         Loader.prototype.dequeue = function () {
         Loader.prototype.dequeue = function () {
             if (queue.length == 0) {
             if (queue.length == 0) {
                 console.log('Scripts loaded');
                 console.log('Scripts loaded');
@@ -236,6 +241,9 @@ var BABYLONDEVTOOLS;
             // Modules
             // Modules
             if (loadModules) {
             if (loadModules) {
                 for (var i = 0; i< settings.modules.length; i++) {
                 for (var i = 0; i< settings.modules.length; i++) {
+                    if (settings.modules[i] === "viewer") {
+                        continue;
+                    }
                     this.loadModule(settings[settings.modules[i]]);
                     this.loadModule(settings[settings.modules[i]]);
                 }
                 }
             }
             }

+ 12 - 1
Tools/Gulp/config.json

@@ -230,7 +230,11 @@
         "particles": {
         "particles": {
             "files": [
             "files": [
                 "../../src/Particles/babylon.particle.js",
                 "../../src/Particles/babylon.particle.js",
-                "../../src/Particles/babylon.particleSystem.js"
+                "../../src/Particles/babylon.particleSystem.js",
+                "../../src/Particles/babylon.boxParticleEmitter.js",
+                "../../src/Particles/babylon.coneParticleEmitter.js",
+                "../../src/Particles/babylon.sphereParticleEmitter.js",
+                "../../src/Particles/babylon.iParticleEmitterType.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
                 "core"
                 "core"
@@ -1557,6 +1561,13 @@
                     "../../serializers/src/OBJ/babylon.objSerializer.ts"
                     "../../serializers/src/OBJ/babylon.objSerializer.ts"
                 ],
                 ],
                 "output": "babylon.objSerializer.js"
                 "output": "babylon.objSerializer.js"
+            },
+            {
+                "files": [
+                    "../../serializers/src/glTF/2.0/babylon.glTFSerializer.ts",
+                    "../../serializers/src/glTF/2.0/babylon.glTFExporter.ts"
+                ],
+                "output": "babylon.glTF2Serializer.js"
             }
             }
         ],
         ],
         "build": {
         "build": {

+ 29 - 11
Tools/Gulp/gulpfile.js

@@ -28,6 +28,8 @@ var config = require("./config.json");
 
 
 var del = require("del");
 var del = require("del");
 
 
+var karmaServer = require('karma').Server;
+
 var debug = require("gulp-debug");
 var debug = require("gulp-debug");
 var includeShadersStream;
 var includeShadersStream;
 var shadersStream;
 var shadersStream;
@@ -229,14 +231,7 @@ gulp.task("build", ["shaders"], function () {
 /*
 /*
 * Compiles all typescript files and creating a js and a declaration file.
 * Compiles all typescript files and creating a js and a declaration file.
 */
 */
-var alreadyCompiled = false;
-var forceCompile = false;
 gulp.task("typescript-compile", function () {
 gulp.task("typescript-compile", function () {
-    if (!forceCompile && alreadyCompiled) {
-        return;
-    }
-    alreadyCompiled = true;
-
     var tsResult = gulp.src(config.typescript)
     var tsResult = gulp.src(config.typescript)
         .pipe(sourcemaps.init())
         .pipe(sourcemaps.init())
         .pipe(tsProject());
         .pipe(tsProject());
@@ -428,7 +423,7 @@ var buildExternalLibrary = function (library, settings, watch) {
  * The default task, concat and min the main BJS files.
  * The default task, concat and min the main BJS files.
  */
  */
 gulp.task("default", function (cb) {
 gulp.task("default", function (cb) {
-    runSequence("typescript-all", "intellisense", cb);
+    runSequence("typescript-all", "intellisense", "tests-saucelabs", cb);
 });
 });
 
 
 gulp.task("mainBuild", function (cb) {
 gulp.task("mainBuild", function (cb) {
@@ -498,11 +493,8 @@ gulp.task("srcTscWatch", function () {
  * Watch ts files and fire repective tasks.
  * Watch ts files and fire repective tasks.
  */
  */
 gulp.task("watch", ["srcTscWatch"], function () {
 gulp.task("watch", ["srcTscWatch"], function () {
-    forceCompile = true;
     var interval = 1000;
     var interval = 1000;
 
 
-    // Keep during Prod Tests of srcTscWatch.
-    // var tasks = [gulp.watch(config.typescript, { interval: interval }, ["typescript-compile"])];
     var tasks = [];
     var tasks = [];
 
 
     config.modules.map(function (module) {
     config.modules.map(function (module) {
@@ -570,6 +562,32 @@ gulp.task("webserver", function () {
 gulp.task("run", ["watch", "webserver"], function () {
 gulp.task("run", ["watch", "webserver"], function () {
 });
 });
 
 
+
+gulp.task("tests-integration", function (done) {
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../tests/validation/karma.conf.js",
+        singleRun: false
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});
+
+gulp.task("tests-saucelabs", function (done) {
+    if (!process.env.TRAVIS) {
+        done();
+        return;
+    }
+
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../tests/validation/karma.conf.saucelabs.js",
+        singleRun: true
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});
+
 gulp.task("clean-JS-MAP", function () {
 gulp.task("clean-JS-MAP", function () {
     return del([
     return del([
         "../../src/**/*.js.map", "../../src/**/*.js"
         "../../src/**/*.js.map", "../../src/**/*.js"

+ 10 - 1
Tools/Gulp/package.json

@@ -43,7 +43,16 @@
         "through2": "~0.6.5",
         "through2": "~0.6.5",
         "ts-loader": "^2.3.7",
         "ts-loader": "^2.3.7",
         "typescript": "^2.6.2",
         "typescript": "^2.6.2",
-        "webpack-stream": "^4.0.0"
+        "webpack-stream": "^4.0.0",
+        "karma": "^2.0.0",
+        "karma-chrome-launcher": "^2.2.0",
+        "karma-sauce-launcher": "^1.2.0",
+        "mocha": "^4.0.1",
+        "chai": "^4.1.2",
+        "sinon": "^4.1.3",
+        "karma-mocha": "^1.3.0",
+        "karma-chai": "^0.1.0",
+        "karma-sinon": "^1.0.5"
     },
     },
     "scripts": {
     "scripts": {
         "install": "npm --prefix ../../Playground/ install ../../Playground/ && gulp typescript-compile && gulp typescript-libraries && gulp deployLocalDev"
         "install": "npm --prefix ../../Playground/ install ../../Playground/ && gulp typescript-compile && gulp typescript-libraries && gulp deployLocalDev"

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


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


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


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


File diff suppressed because it is too large
+ 5948 - 5599
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


File diff suppressed because it is too large
+ 52 - 51
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


File diff suppressed because it is too large
+ 1223 - 402
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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


+ 6 - 0
dist/preview release/inspector/babylon.inspector.js

@@ -3567,6 +3567,12 @@ var INSPECTOR;
                     elem: elemValue,
                     elem: elemValue,
                     updateFct: function () { return _this._sceneInstrumentation.drawCallsCounter.current.toString(); }
                     updateFct: function () { return _this._sceneInstrumentation.drawCallsCounter.current.toString(); }
                 });
                 });
+                _this._createStatLabel("Texture collisions", _this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
+                _this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return _this._sceneInstrumentation.textureCollisionsCounter.current.toString(); }
+                });
                 _this._createStatLabel("Total lights", _this._panel);
                 _this._createStatLabel("Total lights", _this._panel);
                 elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
                 elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
                 _this._updatableProperties.push({
                 _this._updatableProperties.push({

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


+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 48 - 12
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -29,18 +29,30 @@ declare module BABYLON {
         bin: Nullable<ArrayBufferView>;
         bin: Nullable<ArrayBufferView>;
     }
     }
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
     }
     }
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
-        static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        static CreateGLTFLoaderV1: () => IGLTFLoader;
+        static CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
         /**
          * Raised when the asset has been parsed.
          * Raised when the asset has been parsed.
          * The data.json property stores the glTF JSON.
          * The data.json property stores the glTF JSON.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          */
          */
-        onParsed: (data: IGLTFLoaderData) => void;
+        onParsedObservable: Observable<IGLTFLoaderData>;
+        private _onParsedObserver;
+        onParsed: (loaderData: IGLTFLoaderData) => void;
         static IncrementalLoading: boolean;
         static IncrementalLoading: boolean;
         static HomogeneousCoordinates: boolean;
         static HomogeneousCoordinates: boolean;
         /**
         /**
@@ -66,21 +78,35 @@ declare module BABYLON {
         /**
         /**
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          */
          */
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        private _onMeshLoadedObserver;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         /**
         /**
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          */
          */
-        onTextureLoaded: (texture: BaseTexture) => void;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        private _onTextureLoadedObserver;
+        onTextureLoaded: (Texture: BaseTexture) => void;
         /**
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
          */
-        onMaterialLoaded: (material: Material) => void;
+        onMaterialLoadedObservable: Observable<Material>;
+        private _onMaterialLoadedObserver;
+        onMaterialLoaded: (Material: Material) => void;
         /**
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
          */
+        onCompleteObservable: Observable<GLTFFileLoader>;
+        private _onCompleteObserver;
         onComplete: () => void;
         onComplete: () => void;
+        /**
+        * Raised when the loader is disposed.
+        */
+        onDisposeObservable: Observable<GLTFFileLoader>;
+        private _onDisposeObserver;
+        onDispose: () => void;
         private _loader;
         private _loader;
         name: string;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         extensions: ISceneLoaderPluginExtensions;
@@ -88,12 +114,12 @@ declare module BABYLON {
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
-        private static _parse(data);
+        private _parse(data);
         private _getLoader(loaderData);
         private _getLoader(loaderData);
         private static _parseBinary(data);
         private static _parseBinary(data);
         private static _parseV1(binaryReader);
         private static _parseV1(binaryReader);
@@ -497,9 +523,19 @@ declare module BABYLON.GLTF1 {
             [name: string]: GLTFLoaderExtension;
             [name: string]: GLTFLoaderExtension;
         };
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
         static RegisterExtension(extension: GLTFLoaderExtension): void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): boolean;
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): boolean;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): void;
         private _loadShadersAsync(gltfRuntime, onload);
         private _loadShadersAsync(gltfRuntime, onload);
         private _loadBuffersAsync(gltfRuntime, onLoad, onProgress?);
         private _loadBuffersAsync(gltfRuntime, onLoad, onProgress?);
         private _createNodes(gltfRuntime);
         private _createNodes(gltfRuntime);

+ 138 - 19
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -29,6 +29,13 @@ var BABYLON;
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
         function GLTFFileLoader() {
+            // #region Common options
+            /**
+             * Raised when the asset has been parsed.
+             * The data.json property stores the glTF JSON.
+             * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+             */
+            this.onParsedObservable = new BABYLON.Observable();
             // #endregion
             // #endregion
             // #region V2 options
             // #region V2 options
             /**
             /**
@@ -51,26 +58,115 @@ var BABYLON;
              * Set to true to compile shadow generators before raising the success callback.
              * Set to true to compile shadow generators before raising the success callback.
              */
              */
             this.compileShadowGenerators = false;
             this.compileShadowGenerators = false;
+            /**
+             * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+             */
+            this.onMeshLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+             */
+            this.onTextureLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a material after parsing the glTF properties of the material.
+             */
+            this.onMaterialLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the asset is completely loaded, immediately before the loader is disposed.
+             * For assets with LODs, raised when all of the LODs are complete.
+             * For assets without LODs, raised when the model is complete, immediately after onSuccess.
+             */
+            this.onCompleteObservable = new BABYLON.Observable();
+            /**
+            * Raised when the loader is disposed.
+            */
+            this.onDisposeObservable = new BABYLON.Observable();
+            // #endregion
+            this._loader = null;
             this.name = "gltf";
             this.name = "gltf";
             this.extensions = {
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
                 ".glb": { isBinary: true }
             };
             };
         }
         }
+        Object.defineProperty(GLTFFileLoader.prototype, "onParsed", {
+            set: function (callback) {
+                if (this._onParsedObserver) {
+                    this.onParsedObservable.remove(this._onParsedObserver);
+                }
+                this._onParsedObserver = this.onParsedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
+            set: function (callback) {
+                if (this._onMeshLoadedObserver) {
+                    this.onMeshLoadedObservable.remove(this._onMeshLoadedObserver);
+                }
+                this._onMeshLoadedObserver = this.onMeshLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onTextureLoaded", {
+            set: function (callback) {
+                if (this._onTextureLoadedObserver) {
+                    this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
+                }
+                this._onTextureLoadedObserver = this.onTextureLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMaterialLoaded", {
+            set: function (callback) {
+                if (this._onMaterialLoadedObserver) {
+                    this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
+                }
+                this._onMaterialLoadedObserver = this.onMaterialLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
+            set: function (callback) {
+                if (this._onCompleteObserver) {
+                    this.onCompleteObservable.remove(this._onCompleteObserver);
+                }
+                this._onCompleteObserver = this.onCompleteObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onDispose", {
+            set: function (callback) {
+                if (this._onDisposeObserver) {
+                    this.onDisposeObservable.remove(this._onDisposeObserver);
+                }
+                this._onDisposeObserver = this.onDisposeObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
         /**
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         GLTFFileLoader.prototype.dispose = function () {
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
             if (this._loader) {
                 this._loader.dispose();
                 this._loader.dispose();
-            }
+                this._loader = null;
+            }
+            this.onParsedObservable.clear();
+            this.onMeshLoadedObservable.clear();
+            this.onTextureLoadedObservable.clear();
+            this.onMaterialLoadedObservable.clear();
+            this.onCompleteObservable.clear();
+            this.onDisposeObservable.notifyObservers(this);
+            this.onDisposeObservable.clear();
         };
         };
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -85,10 +181,7 @@ var BABYLON;
         };
         };
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -107,16 +200,22 @@ var BABYLON;
         GLTFFileLoader.prototype.createPlugin = function () {
         GLTFFileLoader.prototype.createPlugin = function () {
             return new GLTFFileLoader();
             return new GLTFFileLoader();
         };
         };
-        GLTFFileLoader._parse = function (data) {
+        GLTFFileLoader.prototype._parse = function (data) {
+            var parsedData;
             if (data instanceof ArrayBuffer) {
             if (data instanceof ArrayBuffer) {
-                return GLTFFileLoader._parseBinary(data);
+                parsedData = GLTFFileLoader._parseBinary(data);
             }
             }
-            return {
-                json: JSON.parse(data),
-                bin: null
-            };
+            else {
+                parsedData = {
+                    json: JSON.parse(data),
+                    bin: null
+                };
+            }
+            this.onParsedObservable.notifyObservers(parsedData);
+            return parsedData;
         };
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
+            var _this = this;
             var loaderVersion = { major: 2, minor: 0 };
             var loaderVersion = { major: 2, minor: 0 };
             var asset = loaderData.json.asset || {};
             var asset = loaderData.json.asset || {};
             var version = GLTFFileLoader._parseVersion(asset.version);
             var version = GLTFFileLoader._parseVersion(asset.version);
@@ -140,7 +239,17 @@ var BABYLON;
             if (!createLoader) {
             if (!createLoader) {
                 throw new Error("Unsupported version: " + asset.version);
                 throw new Error("Unsupported version: " + asset.version);
             }
             }
-            return createLoader(this);
+            var loader = createLoader();
+            loader.coordinateSystemMode = this.coordinateSystemMode;
+            loader.animationStartMode = this.animationStartMode;
+            loader.compileMaterials = this.compileMaterials;
+            loader.useClipPlane = this.useClipPlane;
+            loader.compileShadowGenerators = this.compileShadowGenerators;
+            loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
+            loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
+            loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
+            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            return loader;
         };
         };
         GLTFFileLoader._parseBinary = function (data) {
         GLTFFileLoader._parseBinary = function (data) {
             var Binary = {
             var Binary = {
@@ -1676,6 +1785,17 @@ var BABYLON;
         */
         */
         var GLTFLoader = /** @class */ (function () {
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader() {
             function GLTFLoader() {
+                // #region Stubs for IGLTFLoader interface
+                this.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.AUTO;
+                this.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.FIRST;
+                this.compileMaterials = false;
+                this.useClipPlane = false;
+                this.compileShadowGenerators = false;
+                this.onDisposeObservable = new BABYLON.Observable();
+                this.onMeshLoadedObservable = new BABYLON.Observable();
+                this.onTextureLoadedObservable = new BABYLON.Observable();
+                this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onCompleteObservable = new BABYLON.Observable();
             }
             }
             GLTFLoader.RegisterExtension = function (extension) {
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -1684,9 +1804,8 @@ var BABYLON;
                 }
                 }
                 GLTFLoader.Extensions[extension.name] = extension;
                 GLTFLoader.Extensions[extension.name] = extension;
             };
             };
-            GLTFLoader.prototype.dispose = function () {
-                // do nothing
-            };
+            GLTFLoader.prototype.dispose = function () { };
+            // #endregion
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 var _this = this;
                 scene.useRightHandedSystem = true;
                 scene.useRightHandedSystem = true;

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


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

@@ -29,18 +29,30 @@ declare module BABYLON {
         bin: Nullable<ArrayBufferView>;
         bin: Nullable<ArrayBufferView>;
     }
     }
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
     }
     }
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
-        static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        static CreateGLTFLoaderV1: () => IGLTFLoader;
+        static CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
         /**
          * Raised when the asset has been parsed.
          * Raised when the asset has been parsed.
          * The data.json property stores the glTF JSON.
          * The data.json property stores the glTF JSON.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          */
          */
-        onParsed: (data: IGLTFLoaderData) => void;
+        onParsedObservable: Observable<IGLTFLoaderData>;
+        private _onParsedObserver;
+        onParsed: (loaderData: IGLTFLoaderData) => void;
         static IncrementalLoading: boolean;
         static IncrementalLoading: boolean;
         static HomogeneousCoordinates: boolean;
         static HomogeneousCoordinates: boolean;
         /**
         /**
@@ -66,21 +78,35 @@ declare module BABYLON {
         /**
         /**
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          */
          */
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        private _onMeshLoadedObserver;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         /**
         /**
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          */
          */
-        onTextureLoaded: (texture: BaseTexture) => void;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        private _onTextureLoadedObserver;
+        onTextureLoaded: (Texture: BaseTexture) => void;
         /**
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
          */
-        onMaterialLoaded: (material: Material) => void;
+        onMaterialLoadedObservable: Observable<Material>;
+        private _onMaterialLoadedObserver;
+        onMaterialLoaded: (Material: Material) => void;
         /**
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
          */
+        onCompleteObservable: Observable<GLTFFileLoader>;
+        private _onCompleteObserver;
         onComplete: () => void;
         onComplete: () => void;
+        /**
+        * Raised when the loader is disposed.
+        */
+        onDisposeObservable: Observable<GLTFFileLoader>;
+        private _onDisposeObserver;
+        onDispose: () => void;
         private _loader;
         private _loader;
         name: string;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         extensions: ISceneLoaderPluginExtensions;
@@ -88,12 +114,12 @@ declare module BABYLON {
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
-        private static _parse(data);
+        private _parse(data);
         private _getLoader(loaderData);
         private _getLoader(loaderData);
         private static _parseBinary(data);
         private static _parseBinary(data);
         private static _parseV1(binaryReader);
         private static _parseV1(binaryReader);
@@ -361,7 +387,6 @@ declare module BABYLON.GLTF2 {
         _gltf: IGLTF;
         _gltf: IGLTF;
         _babylonScene: Scene;
         _babylonScene: Scene;
         private _disposed;
         private _disposed;
-        private _parent;
         private _rootUrl;
         private _rootUrl;
         private _defaultMaterial;
         private _defaultMaterial;
         private _defaultSampler;
         private _defaultSampler;
@@ -379,11 +404,19 @@ declare module BABYLON.GLTF2 {
             [name: string]: GLTFLoaderExtension;
             [name: string]: GLTFLoaderExtension;
         };
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
         static RegisterExtension(extension: GLTFLoaderExtension): void;
-        private static _createProgressEvent;
-        constructor(parent: GLTFFileLoader);
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess?, onProgress?, onError?);
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess?, onProgress?, onError?);
         private _onProgress();
         private _onProgress();
         _executeWhenRenderReady(func: () => void): void;
         _executeWhenRenderReady(func: () => void): void;
@@ -454,6 +487,8 @@ declare module BABYLON.GLTF2 {
         private _compileMaterialAsync(babylonMaterial, babylonMesh, onSuccess);
         private _compileMaterialAsync(babylonMaterial, babylonMesh, onSuccess);
         private _compileMaterialsAsync(onSuccess);
         private _compileMaterialsAsync(onSuccess);
         private _compileShadowGeneratorsAsync(onSuccess);
         private _compileShadowGeneratorsAsync(onSuccess);
+        private _abortRequests();
+        private _releaseResources();
     }
     }
 }
 }
 
 

+ 179 - 78
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -29,6 +29,13 @@ var BABYLON;
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
         function GLTFFileLoader() {
+            // #region Common options
+            /**
+             * Raised when the asset has been parsed.
+             * The data.json property stores the glTF JSON.
+             * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+             */
+            this.onParsedObservable = new BABYLON.Observable();
             // #endregion
             // #endregion
             // #region V2 options
             // #region V2 options
             /**
             /**
@@ -51,26 +58,115 @@ var BABYLON;
              * Set to true to compile shadow generators before raising the success callback.
              * Set to true to compile shadow generators before raising the success callback.
              */
              */
             this.compileShadowGenerators = false;
             this.compileShadowGenerators = false;
+            /**
+             * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+             */
+            this.onMeshLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+             */
+            this.onTextureLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a material after parsing the glTF properties of the material.
+             */
+            this.onMaterialLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the asset is completely loaded, immediately before the loader is disposed.
+             * For assets with LODs, raised when all of the LODs are complete.
+             * For assets without LODs, raised when the model is complete, immediately after onSuccess.
+             */
+            this.onCompleteObservable = new BABYLON.Observable();
+            /**
+            * Raised when the loader is disposed.
+            */
+            this.onDisposeObservable = new BABYLON.Observable();
+            // #endregion
+            this._loader = null;
             this.name = "gltf";
             this.name = "gltf";
             this.extensions = {
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
                 ".glb": { isBinary: true }
             };
             };
         }
         }
+        Object.defineProperty(GLTFFileLoader.prototype, "onParsed", {
+            set: function (callback) {
+                if (this._onParsedObserver) {
+                    this.onParsedObservable.remove(this._onParsedObserver);
+                }
+                this._onParsedObserver = this.onParsedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
+            set: function (callback) {
+                if (this._onMeshLoadedObserver) {
+                    this.onMeshLoadedObservable.remove(this._onMeshLoadedObserver);
+                }
+                this._onMeshLoadedObserver = this.onMeshLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onTextureLoaded", {
+            set: function (callback) {
+                if (this._onTextureLoadedObserver) {
+                    this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
+                }
+                this._onTextureLoadedObserver = this.onTextureLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMaterialLoaded", {
+            set: function (callback) {
+                if (this._onMaterialLoadedObserver) {
+                    this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
+                }
+                this._onMaterialLoadedObserver = this.onMaterialLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
+            set: function (callback) {
+                if (this._onCompleteObserver) {
+                    this.onCompleteObservable.remove(this._onCompleteObserver);
+                }
+                this._onCompleteObserver = this.onCompleteObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onDispose", {
+            set: function (callback) {
+                if (this._onDisposeObserver) {
+                    this.onDisposeObservable.remove(this._onDisposeObserver);
+                }
+                this._onDisposeObserver = this.onDisposeObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
         /**
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         GLTFFileLoader.prototype.dispose = function () {
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
             if (this._loader) {
                 this._loader.dispose();
                 this._loader.dispose();
+                this._loader = null;
             }
             }
+            this.onParsedObservable.clear();
+            this.onMeshLoadedObservable.clear();
+            this.onTextureLoadedObservable.clear();
+            this.onMaterialLoadedObservable.clear();
+            this.onCompleteObservable.clear();
+            this.onDisposeObservable.notifyObservers(this);
+            this.onDisposeObservable.clear();
         };
         };
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -85,10 +181,7 @@ var BABYLON;
         };
         };
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -107,16 +200,22 @@ var BABYLON;
         GLTFFileLoader.prototype.createPlugin = function () {
         GLTFFileLoader.prototype.createPlugin = function () {
             return new GLTFFileLoader();
             return new GLTFFileLoader();
         };
         };
-        GLTFFileLoader._parse = function (data) {
+        GLTFFileLoader.prototype._parse = function (data) {
+            var parsedData;
             if (data instanceof ArrayBuffer) {
             if (data instanceof ArrayBuffer) {
-                return GLTFFileLoader._parseBinary(data);
+                parsedData = GLTFFileLoader._parseBinary(data);
             }
             }
-            return {
-                json: JSON.parse(data),
-                bin: null
-            };
+            else {
+                parsedData = {
+                    json: JSON.parse(data),
+                    bin: null
+                };
+            }
+            this.onParsedObservable.notifyObservers(parsedData);
+            return parsedData;
         };
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
+            var _this = this;
             var loaderVersion = { major: 2, minor: 0 };
             var loaderVersion = { major: 2, minor: 0 };
             var asset = loaderData.json.asset || {};
             var asset = loaderData.json.asset || {};
             var version = GLTFFileLoader._parseVersion(asset.version);
             var version = GLTFFileLoader._parseVersion(asset.version);
@@ -140,7 +239,17 @@ var BABYLON;
             if (!createLoader) {
             if (!createLoader) {
                 throw new Error("Unsupported version: " + asset.version);
                 throw new Error("Unsupported version: " + asset.version);
             }
             }
-            return createLoader(this);
+            var loader = createLoader();
+            loader.coordinateSystemMode = this.coordinateSystemMode;
+            loader.animationStartMode = this.animationStartMode;
+            loader.compileMaterials = this.compileMaterials;
+            loader.useClipPlane = this.useClipPlane;
+            loader.compileShadowGenerators = this.compileShadowGenerators;
+            loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
+            loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
+            loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
+            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            return loader;
         };
         };
         GLTFFileLoader._parseBinary = function (data) {
         GLTFFileLoader._parseBinary = function (data) {
             var Binary = {
             var Binary = {
@@ -375,7 +484,7 @@ var BABYLON;
             return GLTFLoaderTracker;
             return GLTFLoaderTracker;
         }());
         }());
         var GLTFLoader = /** @class */ (function () {
         var GLTFLoader = /** @class */ (function () {
-            function GLTFLoader(parent) {
+            function GLTFLoader() {
                 this._disposed = false;
                 this._disposed = false;
                 this._defaultSampler = {};
                 this._defaultSampler = {};
                 this._renderReady = false;
                 this._renderReady = false;
@@ -386,7 +495,16 @@ var BABYLON;
                 // Count of pending work that needs to complete before the loader is disposed.
                 // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
                 this._loaderPendingCount = 0;
                 this._loaderTrackers = new Array();
                 this._loaderTrackers = new Array();
-                this._parent = parent;
+                this.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.AUTO;
+                this.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.FIRST;
+                this.compileMaterials = false;
+                this.useClipPlane = false;
+                this.compileShadowGenerators = false;
+                this.onDisposeObservable = new BABYLON.Observable();
+                this.onMeshLoadedObservable = new BABYLON.Observable();
+                this.onTextureLoadedObservable = new BABYLON.Observable();
+                this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onCompleteObservable = new BABYLON.Observable();
             }
             }
             GLTFLoader.RegisterExtension = function (extension) {
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -402,22 +520,9 @@ var BABYLON;
                     return;
                     return;
                 }
                 }
                 this._disposed = true;
                 this._disposed = true;
-                // Abort requests that are not complete
-                for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
-                    var request = _a[_i];
-                    if (request.readyState !== (XMLHttpRequest.DONE || 4)) {
-                        request.abort();
-                    }
-                }
-                // Revoke object urls created during load
-                if (this._gltf.textures) {
-                    for (var _b = 0, _c = this._gltf.textures; _b < _c.length; _b++) {
-                        var texture = _c[_b];
-                        if (texture.url) {
-                            URL.revokeObjectURL(texture.url);
-                        }
-                    }
-                }
+                this._abortRequests();
+                this._releaseResources();
+                this.onDisposeObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 var _this = this;
@@ -461,7 +566,7 @@ var BABYLON;
                     loaded += request._loaded;
                     loaded += request._loaded;
                     total += request._total;
                     total += request._total;
                 }
                 }
-                this._progressCallback(GLTFLoader._createProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
+                this._progressCallback(new BABYLON.SceneLoaderProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
             };
             };
             GLTFLoader.prototype._executeWhenRenderReady = function (func) {
             GLTFLoader.prototype._executeWhenRenderReady = function (func) {
                 if (this._renderReady) {
                 if (this._renderReady) {
@@ -480,10 +585,9 @@ var BABYLON;
                 this._renderReadyObservable.notifyObservers(this);
                 this._renderReadyObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype._onComplete = function () {
             GLTFLoader.prototype._onComplete = function () {
-                if (this._parent.onComplete) {
-                    this._parent.onComplete();
-                }
-                this.dispose();
+                this._abortRequests();
+                this._releaseResources();
+                this.onCompleteObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype._loadData = function (data) {
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
                 this._gltf = data.json;
@@ -547,7 +651,7 @@ var BABYLON;
                 if (!animations) {
                 if (!animations) {
                     return;
                     return;
                 }
                 }
-                switch (this._parent.animationStartMode) {
+                switch (this.animationStartMode) {
                     case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
                     case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
                         // do nothing
                         // do nothing
                         break;
                         break;
@@ -571,7 +675,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        BABYLON.Tools.Error("Invalid animation start mode " + this.animationStartMode);
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -586,7 +690,7 @@ var BABYLON;
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
                 var _this = this;
                 var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
-                switch (this._parent.coordinateSystemMode) {
+                switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
                         if (!this._babylonScene.useRightHandedSystem) {
                         if (!this._babylonScene.useRightHandedSystem) {
                             this._rootNode.rotation = [0, 1, 0, 0];
                             this._rootNode.rotation = [0, 1, 0, 0];
@@ -600,13 +704,11 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid coordinate system mode " + this._parent.coordinateSystemMode);
+                        BABYLON.Tools.Error("Invalid coordinate system mode " + this.coordinateSystemMode);
                         return;
                         return;
                     }
                     }
                 }
                 }
-                if (this._parent.onMeshLoaded) {
-                    this._parent.onMeshLoaded(this._rootNode.babylonMesh);
-                }
+                this.onMeshLoadedObservable.notifyObservers(this._rootNode.babylonMesh);
                 var nodeIndices = scene.nodes;
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(context, nodeIndices, function (node, parentNode) {
                 this._traverseNodes(context, nodeIndices, function (node, parentNode) {
                     node.parent = parentNode;
                     node.parent = parentNode;
@@ -682,9 +784,7 @@ var BABYLON;
                         this._loadNode("#/nodes/" + index, childNode);
                         this._loadNode("#/nodes/" + index, childNode);
                     }
                     }
                 }
                 }
-                if (this._parent.onMeshLoaded) {
-                    this._parent.onMeshLoaded(node.babylonMesh);
-                }
+                this.onMeshLoadedObservable.notifyObservers(node.babylonMesh);
             };
             };
             GLTFLoader.prototype._loadMesh = function (context, node, mesh) {
             GLTFLoader.prototype._loadMesh = function (context, node, mesh) {
                 var _this = this;
                 var _this = this;
@@ -727,8 +827,8 @@ var BABYLON;
                             throw new Error(context + ": Failed to find material " + primitive.material);
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
                         }
                         this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                         this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
-                            if (isNew && _this._parent.onMaterialLoaded) {
-                                _this._parent.onMaterialLoaded(babylonMaterial);
+                            if (isNew) {
+                                _this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                             }
                             }
                             node.babylonMesh.material = babylonMaterial;
                             node.babylonMesh.material = babylonMaterial;
                         });
                         });
@@ -749,8 +849,8 @@ var BABYLON;
                                 throw new Error(context + ": Failed to find material " + primitive.material);
                                 throw new Error(context + ": Failed to find material " + primitive.material);
                             }
                             }
                             this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
-                                if (isNew && _this._parent.onMaterialLoaded) {
-                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                if (isNew) {
+                                    _this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                                 }
                                 }
                                 subMaterials_1[index] = babylonMaterial;
                                 subMaterials_1[index] = babylonMaterial;
                             });
                             });
@@ -1708,9 +1808,7 @@ var BABYLON;
                 babylonTexture.wrapU = sampler.wrapU;
                 babylonTexture.wrapU = sampler.wrapU;
                 babylonTexture.wrapV = sampler.wrapV;
                 babylonTexture.wrapV = sampler.wrapV;
                 babylonTexture.name = texture.name || "texture" + texture.index;
                 babylonTexture.name = texture.name || "texture" + texture.index;
-                if (this._parent.onTextureLoaded) {
-                    this._parent.onTextureLoaded(babylonTexture);
-                }
+                this.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 return babylonTexture;
                 return babylonTexture;
             };
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -1760,12 +1858,11 @@ var BABYLON;
                     _this._tryCatchOnError(function () {
                     _this._tryCatchOnError(function () {
                         throw new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                         throw new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                     });
                     });
-                }, function (oldRequest, newRequest) {
-                    _this._requests.splice(_this._requests.indexOf(oldRequest), 1, newRequest);
                 });
                 });
-                if (request) {
-                    this._requests.push(request);
-                }
+                this._requests.push(request);
+                request.onCompleteObservable.add(function () {
+                    _this._requests.splice(_this._requests.indexOf(request), 1);
+                });
             };
             };
             GLTFLoader.prototype._tryCatchOnError = function (handler) {
             GLTFLoader.prototype._tryCatchOnError = function (handler) {
                 if (this._disposed) {
                 if (this._disposed) {
@@ -1855,7 +1952,7 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
                 var _this = this;
-                if (this._parent.useClipPlane) {
+                if (this.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
                             _this._tryCatchOnError(onSuccess);
                             _this._tryCatchOnError(onSuccess);
@@ -1869,7 +1966,7 @@ var BABYLON;
                 }
                 }
             };
             };
             GLTFLoader.prototype._compileMaterialsAsync = function (onSuccess) {
             GLTFLoader.prototype._compileMaterialsAsync = function (onSuccess) {
-                if (!this._parent.compileMaterials || !this._gltf.materials) {
+                if (!this.compileMaterials || !this._gltf.materials) {
                     onSuccess();
                     onSuccess();
                     return;
                     return;
                 }
                 }
@@ -1918,7 +2015,7 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {
                 var _this = this;
                 var _this = this;
-                if (!this._parent.compileShadowGenerators) {
+                if (!this.compileShadowGenerators) {
                     onSuccess();
                     onSuccess();
                     return;
                     return;
                 }
                 }
@@ -1947,25 +2044,29 @@ var BABYLON;
                     }
                     }
                 }
                 }
             };
             };
-            GLTFLoader.Extensions = {};
-            // IE 11 Compatibility.
-            GLTFLoader._createProgressEvent = (typeof window["ProgressEvent"] === "function")
-                ? function (lengthComputable, loaded, total) {
-                    return new ProgressEvent("GLTFLoaderProgress", {
-                        lengthComputable: lengthComputable,
-                        loaded: loaded,
-                        total: total
-                    });
+            GLTFLoader.prototype._abortRequests = function () {
+                for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
+                    var request = _a[_i];
+                    request.abort();
                 }
                 }
-                : function (lengthComputable, loaded, total) {
-                    var event = document.createEvent("ProgressEvent");
-                    event.initProgressEvent("GLTFLoaderProgress", false, false, lengthComputable, loaded, total);
-                    return event;
-                };
+                this._requests.length = 0;
+            };
+            GLTFLoader.prototype._releaseResources = function () {
+                if (this._gltf.textures) {
+                    for (var _i = 0, _a = this._gltf.textures; _i < _a.length; _i++) {
+                        var texture = _a[_i];
+                        if (texture.url) {
+                            URL.revokeObjectURL(texture.url);
+                            texture.url = undefined;
+                        }
+                    }
+                }
+            };
+            GLTFLoader.Extensions = {};
             return GLTFLoader;
             return GLTFLoader;
         }());
         }());
         GLTF2.GLTFLoader = GLTFLoader;
         GLTF2.GLTFLoader = GLTFLoader;
-        BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = function (parent) { return new GLTFLoader(parent); };
+        BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = function () { return new GLTFLoader(); };
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 

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


+ 62 - 17
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -29,18 +29,30 @@ declare module BABYLON {
         bin: Nullable<ArrayBufferView>;
         bin: Nullable<ArrayBufferView>;
     }
     }
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
     }
     }
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
-        static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        static CreateGLTFLoaderV1: () => IGLTFLoader;
+        static CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
         /**
          * Raised when the asset has been parsed.
          * Raised when the asset has been parsed.
          * The data.json property stores the glTF JSON.
          * The data.json property stores the glTF JSON.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          */
          */
-        onParsed: (data: IGLTFLoaderData) => void;
+        onParsedObservable: Observable<IGLTFLoaderData>;
+        private _onParsedObserver;
+        onParsed: (loaderData: IGLTFLoaderData) => void;
         static IncrementalLoading: boolean;
         static IncrementalLoading: boolean;
         static HomogeneousCoordinates: boolean;
         static HomogeneousCoordinates: boolean;
         /**
         /**
@@ -66,21 +78,35 @@ declare module BABYLON {
         /**
         /**
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          */
          */
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        private _onMeshLoadedObserver;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         /**
         /**
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          */
          */
-        onTextureLoaded: (texture: BaseTexture) => void;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        private _onTextureLoadedObserver;
+        onTextureLoaded: (Texture: BaseTexture) => void;
         /**
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
          */
-        onMaterialLoaded: (material: Material) => void;
+        onMaterialLoadedObservable: Observable<Material>;
+        private _onMaterialLoadedObserver;
+        onMaterialLoaded: (Material: Material) => void;
         /**
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
          */
+        onCompleteObservable: Observable<GLTFFileLoader>;
+        private _onCompleteObserver;
         onComplete: () => void;
         onComplete: () => void;
+        /**
+        * Raised when the loader is disposed.
+        */
+        onDisposeObservable: Observable<GLTFFileLoader>;
+        private _onDisposeObserver;
+        onDispose: () => void;
         private _loader;
         private _loader;
         name: string;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         extensions: ISceneLoaderPluginExtensions;
@@ -88,12 +114,12 @@ declare module BABYLON {
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
-        private static _parse(data);
+        private _parse(data);
         private _getLoader(loaderData);
         private _getLoader(loaderData);
         private static _parseBinary(data);
         private static _parseBinary(data);
         private static _parseV1(binaryReader);
         private static _parseV1(binaryReader);
@@ -497,9 +523,19 @@ declare module BABYLON.GLTF1 {
             [name: string]: GLTFLoaderExtension;
             [name: string]: GLTFLoaderExtension;
         };
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
         static RegisterExtension(extension: GLTFLoaderExtension): void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): boolean;
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): boolean;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): void;
         private _loadShadersAsync(gltfRuntime, onload);
         private _loadShadersAsync(gltfRuntime, onload);
         private _loadBuffersAsync(gltfRuntime, onLoad, onProgress?);
         private _loadBuffersAsync(gltfRuntime, onLoad, onProgress?);
         private _createNodes(gltfRuntime);
         private _createNodes(gltfRuntime);
@@ -908,7 +944,6 @@ declare module BABYLON.GLTF2 {
         _gltf: IGLTF;
         _gltf: IGLTF;
         _babylonScene: Scene;
         _babylonScene: Scene;
         private _disposed;
         private _disposed;
-        private _parent;
         private _rootUrl;
         private _rootUrl;
         private _defaultMaterial;
         private _defaultMaterial;
         private _defaultSampler;
         private _defaultSampler;
@@ -926,11 +961,19 @@ declare module BABYLON.GLTF2 {
             [name: string]: GLTFLoaderExtension;
             [name: string]: GLTFLoaderExtension;
         };
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
         static RegisterExtension(extension: GLTFLoaderExtension): void;
-        private static _createProgressEvent;
-        constructor(parent: GLTFFileLoader);
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess?, onProgress?, onError?);
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess?, onProgress?, onError?);
         private _onProgress();
         private _onProgress();
         _executeWhenRenderReady(func: () => void): void;
         _executeWhenRenderReady(func: () => void): void;
@@ -1001,6 +1044,8 @@ declare module BABYLON.GLTF2 {
         private _compileMaterialAsync(babylonMaterial, babylonMesh, onSuccess);
         private _compileMaterialAsync(babylonMaterial, babylonMesh, onSuccess);
         private _compileMaterialsAsync(onSuccess);
         private _compileMaterialsAsync(onSuccess);
         private _compileShadowGeneratorsAsync(onSuccess);
         private _compileShadowGeneratorsAsync(onSuccess);
+        private _abortRequests();
+        private _releaseResources();
     }
     }
 }
 }
 
 

+ 192 - 81
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -29,6 +29,13 @@ var BABYLON;
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
         function GLTFFileLoader() {
+            // #region Common options
+            /**
+             * Raised when the asset has been parsed.
+             * The data.json property stores the glTF JSON.
+             * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+             */
+            this.onParsedObservable = new BABYLON.Observable();
             // #endregion
             // #endregion
             // #region V2 options
             // #region V2 options
             /**
             /**
@@ -51,26 +58,115 @@ var BABYLON;
              * Set to true to compile shadow generators before raising the success callback.
              * Set to true to compile shadow generators before raising the success callback.
              */
              */
             this.compileShadowGenerators = false;
             this.compileShadowGenerators = false;
+            /**
+             * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+             */
+            this.onMeshLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+             */
+            this.onTextureLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a material after parsing the glTF properties of the material.
+             */
+            this.onMaterialLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the asset is completely loaded, immediately before the loader is disposed.
+             * For assets with LODs, raised when all of the LODs are complete.
+             * For assets without LODs, raised when the model is complete, immediately after onSuccess.
+             */
+            this.onCompleteObservable = new BABYLON.Observable();
+            /**
+            * Raised when the loader is disposed.
+            */
+            this.onDisposeObservable = new BABYLON.Observable();
+            // #endregion
+            this._loader = null;
             this.name = "gltf";
             this.name = "gltf";
             this.extensions = {
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
                 ".glb": { isBinary: true }
             };
             };
         }
         }
+        Object.defineProperty(GLTFFileLoader.prototype, "onParsed", {
+            set: function (callback) {
+                if (this._onParsedObserver) {
+                    this.onParsedObservable.remove(this._onParsedObserver);
+                }
+                this._onParsedObserver = this.onParsedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
+            set: function (callback) {
+                if (this._onMeshLoadedObserver) {
+                    this.onMeshLoadedObservable.remove(this._onMeshLoadedObserver);
+                }
+                this._onMeshLoadedObserver = this.onMeshLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onTextureLoaded", {
+            set: function (callback) {
+                if (this._onTextureLoadedObserver) {
+                    this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
+                }
+                this._onTextureLoadedObserver = this.onTextureLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMaterialLoaded", {
+            set: function (callback) {
+                if (this._onMaterialLoadedObserver) {
+                    this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
+                }
+                this._onMaterialLoadedObserver = this.onMaterialLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
+            set: function (callback) {
+                if (this._onCompleteObserver) {
+                    this.onCompleteObservable.remove(this._onCompleteObserver);
+                }
+                this._onCompleteObserver = this.onCompleteObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onDispose", {
+            set: function (callback) {
+                if (this._onDisposeObserver) {
+                    this.onDisposeObservable.remove(this._onDisposeObserver);
+                }
+                this._onDisposeObserver = this.onDisposeObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
         /**
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         GLTFFileLoader.prototype.dispose = function () {
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
             if (this._loader) {
                 this._loader.dispose();
                 this._loader.dispose();
+                this._loader = null;
             }
             }
+            this.onParsedObservable.clear();
+            this.onMeshLoadedObservable.clear();
+            this.onTextureLoadedObservable.clear();
+            this.onMaterialLoadedObservable.clear();
+            this.onCompleteObservable.clear();
+            this.onDisposeObservable.notifyObservers(this);
+            this.onDisposeObservable.clear();
         };
         };
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -85,10 +181,7 @@ var BABYLON;
         };
         };
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -107,16 +200,22 @@ var BABYLON;
         GLTFFileLoader.prototype.createPlugin = function () {
         GLTFFileLoader.prototype.createPlugin = function () {
             return new GLTFFileLoader();
             return new GLTFFileLoader();
         };
         };
-        GLTFFileLoader._parse = function (data) {
+        GLTFFileLoader.prototype._parse = function (data) {
+            var parsedData;
             if (data instanceof ArrayBuffer) {
             if (data instanceof ArrayBuffer) {
-                return GLTFFileLoader._parseBinary(data);
+                parsedData = GLTFFileLoader._parseBinary(data);
             }
             }
-            return {
-                json: JSON.parse(data),
-                bin: null
-            };
+            else {
+                parsedData = {
+                    json: JSON.parse(data),
+                    bin: null
+                };
+            }
+            this.onParsedObservable.notifyObservers(parsedData);
+            return parsedData;
         };
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
+            var _this = this;
             var loaderVersion = { major: 2, minor: 0 };
             var loaderVersion = { major: 2, minor: 0 };
             var asset = loaderData.json.asset || {};
             var asset = loaderData.json.asset || {};
             var version = GLTFFileLoader._parseVersion(asset.version);
             var version = GLTFFileLoader._parseVersion(asset.version);
@@ -140,7 +239,17 @@ var BABYLON;
             if (!createLoader) {
             if (!createLoader) {
                 throw new Error("Unsupported version: " + asset.version);
                 throw new Error("Unsupported version: " + asset.version);
             }
             }
-            return createLoader(this);
+            var loader = createLoader();
+            loader.coordinateSystemMode = this.coordinateSystemMode;
+            loader.animationStartMode = this.animationStartMode;
+            loader.compileMaterials = this.compileMaterials;
+            loader.useClipPlane = this.useClipPlane;
+            loader.compileShadowGenerators = this.compileShadowGenerators;
+            loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
+            loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
+            loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
+            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            return loader;
         };
         };
         GLTFFileLoader._parseBinary = function (data) {
         GLTFFileLoader._parseBinary = function (data) {
             var Binary = {
             var Binary = {
@@ -1676,6 +1785,17 @@ var BABYLON;
         */
         */
         var GLTFLoader = /** @class */ (function () {
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader() {
             function GLTFLoader() {
+                // #region Stubs for IGLTFLoader interface
+                this.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.AUTO;
+                this.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.FIRST;
+                this.compileMaterials = false;
+                this.useClipPlane = false;
+                this.compileShadowGenerators = false;
+                this.onDisposeObservable = new BABYLON.Observable();
+                this.onMeshLoadedObservable = new BABYLON.Observable();
+                this.onTextureLoadedObservable = new BABYLON.Observable();
+                this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onCompleteObservable = new BABYLON.Observable();
             }
             }
             GLTFLoader.RegisterExtension = function (extension) {
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -1684,9 +1804,8 @@ var BABYLON;
                 }
                 }
                 GLTFLoader.Extensions[extension.name] = extension;
                 GLTFLoader.Extensions[extension.name] = extension;
             };
             };
-            GLTFLoader.prototype.dispose = function () {
-                // do nothing
-            };
+            GLTFLoader.prototype.dispose = function () { };
+            // #endregion
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 var _this = this;
                 scene.useRightHandedSystem = true;
                 scene.useRightHandedSystem = true;
@@ -2531,7 +2650,7 @@ var BABYLON;
             return GLTFLoaderTracker;
             return GLTFLoaderTracker;
         }());
         }());
         var GLTFLoader = /** @class */ (function () {
         var GLTFLoader = /** @class */ (function () {
-            function GLTFLoader(parent) {
+            function GLTFLoader() {
                 this._disposed = false;
                 this._disposed = false;
                 this._defaultSampler = {};
                 this._defaultSampler = {};
                 this._renderReady = false;
                 this._renderReady = false;
@@ -2542,7 +2661,16 @@ var BABYLON;
                 // Count of pending work that needs to complete before the loader is disposed.
                 // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
                 this._loaderPendingCount = 0;
                 this._loaderTrackers = new Array();
                 this._loaderTrackers = new Array();
-                this._parent = parent;
+                this.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.AUTO;
+                this.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.FIRST;
+                this.compileMaterials = false;
+                this.useClipPlane = false;
+                this.compileShadowGenerators = false;
+                this.onDisposeObservable = new BABYLON.Observable();
+                this.onMeshLoadedObservable = new BABYLON.Observable();
+                this.onTextureLoadedObservable = new BABYLON.Observable();
+                this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onCompleteObservable = new BABYLON.Observable();
             }
             }
             GLTFLoader.RegisterExtension = function (extension) {
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -2558,22 +2686,9 @@ var BABYLON;
                     return;
                     return;
                 }
                 }
                 this._disposed = true;
                 this._disposed = true;
-                // Abort requests that are not complete
-                for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
-                    var request = _a[_i];
-                    if (request.readyState !== (XMLHttpRequest.DONE || 4)) {
-                        request.abort();
-                    }
-                }
-                // Revoke object urls created during load
-                if (this._gltf.textures) {
-                    for (var _b = 0, _c = this._gltf.textures; _b < _c.length; _b++) {
-                        var texture = _c[_b];
-                        if (texture.url) {
-                            URL.revokeObjectURL(texture.url);
-                        }
-                    }
-                }
+                this._abortRequests();
+                this._releaseResources();
+                this.onDisposeObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 var _this = this;
@@ -2617,7 +2732,7 @@ var BABYLON;
                     loaded += request._loaded;
                     loaded += request._loaded;
                     total += request._total;
                     total += request._total;
                 }
                 }
-                this._progressCallback(GLTFLoader._createProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
+                this._progressCallback(new BABYLON.SceneLoaderProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
             };
             };
             GLTFLoader.prototype._executeWhenRenderReady = function (func) {
             GLTFLoader.prototype._executeWhenRenderReady = function (func) {
                 if (this._renderReady) {
                 if (this._renderReady) {
@@ -2636,10 +2751,9 @@ var BABYLON;
                 this._renderReadyObservable.notifyObservers(this);
                 this._renderReadyObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype._onComplete = function () {
             GLTFLoader.prototype._onComplete = function () {
-                if (this._parent.onComplete) {
-                    this._parent.onComplete();
-                }
-                this.dispose();
+                this._abortRequests();
+                this._releaseResources();
+                this.onCompleteObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype._loadData = function (data) {
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
                 this._gltf = data.json;
@@ -2703,7 +2817,7 @@ var BABYLON;
                 if (!animations) {
                 if (!animations) {
                     return;
                     return;
                 }
                 }
-                switch (this._parent.animationStartMode) {
+                switch (this.animationStartMode) {
                     case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
                     case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
                         // do nothing
                         // do nothing
                         break;
                         break;
@@ -2727,7 +2841,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        BABYLON.Tools.Error("Invalid animation start mode " + this.animationStartMode);
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -2742,7 +2856,7 @@ var BABYLON;
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
                 var _this = this;
                 var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
-                switch (this._parent.coordinateSystemMode) {
+                switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
                         if (!this._babylonScene.useRightHandedSystem) {
                         if (!this._babylonScene.useRightHandedSystem) {
                             this._rootNode.rotation = [0, 1, 0, 0];
                             this._rootNode.rotation = [0, 1, 0, 0];
@@ -2756,13 +2870,11 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid coordinate system mode " + this._parent.coordinateSystemMode);
+                        BABYLON.Tools.Error("Invalid coordinate system mode " + this.coordinateSystemMode);
                         return;
                         return;
                     }
                     }
                 }
                 }
-                if (this._parent.onMeshLoaded) {
-                    this._parent.onMeshLoaded(this._rootNode.babylonMesh);
-                }
+                this.onMeshLoadedObservable.notifyObservers(this._rootNode.babylonMesh);
                 var nodeIndices = scene.nodes;
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(context, nodeIndices, function (node, parentNode) {
                 this._traverseNodes(context, nodeIndices, function (node, parentNode) {
                     node.parent = parentNode;
                     node.parent = parentNode;
@@ -2838,9 +2950,7 @@ var BABYLON;
                         this._loadNode("#/nodes/" + index, childNode);
                         this._loadNode("#/nodes/" + index, childNode);
                     }
                     }
                 }
                 }
-                if (this._parent.onMeshLoaded) {
-                    this._parent.onMeshLoaded(node.babylonMesh);
-                }
+                this.onMeshLoadedObservable.notifyObservers(node.babylonMesh);
             };
             };
             GLTFLoader.prototype._loadMesh = function (context, node, mesh) {
             GLTFLoader.prototype._loadMesh = function (context, node, mesh) {
                 var _this = this;
                 var _this = this;
@@ -2883,8 +2993,8 @@ var BABYLON;
                             throw new Error(context + ": Failed to find material " + primitive.material);
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
                         }
                         this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                         this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
-                            if (isNew && _this._parent.onMaterialLoaded) {
-                                _this._parent.onMaterialLoaded(babylonMaterial);
+                            if (isNew) {
+                                _this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                             }
                             }
                             node.babylonMesh.material = babylonMaterial;
                             node.babylonMesh.material = babylonMaterial;
                         });
                         });
@@ -2905,8 +3015,8 @@ var BABYLON;
                                 throw new Error(context + ": Failed to find material " + primitive.material);
                                 throw new Error(context + ": Failed to find material " + primitive.material);
                             }
                             }
                             this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
-                                if (isNew && _this._parent.onMaterialLoaded) {
-                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                if (isNew) {
+                                    _this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                                 }
                                 }
                                 subMaterials_1[index] = babylonMaterial;
                                 subMaterials_1[index] = babylonMaterial;
                             });
                             });
@@ -3864,9 +3974,7 @@ var BABYLON;
                 babylonTexture.wrapU = sampler.wrapU;
                 babylonTexture.wrapU = sampler.wrapU;
                 babylonTexture.wrapV = sampler.wrapV;
                 babylonTexture.wrapV = sampler.wrapV;
                 babylonTexture.name = texture.name || "texture" + texture.index;
                 babylonTexture.name = texture.name || "texture" + texture.index;
-                if (this._parent.onTextureLoaded) {
-                    this._parent.onTextureLoaded(babylonTexture);
-                }
+                this.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 return babylonTexture;
                 return babylonTexture;
             };
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -3916,12 +4024,11 @@ var BABYLON;
                     _this._tryCatchOnError(function () {
                     _this._tryCatchOnError(function () {
                         throw new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                         throw new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                     });
                     });
-                }, function (oldRequest, newRequest) {
-                    _this._requests.splice(_this._requests.indexOf(oldRequest), 1, newRequest);
                 });
                 });
-                if (request) {
-                    this._requests.push(request);
-                }
+                this._requests.push(request);
+                request.onCompleteObservable.add(function () {
+                    _this._requests.splice(_this._requests.indexOf(request), 1);
+                });
             };
             };
             GLTFLoader.prototype._tryCatchOnError = function (handler) {
             GLTFLoader.prototype._tryCatchOnError = function (handler) {
                 if (this._disposed) {
                 if (this._disposed) {
@@ -4011,7 +4118,7 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
                 var _this = this;
-                if (this._parent.useClipPlane) {
+                if (this.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
                             _this._tryCatchOnError(onSuccess);
                             _this._tryCatchOnError(onSuccess);
@@ -4025,7 +4132,7 @@ var BABYLON;
                 }
                 }
             };
             };
             GLTFLoader.prototype._compileMaterialsAsync = function (onSuccess) {
             GLTFLoader.prototype._compileMaterialsAsync = function (onSuccess) {
-                if (!this._parent.compileMaterials || !this._gltf.materials) {
+                if (!this.compileMaterials || !this._gltf.materials) {
                     onSuccess();
                     onSuccess();
                     return;
                     return;
                 }
                 }
@@ -4074,7 +4181,7 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {
                 var _this = this;
                 var _this = this;
-                if (!this._parent.compileShadowGenerators) {
+                if (!this.compileShadowGenerators) {
                     onSuccess();
                     onSuccess();
                     return;
                     return;
                 }
                 }
@@ -4103,25 +4210,29 @@ var BABYLON;
                     }
                     }
                 }
                 }
             };
             };
-            GLTFLoader.Extensions = {};
-            // IE 11 Compatibility.
-            GLTFLoader._createProgressEvent = (typeof window["ProgressEvent"] === "function")
-                ? function (lengthComputable, loaded, total) {
-                    return new ProgressEvent("GLTFLoaderProgress", {
-                        lengthComputable: lengthComputable,
-                        loaded: loaded,
-                        total: total
-                    });
+            GLTFLoader.prototype._abortRequests = function () {
+                for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
+                    var request = _a[_i];
+                    request.abort();
                 }
                 }
-                : function (lengthComputable, loaded, total) {
-                    var event = document.createEvent("ProgressEvent");
-                    event.initProgressEvent("GLTFLoaderProgress", false, false, lengthComputable, loaded, total);
-                    return event;
-                };
+                this._requests.length = 0;
+            };
+            GLTFLoader.prototype._releaseResources = function () {
+                if (this._gltf.textures) {
+                    for (var _i = 0, _a = this._gltf.textures; _i < _a.length; _i++) {
+                        var texture = _a[_i];
+                        if (texture.url) {
+                            URL.revokeObjectURL(texture.url);
+                            texture.url = undefined;
+                        }
+                    }
+                }
+            };
+            GLTFLoader.Extensions = {};
             return GLTFLoader;
             return GLTFLoader;
         }());
         }());
         GLTF2.GLTFLoader = GLTFLoader;
         GLTF2.GLTFLoader = GLTFLoader;
-        BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = function (parent) { return new GLTFLoader(parent); };
+        BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = function () { return new GLTFLoader(); };
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 

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


+ 192 - 81
dist/preview release/loaders/babylonjs.loaders.js

@@ -1003,6 +1003,13 @@ var BABYLON;
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     })(GLTFLoaderAnimationStartMode = BABYLON.GLTFLoaderAnimationStartMode || (BABYLON.GLTFLoaderAnimationStartMode = {}));
     var GLTFFileLoader = /** @class */ (function () {
     var GLTFFileLoader = /** @class */ (function () {
         function GLTFFileLoader() {
         function GLTFFileLoader() {
+            // #region Common options
+            /**
+             * Raised when the asset has been parsed.
+             * The data.json property stores the glTF JSON.
+             * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
+             */
+            this.onParsedObservable = new BABYLON.Observable();
             // #endregion
             // #endregion
             // #region V2 options
             // #region V2 options
             /**
             /**
@@ -1025,26 +1032,115 @@ var BABYLON;
              * Set to true to compile shadow generators before raising the success callback.
              * Set to true to compile shadow generators before raising the success callback.
              */
              */
             this.compileShadowGenerators = false;
             this.compileShadowGenerators = false;
+            /**
+             * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+             */
+            this.onMeshLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a texture after parsing the glTF properties of the texture.
+             */
+            this.onTextureLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the loader creates a material after parsing the glTF properties of the material.
+             */
+            this.onMaterialLoadedObservable = new BABYLON.Observable();
+            /**
+             * Raised when the asset is completely loaded, immediately before the loader is disposed.
+             * For assets with LODs, raised when all of the LODs are complete.
+             * For assets without LODs, raised when the model is complete, immediately after onSuccess.
+             */
+            this.onCompleteObservable = new BABYLON.Observable();
+            /**
+            * Raised when the loader is disposed.
+            */
+            this.onDisposeObservable = new BABYLON.Observable();
+            // #endregion
+            this._loader = null;
             this.name = "gltf";
             this.name = "gltf";
             this.extensions = {
             this.extensions = {
                 ".gltf": { isBinary: false },
                 ".gltf": { isBinary: false },
                 ".glb": { isBinary: true }
                 ".glb": { isBinary: true }
             };
             };
         }
         }
+        Object.defineProperty(GLTFFileLoader.prototype, "onParsed", {
+            set: function (callback) {
+                if (this._onParsedObserver) {
+                    this.onParsedObservable.remove(this._onParsedObserver);
+                }
+                this._onParsedObserver = this.onParsedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", {
+            set: function (callback) {
+                if (this._onMeshLoadedObserver) {
+                    this.onMeshLoadedObservable.remove(this._onMeshLoadedObserver);
+                }
+                this._onMeshLoadedObserver = this.onMeshLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onTextureLoaded", {
+            set: function (callback) {
+                if (this._onTextureLoadedObserver) {
+                    this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
+                }
+                this._onTextureLoadedObserver = this.onTextureLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onMaterialLoaded", {
+            set: function (callback) {
+                if (this._onMaterialLoadedObserver) {
+                    this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
+                }
+                this._onMaterialLoadedObserver = this.onMaterialLoadedObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onComplete", {
+            set: function (callback) {
+                if (this._onCompleteObserver) {
+                    this.onCompleteObservable.remove(this._onCompleteObserver);
+                }
+                this._onCompleteObserver = this.onCompleteObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(GLTFFileLoader.prototype, "onDispose", {
+            set: function (callback) {
+                if (this._onDisposeObserver) {
+                    this.onDisposeObservable.remove(this._onDisposeObserver);
+                }
+                this._onDisposeObserver = this.onDisposeObservable.add(callback);
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
         /**
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         GLTFFileLoader.prototype.dispose = function () {
         GLTFFileLoader.prototype.dispose = function () {
             if (this._loader) {
             if (this._loader) {
                 this._loader.dispose();
                 this._loader.dispose();
+                this._loader = null;
             }
             }
+            this.onParsedObservable.clear();
+            this.onMeshLoadedObservable.clear();
+            this.onTextureLoadedObservable.clear();
+            this.onMaterialLoadedObservable.clear();
+            this.onCompleteObservable.clear();
+            this.onDisposeObservable.notifyObservers(this);
+            this.onDisposeObservable.clear();
         };
         };
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -1059,10 +1155,7 @@ var BABYLON;
         };
         };
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
         GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
             try {
             try {
-                var loaderData = GLTFFileLoader._parse(data);
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+                var loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -1081,16 +1174,22 @@ var BABYLON;
         GLTFFileLoader.prototype.createPlugin = function () {
         GLTFFileLoader.prototype.createPlugin = function () {
             return new GLTFFileLoader();
             return new GLTFFileLoader();
         };
         };
-        GLTFFileLoader._parse = function (data) {
+        GLTFFileLoader.prototype._parse = function (data) {
+            var parsedData;
             if (data instanceof ArrayBuffer) {
             if (data instanceof ArrayBuffer) {
-                return GLTFFileLoader._parseBinary(data);
+                parsedData = GLTFFileLoader._parseBinary(data);
             }
             }
-            return {
-                json: JSON.parse(data),
-                bin: null
-            };
+            else {
+                parsedData = {
+                    json: JSON.parse(data),
+                    bin: null
+                };
+            }
+            this.onParsedObservable.notifyObservers(parsedData);
+            return parsedData;
         };
         };
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
         GLTFFileLoader.prototype._getLoader = function (loaderData) {
+            var _this = this;
             var loaderVersion = { major: 2, minor: 0 };
             var loaderVersion = { major: 2, minor: 0 };
             var asset = loaderData.json.asset || {};
             var asset = loaderData.json.asset || {};
             var version = GLTFFileLoader._parseVersion(asset.version);
             var version = GLTFFileLoader._parseVersion(asset.version);
@@ -1114,7 +1213,17 @@ var BABYLON;
             if (!createLoader) {
             if (!createLoader) {
                 throw new Error("Unsupported version: " + asset.version);
                 throw new Error("Unsupported version: " + asset.version);
             }
             }
-            return createLoader(this);
+            var loader = createLoader();
+            loader.coordinateSystemMode = this.coordinateSystemMode;
+            loader.animationStartMode = this.animationStartMode;
+            loader.compileMaterials = this.compileMaterials;
+            loader.useClipPlane = this.useClipPlane;
+            loader.compileShadowGenerators = this.compileShadowGenerators;
+            loader.onMeshLoadedObservable.add(function (mesh) { return _this.onMeshLoadedObservable.notifyObservers(mesh); });
+            loader.onTextureLoadedObservable.add(function (texture) { return _this.onTextureLoadedObservable.notifyObservers(texture); });
+            loader.onMaterialLoadedObservable.add(function (material) { return _this.onMaterialLoadedObservable.notifyObservers(material); });
+            loader.onCompleteObservable.add(function () { return _this.onCompleteObservable.notifyObservers(_this); });
+            return loader;
         };
         };
         GLTFFileLoader._parseBinary = function (data) {
         GLTFFileLoader._parseBinary = function (data) {
             var Binary = {
             var Binary = {
@@ -2650,6 +2759,17 @@ var BABYLON;
         */
         */
         var GLTFLoader = /** @class */ (function () {
         var GLTFLoader = /** @class */ (function () {
             function GLTFLoader() {
             function GLTFLoader() {
+                // #region Stubs for IGLTFLoader interface
+                this.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.AUTO;
+                this.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.FIRST;
+                this.compileMaterials = false;
+                this.useClipPlane = false;
+                this.compileShadowGenerators = false;
+                this.onDisposeObservable = new BABYLON.Observable();
+                this.onMeshLoadedObservable = new BABYLON.Observable();
+                this.onTextureLoadedObservable = new BABYLON.Observable();
+                this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onCompleteObservable = new BABYLON.Observable();
             }
             }
             GLTFLoader.RegisterExtension = function (extension) {
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -2658,9 +2778,8 @@ var BABYLON;
                 }
                 }
                 GLTFLoader.Extensions[extension.name] = extension;
                 GLTFLoader.Extensions[extension.name] = extension;
             };
             };
-            GLTFLoader.prototype.dispose = function () {
-                // do nothing
-            };
+            GLTFLoader.prototype.dispose = function () { };
+            // #endregion
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 var _this = this;
                 scene.useRightHandedSystem = true;
                 scene.useRightHandedSystem = true;
@@ -3487,7 +3606,7 @@ var BABYLON;
             return GLTFLoaderTracker;
             return GLTFLoaderTracker;
         }());
         }());
         var GLTFLoader = /** @class */ (function () {
         var GLTFLoader = /** @class */ (function () {
-            function GLTFLoader(parent) {
+            function GLTFLoader() {
                 this._disposed = false;
                 this._disposed = false;
                 this._defaultSampler = {};
                 this._defaultSampler = {};
                 this._renderReady = false;
                 this._renderReady = false;
@@ -3498,7 +3617,16 @@ var BABYLON;
                 // Count of pending work that needs to complete before the loader is disposed.
                 // Count of pending work that needs to complete before the loader is disposed.
                 this._loaderPendingCount = 0;
                 this._loaderPendingCount = 0;
                 this._loaderTrackers = new Array();
                 this._loaderTrackers = new Array();
-                this._parent = parent;
+                this.coordinateSystemMode = BABYLON.GLTFLoaderCoordinateSystemMode.AUTO;
+                this.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.FIRST;
+                this.compileMaterials = false;
+                this.useClipPlane = false;
+                this.compileShadowGenerators = false;
+                this.onDisposeObservable = new BABYLON.Observable();
+                this.onMeshLoadedObservable = new BABYLON.Observable();
+                this.onTextureLoadedObservable = new BABYLON.Observable();
+                this.onMaterialLoadedObservable = new BABYLON.Observable();
+                this.onCompleteObservable = new BABYLON.Observable();
             }
             }
             GLTFLoader.RegisterExtension = function (extension) {
             GLTFLoader.RegisterExtension = function (extension) {
                 if (GLTFLoader.Extensions[extension.name]) {
                 if (GLTFLoader.Extensions[extension.name]) {
@@ -3514,22 +3642,9 @@ var BABYLON;
                     return;
                     return;
                 }
                 }
                 this._disposed = true;
                 this._disposed = true;
-                // Abort requests that are not complete
-                for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
-                    var request = _a[_i];
-                    if (request.readyState !== (XMLHttpRequest.DONE || 4)) {
-                        request.abort();
-                    }
-                }
-                // Revoke object urls created during load
-                if (this._gltf.textures) {
-                    for (var _b = 0, _c = this._gltf.textures; _b < _c.length; _b++) {
-                        var texture = _c[_b];
-                        if (texture.url) {
-                            URL.revokeObjectURL(texture.url);
-                        }
-                    }
-                }
+                this._abortRequests();
+                this._releaseResources();
+                this.onDisposeObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
             GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onSuccess, onProgress, onError) {
                 var _this = this;
                 var _this = this;
@@ -3573,7 +3688,7 @@ var BABYLON;
                     loaded += request._loaded;
                     loaded += request._loaded;
                     total += request._total;
                     total += request._total;
                 }
                 }
-                this._progressCallback(GLTFLoader._createProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
+                this._progressCallback(new BABYLON.SceneLoaderProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
             };
             };
             GLTFLoader.prototype._executeWhenRenderReady = function (func) {
             GLTFLoader.prototype._executeWhenRenderReady = function (func) {
                 if (this._renderReady) {
                 if (this._renderReady) {
@@ -3592,10 +3707,9 @@ var BABYLON;
                 this._renderReadyObservable.notifyObservers(this);
                 this._renderReadyObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype._onComplete = function () {
             GLTFLoader.prototype._onComplete = function () {
-                if (this._parent.onComplete) {
-                    this._parent.onComplete();
-                }
-                this.dispose();
+                this._abortRequests();
+                this._releaseResources();
+                this.onCompleteObservable.notifyObservers(this);
             };
             };
             GLTFLoader.prototype._loadData = function (data) {
             GLTFLoader.prototype._loadData = function (data) {
                 this._gltf = data.json;
                 this._gltf = data.json;
@@ -3659,7 +3773,7 @@ var BABYLON;
                 if (!animations) {
                 if (!animations) {
                     return;
                     return;
                 }
                 }
-                switch (this._parent.animationStartMode) {
+                switch (this.animationStartMode) {
                     case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
                     case BABYLON.GLTFLoaderAnimationStartMode.NONE: {
                         // do nothing
                         // do nothing
                         break;
                         break;
@@ -3683,7 +3797,7 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                        BABYLON.Tools.Error("Invalid animation start mode " + this.animationStartMode);
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -3698,7 +3812,7 @@ var BABYLON;
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
             GLTFLoader.prototype._loadScene = function (context, scene, nodeNames) {
                 var _this = this;
                 var _this = this;
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
                 this._rootNode = { babylonMesh: new BABYLON.Mesh("__root__", this._babylonScene) };
-                switch (this._parent.coordinateSystemMode) {
+                switch (this.coordinateSystemMode) {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
                     case BABYLON.GLTFLoaderCoordinateSystemMode.AUTO: {
                         if (!this._babylonScene.useRightHandedSystem) {
                         if (!this._babylonScene.useRightHandedSystem) {
                             this._rootNode.rotation = [0, 1, 0, 0];
                             this._rootNode.rotation = [0, 1, 0, 0];
@@ -3712,13 +3826,11 @@ var BABYLON;
                         break;
                         break;
                     }
                     }
                     default: {
                     default: {
-                        BABYLON.Tools.Error("Invalid coordinate system mode " + this._parent.coordinateSystemMode);
+                        BABYLON.Tools.Error("Invalid coordinate system mode " + this.coordinateSystemMode);
                         return;
                         return;
                     }
                     }
                 }
                 }
-                if (this._parent.onMeshLoaded) {
-                    this._parent.onMeshLoaded(this._rootNode.babylonMesh);
-                }
+                this.onMeshLoadedObservable.notifyObservers(this._rootNode.babylonMesh);
                 var nodeIndices = scene.nodes;
                 var nodeIndices = scene.nodes;
                 this._traverseNodes(context, nodeIndices, function (node, parentNode) {
                 this._traverseNodes(context, nodeIndices, function (node, parentNode) {
                     node.parent = parentNode;
                     node.parent = parentNode;
@@ -3794,9 +3906,7 @@ var BABYLON;
                         this._loadNode("#/nodes/" + index, childNode);
                         this._loadNode("#/nodes/" + index, childNode);
                     }
                     }
                 }
                 }
-                if (this._parent.onMeshLoaded) {
-                    this._parent.onMeshLoaded(node.babylonMesh);
-                }
+                this.onMeshLoadedObservable.notifyObservers(node.babylonMesh);
             };
             };
             GLTFLoader.prototype._loadMesh = function (context, node, mesh) {
             GLTFLoader.prototype._loadMesh = function (context, node, mesh) {
                 var _this = this;
                 var _this = this;
@@ -3839,8 +3949,8 @@ var BABYLON;
                             throw new Error(context + ": Failed to find material " + primitive.material);
                             throw new Error(context + ": Failed to find material " + primitive.material);
                         }
                         }
                         this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                         this._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
-                            if (isNew && _this._parent.onMaterialLoaded) {
-                                _this._parent.onMaterialLoaded(babylonMaterial);
+                            if (isNew) {
+                                _this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                             }
                             }
                             node.babylonMesh.material = babylonMaterial;
                             node.babylonMesh.material = babylonMaterial;
                         });
                         });
@@ -3861,8 +3971,8 @@ var BABYLON;
                                 throw new Error(context + ": Failed to find material " + primitive.material);
                                 throw new Error(context + ": Failed to find material " + primitive.material);
                             }
                             }
                             this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
                             this_1._loadMaterial("#/materials/" + material.index, material, function (babylonMaterial, isNew) {
-                                if (isNew && _this._parent.onMaterialLoaded) {
-                                    _this._parent.onMaterialLoaded(babylonMaterial);
+                                if (isNew) {
+                                    _this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                                 }
                                 }
                                 subMaterials_1[index] = babylonMaterial;
                                 subMaterials_1[index] = babylonMaterial;
                             });
                             });
@@ -4820,9 +4930,7 @@ var BABYLON;
                 babylonTexture.wrapU = sampler.wrapU;
                 babylonTexture.wrapU = sampler.wrapU;
                 babylonTexture.wrapV = sampler.wrapV;
                 babylonTexture.wrapV = sampler.wrapV;
                 babylonTexture.name = texture.name || "texture" + texture.index;
                 babylonTexture.name = texture.name || "texture" + texture.index;
-                if (this._parent.onTextureLoaded) {
-                    this._parent.onTextureLoaded(babylonTexture);
-                }
+                this.onTextureLoadedObservable.notifyObservers(babylonTexture);
                 return babylonTexture;
                 return babylonTexture;
             };
             };
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
             GLTFLoader.prototype._loadSampler = function (context, sampler) {
@@ -4872,12 +4980,11 @@ var BABYLON;
                     _this._tryCatchOnError(function () {
                     _this._tryCatchOnError(function () {
                         throw new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                         throw new BABYLON.LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                     });
                     });
-                }, function (oldRequest, newRequest) {
-                    _this._requests.splice(_this._requests.indexOf(oldRequest), 1, newRequest);
                 });
                 });
-                if (request) {
-                    this._requests.push(request);
-                }
+                this._requests.push(request);
+                request.onCompleteObservable.add(function () {
+                    _this._requests.splice(_this._requests.indexOf(request), 1);
+                });
             };
             };
             GLTFLoader.prototype._tryCatchOnError = function (handler) {
             GLTFLoader.prototype._tryCatchOnError = function (handler) {
                 if (this._disposed) {
                 if (this._disposed) {
@@ -4967,7 +5074,7 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
             GLTFLoader.prototype._compileMaterialAsync = function (babylonMaterial, babylonMesh, onSuccess) {
                 var _this = this;
                 var _this = this;
-                if (this._parent.useClipPlane) {
+                if (this.useClipPlane) {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                     babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
                         babylonMaterial.forceCompilation(babylonMesh, function () {
                             _this._tryCatchOnError(onSuccess);
                             _this._tryCatchOnError(onSuccess);
@@ -4981,7 +5088,7 @@ var BABYLON;
                 }
                 }
             };
             };
             GLTFLoader.prototype._compileMaterialsAsync = function (onSuccess) {
             GLTFLoader.prototype._compileMaterialsAsync = function (onSuccess) {
-                if (!this._parent.compileMaterials || !this._gltf.materials) {
+                if (!this.compileMaterials || !this._gltf.materials) {
                     onSuccess();
                     onSuccess();
                     return;
                     return;
                 }
                 }
@@ -5030,7 +5137,7 @@ var BABYLON;
             };
             };
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {
             GLTFLoader.prototype._compileShadowGeneratorsAsync = function (onSuccess) {
                 var _this = this;
                 var _this = this;
-                if (!this._parent.compileShadowGenerators) {
+                if (!this.compileShadowGenerators) {
                     onSuccess();
                     onSuccess();
                     return;
                     return;
                 }
                 }
@@ -5059,25 +5166,29 @@ var BABYLON;
                     }
                     }
                 }
                 }
             };
             };
-            GLTFLoader.Extensions = {};
-            // IE 11 Compatibility.
-            GLTFLoader._createProgressEvent = (typeof window["ProgressEvent"] === "function")
-                ? function (lengthComputable, loaded, total) {
-                    return new ProgressEvent("GLTFLoaderProgress", {
-                        lengthComputable: lengthComputable,
-                        loaded: loaded,
-                        total: total
-                    });
+            GLTFLoader.prototype._abortRequests = function () {
+                for (var _i = 0, _a = this._requests; _i < _a.length; _i++) {
+                    var request = _a[_i];
+                    request.abort();
                 }
                 }
-                : function (lengthComputable, loaded, total) {
-                    var event = document.createEvent("ProgressEvent");
-                    event.initProgressEvent("GLTFLoaderProgress", false, false, lengthComputable, loaded, total);
-                    return event;
-                };
+                this._requests.length = 0;
+            };
+            GLTFLoader.prototype._releaseResources = function () {
+                if (this._gltf.textures) {
+                    for (var _i = 0, _a = this._gltf.textures; _i < _a.length; _i++) {
+                        var texture = _a[_i];
+                        if (texture.url) {
+                            URL.revokeObjectURL(texture.url);
+                            texture.url = undefined;
+                        }
+                    }
+                }
+            };
+            GLTFLoader.Extensions = {};
             return GLTFLoader;
             return GLTFLoader;
         }());
         }());
         GLTF2.GLTFLoader = GLTFLoader;
         GLTF2.GLTFLoader = GLTFLoader;
-        BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = function (parent) { return new GLTFLoader(parent); };
+        BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = function () { return new GLTFLoader(); };
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
     })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
 })(BABYLON || (BABYLON = {}));
 })(BABYLON || (BABYLON = {}));
 
 

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


+ 62 - 17
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -127,18 +127,30 @@ declare module BABYLON {
         bin: Nullable<ArrayBufferView>;
         bin: Nullable<ArrayBufferView>;
     }
     }
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
     }
     }
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
-        static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        static CreateGLTFLoaderV1: () => IGLTFLoader;
+        static CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
         /**
          * Raised when the asset has been parsed.
          * Raised when the asset has been parsed.
          * The data.json property stores the glTF JSON.
          * The data.json property stores the glTF JSON.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          */
          */
-        onParsed: (data: IGLTFLoaderData) => void;
+        onParsedObservable: Observable<IGLTFLoaderData>;
+        private _onParsedObserver;
+        onParsed: (loaderData: IGLTFLoaderData) => void;
         static IncrementalLoading: boolean;
         static IncrementalLoading: boolean;
         static HomogeneousCoordinates: boolean;
         static HomogeneousCoordinates: boolean;
         /**
         /**
@@ -164,21 +176,35 @@ declare module BABYLON {
         /**
         /**
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          */
          */
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        private _onMeshLoadedObserver;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         onMeshLoaded: (mesh: AbstractMesh) => void;
         /**
         /**
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          */
          */
-        onTextureLoaded: (texture: BaseTexture) => void;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        private _onTextureLoadedObserver;
+        onTextureLoaded: (Texture: BaseTexture) => void;
         /**
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
          */
-        onMaterialLoaded: (material: Material) => void;
+        onMaterialLoadedObservable: Observable<Material>;
+        private _onMaterialLoadedObserver;
+        onMaterialLoaded: (Material: Material) => void;
         /**
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
          */
+        onCompleteObservable: Observable<GLTFFileLoader>;
+        private _onCompleteObserver;
         onComplete: () => void;
         onComplete: () => void;
+        /**
+        * Raised when the loader is disposed.
+        */
+        onDisposeObservable: Observable<GLTFFileLoader>;
+        private _onDisposeObserver;
+        onDispose: () => void;
         private _loader;
         private _loader;
         name: string;
         name: string;
         extensions: ISceneLoaderPluginExtensions;
         extensions: ISceneLoaderPluginExtensions;
@@ -186,12 +212,12 @@ declare module BABYLON {
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          * Disposes the loader, releases resources during load, and cancels any outstanding requests.
          */
          */
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
-        private static _parse(data);
+        private _parse(data);
         private _getLoader(loaderData);
         private _getLoader(loaderData);
         private static _parseBinary(data);
         private static _parseBinary(data);
         private static _parseV1(binaryReader);
         private static _parseV1(binaryReader);
@@ -595,9 +621,19 @@ declare module BABYLON.GLTF1 {
             [name: string]: GLTFLoaderExtension;
             [name: string]: GLTFLoaderExtension;
         };
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
         static RegisterExtension(extension: GLTFLoaderExtension): void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): boolean;
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): boolean;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): void;
         private _loadShadersAsync(gltfRuntime, onload);
         private _loadShadersAsync(gltfRuntime, onload);
         private _loadBuffersAsync(gltfRuntime, onLoad, onProgress?);
         private _loadBuffersAsync(gltfRuntime, onLoad, onProgress?);
         private _createNodes(gltfRuntime);
         private _createNodes(gltfRuntime);
@@ -1006,7 +1042,6 @@ declare module BABYLON.GLTF2 {
         _gltf: IGLTF;
         _gltf: IGLTF;
         _babylonScene: Scene;
         _babylonScene: Scene;
         private _disposed;
         private _disposed;
-        private _parent;
         private _rootUrl;
         private _rootUrl;
         private _defaultMaterial;
         private _defaultMaterial;
         private _defaultSampler;
         private _defaultSampler;
@@ -1024,11 +1059,19 @@ declare module BABYLON.GLTF2 {
             [name: string]: GLTFLoaderExtension;
             [name: string]: GLTFLoaderExtension;
         };
         };
         static RegisterExtension(extension: GLTFLoaderExtension): void;
         static RegisterExtension(extension: GLTFLoaderExtension): void;
-        private static _createProgressEvent;
-        constructor(parent: GLTFFileLoader);
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
         dispose(): void;
         dispose(): void;
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess?, onProgress?, onError?);
         private _loadAsync(nodeNames, scene, data, rootUrl, onSuccess?, onProgress?, onError?);
         private _onProgress();
         private _onProgress();
         _executeWhenRenderReady(func: () => void): void;
         _executeWhenRenderReady(func: () => void): void;
@@ -1099,6 +1142,8 @@ declare module BABYLON.GLTF2 {
         private _compileMaterialAsync(babylonMaterial, babylonMesh, onSuccess);
         private _compileMaterialAsync(babylonMaterial, babylonMesh, onSuccess);
         private _compileMaterialsAsync(onSuccess);
         private _compileMaterialsAsync(onSuccess);
         private _compileShadowGeneratorsAsync(onSuccess);
         private _compileShadowGeneratorsAsync(onSuccess);
+        private _abortRequests();
+        private _releaseResources();
     }
     }
 }
 }
 
 

+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 162 - 0
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -0,0 +1,162 @@
+
+declare module BABYLON {
+    class GLTF2Export {
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
+         * as keys and their data and paths as values.
+         */
+        static GLTF(scene: BABYLON.Scene, filename: string): {
+            [fileName: string]: string | Blob;
+        };
+        /**
+         *
+         * @param meshes
+         * @param filename
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
+         */
+        static GLB(scene: BABYLON.Scene, filename: string): {
+            [fileName: string]: string | Blob;
+        };
+        /**
+         * Downloads data from glTF object.
+         *
+         * @param gltfData glTF object with keys being file names and values being data
+         */
+        static downloadFiles(gltfData: {
+            [fileName: string]: string | Blob;
+        }): void;
+    }
+}
+
+declare module BABYLON {
+    class _GLTF2Exporter {
+        private bufferViews;
+        private accessors;
+        private nodes;
+        private asset;
+        private scenes;
+        private meshes;
+        private totalByteLength;
+        private babylonScene;
+        constructor(babylonScene: BABYLON.Scene);
+        /**
+         * Creates a buffer view based on teh supplied arguments
+         * @param bufferIndex
+         * @param byteOffset
+         * @param byteLength
+         *
+         * @returns {_IGLTFBufferView}
+         */
+        private createBufferView(bufferIndex, byteOffset, byteLength);
+        /**
+         * Creates an accessor based on the supplied arguments
+         * @param bufferviewIndex
+         * @param name
+         * @param type
+         * @param componentType
+         * @param count
+         * @param min
+         * @param max
+         *
+         * @returns {_IGLTFAccessor}
+         */
+        private createAccessor(bufferviewIndex, name, type, componentType, count, min?, max?);
+        /**
+         * Calculates the minimum and maximum values of an array of floats, based on stride
+         * @param buff
+         * @param vertexStart
+         * @param vertexCount
+         * @param arrayOffset
+         * @param stride
+         *
+         * @returns {min: number[], max: number[]} min number array and max number array
+         */
+        private calculateMinMax(buff, vertexStart, vertexCount, arrayOffset, stride);
+        /**
+         * Write mesh attribute data to buffer.
+         * Returns the bytelength of the data.
+         * @param vertexBufferType
+         * @param submesh
+         * @param meshAttributeArray
+         * @param strideSize
+         * @param byteOffset
+         * @param dataBuffer
+         * @param useRightHandedSystem
+         *
+         * @returns {number} byte length
+         */
+        private writeAttributeData(vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem);
+        /**
+         * Generates glTF json data
+         * @param glb
+         * @param glTFPrefix
+         * @param prettyPrint
+         *
+         * @returns {string} json data as string
+         */
+        private generateJSON(glb, glTFPrefix?, prettyPrint?);
+        /**
+         * Generates data for .gltf and .bin files based on the glTF prefix string
+         * @param glTFPrefix
+         *
+         * @returns {[x: string]: string | Blob} object with glTF json tex filename
+         * and binary file name as keys and their data as values
+         */
+        _generateGLTF(glTFPrefix: string): {
+            [x: string]: string | Blob;
+        };
+        /**
+         * Creates a binary buffer for glTF
+         *
+         * @returns {ArrayBuffer}
+         */
+        private generateBinary();
+        /**
+         * Generates a glb file from the json and binary data.
+         * Returns an object with the glb file name as the key and data as the value.
+         * @param jsonText
+         * @param binaryBuffer
+         * @param glTFPrefix
+         *
+         * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
+         */
+        _generateGLB(glTFPrefix: string): {
+            [glbFileName: string]: Blob;
+        };
+        /**
+         * Sets the TRS for each node
+         * @param node
+         * @param babylonMesh
+         * @param useRightHandedSystem
+         */
+        private setNodeTransformation(node, babylonMesh, useRightHandedSystem);
+        /**
+         * Sets data for the primitive attributes of each submesh
+         * @param mesh
+         * @param babylonMesh
+         * @param byteOffset
+         * @param useRightHandedSystem
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength of the primitive attributes plus the passed in byteOffset
+         */
+        private setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer?);
+        /**
+         * Creates a glTF scene based on the array of meshes.
+         * Returns the the total byte offset.
+         * @param gltf
+         * @param byteOffset
+         * @param buffer
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength + byteoffset
+         */
+        private createScene(babylonScene, byteOffset, dataBuffer?);
+    }
+}

+ 649 - 0
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -0,0 +1,649 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2Export = /** @class */ (function () {
+        function GLTF2Export() {
+        }
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
+         * as keys and their data and paths as values.
+         */
+        GLTF2Export.GLTF = function (scene, filename) {
+            var glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            var gltfGenerator = new BABYLON._GLTF2Exporter(scene);
+            return gltfGenerator._generateGLTF(glTFPrefix);
+        };
+        /**
+         *
+         * @param meshes
+         * @param filename
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
+         */
+        GLTF2Export.GLB = function (scene, filename) {
+            var glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            var gltfGenerator = new BABYLON._GLTF2Exporter(scene);
+            return gltfGenerator._generateGLB(glTFPrefix);
+        };
+        /**
+         * Downloads data from glTF object.
+         *
+         * @param gltfData glTF object with keys being file names and values being data
+         */
+        GLTF2Export.downloadFiles = function (gltfData) {
+            /**
+             * Checks for a matching suffix at the end of a string (for ES5 and lower)
+             * @param str
+             * @param suffix
+             *
+             * @returns {boolean} indicating whether the suffix matches or not
+             */
+            function endsWith(str, suffix) {
+                return str.indexOf(suffix, str.length - suffix.length) !== -1;
+            }
+            for (var key in gltfData) {
+                var link = document.createElement('a');
+                document.body.appendChild(link);
+                link.setAttribute("type", "hidden");
+                link.download = key;
+                var blob = gltfData[key];
+                var mimeType = void 0;
+                if (endsWith(key, ".glb")) {
+                    mimeType = { type: "model/gltf-binary" };
+                }
+                else if (endsWith(key, ".bin")) {
+                    mimeType = { type: "application/octet-stream" };
+                }
+                else if (endsWith(key, ".gltf")) {
+                    mimeType = { type: "model/gltf+json" };
+                }
+                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
+                link.click();
+            }
+        };
+        return GLTF2Export;
+    }());
+    BABYLON.GLTF2Export = GLTF2Export;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFSerializer.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    var _GLTF2Exporter = /** @class */ (function () {
+        function _GLTF2Exporter(babylonScene) {
+            this.asset = { generator: "BabylonJS", version: "2.0" };
+            this.babylonScene = babylonScene;
+            this.bufferViews = new Array();
+            this.accessors = new Array();
+            this.meshes = new Array();
+            this.scenes = new Array();
+            this.nodes = new Array();
+            var totalByteLength = 0;
+            totalByteLength = this.createScene(this.babylonScene, totalByteLength);
+            this.totalByteLength = totalByteLength;
+        }
+        /**
+         * Creates a buffer view based on teh supplied arguments
+         * @param bufferIndex
+         * @param byteOffset
+         * @param byteLength
+         *
+         * @returns {_IGLTFBufferView}
+         */
+        _GLTF2Exporter.prototype.createBufferView = function (bufferIndex, byteOffset, byteLength) {
+            var bufferview = { buffer: bufferIndex, byteLength: byteLength };
+            if (byteOffset > 0) {
+                bufferview.byteOffset = byteOffset;
+            }
+            return bufferview;
+        };
+        /**
+         * Creates an accessor based on the supplied arguments
+         * @param bufferviewIndex
+         * @param name
+         * @param type
+         * @param componentType
+         * @param count
+         * @param min
+         * @param max
+         *
+         * @returns {_IGLTFAccessor}
+         */
+        _GLTF2Exporter.prototype.createAccessor = function (bufferviewIndex, name, type, componentType, count, min, max) {
+            var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
+            if (min) {
+                accessor.min = min;
+            }
+            if (max) {
+                accessor.max = max;
+            }
+            return accessor;
+        };
+        /**
+         * Calculates the minimum and maximum values of an array of floats, based on stride
+         * @param buff
+         * @param vertexStart
+         * @param vertexCount
+         * @param arrayOffset
+         * @param stride
+         *
+         * @returns {min: number[], max: number[]} min number array and max number array
+         */
+        _GLTF2Exporter.prototype.calculateMinMax = function (buff, vertexStart, vertexCount, arrayOffset, stride) {
+            var min = [Infinity, Infinity, Infinity];
+            var max = [-Infinity, -Infinity, -Infinity];
+            var end = vertexStart + vertexCount;
+            if (vertexCount > 0) {
+                for (var i = vertexStart; i < end; ++i) {
+                    var index = stride * i;
+                    for (var j = 0; j < stride; ++j) {
+                        if (buff[index] < min[j]) {
+                            min[j] = buff[index];
+                        }
+                        if (buff[index] > max[j]) {
+                            max[j] = buff[index];
+                        }
+                        ++index;
+                    }
+                }
+            }
+            return { min: min, max: max };
+        };
+        /**
+         * Write mesh attribute data to buffer.
+         * Returns the bytelength of the data.
+         * @param vertexBufferType
+         * @param submesh
+         * @param meshAttributeArray
+         * @param strideSize
+         * @param byteOffset
+         * @param dataBuffer
+         * @param useRightHandedSystem
+         *
+         * @returns {number} byte length
+         */
+        _GLTF2Exporter.prototype.writeAttributeData = function (vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem) {
+            var byteOff = byteOffset;
+            var end = submesh.verticesStart + submesh.verticesCount;
+            var byteLength = 0;
+            switch (vertexBufferType) {
+                case BABYLON.VertexBuffer.PositionKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        if (useRightHandedSystem) {
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        }
+                        else {
+                            dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                        }
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 12;
+                    break;
+                }
+                case BABYLON.VertexBuffer.NormalKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        if (useRightHandedSystem) {
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        }
+                        else {
+                            dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                        }
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 12;
+                    break;
+                }
+                case BABYLON.VertexBuffer.TangentKind: {
+                    for (var k = submesh.indexStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        if (useRightHandedSystem) {
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        }
+                        else {
+                            dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                        }
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 16;
+                    break;
+                }
+                case BABYLON.VertexBuffer.ColorKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 16;
+                    break;
+                }
+                case BABYLON.VertexBuffer.UVKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 8;
+                    break;
+                }
+                case BABYLON.VertexBuffer.UV2Kind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 8;
+                    break;
+                }
+                default: {
+                    throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                }
+            }
+            return byteLength;
+        };
+        /**
+         * Generates glTF json data
+         * @param glb
+         * @param glTFPrefix
+         * @param prettyPrint
+         *
+         * @returns {string} json data as string
+         */
+        _GLTF2Exporter.prototype.generateJSON = function (glb, glTFPrefix, prettyPrint) {
+            var buffer = { byteLength: this.totalByteLength };
+            var glTf = {
+                buffers: [buffer],
+                asset: this.asset,
+                meshes: this.meshes,
+                scenes: this.scenes,
+                nodes: this.nodes,
+                bufferViews: this.bufferViews,
+                accessors: this.accessors
+            };
+            if (this.scenes.length > 0) {
+                glTf.scene = 0;
+            }
+            if (!glb) {
+                buffer.uri = glTFPrefix + ".bin";
+            }
+            var jsonText = prettyPrint ? JSON.stringify(glTf, null, 2) : JSON.stringify(glTf);
+            return jsonText;
+        };
+        /**
+         * Generates data for .gltf and .bin files based on the glTF prefix string
+         * @param glTFPrefix
+         *
+         * @returns {[x: string]: string | Blob} object with glTF json tex filename
+         * and binary file name as keys and their data as values
+         */
+        _GLTF2Exporter.prototype._generateGLTF = function (glTFPrefix) {
+            var jsonText = this.generateJSON(false, glTFPrefix, true);
+            var binaryBuffer = this.generateBinary();
+            var bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
+            var glTFFileName = glTFPrefix + '.gltf';
+            var glTFBinFile = glTFPrefix + '.bin';
+            return _a = {},
+                _a[glTFFileName] = jsonText,
+                _a[glTFBinFile] = bin,
+                _a;
+            var _a;
+        };
+        /**
+         * Creates a binary buffer for glTF
+         *
+         * @returns {ArrayBuffer}
+         */
+        _GLTF2Exporter.prototype.generateBinary = function () {
+            var byteOffset = 0;
+            var binaryBuffer = new ArrayBuffer(this.totalByteLength);
+            var dataBuffer = new DataView(binaryBuffer);
+            byteOffset = this.createScene(this.babylonScene, byteOffset, dataBuffer);
+            return binaryBuffer;
+        };
+        /**
+         * Generates a glb file from the json and binary data.
+         * Returns an object with the glb file name as the key and data as the value.
+         * @param jsonText
+         * @param binaryBuffer
+         * @param glTFPrefix
+         *
+         * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
+         */
+        _GLTF2Exporter.prototype._generateGLB = function (glTFPrefix) {
+            var jsonText = this.generateJSON(true);
+            var binaryBuffer = this.generateBinary();
+            var glbFileName = glTFPrefix + '.glb';
+            var headerLength = 12;
+            var chunkLengthPrefix = 8;
+            var jsonLength = jsonText.length;
+            var jsonRemainder = jsonLength % 4;
+            var binRemainder = binaryBuffer.byteLength % 4;
+            var jsonPadding = jsonRemainder === 0 ? jsonRemainder : 4 - jsonRemainder;
+            var binPadding = binRemainder === 0 ? binRemainder : 4 - binRemainder;
+            var byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding;
+            //header
+            var headerBuffer = new ArrayBuffer(headerLength);
+            var headerBufferView = new DataView(headerBuffer);
+            headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+            headerBufferView.setUint32(4, 2, true); // version
+            headerBufferView.setUint32(8, byteLength, true); // total bytes in file
+            //json chunk
+            var jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+            var jsonChunkBufferView = new DataView(jsonChunkBuffer);
+            jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+            jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+            //json chunk bytes
+            var jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+            for (var i = 0; i < jsonLength; ++i) {
+                jsonData[i] = jsonText.charCodeAt(i);
+            }
+            //json padding
+            var jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+            for (var i = 0; i < jsonPadding; ++i) {
+                jsonPaddingView[i] = 0x20;
+            }
+            //binary chunk
+            var binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+            var binaryChunkBufferView = new DataView(binaryChunkBuffer);
+            binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength, true);
+            binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+            // binary padding
+            var binPaddingBuffer = new ArrayBuffer(binPadding);
+            var binPaddingView = new Uint8Array(binPaddingBuffer);
+            for (var i = 0; i < binPadding; ++i) {
+                binPaddingView[i] = 0;
+            }
+            // binary data
+            var glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
+            return _a = {},
+                _a[glbFileName] = glbFile,
+                _a;
+            var _a;
+        };
+        /**
+         * Sets the TRS for each node
+         * @param node
+         * @param babylonMesh
+         * @param useRightHandedSystem
+         */
+        _GLTF2Exporter.prototype.setNodeTransformation = function (node, babylonMesh, useRightHandedSystem) {
+            if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                if (useRightHandedSystem) {
+                    node.translation = babylonMesh.position.asArray();
+                }
+                else {
+                    node.translation = [babylonMesh.position.x, babylonMesh.position.y, -babylonMesh.position.z];
+                }
+            }
+            if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                if (useRightHandedSystem) {
+                    node.scale = babylonMesh.scaling.asArray();
+                }
+                else {
+                    node.scale = [babylonMesh.scaling.x, babylonMesh.scaling.y, -babylonMesh.scaling.z];
+                }
+            }
+            var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
+            if (babylonMesh.rotationQuaternion) {
+                rotationQuaternion = rotationQuaternion.multiply(babylonMesh.rotationQuaternion);
+            }
+            if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
+                if (useRightHandedSystem) {
+                    node.rotation = rotationQuaternion.asArray();
+                }
+                else {
+                    node.rotation = [-rotationQuaternion.x, -rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w];
+                }
+            }
+        };
+        /**
+         * Sets data for the primitive attributes of each submesh
+         * @param mesh
+         * @param babylonMesh
+         * @param byteOffset
+         * @param useRightHandedSystem
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength of the primitive attributes plus the passed in byteOffset
+         */
+        _GLTF2Exporter.prototype.setPrimitiveAttributes = function (mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
+            // go through all mesh primitives (submeshes)
+            for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                var bufferMesh = null;
+                var submesh = babylonMesh.subMeshes[j];
+                var meshPrimitive = { attributes: {} };
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = babylonMesh;
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = babylonMesh.sourceMesh;
+                }
+                // Loop through each attribute of the submesh (mesh primitive)
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                    var positionVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                    var positionVertexBufferOffset = positionVertexBuffer.getOffset();
+                    var positions = positionVertexBuffer.getData();
+                    var positionStrideSize = positionVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.PositionKind, submesh, positions, positionStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 12;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var result = this.calculateMinMax(positions, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset, positionStrideSize);
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Position", "VEC3", 5126, submesh.verticesCount, result.min, result.max);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.POSITION = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                    var normalVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                    var normals = normalVertexBuffer.getData();
+                    var normalStrideSize = normalVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.NormalKind, submesh, normals, normalStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 12;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Normal", "VEC3", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                    var tangentVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                    var tangents = tangentVertexBuffer.getData();
+                    var tangentStrideSize = tangentVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.TangentKind, submesh, tangents, tangentStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 16;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Tangent", "VEC4", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                    var colorVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                    var colors = colorVertexBuffer.getData();
+                    var colorStrideSize = colorVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.ColorKind, submesh, colors, colorStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 16;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Color", "VEC4", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                    var texCoord0VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                    var texCoords0 = texCoord0VertexBuffer.getData();
+                    var texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UVKind, submesh, texCoords0, texCoord0StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 8;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                    var texCoord1VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                    var texCoords1 = texCoord1VertexBuffer.getData();
+                    var texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UV2Kind, submesh, texCoords1, texCoord1StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 8;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.getTotalIndices() > 0) {
+                    if (dataBuffer) {
+                        var indices = bufferMesh.getIndices();
+                        var start = submesh.indexStart;
+                        var end = submesh.indexCount + start;
+                        var byteOff = byteOffset;
+                        for (var k = start; k < end; k = k + 3) {
+                            dataBuffer.setUint32(byteOff, indices[k], true);
+                            byteOff += 4;
+                            dataBuffer.setUint32(byteOff, indices[k + 1], true);
+                            byteOff += 4;
+                            dataBuffer.setUint32(byteOff, indices[k + 2], true);
+                            byteOff += 4;
+                        }
+                        var byteLength = submesh.indexCount * 4;
+                        byteOffset += byteLength;
+                    }
+                    else {
+                        // Create bufferview
+                        var indicesCount = submesh.indexCount;
+                        var byteLength = indicesCount * 4;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Indices", "SCALAR", 5125, indicesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.indices = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.material) {
+                    //TODO: Implement Material
+                }
+                mesh.primitives.push(meshPrimitive);
+            }
+            return byteOffset;
+        };
+        /**
+         * Creates a glTF scene based on the array of meshes.
+         * Returns the the total byte offset.
+         * @param gltf
+         * @param byteOffset
+         * @param buffer
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength + byteoffset
+         */
+        _GLTF2Exporter.prototype.createScene = function (babylonScene, byteOffset, dataBuffer) {
+            if (babylonScene.meshes.length > 0) {
+                var babylonMeshes = babylonScene.meshes;
+                var scene = { nodes: new Array() };
+                for (var i = 0; i < babylonMeshes.length; ++i) {
+                    // create node to hold translation/rotation/scale and the mesh
+                    var node = { mesh: -1 };
+                    var babylonMesh = babylonMeshes[i];
+                    var useRightHandedSystem = babylonMesh.getScene().useRightHandedSystem;
+                    // Set transformation
+                    this.setNodeTransformation(node, babylonMesh, useRightHandedSystem);
+                    // create mesh
+                    var mesh = { primitives: new Array() };
+                    mesh.primitives = [];
+                    byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                    // go through all mesh primitives (submeshes)
+                    this.meshes.push(mesh);
+                    node.mesh = this.meshes.length - 1;
+                    if (babylonMesh.name) {
+                        node.name = babylonMesh.name;
+                    }
+                    this.nodes.push(node);
+                    scene.nodes.push(this.nodes.length - 1);
+                }
+                this.scenes.push(scene);
+            }
+            return byteOffset;
+        };
+        return _GLTF2Exporter;
+    }());
+    BABYLON._GLTF2Exporter = _GLTF2Exporter;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFExporter.js.map

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


+ 650 - 0
dist/preview release/serializers/babylonjs.serializers.js

@@ -143,6 +143,656 @@ var BABYLON;
 //# sourceMappingURL=babylon.objSerializer.js.map
 //# sourceMappingURL=babylon.objSerializer.js.map
 
 
 
 
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2Export = /** @class */ (function () {
+        function GLTF2Export() {
+        }
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
+         * as keys and their data and paths as values.
+         */
+        GLTF2Export.GLTF = function (scene, filename) {
+            var glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            var gltfGenerator = new BABYLON._GLTF2Exporter(scene);
+            return gltfGenerator._generateGLTF(glTFPrefix);
+        };
+        /**
+         *
+         * @param meshes
+         * @param filename
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
+         */
+        GLTF2Export.GLB = function (scene, filename) {
+            var glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            var gltfGenerator = new BABYLON._GLTF2Exporter(scene);
+            return gltfGenerator._generateGLB(glTFPrefix);
+        };
+        /**
+         * Downloads data from glTF object.
+         *
+         * @param gltfData glTF object with keys being file names and values being data
+         */
+        GLTF2Export.downloadFiles = function (gltfData) {
+            /**
+             * Checks for a matching suffix at the end of a string (for ES5 and lower)
+             * @param str
+             * @param suffix
+             *
+             * @returns {boolean} indicating whether the suffix matches or not
+             */
+            function endsWith(str, suffix) {
+                return str.indexOf(suffix, str.length - suffix.length) !== -1;
+            }
+            for (var key in gltfData) {
+                var link = document.createElement('a');
+                document.body.appendChild(link);
+                link.setAttribute("type", "hidden");
+                link.download = key;
+                var blob = gltfData[key];
+                var mimeType = void 0;
+                if (endsWith(key, ".glb")) {
+                    mimeType = { type: "model/gltf-binary" };
+                }
+                else if (endsWith(key, ".bin")) {
+                    mimeType = { type: "application/octet-stream" };
+                }
+                else if (endsWith(key, ".gltf")) {
+                    mimeType = { type: "model/gltf+json" };
+                }
+                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
+                link.click();
+            }
+        };
+        return GLTF2Export;
+    }());
+    BABYLON.GLTF2Export = GLTF2Export;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFSerializer.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    var _GLTF2Exporter = /** @class */ (function () {
+        function _GLTF2Exporter(babylonScene) {
+            this.asset = { generator: "BabylonJS", version: "2.0" };
+            this.babylonScene = babylonScene;
+            this.bufferViews = new Array();
+            this.accessors = new Array();
+            this.meshes = new Array();
+            this.scenes = new Array();
+            this.nodes = new Array();
+            var totalByteLength = 0;
+            totalByteLength = this.createScene(this.babylonScene, totalByteLength);
+            this.totalByteLength = totalByteLength;
+        }
+        /**
+         * Creates a buffer view based on teh supplied arguments
+         * @param bufferIndex
+         * @param byteOffset
+         * @param byteLength
+         *
+         * @returns {_IGLTFBufferView}
+         */
+        _GLTF2Exporter.prototype.createBufferView = function (bufferIndex, byteOffset, byteLength) {
+            var bufferview = { buffer: bufferIndex, byteLength: byteLength };
+            if (byteOffset > 0) {
+                bufferview.byteOffset = byteOffset;
+            }
+            return bufferview;
+        };
+        /**
+         * Creates an accessor based on the supplied arguments
+         * @param bufferviewIndex
+         * @param name
+         * @param type
+         * @param componentType
+         * @param count
+         * @param min
+         * @param max
+         *
+         * @returns {_IGLTFAccessor}
+         */
+        _GLTF2Exporter.prototype.createAccessor = function (bufferviewIndex, name, type, componentType, count, min, max) {
+            var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
+            if (min) {
+                accessor.min = min;
+            }
+            if (max) {
+                accessor.max = max;
+            }
+            return accessor;
+        };
+        /**
+         * Calculates the minimum and maximum values of an array of floats, based on stride
+         * @param buff
+         * @param vertexStart
+         * @param vertexCount
+         * @param arrayOffset
+         * @param stride
+         *
+         * @returns {min: number[], max: number[]} min number array and max number array
+         */
+        _GLTF2Exporter.prototype.calculateMinMax = function (buff, vertexStart, vertexCount, arrayOffset, stride) {
+            var min = [Infinity, Infinity, Infinity];
+            var max = [-Infinity, -Infinity, -Infinity];
+            var end = vertexStart + vertexCount;
+            if (vertexCount > 0) {
+                for (var i = vertexStart; i < end; ++i) {
+                    var index = stride * i;
+                    for (var j = 0; j < stride; ++j) {
+                        if (buff[index] < min[j]) {
+                            min[j] = buff[index];
+                        }
+                        if (buff[index] > max[j]) {
+                            max[j] = buff[index];
+                        }
+                        ++index;
+                    }
+                }
+            }
+            return { min: min, max: max };
+        };
+        /**
+         * Write mesh attribute data to buffer.
+         * Returns the bytelength of the data.
+         * @param vertexBufferType
+         * @param submesh
+         * @param meshAttributeArray
+         * @param strideSize
+         * @param byteOffset
+         * @param dataBuffer
+         * @param useRightHandedSystem
+         *
+         * @returns {number} byte length
+         */
+        _GLTF2Exporter.prototype.writeAttributeData = function (vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem) {
+            var byteOff = byteOffset;
+            var end = submesh.verticesStart + submesh.verticesCount;
+            var byteLength = 0;
+            switch (vertexBufferType) {
+                case BABYLON.VertexBuffer.PositionKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        if (useRightHandedSystem) {
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        }
+                        else {
+                            dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                        }
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 12;
+                    break;
+                }
+                case BABYLON.VertexBuffer.NormalKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        if (useRightHandedSystem) {
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        }
+                        else {
+                            dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                        }
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 12;
+                    break;
+                }
+                case BABYLON.VertexBuffer.TangentKind: {
+                    for (var k = submesh.indexStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        if (useRightHandedSystem) {
+                            dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        }
+                        else {
+                            dataBuffer.setFloat32(byteOff, -meshAttributeArray[index + 2], true);
+                        }
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 16;
+                    break;
+                }
+                case BABYLON.VertexBuffer.ColorKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 2], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 3], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 16;
+                    break;
+                }
+                case BABYLON.VertexBuffer.UVKind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 8;
+                    break;
+                }
+                case BABYLON.VertexBuffer.UV2Kind: {
+                    for (var k = submesh.verticesStart; k < end; ++k) {
+                        var index = k * strideSize;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index], true);
+                        byteOff += 4;
+                        dataBuffer.setFloat32(byteOff, meshAttributeArray[index + 1], true);
+                        byteOff += 4;
+                    }
+                    byteLength = submesh.verticesCount * 8;
+                    break;
+                }
+                default: {
+                    throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                }
+            }
+            return byteLength;
+        };
+        /**
+         * Generates glTF json data
+         * @param glb
+         * @param glTFPrefix
+         * @param prettyPrint
+         *
+         * @returns {string} json data as string
+         */
+        _GLTF2Exporter.prototype.generateJSON = function (glb, glTFPrefix, prettyPrint) {
+            var buffer = { byteLength: this.totalByteLength };
+            var glTf = {
+                buffers: [buffer],
+                asset: this.asset,
+                meshes: this.meshes,
+                scenes: this.scenes,
+                nodes: this.nodes,
+                bufferViews: this.bufferViews,
+                accessors: this.accessors
+            };
+            if (this.scenes.length > 0) {
+                glTf.scene = 0;
+            }
+            if (!glb) {
+                buffer.uri = glTFPrefix + ".bin";
+            }
+            var jsonText = prettyPrint ? JSON.stringify(glTf, null, 2) : JSON.stringify(glTf);
+            return jsonText;
+        };
+        /**
+         * Generates data for .gltf and .bin files based on the glTF prefix string
+         * @param glTFPrefix
+         *
+         * @returns {[x: string]: string | Blob} object with glTF json tex filename
+         * and binary file name as keys and their data as values
+         */
+        _GLTF2Exporter.prototype._generateGLTF = function (glTFPrefix) {
+            var jsonText = this.generateJSON(false, glTFPrefix, true);
+            var binaryBuffer = this.generateBinary();
+            var bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
+            var glTFFileName = glTFPrefix + '.gltf';
+            var glTFBinFile = glTFPrefix + '.bin';
+            return _a = {},
+                _a[glTFFileName] = jsonText,
+                _a[glTFBinFile] = bin,
+                _a;
+            var _a;
+        };
+        /**
+         * Creates a binary buffer for glTF
+         *
+         * @returns {ArrayBuffer}
+         */
+        _GLTF2Exporter.prototype.generateBinary = function () {
+            var byteOffset = 0;
+            var binaryBuffer = new ArrayBuffer(this.totalByteLength);
+            var dataBuffer = new DataView(binaryBuffer);
+            byteOffset = this.createScene(this.babylonScene, byteOffset, dataBuffer);
+            return binaryBuffer;
+        };
+        /**
+         * Generates a glb file from the json and binary data.
+         * Returns an object with the glb file name as the key and data as the value.
+         * @param jsonText
+         * @param binaryBuffer
+         * @param glTFPrefix
+         *
+         * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
+         */
+        _GLTF2Exporter.prototype._generateGLB = function (glTFPrefix) {
+            var jsonText = this.generateJSON(true);
+            var binaryBuffer = this.generateBinary();
+            var glbFileName = glTFPrefix + '.glb';
+            var headerLength = 12;
+            var chunkLengthPrefix = 8;
+            var jsonLength = jsonText.length;
+            var jsonRemainder = jsonLength % 4;
+            var binRemainder = binaryBuffer.byteLength % 4;
+            var jsonPadding = jsonRemainder === 0 ? jsonRemainder : 4 - jsonRemainder;
+            var binPadding = binRemainder === 0 ? binRemainder : 4 - binRemainder;
+            var byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding;
+            //header
+            var headerBuffer = new ArrayBuffer(headerLength);
+            var headerBufferView = new DataView(headerBuffer);
+            headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+            headerBufferView.setUint32(4, 2, true); // version
+            headerBufferView.setUint32(8, byteLength, true); // total bytes in file
+            //json chunk
+            var jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+            var jsonChunkBufferView = new DataView(jsonChunkBuffer);
+            jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+            jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+            //json chunk bytes
+            var jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+            for (var i = 0; i < jsonLength; ++i) {
+                jsonData[i] = jsonText.charCodeAt(i);
+            }
+            //json padding
+            var jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+            for (var i = 0; i < jsonPadding; ++i) {
+                jsonPaddingView[i] = 0x20;
+            }
+            //binary chunk
+            var binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+            var binaryChunkBufferView = new DataView(binaryChunkBuffer);
+            binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength, true);
+            binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+            // binary padding
+            var binPaddingBuffer = new ArrayBuffer(binPadding);
+            var binPaddingView = new Uint8Array(binPaddingBuffer);
+            for (var i = 0; i < binPadding; ++i) {
+                binPaddingView[i] = 0;
+            }
+            // binary data
+            var glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
+            return _a = {},
+                _a[glbFileName] = glbFile,
+                _a;
+            var _a;
+        };
+        /**
+         * Sets the TRS for each node
+         * @param node
+         * @param babylonMesh
+         * @param useRightHandedSystem
+         */
+        _GLTF2Exporter.prototype.setNodeTransformation = function (node, babylonMesh, useRightHandedSystem) {
+            if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                if (useRightHandedSystem) {
+                    node.translation = babylonMesh.position.asArray();
+                }
+                else {
+                    node.translation = [babylonMesh.position.x, babylonMesh.position.y, -babylonMesh.position.z];
+                }
+            }
+            if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                if (useRightHandedSystem) {
+                    node.scale = babylonMesh.scaling.asArray();
+                }
+                else {
+                    node.scale = [babylonMesh.scaling.x, babylonMesh.scaling.y, -babylonMesh.scaling.z];
+                }
+            }
+            var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
+            if (babylonMesh.rotationQuaternion) {
+                rotationQuaternion = rotationQuaternion.multiply(babylonMesh.rotationQuaternion);
+            }
+            if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
+                if (useRightHandedSystem) {
+                    node.rotation = rotationQuaternion.asArray();
+                }
+                else {
+                    node.rotation = [-rotationQuaternion.x, -rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w];
+                }
+            }
+        };
+        /**
+         * Sets data for the primitive attributes of each submesh
+         * @param mesh
+         * @param babylonMesh
+         * @param byteOffset
+         * @param useRightHandedSystem
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength of the primitive attributes plus the passed in byteOffset
+         */
+        _GLTF2Exporter.prototype.setPrimitiveAttributes = function (mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer) {
+            // go through all mesh primitives (submeshes)
+            for (var j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                var bufferMesh = null;
+                var submesh = babylonMesh.subMeshes[j];
+                var meshPrimitive = { attributes: {} };
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = babylonMesh;
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = babylonMesh.sourceMesh;
+                }
+                // Loop through each attribute of the submesh (mesh primitive)
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                    var positionVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                    var positionVertexBufferOffset = positionVertexBuffer.getOffset();
+                    var positions = positionVertexBuffer.getData();
+                    var positionStrideSize = positionVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.PositionKind, submesh, positions, positionStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 12;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var result = this.calculateMinMax(positions, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset, positionStrideSize);
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Position", "VEC3", 5126, submesh.verticesCount, result.min, result.max);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.POSITION = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                    var normalVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                    var normals = normalVertexBuffer.getData();
+                    var normalStrideSize = normalVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.NormalKind, submesh, normals, normalStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 12;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Normal", "VEC3", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                    var tangentVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                    var tangents = tangentVertexBuffer.getData();
+                    var tangentStrideSize = tangentVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.TangentKind, submesh, tangents, tangentStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 16;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Tangent", "VEC4", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                    var colorVertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                    var colors = colorVertexBuffer.getData();
+                    var colorStrideSize = colorVertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.ColorKind, submesh, colors, colorStrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 16;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Color", "VEC4", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                    var texCoord0VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                    var texCoords0 = texCoord0VertexBuffer.getData();
+                    var texCoord0StrideSize = texCoord0VertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UVKind, submesh, texCoords0, texCoord0StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 8;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                    var texCoord1VertexBuffer = bufferMesh.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                    var texCoords1 = texCoord1VertexBuffer.getData();
+                    var texCoord1StrideSize = texCoord1VertexBuffer.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(BABYLON.VertexBuffer.UV2Kind, submesh, texCoords1, texCoord1StrideSize, byteOffset, dataBuffer, useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        var byteLength = submesh.verticesCount * 8;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.getTotalIndices() > 0) {
+                    if (dataBuffer) {
+                        var indices = bufferMesh.getIndices();
+                        var start = submesh.indexStart;
+                        var end = submesh.indexCount + start;
+                        var byteOff = byteOffset;
+                        for (var k = start; k < end; k = k + 3) {
+                            dataBuffer.setUint32(byteOff, indices[k], true);
+                            byteOff += 4;
+                            dataBuffer.setUint32(byteOff, indices[k + 1], true);
+                            byteOff += 4;
+                            dataBuffer.setUint32(byteOff, indices[k + 2], true);
+                            byteOff += 4;
+                        }
+                        var byteLength = submesh.indexCount * 4;
+                        byteOffset += byteLength;
+                    }
+                    else {
+                        // Create bufferview
+                        var indicesCount = submesh.indexCount;
+                        var byteLength = indicesCount * 4;
+                        var bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+                        // Create accessor
+                        var accessor = this.createAccessor(this.bufferViews.length - 1, "Indices", "SCALAR", 5125, indicesCount);
+                        this.accessors.push(accessor);
+                        meshPrimitive.indices = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh.material) {
+                    //TODO: Implement Material
+                }
+                mesh.primitives.push(meshPrimitive);
+            }
+            return byteOffset;
+        };
+        /**
+         * Creates a glTF scene based on the array of meshes.
+         * Returns the the total byte offset.
+         * @param gltf
+         * @param byteOffset
+         * @param buffer
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength + byteoffset
+         */
+        _GLTF2Exporter.prototype.createScene = function (babylonScene, byteOffset, dataBuffer) {
+            if (babylonScene.meshes.length > 0) {
+                var babylonMeshes = babylonScene.meshes;
+                var scene = { nodes: new Array() };
+                for (var i = 0; i < babylonMeshes.length; ++i) {
+                    // create node to hold translation/rotation/scale and the mesh
+                    var node = { mesh: -1 };
+                    var babylonMesh = babylonMeshes[i];
+                    var useRightHandedSystem = babylonMesh.getScene().useRightHandedSystem;
+                    // Set transformation
+                    this.setNodeTransformation(node, babylonMesh, useRightHandedSystem);
+                    // create mesh
+                    var mesh = { primitives: new Array() };
+                    mesh.primitives = [];
+                    byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                    // go through all mesh primitives (submeshes)
+                    this.meshes.push(mesh);
+                    node.mesh = this.meshes.length - 1;
+                    if (babylonMesh.name) {
+                        node.name = babylonMesh.name;
+                    }
+                    this.nodes.push(node);
+                    scene.nodes.push(this.nodes.length - 1);
+                }
+                this.scenes.push(scene);
+            }
+            return byteOffset;
+        };
+        return _GLTF2Exporter;
+    }());
+    BABYLON._GLTF2Exporter = _GLTF2Exporter;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.glTFExporter.js.map
+
+
 (function universalModuleDefinition(root, factory) {
 (function universalModuleDefinition(root, factory) {
                 var f = factory();
                 var f = factory();
                 if (root && root["BABYLON"]) {
                 if (root && root["BABYLON"]) {

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


+ 163 - 0
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -10,3 +10,166 @@ declare module BABYLON {
         static MTL(mesh: Mesh): string;
         static MTL(mesh: Mesh): string;
     }
     }
 }
 }
+
+
+declare module BABYLON {
+    class GLTF2Export {
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes
+         * @param materials
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
+         * as keys and their data and paths as values.
+         */
+        static GLTF(scene: BABYLON.Scene, filename: string): {
+            [fileName: string]: string | Blob;
+        };
+        /**
+         *
+         * @param meshes
+         * @param filename
+         *
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
+         */
+        static GLB(scene: BABYLON.Scene, filename: string): {
+            [fileName: string]: string | Blob;
+        };
+        /**
+         * Downloads data from glTF object.
+         *
+         * @param gltfData glTF object with keys being file names and values being data
+         */
+        static downloadFiles(gltfData: {
+            [fileName: string]: string | Blob;
+        }): void;
+    }
+}
+
+declare module BABYLON {
+    class _GLTF2Exporter {
+        private bufferViews;
+        private accessors;
+        private nodes;
+        private asset;
+        private scenes;
+        private meshes;
+        private totalByteLength;
+        private babylonScene;
+        constructor(babylonScene: BABYLON.Scene);
+        /**
+         * Creates a buffer view based on teh supplied arguments
+         * @param bufferIndex
+         * @param byteOffset
+         * @param byteLength
+         *
+         * @returns {_IGLTFBufferView}
+         */
+        private createBufferView(bufferIndex, byteOffset, byteLength);
+        /**
+         * Creates an accessor based on the supplied arguments
+         * @param bufferviewIndex
+         * @param name
+         * @param type
+         * @param componentType
+         * @param count
+         * @param min
+         * @param max
+         *
+         * @returns {_IGLTFAccessor}
+         */
+        private createAccessor(bufferviewIndex, name, type, componentType, count, min?, max?);
+        /**
+         * Calculates the minimum and maximum values of an array of floats, based on stride
+         * @param buff
+         * @param vertexStart
+         * @param vertexCount
+         * @param arrayOffset
+         * @param stride
+         *
+         * @returns {min: number[], max: number[]} min number array and max number array
+         */
+        private calculateMinMax(buff, vertexStart, vertexCount, arrayOffset, stride);
+        /**
+         * Write mesh attribute data to buffer.
+         * Returns the bytelength of the data.
+         * @param vertexBufferType
+         * @param submesh
+         * @param meshAttributeArray
+         * @param strideSize
+         * @param byteOffset
+         * @param dataBuffer
+         * @param useRightHandedSystem
+         *
+         * @returns {number} byte length
+         */
+        private writeAttributeData(vertexBufferType, submesh, meshAttributeArray, strideSize, byteOffset, dataBuffer, useRightHandedSystem);
+        /**
+         * Generates glTF json data
+         * @param glb
+         * @param glTFPrefix
+         * @param prettyPrint
+         *
+         * @returns {string} json data as string
+         */
+        private generateJSON(glb, glTFPrefix?, prettyPrint?);
+        /**
+         * Generates data for .gltf and .bin files based on the glTF prefix string
+         * @param glTFPrefix
+         *
+         * @returns {[x: string]: string | Blob} object with glTF json tex filename
+         * and binary file name as keys and their data as values
+         */
+        _generateGLTF(glTFPrefix: string): {
+            [x: string]: string | Blob;
+        };
+        /**
+         * Creates a binary buffer for glTF
+         *
+         * @returns {ArrayBuffer}
+         */
+        private generateBinary();
+        /**
+         * Generates a glb file from the json and binary data.
+         * Returns an object with the glb file name as the key and data as the value.
+         * @param jsonText
+         * @param binaryBuffer
+         * @param glTFPrefix
+         *
+         * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
+         */
+        _generateGLB(glTFPrefix: string): {
+            [glbFileName: string]: Blob;
+        };
+        /**
+         * Sets the TRS for each node
+         * @param node
+         * @param babylonMesh
+         * @param useRightHandedSystem
+         */
+        private setNodeTransformation(node, babylonMesh, useRightHandedSystem);
+        /**
+         * Sets data for the primitive attributes of each submesh
+         * @param mesh
+         * @param babylonMesh
+         * @param byteOffset
+         * @param useRightHandedSystem
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength of the primitive attributes plus the passed in byteOffset
+         */
+        private setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer?);
+        /**
+         * Creates a glTF scene based on the array of meshes.
+         * Returns the the total byte offset.
+         * @param gltf
+         * @param byteOffset
+         * @param buffer
+         * @param dataBuffer
+         *
+         * @returns {number} bytelength + byteoffset
+         */
+        private createScene(babylonScene, byteOffset, dataBuffer?);
+    }
+}

+ 1 - 1
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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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


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

@@ -4,7 +4,7 @@
     },
     },
     "name": "babylonjs-viewer",
     "name": "babylonjs-viewer",
     "description": "A simple-to-use viewer based on BabylonJS to display 3D elements natively",
     "description": "A simple-to-use viewer based on BabylonJS to display 3D elements natively",
-    "version": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -2,11 +2,17 @@
 
 
 ## Major updates
 ## Major updates
 - Introduced texture binding atlas. This optimization allows the engine to reuse texture bindings instead of rebinding textures when they are not on constant sampler indexes ([deltakosh](https://github.com/deltakosh))
 - Introduced texture binding atlas. This optimization allows the engine to reuse texture bindings instead of rebinding textures when they are not on constant sampler indexes ([deltakosh](https://github.com/deltakosh))
+- New [AnimationGroup class](http://doc.babylonjs.com/how_to/group) to control simultaneously multiple animations with different targets ([deltakosh](https://github.com/deltakosh))
 
 
 ## Updates
 ## Updates
 - New watcher configuration for VSCode. Now the task only compiles changed files ([sebavan](https://github.com/sebavan))
 - New watcher configuration for VSCode. Now the task only compiles changed files ([sebavan](https://github.com/sebavan))
 - Added new draw modes to engine (points, lines, linesloop, linestrip, trianglestrip, trianglefan) ([benaadams](https://github.com/benaadams))
 - Added new draw modes to engine (points, lines, linesloop, linestrip, trianglestrip, trianglefan) ([benaadams](https://github.com/benaadams))
 - Added GUI Textblock.lineSpacing setter and getter to configure vertical space between lines in pixels or percentage values when working with text wrapping ([carloslanderas](github.com/carloslanderas))
 - Added GUI Textblock.lineSpacing setter and getter to configure vertical space between lines in pixels or percentage values when working with text wrapping ([carloslanderas](github.com/carloslanderas))
+- VRHelper now has onSelectedMeshUnselected observable that will notify observers when the current selected mesh gets unselected
+  ([carloslanderas](github.com/carloslanderas))
+- VRHelper now has onBeforeCameraTeleport and onAfterCameraTeleport observables that will be notified before and after camera teleportation is triggered.
+  ([carloslanderas](github.com/carloslanderas))
+  
 
 
 ## Bug fixes
 ## Bug fixes
 
 

+ 7 - 0
inspector/src/tabs/StatsTab.ts

@@ -86,6 +86,13 @@ module INSPECTOR {
                     updateFct: () => { return this._sceneInstrumentation!.drawCallsCounter.current.toString() }
                     updateFct: () => { return this._sceneInstrumentation!.drawCallsCounter.current.toString() }
                 });
                 });
 
 
+                this._createStatLabel("Texture collisions", this._panel);
+                elemValue = Helpers.CreateDiv('stat-value', this._panel);
+                this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: () => { return this._sceneInstrumentation!.textureCollisionsCounter.current.toString() }
+                });
+
                 this._createStatLabel("Total lights", this._panel);
                 this._createStatLabel("Total lights", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({
                 this._updatableProperties.push({

+ 19 - 7
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -1557,11 +1557,23 @@ module BABYLON.GLTF1 {
             GLTFLoader.Extensions[extension.name] = extension;
             GLTFLoader.Extensions[extension.name] = extension;
         }
         }
 
 
-        public dispose(): void {
-            // do nothing
-        }
-
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): boolean {
+        // #region Stubs for IGLTFLoader interface
+        public coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+        public animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+        public compileMaterials = false;
+        public useClipPlane = false;
+        public compileShadowGenerators = false;
+
+        public onDisposeObservable = new Observable<IGLTFLoader>();
+        public onMeshLoadedObservable = new Observable<AbstractMesh>();
+        public onTextureLoadedObservable = new Observable<BaseTexture>();
+        public onMaterialLoadedObservable = new Observable<Material>();
+        public onCompleteObservable = new Observable<IGLTFLoader>();
+
+        public dispose(): void {}
+        // #endregion
+
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): boolean {
             scene.useRightHandedSystem = true;
             scene.useRightHandedSystem = true;
 
 
             GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
             GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
@@ -1624,7 +1636,7 @@ module BABYLON.GLTF1 {
             return true;
             return true;
         }
         }
 
 
-        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void {
+        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: SceneLoaderProgressEvent) => void, onError: (message: string) => void): void {
             scene.useRightHandedSystem = true;
             scene.useRightHandedSystem = true;
 
 
             GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
             GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
@@ -1688,7 +1700,7 @@ module BABYLON.GLTF1 {
             }
             }
         };
         };
 
 
-        private _loadBuffersAsync(gltfRuntime: IGLTFRuntime, onLoad: () => void, onProgress?: (event: ProgressEvent) => void): void {
+        private _loadBuffersAsync(gltfRuntime: IGLTFRuntime, onLoad: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void): void {
             var hasBuffers = false;
             var hasBuffers = false;
 
 
             var processBuffer = (buf: string, buffer: IGLTFBuffer) => {
             var processBuffer = (buf: string, buffer: IGLTFBuffer) => {

+ 62 - 72
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -33,7 +33,7 @@ module BABYLON.GLTF2 {
         readonly BYTES_PER_ELEMENT: number;
         readonly BYTES_PER_ELEMENT: number;
     }
     }
 
 
-    interface GLTFLoaderRequest extends XMLHttpRequest {
+    interface IGLTFLoaderFileRequest extends IFileRequest {
         _lengthComputable?: boolean;
         _lengthComputable?: boolean;
         _loaded?: number;
         _loaded?: number;
         _total?: number;
         _total?: number;
@@ -44,16 +44,15 @@ module BABYLON.GLTF2 {
         public _babylonScene: Scene;
         public _babylonScene: Scene;
 
 
         private _disposed = false;
         private _disposed = false;
-        private _parent: GLTFFileLoader;
         private _rootUrl: string;
         private _rootUrl: string;
         private _defaultMaterial: PBRMaterial;
         private _defaultMaterial: PBRMaterial;
         private _defaultSampler = {} as IGLTFSampler;
         private _defaultSampler = {} as IGLTFSampler;
         private _rootNode: IGLTFNode;
         private _rootNode: IGLTFNode;
         private _successCallback?: () => void;
         private _successCallback?: () => void;
-        private _progressCallback?: (event: ProgressEvent) => void;
+        private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
         private _errorCallback?: (message: string, exception?: any) => void;
         private _errorCallback?: (message: string, exception?: any) => void;
         private _renderReady = false;
         private _renderReady = false;
-        private _requests = new Array<GLTFLoaderRequest>();
+        private _requests = new Array<IGLTFLoaderFileRequest>();
 
 
         private _renderReadyObservable = new Observable<GLTFLoader>();
         private _renderReadyObservable = new Observable<GLTFLoader>();
 
 
@@ -79,25 +78,17 @@ module BABYLON.GLTF2 {
             GLTFLoaderExtension._Extensions.push(extension);
             GLTFLoaderExtension._Extensions.push(extension);
         }
         }
 
 
-        // IE 11 Compatibility.
-        private static _createProgressEvent: (lengthComputable: boolean, loaded: number, total: number) => ProgressEvent =
-            (typeof (<any>window)["ProgressEvent"] === "function")
-                ? (lengthComputable, loaded, total) => {
-                    return new ProgressEvent("GLTFLoaderProgress", {
-                        lengthComputable: lengthComputable,
-                        loaded: loaded,
-                        total: total
-                    });
-                }
-                : (lengthComputable, loaded, total) => {
-                    const event = document.createEvent("ProgressEvent");
-                    event.initProgressEvent("GLTFLoaderProgress", false, false, lengthComputable, loaded, total);
-                    return event;
-                };
+        public coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
+        public animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
+        public compileMaterials = false;
+        public useClipPlane = false;
+        public compileShadowGenerators = false;
 
 
-        public constructor(parent: GLTFFileLoader) {
-            this._parent = parent;
-        }
+        public onDisposeObservable = new Observable<IGLTFLoader>();
+        public onMeshLoadedObservable = new Observable<AbstractMesh>();
+        public onTextureLoadedObservable = new Observable<BaseTexture>();
+        public onMaterialLoadedObservable = new Observable<Material>();
+        public onCompleteObservable = new Observable<IGLTFLoader>();
 
 
         public dispose(): void {
         public dispose(): void {
             if (this._disposed) {
             if (this._disposed) {
@@ -106,24 +97,13 @@ module BABYLON.GLTF2 {
 
 
             this._disposed = true;
             this._disposed = true;
 
 
-            // Abort requests that are not complete
-            for (const request of this._requests) {
-                if (request.readyState !== (XMLHttpRequest.DONE || 4)) {
-                    request.abort();
-                }
-            }
+            this._abortRequests();
+            this._releaseResources();
 
 
-            // Revoke object urls created during load
-            if (this._gltf.textures) {
-                for (const texture of this._gltf.textures) {
-                    if (texture.url) {
-                        URL.revokeObjectURL(texture.url);
-                    }
-                }
-            }
+            this.onDisposeObservable.notifyObservers(this);
         }
         }
 
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
             this._loadAsync(meshesNames, scene, data, rootUrl, () => {
             this._loadAsync(meshesNames, scene, data, rootUrl, () => {
                 if (onSuccess) {
                 if (onSuccess) {
                     onSuccess(this._getMeshes(), [], this._getSkeletons());
                     onSuccess(this._getMeshes(), [], this._getSkeletons());
@@ -131,11 +111,11 @@ module BABYLON.GLTF2 {
             }, onProgress, onError);
             }, onProgress, onError);
         }
         }
 
 
-        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
+        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
             this._loadAsync(null, scene, data, rootUrl, onSuccess, onProgress, onError);
             this._loadAsync(null, scene, data, rootUrl, onSuccess, onProgress, onError);
         }
         }
 
 
-        private _loadAsync(nodeNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
+        private _loadAsync(nodeNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
             this._babylonScene = scene;
             this._babylonScene = scene;
             this._rootUrl = rootUrl;
             this._rootUrl = rootUrl;
             this._successCallback = onSuccess;
             this._successCallback = onSuccess;
@@ -170,7 +150,7 @@ module BABYLON.GLTF2 {
                 total += request._total;
                 total += request._total;
             }
             }
 
 
-            this._progressCallback(GLTFLoader._createProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
+            this._progressCallback(new SceneLoaderProgressEvent(lengthComputable, loaded, lengthComputable ? total : 0));
         }
         }
 
 
         public _executeWhenRenderReady(func: () => void): void {
         public _executeWhenRenderReady(func: () => void): void {
@@ -195,11 +175,10 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private _onComplete(): void {
         private _onComplete(): void {
-            if (this._parent.onComplete) {
-                this._parent.onComplete();
-            }
+            this._abortRequests();
+            this._releaseResources();
 
 
-            this.dispose();
+            this.onCompleteObservable.notifyObservers(this);
         }
         }
 
 
         private _loadData(data: IGLTFLoaderData): void {
         private _loadData(data: IGLTFLoaderData): void {
@@ -274,7 +253,7 @@ module BABYLON.GLTF2 {
                 return;
                 return;
             }
             }
 
 
-            switch (this._parent.animationStartMode) {
+            switch (this.animationStartMode) {
                 case GLTFLoaderAnimationStartMode.NONE: {
                 case GLTFLoaderAnimationStartMode.NONE: {
                     // do nothing
                     // do nothing
                     break;
                     break;
@@ -295,7 +274,7 @@ module BABYLON.GLTF2 {
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
-                    Tools.Error("Invalid animation start mode " + this._parent.animationStartMode);
+                    Tools.Error("Invalid animation start mode " + this.animationStartMode);
                     return;
                     return;
                 }
                 }
             }
             }
@@ -313,7 +292,7 @@ module BABYLON.GLTF2 {
         private _loadScene(context: string, scene: IGLTFScene, nodeNames: any): void {
         private _loadScene(context: string, scene: IGLTFScene, nodeNames: any): void {
             this._rootNode = { babylonMesh: new Mesh("__root__", this._babylonScene) } as IGLTFNode;
             this._rootNode = { babylonMesh: new Mesh("__root__", this._babylonScene) } as IGLTFNode;
 
 
-            switch (this._parent.coordinateSystemMode) {
+            switch (this.coordinateSystemMode) {
                 case GLTFLoaderCoordinateSystemMode.AUTO: {
                 case GLTFLoaderCoordinateSystemMode.AUTO: {
                     if (!this._babylonScene.useRightHandedSystem) {
                     if (!this._babylonScene.useRightHandedSystem) {
                         this._rootNode.rotation = [0, 1, 0, 0];
                         this._rootNode.rotation = [0, 1, 0, 0];
@@ -327,14 +306,12 @@ module BABYLON.GLTF2 {
                     break;
                     break;
                 }
                 }
                 default: {
                 default: {
-                    Tools.Error("Invalid coordinate system mode " + this._parent.coordinateSystemMode);
+                    Tools.Error("Invalid coordinate system mode " + this.coordinateSystemMode);
                     return;
                     return;
                 }
                 }
             }
             }
 
 
-            if (this._parent.onMeshLoaded) {
-                this._parent.onMeshLoaded(this._rootNode.babylonMesh);
-            }
+            this.onMeshLoadedObservable.notifyObservers(this._rootNode.babylonMesh);
 
 
             let nodeIndices = scene.nodes;
             let nodeIndices = scene.nodes;
 
 
@@ -431,9 +408,7 @@ module BABYLON.GLTF2 {
                 }
                 }
             }
             }
 
 
-            if (this._parent.onMeshLoaded) {
-                this._parent.onMeshLoaded(node.babylonMesh);
-            }
+            this.onMeshLoadedObservable.notifyObservers(node.babylonMesh);
         }
         }
 
 
         private _loadMesh(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
         private _loadMesh(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
@@ -482,8 +457,8 @@ module BABYLON.GLTF2 {
                     }
                     }
 
 
                     this._loadMaterial("#/materials/" + material.index, material, (babylonMaterial, isNew) => {
                     this._loadMaterial("#/materials/" + material.index, material, (babylonMaterial, isNew) => {
-                        if (isNew && this._parent.onMaterialLoaded) {
-                            this._parent.onMaterialLoaded(babylonMaterial);
+                        if (isNew) {
+                            this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                         }
                         }
                         node.babylonMesh.material = babylonMaterial;
                         node.babylonMesh.material = babylonMaterial;
                     });
                     });
@@ -506,8 +481,8 @@ module BABYLON.GLTF2 {
                         }
                         }
 
 
                         this._loadMaterial("#/materials/" + material.index, material, (babylonMaterial, isNew) => {
                         this._loadMaterial("#/materials/" + material.index, material, (babylonMaterial, isNew) => {
-                            if (isNew && this._parent.onMaterialLoaded) {
-                                this._parent.onMaterialLoaded(babylonMaterial);
+                            if (isNew) {
+                                this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                             }
                             }
 
 
                             subMaterials[index] = babylonMaterial;
                             subMaterials[index] = babylonMaterial;
@@ -1575,10 +1550,7 @@ module BABYLON.GLTF2 {
             babylonTexture.wrapV = sampler.wrapV;
             babylonTexture.wrapV = sampler.wrapV;
             babylonTexture.name = texture.name || "texture" + texture.index;
             babylonTexture.name = texture.name || "texture" + texture.index;
 
 
-            if (this._parent.onTextureLoaded) {
-                this._parent.onTextureLoaded(babylonTexture);
-            }
-
+            this.onTextureLoadedObservable.notifyObservers(babylonTexture);
             return babylonTexture;
             return babylonTexture;
         }
         }
 
 
@@ -1634,13 +1606,12 @@ module BABYLON.GLTF2 {
                 this._tryCatchOnError(() => {
                 this._tryCatchOnError(() => {
                     throw new LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                     throw new LoadFileError(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""), request);
                 });
                 });
-            }, (oldRequest, newRequest) => {
-                this._requests.splice(this._requests.indexOf(oldRequest), 1, newRequest);
-            }) as GLTFLoaderRequest;
+            }) as IGLTFLoaderFileRequest;
 
 
-            if (request) {
-                this._requests.push(request);
-            }
+            this._requests.push(request);
+            request.onCompleteObservable.add(() => {
+                this._requests.splice(this._requests.indexOf(request), 1);
+            });
         }
         }
 
 
         public _tryCatchOnError(handler: () => void): void {
         public _tryCatchOnError(handler: () => void): void {
@@ -1744,7 +1715,7 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private _compileMaterialAsync(babylonMaterial: Material, babylonMesh: AbstractMesh, onSuccess: () => void): void {
         private _compileMaterialAsync(babylonMaterial: Material, babylonMesh: AbstractMesh, onSuccess: () => void): void {
-            if (this._parent.useClipPlane) {
+            if (this.useClipPlane) {
                 babylonMaterial.forceCompilation(babylonMesh, () => {
                 babylonMaterial.forceCompilation(babylonMesh, () => {
                     babylonMaterial.forceCompilation(babylonMesh, () => {
                     babylonMaterial.forceCompilation(babylonMesh, () => {
                         this._tryCatchOnError(onSuccess);
                         this._tryCatchOnError(onSuccess);
@@ -1759,7 +1730,7 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private _compileMaterialsAsync(onSuccess: () => void): void {
         private _compileMaterialsAsync(onSuccess: () => void): void {
-            if (!this._parent.compileMaterials || !this._gltf.materials) {
+            if (!this.compileMaterials || !this._gltf.materials) {
                 onSuccess();
                 onSuccess();
                 return;
                 return;
             }
             }
@@ -1808,7 +1779,7 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         private _compileShadowGeneratorsAsync(onSuccess: () => void): void {
         private _compileShadowGeneratorsAsync(onSuccess: () => void): void {
-            if (!this._parent.compileShadowGenerators) {
+            if (!this.compileShadowGenerators) {
                 onSuccess();
                 onSuccess();
                 return;
                 return;
             }
             }
@@ -1839,7 +1810,26 @@ module BABYLON.GLTF2 {
                 }
                 }
             }
             }
         }
         }
+
+        private _abortRequests(): void {
+            for (const request of this._requests) {
+                request.abort();
+            }
+
+            this._requests.length = 0;
+        }
+
+        private _releaseResources(): void {
+            if (this._gltf.textures) {
+                for (const texture of this._gltf.textures) {
+                    if (texture.url) {
+                        URL.revokeObjectURL(texture.url);
+                        texture.url = undefined;
+                    }
+                }
+            }
+        }
     }
     }
 
 
-    GLTFFileLoader.CreateGLTFLoaderV2 = parent => new GLTFLoader(parent);
+    GLTFFileLoader.CreateGLTFLoaderV2 = () => new GLTFLoader();
 }
 }

+ 112 - 32
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -36,13 +36,25 @@ module BABYLON {
     }
     }
 
 
     export interface IGLTFLoader extends IDisposable {
     export interface IGLTFLoader extends IDisposable {
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        coordinateSystemMode: GLTFLoaderCoordinateSystemMode;
+        animationStartMode: GLTFLoaderAnimationStartMode;
+        compileMaterials: boolean;
+        useClipPlane: boolean;
+        compileShadowGenerators: boolean;
+
+        onDisposeObservable: Observable<IGLTFLoader>;
+        onMeshLoadedObservable: Observable<AbstractMesh>;
+        onTextureLoadedObservable: Observable<BaseTexture>;
+        onMaterialLoadedObservable: Observable<Material>;
+        onCompleteObservable: Observable<IGLTFLoader>;
+
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
     }
     }
 
 
     export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
     export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        public static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
-        public static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
+        public static CreateGLTFLoaderV1: () => IGLTFLoader;
+        public static CreateGLTFLoaderV2: () => IGLTFLoader;
 
 
         // #region Common options
         // #region Common options
 
 
@@ -51,7 +63,15 @@ module BABYLON {
          * The data.json property stores the glTF JSON.
          * The data.json property stores the glTF JSON.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          * The data.bin property stores the BIN chunk from a glTF binary or null if the input is not a glTF binary.
          */
          */
-        public onParsed: (data: IGLTFLoaderData) => void;
+        public onParsedObservable = new Observable<IGLTFLoaderData>();
+
+        private _onParsedObserver: Nullable<Observer<IGLTFLoaderData>>;
+        public set onParsed(callback: (loaderData: IGLTFLoaderData) => void) {
+            if (this._onParsedObserver) {
+                this.onParsedObservable.remove(this._onParsedObserver);
+            }
+            this._onParsedObserver = this.onParsedObservable.add(callback);
+        }
 
 
         // #endregion
         // #endregion
 
 
@@ -93,28 +113,73 @@ module BABYLON {
         /**
         /**
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          * Raised when the loader creates a mesh after parsing the glTF properties of the mesh.
          */
          */
-        public onMeshLoaded: (mesh: AbstractMesh) => void;
+        public onMeshLoadedObservable = new Observable<AbstractMesh>();
+
+        private _onMeshLoadedObserver: Nullable<Observer<AbstractMesh>>;
+        public set onMeshLoaded(callback: (mesh: AbstractMesh) => void) {
+            if (this._onMeshLoadedObserver) {
+                this.onMeshLoadedObservable.remove(this._onMeshLoadedObserver);
+            }
+            this._onMeshLoadedObserver = this.onMeshLoadedObservable.add(callback);
+        }
 
 
         /**
         /**
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          * Raised when the loader creates a texture after parsing the glTF properties of the texture.
          */
          */
-        public onTextureLoaded: (texture: BaseTexture) => void;
+        public onTextureLoadedObservable = new Observable<BaseTexture>();
+
+        private _onTextureLoadedObserver: Nullable<Observer<BaseTexture>>;
+        public set onTextureLoaded(callback: (Texture: BaseTexture) => void) {
+            if (this._onTextureLoadedObserver) {
+                this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver);
+            }
+            this._onTextureLoadedObserver = this.onTextureLoadedObservable.add(callback);
+        }
 
 
         /**
         /**
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          * Raised when the loader creates a material after parsing the glTF properties of the material.
          */
          */
-        public onMaterialLoaded: (material: Material) => void;
+        public onMaterialLoadedObservable = new Observable<Material>();
+
+        private _onMaterialLoadedObserver: Nullable<Observer<Material>>;
+        public set onMaterialLoaded(callback: (Material: Material) => void) {
+            if (this._onMaterialLoadedObserver) {
+                this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver);
+            }
+            this._onMaterialLoadedObserver = this.onMaterialLoadedObservable.add(callback);
+        }
 
 
         /**
         /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
          */
          */
-        public onComplete: () => void;
+        public onCompleteObservable = new Observable<GLTFFileLoader>();
+
+        private _onCompleteObserver: Nullable<Observer<GLTFFileLoader>>;
+        public set onComplete(callback: () => void) {
+            if (this._onCompleteObserver) {
+                this.onCompleteObservable.remove(this._onCompleteObserver);
+            }
+            this._onCompleteObserver = this.onCompleteObservable.add(callback);
+        }
+
+        /**
+        * Raised when the loader is disposed.
+        */
+        public onDisposeObservable = new Observable<GLTFFileLoader>();
+
+        private _onDisposeObserver: Nullable<Observer<GLTFFileLoader>>;
+        public set onDispose(callback: () => void) {
+            if (this._onDisposeObserver) {
+                this.onDisposeObservable.remove(this._onDisposeObserver);
+            }
+            this._onDisposeObserver = this.onDisposeObservable.add(callback);
+        }
 
 
         // #endregion
         // #endregion
 
 
-        private _loader: IGLTFLoader;
+        private _loader: Nullable<IGLTFLoader> = null;
 
 
         public name = "gltf";
         public name = "gltf";
 
 
@@ -129,17 +194,22 @@ module BABYLON {
         public dispose(): void {
         public dispose(): void {
             if (this._loader) {
             if (this._loader) {
                 this._loader.dispose();
                 this._loader.dispose();
+                this._loader = null;
             }
             }
-        }
 
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
-            try {
-                const loaderData = GLTFFileLoader._parse(data);
+            this.onParsedObservable.clear();
+            this.onMeshLoadedObservable.clear();
+            this.onTextureLoadedObservable.clear();
+            this.onMaterialLoadedObservable.clear();
+            this.onCompleteObservable.clear();
 
 
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
+            this.onDisposeObservable.notifyObservers(this);
+            this.onDisposeObservable.clear();
+        }
 
 
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
+            try {
+                const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -153,14 +223,9 @@ module BABYLON {
             }
             }
         }
         }
 
 
-        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
+        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
             try {
             try {
-                const loaderData = GLTFFileLoader._parse(data);
-
-                if (this.onParsed) {
-                    this.onParsed(loaderData);
-                }
-
+                const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
                 this._loader.loadAsync(scene, loaderData, rootUrl, onSuccess, onProgress, onError);
             }
             }
@@ -184,15 +249,20 @@ module BABYLON {
             return new GLTFFileLoader();
             return new GLTFFileLoader();
         }
         }
 
 
-        private static _parse(data: string | ArrayBuffer): IGLTFLoaderData {
+        private _parse(data: string | ArrayBuffer): IGLTFLoaderData {
+            let parsedData: IGLTFLoaderData;
             if (data instanceof ArrayBuffer) {
             if (data instanceof ArrayBuffer) {
-                return GLTFFileLoader._parseBinary(data);
+                parsedData = GLTFFileLoader._parseBinary(data);
+            }
+            else {
+                parsedData = {
+                    json: JSON.parse(data),
+                    bin: null
+                };
             }
             }
 
 
-            return {
-                json: JSON.parse(data),
-                bin: null
-            };
+            this.onParsedObservable.notifyObservers(parsedData);
+            return parsedData;
         }
         }
 
 
         private _getLoader(loaderData: IGLTFLoaderData): IGLTFLoader {
         private _getLoader(loaderData: IGLTFLoaderData): IGLTFLoader {
@@ -216,7 +286,7 @@ module BABYLON {
                 }
                 }
             }
             }
 
 
-            const createLoaders: { [key: number]: (parent: GLTFFileLoader) => IGLTFLoader } = {
+            const createLoaders: { [key: number]: () => IGLTFLoader } = {
                 1: GLTFFileLoader.CreateGLTFLoaderV1,
                 1: GLTFFileLoader.CreateGLTFLoaderV1,
                 2: GLTFFileLoader.CreateGLTFLoaderV2
                 2: GLTFFileLoader.CreateGLTFLoaderV2
             };
             };
@@ -226,7 +296,17 @@ module BABYLON {
                 throw new Error("Unsupported version: " + asset.version);
                 throw new Error("Unsupported version: " + asset.version);
             }
             }
 
 
-            return createLoader(this);
+            const loader = createLoader();
+            loader.coordinateSystemMode = this.coordinateSystemMode;
+            loader.animationStartMode = this.animationStartMode;
+            loader.compileMaterials = this.compileMaterials;
+            loader.useClipPlane = this.useClipPlane;
+            loader.compileShadowGenerators = this.compileShadowGenerators;
+            loader.onMeshLoadedObservable.add(mesh => this.onMeshLoadedObservable.notifyObservers(mesh));
+            loader.onTextureLoadedObservable.add(texture => this.onTextureLoadedObservable.notifyObservers(texture));
+            loader.onMaterialLoadedObservable.add(material => this.onMaterialLoadedObservable.notifyObservers(material));
+            loader.onCompleteObservable.add(() => this.onCompleteObservable.notifyObservers(this));
+            return loader;
         }
         }
 
 
         private static _parseBinary(data: ArrayBuffer): IGLTFLoaderData {
         private static _parseBinary(data: ArrayBuffer): IGLTFLoaderData {

+ 3 - 2
package.json

@@ -4,11 +4,12 @@
     },
     },
     "contributors": [
     "contributors": [
         "David ROUSSET",
         "David ROUSSET",
-        "Sebastien VANDENBERGHE"
+        "Sebastien VANDENBERGHE",
+        "Raanan Weber"
     ],
     ],
     "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": "3.1.1",
+    "version": "3.2.0-alpha0",
     "repository": {
     "repository": {
         "type": "git",
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 0 - 1
sandbox/index.js

@@ -38,7 +38,6 @@ if (BABYLON.Engine.isSupported()) {
         currentPluginName = plugin.name;
         currentPluginName = plugin.name;
 
 
         if (plugin.name === "gltf" && plugin instanceof BABYLON.GLTFFileLoader) {
         if (plugin.name === "gltf" && plugin instanceof BABYLON.GLTFFileLoader) {
-            plugin.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.ALL;
             plugin.compileMaterials = true;
             plugin.compileMaterials = true;
         }
         }
     });
     });

+ 728 - 0
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -0,0 +1,728 @@
+module BABYLON {
+    interface _IGLTFAsset {
+        generator: string;
+        version: string;
+    }
+    interface _IGLTFScene {
+        nodes: number[];
+    }
+    interface _IGLTFNode {
+        mesh: number;
+        name?: string;
+        translation?: number[];
+        scale?: number[];
+        rotation?: number[];
+    }
+    interface _IGLTFMeshPrimitive {
+        attributes: { [index: string]: number };
+        indices?: number;
+        material?: number;
+    }
+    interface _IGLTFMesh {
+        primitives: _IGLTFMeshPrimitive[];
+    }
+    interface _IGLTFMaterial {
+    }
+    interface _IGLTFBuffer {
+        byteLength: number;
+        uri?: string;
+    }
+    interface _IGLTFBufferView {
+        buffer: number;
+        byteOffset?: number;
+        byteLength: number;
+    }
+    interface _IGLTFAccessor {
+        name: string;
+        bufferView: number;
+        componentType: number;
+        count: number;
+        type: string;
+        min?: number[];
+        max?: number[];
+    }
+    interface _IGLTF {
+        buffers: _IGLTFBuffer[];
+        asset: _IGLTFAsset;
+        meshes: _IGLTFMesh[];
+        materials?: _IGLTFMaterial[];
+        scenes: _IGLTFScene[];
+        scene?: number;
+        nodes: _IGLTFNode[];
+        bufferViews: _IGLTFBufferView[];
+        accessors: _IGLTFAccessor[];
+    }
+    export class _GLTF2Exporter {
+
+        private bufferViews: _IGLTFBufferView[];
+        private accessors: _IGLTFAccessor[];
+        private nodes: _IGLTFNode[];
+        private asset: _IGLTFAsset;
+        private scenes: _IGLTFScene[];
+        private meshes: _IGLTFMesh[];
+        private totalByteLength: number;
+        private babylonScene: BABYLON.Scene;
+
+        public constructor(babylonScene: BABYLON.Scene) {
+            this.asset = { generator: "BabylonJS", version: "2.0" };
+            this.babylonScene = babylonScene;
+            this.bufferViews = new Array<_IGLTFBufferView>();
+            this.accessors = new Array<_IGLTFAccessor>();
+            this.meshes = new Array<_IGLTFMesh>();
+            this.scenes = new Array<_IGLTFScene>();
+            this.nodes = new Array<_IGLTFNode>();
+
+            let totalByteLength = 0;
+
+            totalByteLength = this.createScene(this.babylonScene, totalByteLength);
+            
+            this.totalByteLength = totalByteLength;
+        }
+
+        /**
+         * Creates a buffer view based on teh supplied arguments
+         * @param bufferIndex 
+         * @param byteOffset 
+         * @param byteLength 
+         * 
+         * @returns {_IGLTFBufferView} 
+         */
+        private createBufferView(bufferIndex: number, byteOffset: number, byteLength: number): _IGLTFBufferView {
+            let bufferview: _IGLTFBufferView = { buffer: bufferIndex, byteLength: byteLength };
+            if (byteOffset > 0) {
+                bufferview.byteOffset = byteOffset;
+            }
+
+            return bufferview;
+        }
+
+        /**
+         * Creates an accessor based on the supplied arguments
+         * @param bufferviewIndex 
+         * @param name 
+         * @param type 
+         * @param componentType 
+         * @param count 
+         * @param min 
+         * @param max 
+         * 
+         * @returns {_IGLTFAccessor} 
+         */
+        private createAccessor(bufferviewIndex: number, name: string, type: string, componentType: number, count: number, min?: number[], max?: number[]): _IGLTFAccessor {
+            let accessor: _IGLTFAccessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
+
+            if (min) {
+                accessor.min = min;
+            }
+            if (max) {
+                accessor.max = max;
+            }
+
+            return accessor;
+        }
+        /**
+         * Calculates the minimum and maximum values of an array of floats, based on stride
+         * @param buff 
+         * @param vertexStart 
+         * @param vertexCount 
+         * @param arrayOffset 
+         * @param stride 
+         * 
+         * @returns {min: number[], max: number[]} min number array and max number array
+         */
+        private calculateMinMax(buff: FloatArray, vertexStart: number, vertexCount: number, arrayOffset: number, stride: number): { min: number[], max: number[] } {
+            let min = [Infinity, Infinity, Infinity];
+            let max = [-Infinity, -Infinity, -Infinity];
+            let end = vertexStart + vertexCount;
+            if (vertexCount > 0) {
+                for (let i = vertexStart; i < end; ++i) {
+                    let index = stride * i;
+                    for (let j = 0; j < stride; ++j) {
+                        if (buff[index] < min[j]) {
+                            min[j] = buff[index];
+                        }
+                        if (buff[index] > max[j]) {
+                            max[j] = buff[index];
+                        }
+                        ++index;
+                    }
+                }
+            }
+            return { min, max };
+        }
+        /**
+         * Write mesh attribute data to buffer.
+         * Returns the bytelength of the data.
+         * @param vertexBufferType 
+         * @param submesh 
+         * @param meshAttributeArray 
+         * @param strideSize 
+         * @param byteOffset 
+         * @param dataBuffer 
+         * @param useRightHandedSystem 
+         * 
+         * @returns {number} byte length
+         */
+        private writeAttributeData(vertexBufferType: string, submesh: BABYLON.SubMesh, meshAttributeArray: FloatArray, strideSize: number, byteOffset: number, dataBuffer: DataView, useRightHandedSystem: boolean): number {
+            let byteOff = byteOffset;
+            let end = submesh.verticesStart + submesh.verticesCount;
+            let byteLength = 0;
+
+            switch (vertexBufferType) {
+                case BABYLON.VertexBuffer.PositionKind: {
+                    for (let k = submesh.verticesStart; k < end; ++k) {
+                        let index = k * strideSize!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                        byteOff += 4!;
+                        if (useRightHandedSystem) {
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                        }
+                        else {
+                            dataBuffer!.setFloat32(byteOff, -meshAttributeArray![index + 2], true);
+                        }
+
+                        byteOff += 4!;
+                    }
+                    byteLength = submesh.verticesCount * 12;
+                    break;
+                }
+                case BABYLON.VertexBuffer.NormalKind: {
+                    for (let k = submesh.verticesStart; k < end; ++k) {
+                        let index = k * strideSize!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                        byteOff += 4!;
+                        if (useRightHandedSystem) {
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                        }
+                        else {
+                            dataBuffer!.setFloat32(byteOff, -meshAttributeArray![index + 2], true);
+                        }
+
+                        byteOff += 4!;
+                    }
+                    byteLength = submesh.verticesCount * 12;
+                    break;
+                }
+                case BABYLON.VertexBuffer.TangentKind: {
+                    for (let k = submesh.indexStart; k < end; ++k) {
+                        let index = k * strideSize!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                        byteOff += 4!;
+                        if (useRightHandedSystem) {
+                            dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                        }
+                        else {
+                            dataBuffer!.setFloat32(byteOff, -meshAttributeArray![index + 2], true);
+                        }
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 3], true);
+                        byteOff += 4!;
+                    }
+                    byteLength = submesh.verticesCount * 16;
+                    break;
+                }
+                case BABYLON.VertexBuffer.ColorKind: {
+                    for (let k = submesh.verticesStart; k < end; ++k) {
+                        let index = k * strideSize!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 2], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 3], true);
+                        byteOff += 4!;
+                    }
+                    byteLength = submesh.verticesCount * 16;
+                    break;
+                }
+                case BABYLON.VertexBuffer.UVKind: {
+                    for (let k = submesh.verticesStart; k < end; ++k) {
+                        let index = k * strideSize!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                        byteOff += 4!;
+                    }
+                    byteLength = submesh.verticesCount * 8;
+                    break;
+                }
+                case BABYLON.VertexBuffer.UV2Kind: {
+                    for (let k = submesh.verticesStart; k < end; ++k) {
+                        let index = k * strideSize!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index], true);
+                        byteOff += 4!;
+                        dataBuffer!.setFloat32(byteOff, meshAttributeArray![index + 1], true);
+                        byteOff += 4!;
+                    }
+                    byteLength = submesh.verticesCount * 8;
+                    break;
+                }
+                default: {
+                    throw new Error("Unsupported vertex buffer type: " + vertexBufferType);
+                }
+            }
+
+            return byteLength;
+        }
+        /**
+         * Generates glTF json data
+         * @param glb 
+         * @param glTFPrefix 
+         * @param prettyPrint 
+         * 
+         * @returns {string} json data as string
+         */
+        private generateJSON(glb: boolean, glTFPrefix?: string, prettyPrint?: boolean): string {
+            let buffer: _IGLTFBuffer = { byteLength: this.totalByteLength };
+
+            let glTf: _IGLTF = {
+                buffers: [buffer],
+                asset: this.asset,
+                meshes: this.meshes,
+                scenes: this.scenes,
+                nodes: this.nodes,
+                bufferViews: this.bufferViews,
+                accessors: this.accessors
+            };
+            if (this.scenes.length > 0) {
+                glTf.scene = 0;
+            }
+
+            if (!glb) {
+                buffer.uri = glTFPrefix + ".bin";
+            }
+
+            let jsonText = prettyPrint ? JSON.stringify(glTf, null, 2) : JSON.stringify(glTf);
+
+            return jsonText;
+        }
+        /**
+         * Generates data for .gltf and .bin files based on the glTF prefix string
+         * @param glTFPrefix 
+         * 
+         * @returns {[x: string]: string | Blob} object with glTF json tex filename 
+         * and binary file name as keys and their data as values
+         */
+        public _generateGLTF(glTFPrefix: string): { [x: string]: string | Blob } {
+            const jsonText = this.generateJSON(false, glTFPrefix, true);
+            const binaryBuffer = this.generateBinary();
+            const bin = new Blob([binaryBuffer], { type: 'application/octet-stream' });
+
+            const glTFFileName = glTFPrefix + '.gltf';
+            const glTFBinFile = glTFPrefix + '.bin';
+
+            return {
+                [glTFFileName]: jsonText,
+                [glTFBinFile]: bin
+            };
+        }
+        /**
+         * Creates a binary buffer for glTF
+         * 
+         * @returns {ArrayBuffer}
+         */
+        private generateBinary(): ArrayBuffer {
+            let byteOffset = 0;
+            let binaryBuffer = new ArrayBuffer(this.totalByteLength);
+            let dataBuffer = new DataView(binaryBuffer);
+            byteOffset = this.createScene(this.babylonScene, byteOffset, dataBuffer);
+            
+            return binaryBuffer;
+        }
+        /**
+         * Generates a glb file from the json and binary data.  
+         * Returns an object with the glb file name as the key and data as the value.
+         * @param jsonText 
+         * @param binaryBuffer 
+         * @param glTFPrefix 
+         * 
+         * @returns {[glbFileName: string]: Blob} object with glb filename as key and data as value
+         */
+        public _generateGLB(glTFPrefix: string): { [glbFileName: string]: Blob } {
+            const jsonText = this.generateJSON(true);
+            const binaryBuffer = this.generateBinary();
+            let glbFileName = glTFPrefix + '.glb';
+            let headerLength = 12;
+            let chunkLengthPrefix = 8;
+            let jsonLength = jsonText.length;
+            let jsonRemainder = jsonLength % 4;
+            let binRemainder = binaryBuffer.byteLength % 4;
+            let jsonPadding = jsonRemainder === 0 ? jsonRemainder : 4 - jsonRemainder;
+            let binPadding = binRemainder === 0 ? binRemainder : 4 - binRemainder;
+            let byteLength = headerLength + (2 * chunkLengthPrefix) + jsonLength + jsonPadding + binaryBuffer.byteLength + binPadding;
+
+            //header
+            let headerBuffer = new ArrayBuffer(headerLength);
+            let headerBufferView = new DataView(headerBuffer);
+            headerBufferView.setUint32(0, 0x46546C67, true); //glTF
+            headerBufferView.setUint32(4, 2, true); // version
+            headerBufferView.setUint32(8, byteLength, true); // total bytes in file
+
+            //json chunk
+            let jsonChunkBuffer = new ArrayBuffer(chunkLengthPrefix + jsonLength + jsonPadding);
+            let jsonChunkBufferView = new DataView(jsonChunkBuffer);
+            jsonChunkBufferView.setUint32(0, jsonLength + jsonPadding, true);
+            jsonChunkBufferView.setUint32(4, 0x4E4F534A, true);
+
+            //json chunk bytes
+            let jsonData = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix);
+            for (let i = 0; i < jsonLength; ++i) {
+                jsonData[i] = jsonText.charCodeAt(i);
+            }
+
+            //json padding
+            let jsonPaddingView = new Uint8Array(jsonChunkBuffer, chunkLengthPrefix + jsonLength);
+            for (let i = 0; i < jsonPadding; ++i) {
+                jsonPaddingView[i] = 0x20;
+            }
+
+            //binary chunk
+            let binaryChunkBuffer = new ArrayBuffer(chunkLengthPrefix);
+            let binaryChunkBufferView = new DataView(binaryChunkBuffer);
+            binaryChunkBufferView.setUint32(0, binaryBuffer.byteLength, true);
+            binaryChunkBufferView.setUint32(4, 0x004E4942, true);
+
+            // binary padding
+            let binPaddingBuffer = new ArrayBuffer(binPadding);
+            let binPaddingView = new Uint8Array(binPaddingBuffer);
+            for (let i = 0; i < binPadding; ++i) {
+                binPaddingView[i] = 0;
+            }
+
+            // binary data
+            let glbFile = new Blob([headerBuffer, jsonChunkBuffer, binaryChunkBuffer, binaryBuffer, binPaddingBuffer], { type: 'application/octet-stream' });
+
+            return {
+                [glbFileName]: glbFile
+            };
+        }
+        /**
+         * Sets the TRS for each node
+         * @param node 
+         * @param babylonMesh 
+         * @param useRightHandedSystem 
+         */
+        private setNodeTransformation(node: _IGLTFNode, babylonMesh: BABYLON.AbstractMesh, useRightHandedSystem: boolean): void {
+            if (!(babylonMesh.position.x === 0 && babylonMesh.position.y === 0 && babylonMesh.position.z === 0)) {
+                if (useRightHandedSystem) {
+                    node.translation = babylonMesh.position.asArray();
+                }
+                else {
+                    node.translation = [babylonMesh.position.x, babylonMesh.position.y, -babylonMesh.position.z];
+                }
+
+            }
+            if (!(babylonMesh.scaling.x === 1 && babylonMesh.scaling.y === 1 && babylonMesh.scaling.z === 1)) {
+                if (useRightHandedSystem) {
+                    node.scale = babylonMesh.scaling.asArray();
+                }
+                else {
+                    node.scale = [babylonMesh.scaling.x, babylonMesh.scaling.y, -babylonMesh.scaling.z];
+                }
+            }
+            let rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(babylonMesh.rotation.y, babylonMesh.rotation.x, babylonMesh.rotation.z);
+            if (babylonMesh.rotationQuaternion) {
+                rotationQuaternion = rotationQuaternion.multiply(babylonMesh.rotationQuaternion);
+            }
+            if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
+                if (useRightHandedSystem) {
+                    node.rotation = rotationQuaternion.asArray();
+                }
+                else {
+                    node.rotation = [-rotationQuaternion.x, -rotationQuaternion.y, rotationQuaternion.z, rotationQuaternion.w];
+                }
+            }
+        }
+        /**
+         * Sets data for the primitive attributes of each submesh
+         * @param mesh 
+         * @param babylonMesh 
+         * @param byteOffset 
+         * @param useRightHandedSystem 
+         * @param dataBuffer 
+         * 
+         * @returns {number} bytelength of the primitive attributes plus the passed in byteOffset
+         */
+        private setPrimitiveAttributes(mesh: _IGLTFMesh, babylonMesh: BABYLON.AbstractMesh, byteOffset: number, useRightHandedSystem: boolean, dataBuffer?: DataView): number {
+            // go through all mesh primitives (submeshes)
+            for (let j = 0; j < babylonMesh.subMeshes.length; ++j) {
+                let bufferMesh = null;
+                let submesh = babylonMesh.subMeshes[j];
+                let meshPrimitive: _IGLTFMeshPrimitive = { attributes: {} };
+
+                if (babylonMesh instanceof BABYLON.Mesh) {
+                    bufferMesh = (babylonMesh as BABYLON.Mesh);
+                }
+                else if (babylonMesh instanceof BABYLON.InstancedMesh) {
+                    bufferMesh = (babylonMesh as BABYLON.InstancedMesh).sourceMesh;
+                }
+
+                // Loop through each attribute of the submesh (mesh primitive)
+                if (bufferMesh!.isVerticesDataPresent(BABYLON.VertexBuffer.PositionKind)) {
+                    const positionVertexBuffer = bufferMesh!.getVertexBuffer(BABYLON.VertexBuffer.PositionKind);
+                    const positionVertexBufferOffset = positionVertexBuffer!.getOffset();
+                    const positions = positionVertexBuffer!.getData();
+                    const positionStrideSize = positionVertexBuffer!.getStrideSize();
+                    if (dataBuffer) {
+
+                        byteOffset += this.writeAttributeData(
+                            BABYLON.VertexBuffer.PositionKind,
+                            submesh,
+                            positions!,
+                            positionStrideSize!,
+                            byteOffset,
+                            dataBuffer,
+                            useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        const byteLength = submesh.verticesCount * 12;
+                        const bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        const result = this.calculateMinMax(positions!, submesh.verticesStart, submesh.verticesCount, positionVertexBufferOffset!, positionStrideSize!);
+                        const accessor = this.createAccessor(this.bufferViews.length - 1, "Position", "VEC3", 5126, submesh.verticesCount, result.min, result.max);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.attributes.POSITION = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh!.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
+                    const normalVertexBuffer = bufferMesh!.getVertexBuffer(BABYLON.VertexBuffer.NormalKind);
+                    const normals = normalVertexBuffer!.getData();
+                    const normalStrideSize = normalVertexBuffer!.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(
+                            BABYLON.VertexBuffer.NormalKind,
+                            submesh,
+                            normals!,
+                            normalStrideSize!,
+                            byteOffset,
+                            dataBuffer,
+                            useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        const byteLength = submesh.verticesCount * 12;
+                        const bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        let accessor = this.createAccessor(this.bufferViews.length - 1, "Normal", "VEC3", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.attributes.NORMAL = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh!.isVerticesDataPresent(BABYLON.VertexBuffer.TangentKind)) {
+                    const tangentVertexBuffer = bufferMesh!.getVertexBuffer(BABYLON.VertexBuffer.TangentKind);
+                    const tangents = tangentVertexBuffer!.getData();
+                    const tangentStrideSize = tangentVertexBuffer!.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(
+                            BABYLON.VertexBuffer.TangentKind,
+                            submesh,
+                            tangents!,
+                            tangentStrideSize!,
+                            byteOffset,
+                            dataBuffer,
+                            useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        const byteLength = submesh.verticesCount * 16;
+                        const bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        const accessor = this.createAccessor(this.bufferViews.length - 1, "Tangent", "VEC4", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.attributes.TANGENT = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh!.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
+                    const colorVertexBuffer = bufferMesh!.getVertexBuffer(BABYLON.VertexBuffer.ColorKind);
+                    const colors = colorVertexBuffer!.getData();
+                    const colorStrideSize = colorVertexBuffer!.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(
+                            BABYLON.VertexBuffer.ColorKind,
+                            submesh,
+                            colors!,
+                            colorStrideSize!,
+                            byteOffset,
+                            dataBuffer,
+                            useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        let byteLength = submesh.verticesCount * 16;
+                        let bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        let accessor = this.createAccessor(this.bufferViews.length - 1, "Color", "VEC4", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.attributes.COLOR_0 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh!.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
+                    const texCoord0VertexBuffer = bufferMesh!.getVertexBuffer(BABYLON.VertexBuffer.UVKind);
+                    const texCoords0 = texCoord0VertexBuffer!.getData();
+                    const texCoord0StrideSize = texCoord0VertexBuffer!.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(
+                            BABYLON.VertexBuffer.UVKind,
+                            submesh,
+                            texCoords0!,
+                            texCoord0StrideSize!,
+                            byteOffset,
+                            dataBuffer,
+                            useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        let byteLength = submesh.verticesCount * 8;
+                        let bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        let accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.attributes.TEXCOORD_0 = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh!.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
+                    let texCoord1VertexBuffer = bufferMesh!.getVertexBuffer(BABYLON.VertexBuffer.UV2Kind);
+                    let texCoords1 = texCoord1VertexBuffer!.getData();
+                    let texCoord1StrideSize = texCoord1VertexBuffer!.getStrideSize();
+                    if (dataBuffer) {
+                        byteOffset += this.writeAttributeData(
+                            BABYLON.VertexBuffer.UV2Kind,
+                            submesh,
+                            texCoords1!,
+                            texCoord1StrideSize!,
+                            byteOffset,
+                            dataBuffer,
+                            useRightHandedSystem);
+                    }
+                    else {
+                        // Create bufferview
+                        let byteLength = submesh.verticesCount * 8;
+                        let bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        let accessor = this.createAccessor(this.bufferViews.length - 1, "Texture Coords", "VEC2", 5126, submesh.verticesCount);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.attributes.TEXCOORD_1 = this.accessors.length - 1;
+                    }
+                }
+
+                if (bufferMesh!.getTotalIndices() > 0) {
+                    if (dataBuffer) {
+                        let indices = bufferMesh!.getIndices();
+                        let start = submesh.indexStart;
+                        let end = submesh.indexCount + start;
+                        let byteOff = byteOffset;
+
+                        for (let k = start; k < end; k = k + 3) {
+                            dataBuffer!.setUint32(byteOff, indices![k], true);
+                            byteOff += 4;
+                            dataBuffer!.setUint32(byteOff, indices![k + 1], true);
+                            byteOff += 4;
+                            dataBuffer!.setUint32(byteOff, indices![k + 2], true);
+                            byteOff += 4;
+                        }
+
+                        let byteLength = submesh.indexCount * 4;
+                        byteOffset += byteLength;
+                    }
+                    else {
+                        // Create bufferview
+                        let indicesCount = submesh.indexCount;
+                        let byteLength = indicesCount * 4;
+                        let bufferview = this.createBufferView(0, byteOffset, byteLength);
+                        byteOffset += byteLength;
+                        this.bufferViews.push(bufferview);
+
+                        // Create accessor
+                        let accessor = this.createAccessor(this.bufferViews.length - 1, "Indices", "SCALAR", 5125, indicesCount);
+                        this.accessors.push(accessor);
+
+                        meshPrimitive.indices = this.accessors.length - 1;
+                    }
+                }
+                if (bufferMesh!.material) {
+                    //TODO: Implement Material
+                }
+                mesh.primitives.push(meshPrimitive);
+            }
+            return byteOffset;
+        }
+        /**
+         * Creates a glTF scene based on the array of meshes.
+         * Returns the the total byte offset.
+         * @param gltf 
+         * @param byteOffset 
+         * @param buffer 
+         * @param dataBuffer 
+         * 
+         * @returns {number} bytelength + byteoffset
+         */
+        private createScene(babylonScene: BABYLON.Scene, byteOffset: number, dataBuffer?: DataView): number {
+            if (babylonScene.meshes.length > 0 ) {
+                let babylonMeshes = babylonScene.meshes;
+                let scene = { nodes: new Array<number>() };
+
+                for (let i = 0; i < babylonMeshes.length; ++i) {
+                    // create node to hold translation/rotation/scale and the mesh
+                    let node: _IGLTFNode = { mesh: -1 };
+                    let babylonMesh = babylonMeshes[i];
+                    let useRightHandedSystem = babylonMesh.getScene().useRightHandedSystem;
+
+                    // Set transformation
+                    this.setNodeTransformation(node, babylonMesh, useRightHandedSystem);
+
+                    // create mesh
+                    let mesh: _IGLTFMesh = { primitives: new Array<_IGLTFMeshPrimitive>() };
+                    mesh.primitives = [];
+                    byteOffset = this.setPrimitiveAttributes(mesh, babylonMesh, byteOffset, useRightHandedSystem, dataBuffer);
+                    // go through all mesh primitives (submeshes)
+
+                    this.meshes.push(mesh);
+
+                    node.mesh = this.meshes.length - 1;
+                    if (babylonMesh.name) {
+                        node.name = babylonMesh.name;
+                    }
+                    this.nodes.push(node);
+
+                    scene.nodes.push(this.nodes.length - 1);
+                }
+
+                this.scenes.push(scene);
+            }
+
+            return byteOffset;
+        }
+    }
+}

+ 72 - 0
serializers/src/glTF/2.0/babylon.glTFSerializer.ts

@@ -0,0 +1,72 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    export class GLTF2Export {
+        /**
+         * Exports the geometry of a Mesh array in .gltf file format.
+         * If glb is set to true, exports as .glb.
+         * @param meshes 
+         * @param materials 
+         * 
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .gltf, .glb and associates textures
+         * as keys and their data and paths as values.
+         */
+        public static GLTF(scene: BABYLON.Scene, filename: string): {[fileName: string]: string | Blob} {
+            let glTFPrefix = filename.replace(/\.[^/.]+$/, "");
+            let gltfGenerator = new _GLTF2Exporter(scene);
+
+            return gltfGenerator._generateGLTF(glTFPrefix);
+        }
+        /**
+         * 
+         * @param meshes 
+         * @param filename 
+         * 
+         * @returns {[fileName: string]: string | Blob} Returns an object with a .glb filename as key and data as value
+         */
+        public static GLB(scene: BABYLON.Scene, filename: string): {[fileName: string]: string | Blob} {
+            let glTFPrefix = filename.replace(/\.[^/.]+$/, "");        
+            let gltfGenerator = new _GLTF2Exporter(scene);
+
+            return gltfGenerator._generateGLB(glTFPrefix);
+        }
+        /**
+         * Downloads data from glTF object.
+         * 
+         * @param gltfData glTF object with keys being file names and values being data
+         */
+        public static downloadFiles(gltfData: {[fileName: string]: string | Blob} ): void {
+            /**
+             * Checks for a matching suffix at the end of a string (for ES5 and lower)
+             * @param str 
+             * @param suffix 
+             * 
+             * @returns {boolean} indicating whether the suffix matches or not
+             */
+            function endsWith(str: string, suffix: string): boolean {
+                return str.indexOf(suffix, str.length - suffix.length) !== -1;
+            }
+            for (let key in gltfData) {
+                let link = document.createElement('a');
+                document.body.appendChild(link);
+                link.setAttribute("type", "hidden");
+                link.download = key;
+                let blob = gltfData[key];
+                let mimeType;
+                
+                if (endsWith(key, ".glb")) {
+                    mimeType = {type: "model/gltf-binary"};
+                }
+                else if (endsWith(key, ".bin")) {
+                    mimeType = {type: "application/octet-stream"};
+                }
+                else if (endsWith(key, ".gltf")) {
+                    mimeType = {type: "model/gltf+json"};
+                }
+
+                link.href = window.URL.createObjectURL(new Blob([blob], mimeType));
+                link.click();
+            }
+        }
+    }
+}

+ 6 - 5
src/Animations/babylon.animatable.ts

@@ -76,13 +76,14 @@
                 runtimeAnimations[index].reset();
                 runtimeAnimations[index].reset();
             }
             }
 
 
+            // Reset to original value
+            for (index = 0; index < runtimeAnimations.length; index++) {
+                var animation = runtimeAnimations[index];
+                animation.animate(0, this.fromFrame, this.toFrame, false, this._speedRatio);
+            }
+
             this._localDelayOffset = null;
             this._localDelayOffset = null;
             this._pausedDelay = null;
             this._pausedDelay = null;
-
-            let oldPauseState = this._paused;
-            this._paused = false;
-            this._animate(0);
-            this._paused = oldPauseState;
         }
         }
 
 
         public enableBlending(blendingSpeed: number): void {
         public enableBlending(blendingSpeed: number): void {

+ 44 - 0
src/Animations/babylon.animation.ts

@@ -151,6 +151,20 @@
             return animation;
             return animation;
         }
         }
 
 
+        /**
+         * Create and start an animation on a node
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when animation end
+         * @returns the animatable created for this animation
+         */
         public static CreateAndStartAnimation(name: string, node: Node, targetProperty: string,
         public static CreateAndStartAnimation(name: string, node: Node, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {
@@ -164,6 +178,36 @@
             return node.getScene().beginDirectAnimation(node, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
             return node.getScene().beginDirectAnimation(node, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
         }
         }
 
 
+        /**
+         * Create and start an animation on a node and its descendants
+         * @param {string} name defines the name of the global animation that will be run on all nodes
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} 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 {string} targetProperty defines property to animate
+         * @param {number} framePerSecond defines the number of frame per second yo use
+         * @param {number} totalFrame defines the number of frames in total
+         * @param {any} from defines the initial value
+         * @param {any} to defines the final value
+         * @param {number} loopMode defines which loop mode you want to use (off by default)
+         * @param {BABYLON.EasingFunction} easingFunction defines the easing function to use (linear by default)
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         * @example https://www.babylonjs-playground.com/#MH0VLI
+         */
+        public static CreateAndStartHierarchyAnimation(name: string, node: Node, directDescendantsOnly: boolean, targetProperty: string,
+            framePerSecond: number, totalFrame: number,
+            from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable[]> {
+
+            var animation = Animation._PrepareAnimation(name, targetProperty, framePerSecond, totalFrame, from, to, loopMode, easingFunction);
+
+            if (!animation) {
+                return null;
+            }
+
+            let scene = node.getScene();
+            return scene.beginDirectHierarchyAnimation(node, directDescendantsOnly, [animation], 0, totalFrame, (animation.loopMode === 1), 1.0, onAnimationEnd);
+        }
+
         public static CreateMergeAndStartAnimation(name: string, node: Node, targetProperty: string,
         public static CreateMergeAndStartAnimation(name: string, node: Node, targetProperty: string,
             framePerSecond: number, totalFrame: number,
             framePerSecond: number, totalFrame: number,
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {
             from: any, to: any, loopMode?: number, easingFunction?: EasingFunction, onAnimationEnd?: () => void): Nullable<Animatable> {

+ 40 - 4
src/Animations/babylon.animationGroup.ts

@@ -18,13 +18,40 @@ module BABYLON {
         private _from = Number.MAX_VALUE;
         private _from = Number.MAX_VALUE;
         private _to = Number.MIN_VALUE;
         private _to = Number.MIN_VALUE;
         private _isStarted: boolean;
         private _isStarted: boolean;
+        private _speedRatio = 1;
 
 
-        public onAnimationEndObservable = new Observable<Animation>();
+        public onAnimationEndObservable = new Observable<TargetedAnimation>();
 
 
+        /**
+         * Define if the animations are started
+         */
         public get isStarted(): boolean {
         public get isStarted(): boolean {
             return this._isStarted;
             return this._isStarted;
         }
         }
 
 
+        /**
+         * Gets or sets the speed ratio to use for all animations
+         */
+        public get speedRatio(): number {
+            return this._speedRatio;
+        }
+
+        /**
+         * Gets or sets the speed ratio to use for all animations
+         */
+        public set speedRatio(value: number) {
+            if (this._speedRatio === value) {
+                return;
+            }
+
+            this._speedRatio = value;
+
+            for (var index = 0; index < this._animatables.length; index++) {
+                let animatable = this._animatables[index];
+                animatable.speedRatio = this._speedRatio;
+            }
+        }
+
         public constructor(public name: string, scene: Nullable<Scene> = null) {
         public constructor(public name: string, scene: Nullable<Scene> = null) {
             this._scene = scene || Engine.LastCreatedScene!;
             this._scene = scene || Engine.LastCreatedScene!;
 
 
@@ -110,10 +137,12 @@ module BABYLON {
             for (var index = 0; index < this._targetedAnimations.length; index++) {
             for (var index = 0; index < this._targetedAnimations.length; index++) {
                 let targetedAnimation = this._targetedAnimations[index];
                 let targetedAnimation = this._targetedAnimations[index];
                 this._animatables.push(this._scene.beginDirectAnimation(targetedAnimation.target, [targetedAnimation.animation], this._from, this._to, loop, speedRatio, () => {
                 this._animatables.push(this._scene.beginDirectAnimation(targetedAnimation.target, [targetedAnimation.animation], this._from, this._to, loop, speedRatio, () => {
-
+                    this.onAnimationEndObservable.notifyObservers(targetedAnimation);
                 }));
                 }));
             }
             }
 
 
+            this._speedRatio = speedRatio;
+
             this._isStarted = true;
             this._isStarted = true;
 
 
             return this;
             return this;
@@ -138,12 +167,19 @@ module BABYLON {
         /**
         /**
          * Play all animations to initial state
          * Play all animations to initial state
          * This function will start() the animations if they were not started or will restart() them if they were paused
          * This function will start() the animations if they were not started or will restart() them if they were paused
+         * @param loop defines if animations must loop
          */
          */
-        public play(loop = false): AnimationGroup {
+        public play(loop?: boolean): AnimationGroup {
             if (this.isStarted) {
             if (this.isStarted) {
+                if (loop !== undefined) {
+                    for (var index = 0; index < this._animatables.length; index++) {
+                        let animatable = this._animatables[index];
+                        animatable.loopAnimation = loop;
+                    }
+                }
                 this.restart();
                 this.restart();
             } else {
             } else {
-                this.start(loop);
+                this.start(loop, this._speedRatio);
             }
             }
 
 
             return this;
             return this;

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

@@ -129,7 +129,7 @@ module BABYLON {
                                 if (codecSupportedFound) {
                                 if (codecSupportedFound) {
                                     // Loading sound using XHR2
                                     // Loading sound using XHR2
                                     if (!this._streaming) {
                                     if (!this._streaming) {
-                                        Tools.LoadFile(url, (data) => { this._soundLoaded(data as ArrayBuffer); }, undefined, this._scene.database, true);
+                                        this._scene._loadFile(url, (data) => { this._soundLoaded(data as ArrayBuffer); }, undefined, true, true);
                                     }
                                     }
                                     // Streaming sound using HTML5 Audio tag
                                     // Streaming sound using HTML5 Audio tag
                                     else {
                                     else {

+ 38 - 10
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -112,6 +112,21 @@ module BABYLON {
         public onNewMeshSelected = new Observable<AbstractMesh>();
         public onNewMeshSelected = new Observable<AbstractMesh>();
         private _circleEase: CircleEase;
         private _circleEase: CircleEase;
 
 
+        /**
+         * Observable raised before camera teleportation        
+        */
+        public onBeforeCameraTeleport = new Observable<Vector3>();
+
+        /**
+         *  Observable raised after camera teleportation
+        */
+        public onAfterCameraTeleport = new Observable<Vector3>();
+
+        /**
+        * Observable raised when current selected mesh gets unselected
+        */
+        public onSelectedMeshUnselected = new Observable<AbstractMesh>();
+
         private _raySelectionPredicate: (mesh: AbstractMesh) => boolean;
         private _raySelectionPredicate: (mesh: AbstractMesh) => boolean;
 
 
         /**
         /**
@@ -483,6 +498,8 @@ module BABYLON {
 
 
             if (this._scene.activeCamera) {
             if (this._scene.activeCamera) {
                 this._position = this._scene.activeCamera.position.clone();
                 this._position = this._scene.activeCamera.position.clone();
+                // make sure that we return to the last active camera
+                this._existingCamera = this._scene.activeCamera;
             }
             }
 
 
             if (this._webVRrequesting)
             if (this._webVRrequesting)
@@ -505,6 +522,10 @@ module BABYLON {
             if (this._scene.activeCamera && this._canvas) {
             if (this._scene.activeCamera && this._canvas) {
                 this._scene.activeCamera.attachControl(this._canvas);
                 this._scene.activeCamera.attachControl(this._canvas);
             }
             }
+
+            if (this._interactionsEnabled) {
+                this._scene.registerBeforeRender(this.beforeRender);
+            }
         }
         }
 
 
         /**
         /**
@@ -539,6 +560,10 @@ module BABYLON {
             }
             }
 
 
             this.updateButtonVisibility();
             this.updateButtonVisibility();
+
+            if (this._interactionsEnabled) {
+                this._scene.unregisterBeforeRender(this.beforeRender);
+            }
         }
         }
 
 
         public get position(): Vector3 {
         public get position(): Vector3 {
@@ -584,8 +609,6 @@ module BABYLON {
                     return false;
                     return false;
                 }
                 }
 
 
-                this._scene.registerBeforeRender(this.beforeRender);
-
                 this._interactionsEnabled = true;
                 this._interactionsEnabled = true;
             }
             }
         }
         }
@@ -635,13 +658,11 @@ module BABYLON {
 
 
                 this.enableInteractions();
                 this.enableInteractions();
 
 
-                if (vrTeleportationOptions) {
-                    if (vrTeleportationOptions.floorMeshName) {
-                        this._floorMeshName = vrTeleportationOptions.floorMeshName;
-                    }
-                    if (vrTeleportationOptions.floorMeshes) {
-                        this._floorMeshesCollection = vrTeleportationOptions.floorMeshes;
-                    }
+                if (vrTeleportationOptions.floorMeshName) {
+                    this._floorMeshName = vrTeleportationOptions.floorMeshName;
+                }
+                if (vrTeleportationOptions.floorMeshes) {
+                    this._floorMeshesCollection = vrTeleportationOptions.floorMeshes;
                 }
                 }
 
 
                 if (this._leftControllerReady && this._webVRCamera.leftController) {
                 if (this._leftControllerReady && this._webVRCamera.leftController) {
@@ -1212,6 +1233,8 @@ module BABYLON {
                 this._workingVector.y += this._defaultHeight;
                 this._workingVector.y += this._defaultHeight;
             }
             }
 
 
+            this.onBeforeCameraTeleport.notifyObservers(this._workingVector);
+
             // Create animation from the camera's position to the new location
             // Create animation from the camera's position to the new location
             this.currentVRCamera.animations = [];
             this.currentVRCamera.animations = [];
             var animationCameraTeleportation = new Animation("animationCameraTeleportation", "position", 90, Animation.ANIMATIONTYPE_VECTOR3, Animation.ANIMATIONLOOPMODE_CONSTANT);
             var animationCameraTeleportation = new Animation("animationCameraTeleportation", "position", 90, Animation.ANIMATIONTYPE_VECTOR3, Animation.ANIMATIONLOOPMODE_CONSTANT);
@@ -1278,7 +1301,9 @@ module BABYLON {
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, () => {
             this._scene.beginAnimation(this._postProcessMove, 0, 11, false, 1, () => {
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
                 this._webVRCamera.detachPostProcess(this._postProcessMove)
             });
             });
-            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1);
+            this._scene.beginAnimation(this.currentVRCamera, 0, 11, false, 1, () => {
+                this.onAfterCameraTeleport.notifyObservers(this._workingVector);
+            });
         }
         }
 
 
         private _castRayAndSelectObject() {
         private _castRayAndSelectObject() {
@@ -1392,6 +1417,9 @@ module BABYLON {
                         }
                         }
                     }
                     }
                     else {
                     else {
+                        if (this._currentMeshSelected) {
+                            this.onSelectedMeshUnselected.notifyObservers(this._currentMeshSelected);
+                        }
                         this._currentMeshSelected = null;
                         this._currentMeshSelected = null;
                         this.changeGazeColor(new Color3(0.7, 0.7, 0.7));
                         this.changeGazeColor(new Color3(0.7, 0.7, 0.7));
                         this.changeLaserColor(new Color3(0.7, 0.7, 0.7));
                         this.changeLaserColor(new Color3(0.7, 0.7, 0.7));

+ 16 - 8
src/Cameras/VR/babylon.webVRCamera.ts

@@ -325,40 +325,48 @@ module BABYLON {
         private _workingVector = Vector3.Zero();
         private _workingVector = Vector3.Zero();
         private _oneVector = Vector3.One();
         private _oneVector = Vector3.One();
         private _workingMatrix = Matrix.Identity();
         private _workingMatrix = Matrix.Identity();
+
+        private updateCacheCalled: boolean;
+
         public _updateCache(ignoreParentClass?: boolean): void {
         public _updateCache(ignoreParentClass?: boolean): void {
             if (!this.rotationQuaternion.equals(this._cache.rotationQuaternion) || !this.position.equals(this._cache.position)) {
             if (!this.rotationQuaternion.equals(this._cache.rotationQuaternion) || !this.position.equals(this._cache.position)) {
                 // Update to ensure devicePosition is up to date with most recent _deviceRoomPosition
                 // Update to ensure devicePosition is up to date with most recent _deviceRoomPosition
-                this.update();
+                if (!this.updateCacheCalled) {
+                    // make sure it is only called once per loop. this.update() might cause an infinite loop.
+                    this.updateCacheCalled = true;
+                    this.update();
+                }
 
 
                 // Set working vector to the device position in room space rotated by the new rotation
                 // Set working vector to the device position in room space rotated by the new rotation
                 this.rotationQuaternion.toRotationMatrix(this._workingMatrix);
                 this.rotationQuaternion.toRotationMatrix(this._workingMatrix);
                 Vector3.TransformCoordinatesToRef(this._deviceRoomPosition, this._workingMatrix, this._workingVector);
                 Vector3.TransformCoordinatesToRef(this._deviceRoomPosition, this._workingMatrix, this._workingVector);
 
 
                 // Subtract this vector from the current device position in world to get the translation for the device world matrix
                 // Subtract this vector from the current device position in world to get the translation for the device world matrix
-                this.devicePosition.subtractToRef(this._workingVector, this._workingVector)
+                this.devicePosition.subtractToRef(this._workingVector, this._workingVector);
                 Matrix.ComposeToRef(this._oneVector, this.rotationQuaternion, this._workingVector, this._deviceToWorld);
                 Matrix.ComposeToRef(this._oneVector, this.rotationQuaternion, this._workingVector, this._deviceToWorld);
 
 
                 // Add translation from anchor position
                 // Add translation from anchor position
-                this._deviceToWorld.getTranslationToRef(this._workingVector)
+                this._deviceToWorld.getTranslationToRef(this._workingVector);
                 this._workingVector.addInPlace(this.position);
                 this._workingVector.addInPlace(this.position);
-                this._workingVector.subtractInPlace(this._cache.position)
-                this._deviceToWorld.setTranslation(this._workingVector)
+                this._workingVector.subtractInPlace(this._cache.position);
+                this._deviceToWorld.setTranslation(this._workingVector);
 
 
                 // Set an inverted matrix to be used when updating the camera
                 // Set an inverted matrix to be used when updating the camera
-                this._deviceToWorld.invertToRef(this._worldToDevice)
+                this._deviceToWorld.invertToRef(this._worldToDevice);
 
 
                 // Update the gamepad to ensure the mesh is updated on the same frame as camera
                 // Update the gamepad to ensure the mesh is updated on the same frame as camera
                 this.controllers.forEach((controller) => {
                 this.controllers.forEach((controller) => {
                     controller._deviceToWorld = this._deviceToWorld;
                     controller._deviceToWorld = this._deviceToWorld;
                     controller.update();
                     controller.update();
-                })
-                this.update();
+                });
             }
             }
 
 
             if (!ignoreParentClass) {
             if (!ignoreParentClass) {
                 super._updateCache();
                 super._updateCache();
             }
             }
+            this.updateCacheCalled = false;
         }
         }
+
         public update() {
         public update() {
             // Get current device position in babylon world
             // Get current device position in babylon world
             Vector3.TransformCoordinatesToRef(this._deviceRoomPosition, this._deviceToWorld, this.devicePosition);
             Vector3.TransformCoordinatesToRef(this._deviceRoomPosition, this._deviceToWorld, this.devicePosition);

+ 1 - 0
src/Cameras/babylon.targetCamera.ts

@@ -37,6 +37,7 @@
         }
         }
 
 
         public getFrontPosition(distance: number): Vector3 {
         public getFrontPosition(distance: number): Vector3 {
+            this.getWorldMatrix();
             var direction = this.getTarget().subtract(this.position);
             var direction = this.getTarget().subtract(this.position);
             direction.normalize();
             direction.normalize();
             direction.scaleInPlace(distance);
             direction.scaleInPlace(distance);

+ 323 - 116
src/Engine/babylon.engine.ts

@@ -161,38 +161,6 @@
         }
         }
     };
     };
 
 
-    var partialLoadFile = (url: string, index: number, loadedFiles: (string | ArrayBuffer)[], scene: Nullable<Scene>,
-        onfinish: (files: (string | ArrayBuffer)[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void> = null) => {
-
-        var onload = (data: string | ArrayBuffer) => {
-            loadedFiles[index] = data;
-            (<any>loadedFiles)._internalCount++;
-
-            if ((<any>loadedFiles)._internalCount === 6) {
-                onfinish(loadedFiles);
-            }
-        };
-
-        const onerror = (request?: XMLHttpRequest, exception?: any) => {
-            if (onErrorCallBack && request) {
-                onErrorCallBack(request.status + " " + request.statusText, exception);
-            }
-        };
-
-        Tools.LoadFile(url, onload, undefined, undefined, true, onerror);
-    }
-
-    var cascadeLoadFiles = (rootUrl: string, scene: Nullable<Scene>,
-        onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void> = null) => {
-
-        var loadedFiles: (string | ArrayBuffer)[] = [];
-        (<any>loadedFiles)._internalCount = 0;
-
-        for (let index = 0; index < 6; index++) {
-            partialLoadFile(files[index], index, loadedFiles, scene, onfinish, onError);
-        }
-    };
-
     class BufferPointer {
     class BufferPointer {
         public active: boolean;
         public active: boolean;
         public index: number;
         public index: number;
@@ -255,6 +223,7 @@
         /** The maximum textures image */
         /** The maximum textures image */
         public maxTexturesImageUnits: number;
         public maxTexturesImageUnits: number;
         public maxVertexTextureImageUnits: number;
         public maxVertexTextureImageUnits: number;
+        public maxCombinedTexturesImageUnits: number;
         /** The maximum texture size */
         /** The maximum texture size */
         public maxTextureSize: number;
         public maxTextureSize: number;
         public maxCubemapTextureSize: number;
         public maxCubemapTextureSize: number;
@@ -310,7 +279,10 @@
      */
      */
     export class Engine {
     export class Engine {
         /** Use this array to turn off some WebGL2 features on known buggy browsers version */
         /** Use this array to turn off some WebGL2 features on known buggy browsers version */
-        public static WebGL2UniformBuffersExceptionList = ["Chrome/63"];
+        public static ExceptionList = [
+            { key: "Chrome/63.0", capture: "63\\.0\\.3239\\.(\\d+)", captureConstraint: 108, targets: ["uniformBuffer"] },
+            { key: "Firefox/58", capture: null, captureConstraint: null, targets: ["uniformBuffer"] }
+        ];
 
 
         public static Instances = new Array<Engine>();
         public static Instances = new Array<Engine>();
 
 
@@ -566,7 +538,7 @@
         }
         }
 
 
         public static get Version(): string {
         public static get Version(): string {
-            return "3.2.0-alpha0";
+            return "3.2.0-alpha2";
         }
         }
 
 
         // Updatable statics so stick with vars here
         // Updatable statics so stick with vars here
@@ -708,6 +680,7 @@
         private _loadingScreen: ILoadingScreen;
         private _loadingScreen: ILoadingScreen;
 
 
         public _drawCalls = new PerfCounter();
         public _drawCalls = new PerfCounter();
+        public _textureCollisions = new PerfCounter();
 
 
         private _glVersion: string;
         private _glVersion: string;
         private _glRenderer: string;
         private _glRenderer: string;
@@ -753,7 +726,7 @@
         private _internalTexturesCache = new Array<InternalTexture>();
         private _internalTexturesCache = new Array<InternalTexture>();
         protected _activeChannel: number;
         protected _activeChannel: number;
         protected _boundTexturesCache: { [key: string]: Nullable<InternalTexture> } = {};
         protected _boundTexturesCache: { [key: string]: Nullable<InternalTexture> } = {};
-        protected _boundTexturesOrder = new Array<InternalTexture>();
+        protected _boundTexturesStack = new Array<InternalTexture>();
         protected _currentEffect: Nullable<Effect>;
         protected _currentEffect: Nullable<Effect>;
         protected _currentProgram: Nullable<WebGLProgram>;
         protected _currentProgram: Nullable<WebGLProgram>;
         private _compiledEffects: { [key: string]: Effect } = {}
         private _compiledEffects: { [key: string]: Effect } = {}
@@ -790,7 +763,9 @@
 
 
         private _frameHandler: number;
         private _frameHandler: number;
 
 
-        private _nextFreeTextureSlot = 0;
+        private _nextFreeTextureSlots = new Array<number>();
+
+        private _activeRequests = new Array<IFileRequest>();
 
 
         // Hardware supported Compressed Textures
         // Hardware supported Compressed Textures
         private _texturesSupported = new Array<string>();
         private _texturesSupported = new Array<string>();
@@ -882,15 +857,37 @@
                 this._doNotHandleContextLost = options.doNotHandleContextLost ? true : false;
                 this._doNotHandleContextLost = options.doNotHandleContextLost ? true : false;
 
 
                 // Exceptions
                 // Exceptions
-                if (!options.disableWebGL2Support) {
-                    if (navigator && navigator.userAgent) {
-                        let ua = navigator.userAgent;
+                if (navigator && navigator.userAgent) {
+                    let ua = navigator.userAgent;
+
+                    for (var exception of Engine.ExceptionList) {
+                        let key = exception.key;
+                        let targets = exception.targets;
+
+                        if (ua.indexOf(key) > -1) {
+                            if (exception.capture && exception.captureConstraint) {
+                                let capture = exception.capture;
+                                let constraint = exception.captureConstraint;
+
+                                let regex = new RegExp(capture);
+                                let matches = regex.exec(ua);
+
+                                if (matches && matches.length > 0) {
+                                    let capturedValue = parseInt(matches[matches.length - 1]);
+                                    if (capturedValue >= constraint) {
+                                        continue;
+                                    }
+                                }
+                            }
 
 
-                        for (var exception of Engine.WebGL2UniformBuffersExceptionList) {
-                            if (ua.indexOf(exception) > -1) {
-                                this.disableUniformBuffers = true;
-                                break;
+                            for (var target of targets) {
+                                switch (target) {
+                                    case "uniformBuffer":
+                                        this.disableUniformBuffers = true;
+                                        break;
+                                }
                             }
                             }
+                            break;
                         }
                         }
                     }
                     }
                 }
                 }
@@ -1134,6 +1131,7 @@
             // Caps
             // Caps
             this._caps = new EngineCapabilities();
             this._caps = new EngineCapabilities();
             this._caps.maxTexturesImageUnits = this._gl.getParameter(this._gl.MAX_TEXTURE_IMAGE_UNITS);
             this._caps.maxTexturesImageUnits = this._gl.getParameter(this._gl.MAX_TEXTURE_IMAGE_UNITS);
+            this._caps.maxCombinedTexturesImageUnits = this._gl.getParameter(this._gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
             this._caps.maxVertexTextureImageUnits = this._gl.getParameter(this._gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
             this._caps.maxVertexTextureImageUnits = this._gl.getParameter(this._gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
             this._caps.maxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);
             this._caps.maxTextureSize = this._gl.getParameter(this._gl.MAX_TEXTURE_SIZE);
             this._caps.maxCubemapTextureSize = this._gl.getParameter(this._gl.MAX_CUBE_MAP_TEXTURE_SIZE);
             this._caps.maxCubemapTextureSize = this._gl.getParameter(this._gl.MAX_CUBE_MAP_TEXTURE_SIZE);
@@ -1295,6 +1293,11 @@
             this.setDepthBuffer(true);
             this.setDepthBuffer(true);
             this.setDepthFunctionToLessOrEqual();
             this.setDepthFunctionToLessOrEqual();
             this.setDepthWrite(true);
             this.setDepthWrite(true);
+
+            // Texture maps
+            for (let slot = 0; slot < this._caps.maxCombinedTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
         }
         }
 
 
         public get webGLVersion(): number {
         public get webGLVersion(): number {
@@ -1329,7 +1332,10 @@
                 }
                 }
                 this._boundTexturesCache[key] = null;
                 this._boundTexturesCache[key] = null;
             }
             }
-            this._nextFreeTextureSlot = 0;
+            this._nextFreeTextureSlots = [];
+            for (let slot = 0; slot < this._caps.maxCombinedTexturesImageUnits; slot++) {
+                this._nextFreeTextureSlots.push(slot);
+            }
             this._activeChannel = -1;
             this._activeChannel = -1;
         }
         }
 
 
@@ -1890,7 +1896,7 @@
             }
             }
 
 
             if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
             if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
-                this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
                 gl.generateMipmap(gl.TEXTURE_2D);
                 gl.generateMipmap(gl.TEXTURE_2D);
                 this._bindTextureDirectly(gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(gl.TEXTURE_2D, null);
             }
             }
@@ -1906,10 +1912,68 @@
             this.bindUnboundFramebuffer(null);
             this.bindUnboundFramebuffer(null);
         }
         }
 
 
+        public unBindMultiColorAttachmentFramebuffer(textures: InternalTexture[], disableGenerateMipMaps = false, onBeforeUnbind?: () => void): void {
+            this._currentRenderTarget = null;
+
+            // If MSAA, we need to bitblt back to main texture
+            var gl = this._gl;
+
+
+            if (textures[0]._MSAAFramebuffer) {
+                gl.bindFramebuffer(gl.READ_FRAMEBUFFER, textures[0]._MSAAFramebuffer);
+                gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, textures[0]._framebuffer);
+
+                var attachments = textures[0]._attachments;
+                if (!attachments) {
+                    attachments = new Array(textures.length);
+                    textures[0]._attachments = attachments;
+                }
+
+                for (var i = 0; i < textures.length; i++) {
+                    var texture = textures[i];
+
+                    for (var j = 0; j < attachments.length; j++) {
+                        attachments[j] = gl.NONE;
+                    }
+
+                    attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
+                    gl.readBuffer(attachments[i]);
+                    gl.drawBuffers(attachments);
+                    gl.blitFramebuffer(0, 0, texture.width, texture.height,
+                        0, 0, texture.width, texture.height,
+                        gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+                }
+                for (var i = 0; i < attachments.length; i++) {
+                    attachments[i] = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
+                }
+                gl.drawBuffers(attachments);
+            }
+
+            for (var i = 0; i < textures.length; i++) {
+                var texture = textures[i];
+                if (texture.generateMipMaps && !disableGenerateMipMaps && !texture.isCube) {
+                    this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+                    gl.generateMipmap(gl.TEXTURE_2D);
+                    this._bindTextureDirectly(gl.TEXTURE_2D, null);
+                }
+            }
+
+            if (onBeforeUnbind) {
+                if (textures[0]._MSAAFramebuffer) {
+                    // Bind the correct framebuffer
+                    this.bindUnboundFramebuffer(textures[0]._framebuffer);
+                }
+                onBeforeUnbind();
+            }
+
+            this.bindUnboundFramebuffer(null);
+        }
+
         public generateMipMapsForCubemap(texture: InternalTexture) {
         public generateMipMapsForCubemap(texture: InternalTexture) {
             if (texture.generateMipMaps) {
             if (texture.generateMipMaps) {
                 var gl = this._gl;
                 var gl = this._gl;
-                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                 this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
                 this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
             }
             }
@@ -2778,6 +2842,13 @@
             this._gl.uniformMatrix2fv(uniform, false, matrix);
             this._gl.uniformMatrix2fv(uniform, false, matrix);
         }
         }
 
 
+        public setInt(uniform: Nullable<WebGLUniformLocation>, value: number): void {
+            if (!uniform)
+                return;
+
+            this._gl.uniform1i(uniform, value);
+        }
+
         public setFloat(uniform: Nullable<WebGLUniformLocation>, value: number): void {
         public setFloat(uniform: Nullable<WebGLUniformLocation>, value: number): void {
             if (!uniform)
             if (!uniform)
                 return;
                 return;
@@ -2956,12 +3027,12 @@
             if (this.preventCacheWipeBetweenFrames && !bruteForce) {
             if (this.preventCacheWipeBetweenFrames && !bruteForce) {
                 return;
                 return;
             }
             }
-            this.resetTextureCache();
             this._currentEffect = null;
             this._currentEffect = null;
 
 
             // 6/8/2017: deltakosh: Should not be required anymore.
             // 6/8/2017: deltakosh: Should not be required anymore.
-            // This message is then mostly for the future myself which will scream out loud when seeing that actually it was required :)
+            // This message is then mostly for the future myself who will scream out loud when seeing that it was actually required :)
             if (bruteForce) {
             if (bruteForce) {
+                this.resetTextureCache();
                 this._currentProgram = null;
                 this._currentProgram = null;
 
 
                 this._stencilState.reset();
                 this._stencilState.reset();
@@ -2970,12 +3041,11 @@
                 this._alphaState.reset();
                 this._alphaState.reset();
             }
             }
 
 
-            this._cachedVertexBuffers = null;
+            this._resetVertexBufferBinding();
             this._cachedIndexBuffer = null;
             this._cachedIndexBuffer = null;
             this._cachedEffectForVertexBuffers = null;
             this._cachedEffectForVertexBuffers = null;
             this._unbindVertexArrayObject();
             this._unbindVertexArrayObject();
             this.bindIndexBuffer(null);
             this.bindIndexBuffer(null);
-            this.bindArrayBuffer(null);
         }
         }
 
 
         /**
         /**
@@ -3143,7 +3213,7 @@
                 }
                 }
 
 
                 if (!buffer) {
                 if (!buffer) {
-                    Tools.LoadFile(url, data => {
+                    this._loadFile(url, data => {
                         if (callback) {
                         if (callback) {
                             callback(data);
                             callback(data);
                         }
                         }
@@ -3176,7 +3246,7 @@
 
 
                         // Using shaders to rescale because canvas.drawImage is lossy
                         // Using shaders to rescale because canvas.drawImage is lossy
                         let source = new InternalTexture(this, InternalTexture.DATASOURCE_TEMP);
                         let source = new InternalTexture(this, InternalTexture.DATASOURCE_TEMP);
-                        this._bindTextureDirectly(gl.TEXTURE_2D, source);
+                        this._bindTextureDirectly(gl.TEXTURE_2D, source, true);
                         gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img);
                         gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img);
 
 
                         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                         gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
@@ -3186,7 +3256,7 @@
 
 
                         this._rescaleTexture(source, texture, scene, internalFormat, () => {
                         this._rescaleTexture(source, texture, scene, internalFormat, () => {
                             this._releaseTexture(source);
                             this._releaseTexture(source);
-                            this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+                            this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
 
 
                             continuationCallback();
                             continuationCallback();
                         });
                         });
@@ -3239,7 +3309,7 @@
                 }
                 }
                 hostingScene.postProcessManager.directRender([this._rescalePostProcess], rtt, true);
                 hostingScene.postProcessManager.directRender([this._rescalePostProcess], rtt, true);
 
 
-                this._bindTextureDirectly(this._gl.TEXTURE_2D, destination);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, destination, true);
                 this._gl.copyTexImage2D(this._gl.TEXTURE_2D, 0, internalFormat, 0, 0, destination.width, destination.height, 0);
                 this._gl.copyTexImage2D(this._gl.TEXTURE_2D, 0, internalFormat, 0, 0, destination.width, destination.height, 0);
 
 
                 this.unBindFramebuffer(rtt);
                 this.unBindFramebuffer(rtt);
@@ -3282,7 +3352,7 @@
             var internalFormat = this._getInternalFormat(format);
             var internalFormat = this._getInternalFormat(format);
             var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
             var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
             var textureType = this._getWebGLTextureType(type);
             var textureType = this._getWebGLTextureType(type);
-            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
 
 
             if (!this._doNotHandleContextLost) {
             if (!this._doNotHandleContextLost) {
@@ -3307,7 +3377,7 @@
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
             }
             }
             this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
-            this.resetTextureCache();
+            //  this.resetTextureCache();
             texture.isReady = true;
             texture.isReady = true;
         }
         }
 
 
@@ -3329,7 +3399,7 @@
             }
             }
 
 
             this.updateRawTexture(texture, data, format, invertY, compression, type);
             this.updateRawTexture(texture, data, format, invertY, compression, type);
-            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
 
 
             // Filters
             // Filters
             var filters = getSamplingParameters(samplingMode, generateMipMaps, this._gl);
             var filters = getSamplingParameters(samplingMode, generateMipMaps, this._gl);
@@ -3359,7 +3429,7 @@
                 height = this.needPOTTextures ? Tools.GetExponentOfTwo(height, this._caps.maxTextureSize) : height;
                 height = this.needPOTTextures ? Tools.GetExponentOfTwo(height, this._caps.maxTextureSize) : height;
             }
             }
 
 
-            this.resetTextureCache();
+            //  this.resetTextureCache();
             texture.width = width;
             texture.width = width;
             texture.height = height;
             texture.height = height;
             texture.isReady = false;
             texture.isReady = false;
@@ -3377,19 +3447,19 @@
             var filters = getSamplingParameters(samplingMode, texture.generateMipMaps, this._gl);
             var filters = getSamplingParameters(samplingMode, texture.generateMipMaps, this._gl);
 
 
             if (texture.isCube) {
             if (texture.isCube) {
-                this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
+                this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture, true);
 
 
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._gl.texParameteri(this._gl.TEXTURE_CUBE_MAP, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             } else if (texture.is3D) {
             } else if (texture.is3D) {
-                this._bindTextureDirectly(this._gl.TEXTURE_3D, texture);
+                this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true);
 
 
                 this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._gl.texParameteri(this._gl.TEXTURE_3D, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
             } else {
             } else {
-                this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
+                this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
 
 
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
                 this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
@@ -3400,12 +3470,11 @@
         }
         }
 
 
         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): void {
-
             if (!texture) {
             if (!texture) {
                 return;
                 return;
             }
             }
 
 
-            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY ? 1 : 0);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY ? 1 : 0);
             if (premulAlpha) {
             if (premulAlpha) {
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
@@ -3419,7 +3488,6 @@
             if (premulAlpha) {
             if (premulAlpha) {
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
                 this._gl.pixelStorei(this._gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0);
             }
             }
-            this.resetTextureCache();
             texture.isReady = true;
             texture.isReady = true;
         }
         }
 
 
@@ -3428,7 +3496,7 @@
                 return;
                 return;
             }
             }
 
 
-            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY ? 0 : 1); // Video are upside down by default
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY ? 0 : 1); // Video are upside down by default
 
 
             try {
             try {
@@ -3470,7 +3538,7 @@
                 }
                 }
 
 
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
-                this.resetTextureCache();
+                //    this.resetTextureCache();
                 texture.isReady = true;
                 texture.isReady = true;
 
 
             } catch (ex) {
             } catch (ex) {
@@ -3508,7 +3576,7 @@
             var gl = this._gl;
             var gl = this._gl;
 
 
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RENDERTARGET);
             var texture = new InternalTexture(this, InternalTexture.DATASOURCE_RENDERTARGET);
-            this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
 
 
             var width = (<{ width: number, height: number }>size).width || <number>size;
             var width = (<{ width: number, height: number }>size).width || <number>size;
             var height = (<{ width: number, height: number }>size).height || <number>size;
             var height = (<{ width: number, height: number }>size).height || <number>size;
@@ -3556,7 +3624,7 @@
             texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
             texture._generateDepthBuffer = fullOptions.generateDepthBuffer;
             texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
             texture._generateStencilBuffer = fullOptions.generateStencilBuffer ? true : false;
 
 
-            this.resetTextureCache();
+            // this.resetTextureCache();
 
 
             this._internalTexturesCache.push(texture);
             this._internalTexturesCache.push(texture);
 
 
@@ -3660,6 +3728,7 @@
                 texture.type = type;
                 texture.type = type;
                 texture._generateDepthBuffer = generateDepthBuffer;
                 texture._generateDepthBuffer = generateDepthBuffer;
                 texture._generateStencilBuffer = generateStencilBuffer;
                 texture._generateStencilBuffer = generateStencilBuffer;
+                texture._attachments = attachments;
 
 
                 this._internalTexturesCache.push(texture);
                 this._internalTexturesCache.push(texture);
             }
             }
@@ -3768,14 +3837,17 @@
             // Dispose previous render buffers
             // Dispose previous render buffers
             if (texture._depthStencilBuffer) {
             if (texture._depthStencilBuffer) {
                 gl.deleteRenderbuffer(texture._depthStencilBuffer);
                 gl.deleteRenderbuffer(texture._depthStencilBuffer);
+                texture._depthStencilBuffer = null;
             }
             }
 
 
             if (texture._MSAAFramebuffer) {
             if (texture._MSAAFramebuffer) {
                 gl.deleteFramebuffer(texture._MSAAFramebuffer);
                 gl.deleteFramebuffer(texture._MSAAFramebuffer);
+                texture._MSAAFramebuffer = null;
             }
             }
 
 
             if (texture._MSAARenderBuffer) {
             if (texture._MSAARenderBuffer) {
                 gl.deleteRenderbuffer(texture._MSAARenderBuffer);
                 gl.deleteRenderbuffer(texture._MSAARenderBuffer);
+                texture._MSAARenderBuffer = null;
             }
             }
 
 
             if (samples > 1) {
             if (samples > 1) {
@@ -3795,7 +3867,7 @@
                 }
                 }
 
 
                 gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
                 gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
-                gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8, texture.width, texture.height);
+                gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, this._getRGBAMultiSampleBufferFormat(texture.type), texture.width, texture.height);
 
 
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
 
 
@@ -3813,6 +3885,82 @@
             return samples;
             return samples;
         }
         }
 
 
+        public updateMultipleRenderTargetTextureSampleCount(textures: Nullable<InternalTexture[]>, samples: number): number {
+            if (this.webGLVersion < 2 || !textures || textures.length == 0) {
+                return 1;
+            }
+
+            if (textures[0].samples === samples) {
+                return samples;
+            }
+
+            var gl = this._gl;
+
+            samples = Math.min(samples, gl.getParameter(gl.MAX_SAMPLES));
+
+            // Dispose previous render buffers
+            if (textures[0]._depthStencilBuffer) {
+                gl.deleteRenderbuffer(textures[0]._depthStencilBuffer);
+                textures[0]._depthStencilBuffer = null;
+            }
+
+            if (textures[0]._MSAAFramebuffer) {
+                gl.deleteFramebuffer(textures[0]._MSAAFramebuffer);
+                textures[0]._MSAAFramebuffer = null;
+            }
+
+            for (var i = 0; i < textures.length; i++) {
+                if (textures[i]._MSAARenderBuffer) {
+                    gl.deleteRenderbuffer(textures[i]._MSAARenderBuffer);
+                    textures[i]._MSAARenderBuffer = null;
+                }
+            }
+
+            if (samples > 1) {
+                let framebuffer = gl.createFramebuffer();
+
+                if (!framebuffer) {
+                    throw new Error("Unable to create multi sampled framebuffer");
+                }
+
+                this.bindUnboundFramebuffer(framebuffer);
+
+                let depthStencilBuffer = this._setupFramebufferDepthAttachments(textures[0]._generateStencilBuffer, textures[0]._generateDepthBuffer, textures[0].width, textures[0].height, samples);
+
+                var attachments = [];
+
+                for (var i = 0; i < textures.length; i++) {
+                    var texture = textures[i];
+                    var attachment = (<any>gl)[this.webGLVersion > 1 ? "COLOR_ATTACHMENT" + i : "COLOR_ATTACHMENT" + i + "_WEBGL"];
+
+                    var colorRenderbuffer = gl.createRenderbuffer();
+
+                    if (!colorRenderbuffer) {
+                        throw new Error("Unable to create multi sampled framebuffer");
+                    }
+
+                    gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
+                    gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, this._getRGBAMultiSampleBufferFormat(texture.type), texture.width, texture.height);
+
+                    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, colorRenderbuffer);
+
+                    texture._MSAAFramebuffer = framebuffer;
+                    texture._MSAARenderBuffer = colorRenderbuffer;
+                    texture.samples = samples;
+                    texture._depthStencilBuffer = depthStencilBuffer;
+                    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+                    attachments.push(attachment);
+                }
+                gl.drawBuffers(attachments);
+            } else {
+                this.bindUnboundFramebuffer(textures[0]._framebuffer);
+            }
+
+            this.bindUnboundFramebuffer(null);
+
+            return samples;
+        }
+
         public _uploadDataToTexture(target: number, lod: number, internalFormat: number, width: number, height: number, format: number, type: number, data: ArrayBufferView) {
         public _uploadDataToTexture(target: number, lod: number, internalFormat: number, width: number, height: number, format: number, type: number, data: ArrayBufferView) {
             this._gl.texImage2D(target, lod, internalFormat, width, height, 0, format, type, data);
             this._gl.texImage2D(target, lod, internalFormat, width, height, 0, format, type, data);
         }
         }
@@ -3848,7 +3996,7 @@
 
 
             var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
             var filters = getSamplingParameters(samplingMode, generateMipMaps, gl);
 
 
-            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
 
 
             for (var face = 0; face < 6; face++) {
             for (var face = 0; face < 6; face++) {
                 gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
                 gl.texImage2D((gl.TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
@@ -3867,7 +4015,6 @@
 
 
             // Mipmaps
             // Mipmaps
             if (texture.generateMipMaps) {
             if (texture.generateMipMaps) {
-                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                 gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
             }
             }
 
 
@@ -3881,7 +4028,7 @@
             texture.height = size;
             texture.height = size;
             texture.isReady = true;
             texture.isReady = true;
 
 
-            this.resetTextureCache();
+            //this.resetTextureCache();
 
 
             this._internalTexturesCache.push(texture);
             this._internalTexturesCache.push(texture);
 
 
@@ -3934,7 +4081,7 @@
 
 
                     var glTextureFromLod = new InternalTexture(this, InternalTexture.DATASOURCE_TEMP);
                     var glTextureFromLod = new InternalTexture(this, InternalTexture.DATASOURCE_TEMP);
                     glTextureFromLod.isCube = true;
                     glTextureFromLod.isCube = true;
-                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, glTextureFromLod);
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, glTextureFromLod, true);
 
 
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
@@ -4007,12 +4154,12 @@
             }
             }
 
 
             if (isKTX) {
             if (isKTX) {
-                Tools.LoadFile(rootUrl, data => {
+                this._loadFile(rootUrl, data => {
                     var ktx = new KhronosTextureContainer(data, 6);
                     var ktx = new KhronosTextureContainer(data, 6);
 
 
                     var loadMipmap = ktx.numberOfMipmapLevels > 1 && !noMipmap;
                     var loadMipmap = ktx.numberOfMipmapLevels > 1 && !noMipmap;
 
 
-                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
 
 
                     ktx.uploadLevels(this._gl, !noMipmap);
                     ktx.uploadLevels(this._gl, !noMipmap);
@@ -4025,7 +4172,7 @@
                 }, undefined, undefined, true, onerror);
                 }, undefined, undefined, true, onerror);
             } else if (isDDS) {
             } else if (isDDS) {
                 if (files && files.length === 6) {
                 if (files && files.length === 6) {
-                    cascadeLoadFiles(rootUrl,
+                    this._cascadeLoadFiles(rootUrl,
                         scene,
                         scene,
                         imgs => {
                         imgs => {
                             var info: DDSInfo | undefined;
                             var info: DDSInfo | undefined;
@@ -4037,7 +4184,7 @@
 
 
                                 loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
                                 loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
 
-                                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                                 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, info.isCompressed ? 1 : 0);
                                 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, info.isCompressed ? 1 : 0);
 
 
                                 DDSTools.UploadDDSLevels(this, this._gl, data, info, loadMipmap, 6, -1, index);
                                 DDSTools.UploadDDSLevels(this, this._gl, data, info, loadMipmap, 6, -1, index);
@@ -4063,13 +4210,13 @@
                         onError);
                         onError);
 
 
                 } else {
                 } else {
-                    Tools.LoadFile(rootUrl,
+                    this._loadFile(rootUrl,
                         data => {
                         data => {
                             var info = DDSTools.GetDDSInfo(data);
                             var info = DDSTools.GetDDSInfo(data);
 
 
                             var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
                             var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
 
-                            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, info.isCompressed ? 1 : 0);
                             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, info.isCompressed ? 1 : 0);
 
 
                             DDSTools.UploadDDSLevels(this, this._gl, data, info, loadMipmap, 6);
                             DDSTools.UploadDDSLevels(this, this._gl, data, info, loadMipmap, 6);
@@ -4115,7 +4262,7 @@
                         gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
                         gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
                     ];
                     ];
 
 
-                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
 
 
                     let internalFormat = format ? this._getInternalFormat(format) : this._gl.RGBA;
                     let internalFormat = format ? this._getInternalFormat(format) : this._gl.RGBA;
@@ -4159,7 +4306,7 @@
 
 
             this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
             this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
 
 
-            this.resetTextureCache();
+            //  this.resetTextureCache();
         }
         }
 
 
         public updateRawCubeTexture(texture: InternalTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable<string> = null, level = 0): void {
         public updateRawCubeTexture(texture: InternalTexture, data: ArrayBufferView[], format: number, type: number, invertY: boolean, compression: Nullable<string> = null, level = 0): void {
@@ -4180,7 +4327,7 @@
                 needConversion = true;
                 needConversion = true;
             }
             }
 
 
-            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
 
 
             if (texture.width % 4 !== 0) {
             if (texture.width % 4 !== 0) {
@@ -4207,7 +4354,7 @@
             }
             }
             this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
             this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
 
 
-            this.resetTextureCache();
+            // this.resetTextureCache();
             texture.isReady = true;
             texture.isReady = true;
         }
         }
 
 
@@ -4246,7 +4393,7 @@
                 this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
                 this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
             }
             }
 
 
-            this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture, true);
 
 
             // Filters
             // Filters
             if (data && generateMipMaps) {
             if (data && generateMipMaps) {
@@ -4314,7 +4461,7 @@
                         needConversion = true;
                         needConversion = true;
                     }
                     }
 
 
-                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
 
 
                     var mipData = mipmmapGenerator(faceDataArrays);
                     var mipData = mipmmapGenerator(faceDataArrays);
@@ -4338,7 +4485,7 @@
                 }
                 }
 
 
                 texture.isReady = true;
                 texture.isReady = true;
-                this.resetTextureCache();
+                // this.resetTextureCache();
                 scene._removePendingData(texture);
                 scene._removePendingData(texture);
 
 
                 if (onLoad) {
                 if (onLoad) {
@@ -4346,7 +4493,7 @@
                 }
                 }
             };
             };
 
 
-            Tools.LoadFile(url, data => {
+            this._loadFile(url, data => {
                 internalCallback(data);
                 internalCallback(data);
             }, undefined, scene.database, true, onerror);
             }, undefined, scene.database, true, onerror);
 
 
@@ -4355,7 +4502,7 @@
 
 
         public updateRawTexture3D(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null): void {
         public updateRawTexture3D(texture: InternalTexture, data: Nullable<ArrayBufferView>, format: number, invertY: boolean, compression: Nullable<string> = null): void {
             var internalFormat = this._getInternalFormat(format);
             var internalFormat = this._getInternalFormat(format);
-            this._bindTextureDirectly(this._gl.TEXTURE_3D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true);
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
             this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
 
 
             if (!this._doNotHandleContextLost) {
             if (!this._doNotHandleContextLost) {
@@ -4379,7 +4526,7 @@
                 this._gl.generateMipmap(this._gl.TEXTURE_3D);
                 this._gl.generateMipmap(this._gl.TEXTURE_3D);
             }
             }
             this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
             this._bindTextureDirectly(this._gl.TEXTURE_3D, null);
-            this.resetTextureCache();
+            // this.resetTextureCache();
             texture.isReady = true;
             texture.isReady = true;
         }
         }
 
 
@@ -4401,7 +4548,7 @@
             }
             }
 
 
             this.updateRawTexture3D(texture, data, format, invertY, compression);
             this.updateRawTexture3D(texture, data, format, invertY, compression);
-            this._bindTextureDirectly(this._gl.TEXTURE_3D, texture);
+            this._bindTextureDirectly(this._gl.TEXTURE_3D, texture, true);
 
 
             // Filters
             // Filters
             var filters = getSamplingParameters(samplingMode, generateMipMaps, this._gl);
             var filters = getSamplingParameters(samplingMode, generateMipMaps, this._gl);
@@ -4437,7 +4584,7 @@
 
 
             this._bindTextureDirectly(gl.TEXTURE_2D, null);
             this._bindTextureDirectly(gl.TEXTURE_2D, null);
 
 
-            this.resetTextureCache();
+            // this.resetTextureCache();
             if (scene) {
             if (scene) {
                 scene._removePendingData(texture);
                 scene._removePendingData(texture);
             }
             }
@@ -4457,7 +4604,7 @@
             }
             }
 
 
             if (!texture._webGLTexture) {
             if (!texture._webGLTexture) {
-                this.resetTextureCache();
+                //  this.resetTextureCache();
                 if (scene) {
                 if (scene) {
                     scene._removePendingData(texture);
                     scene._removePendingData(texture);
                 }
                 }
@@ -4465,7 +4612,7 @@
                 return;
                 return;
             }
             }
 
 
-            this._bindTextureDirectly(gl.TEXTURE_2D, texture);
+            this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, invertY === undefined ? 1 : (invertY ? 1 : 0));
 
 
             texture.baseWidth = width;
             texture.baseWidth = width;
@@ -4588,34 +4735,38 @@
         }
         }
 
 
         private _activateTextureChannel(channel: number): void {
         private _activateTextureChannel(channel: number): void {
-            if (this._activeChannel !== channel) {
+            if (this._activeChannel !== channel && channel > -1) {
                 this._gl.activeTexture(this._gl.TEXTURE0 + channel);
                 this._gl.activeTexture(this._gl.TEXTURE0 + channel);
                 this._activeChannel = channel;
                 this._activeChannel = channel;
             }
             }
         }
         }
 
 
         private _moveBoundTextureOnTop(internalTexture: InternalTexture): void {
         private _moveBoundTextureOnTop(internalTexture: InternalTexture): void {
-            let index = this._boundTexturesOrder.indexOf(internalTexture);
+            let index = this._boundTexturesStack.indexOf(internalTexture);
 
 
-            if (index > -1 && index !== this._boundTexturesOrder.length - 1) {
-                this._boundTexturesOrder.splice(index, 1);
-                this._boundTexturesOrder.push(internalTexture);
+            if (index > -1 && index !== this._boundTexturesStack.length - 1) {
+                this._boundTexturesStack.splice(index, 1);
+                this._boundTexturesStack.push(internalTexture);
             }
             }
         }
         }
 
 
         private _removeDesignatedSlot(internalTexture: InternalTexture): number {
         private _removeDesignatedSlot(internalTexture: InternalTexture): number {
             let currentSlot = internalTexture._designatedSlot;
             let currentSlot = internalTexture._designatedSlot;
             internalTexture._designatedSlot = -1;
             internalTexture._designatedSlot = -1;
-            let index = this._boundTexturesOrder.indexOf(internalTexture);
+            let index = this._boundTexturesStack.indexOf(internalTexture);
 
 
             if (index > -1) {
             if (index > -1) {
-                this._boundTexturesOrder.splice(index, 1);
+                this._boundTexturesStack.splice(index, 1);
+                if (currentSlot > -1) {
+                    this._boundTexturesCache[currentSlot] = null;
+                    this._nextFreeTextureSlots.push(currentSlot);
+                }
             }
             }
 
 
             return currentSlot;
             return currentSlot;
         }
         }
 
 
-        public _bindTextureDirectly(target: number, texture: Nullable<InternalTexture>, isPartOfTextureArray = false): void {
+        public _bindTextureDirectly(target: number, texture: Nullable<InternalTexture>, doNotBindUniformToTextureChannel = false): void {
             let currentTextureBound = this._boundTexturesCache[this._activeChannel];
             let currentTextureBound = this._boundTexturesCache[this._activeChannel];
             let isTextureForRendering = texture && texture._initialSlot > -1;
             let isTextureForRendering = texture && texture._initialSlot > -1;
 
 
@@ -4630,14 +4781,18 @@
                     this._boundTexturesCache[this._activeChannel] = texture;
                     this._boundTexturesCache[this._activeChannel] = texture;
 
 
                     if (isTextureForRendering) {
                     if (isTextureForRendering) {
-                        this._boundTexturesOrder.push(texture!);
+                        let slotIndex = this._nextFreeTextureSlots.indexOf(this._activeChannel);
+                        if (slotIndex > -1) {
+                            this._nextFreeTextureSlots.splice(slotIndex, 1);
+                        }
+                        this._boundTexturesStack.push(texture!);
                     }
                     }
                 }
                 }
             }
             }
 
 
-            if (isTextureForRendering) {
+            if (isTextureForRendering && this._activeChannel > -1) {
                 texture!._designatedSlot = this._activeChannel;
                 texture!._designatedSlot = this._activeChannel;
-                if (!isPartOfTextureArray) {
+                if (!doNotBindUniformToTextureChannel) {
                     this._bindSamplerUniformToChannel(texture!._initialSlot, this._activeChannel);
                     this._bindSamplerUniformToChannel(texture!._initialSlot, this._activeChannel);
                 }
                 }
             }
             }
@@ -4661,7 +4816,7 @@
         }
         }
 
 
         public unbindAllTextures(): void {
         public unbindAllTextures(): void {
-            for (var channel = 0; channel < this._caps.maxTexturesImageUnits; channel++) {
+            for (var channel = 0; channel < this._caps.maxCombinedTexturesImageUnits; channel++) {
                 this._activateTextureChannel(channel);
                 this._activateTextureChannel(channel);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
@@ -4683,7 +4838,7 @@
             this._setTexture(channel, texture);
             this._setTexture(channel, texture);
         }
         }
 
 
-        private _getCorrectTextureChannel(channel: number, internalTexture: Nullable<InternalTexture>) {
+        private _getCorrectTextureChannel(channel: number, internalTexture: Nullable<InternalTexture>): number {
             if (!internalTexture) {
             if (!internalTexture) {
                 return -1;
                 return -1;
             }
             }
@@ -4692,17 +4847,16 @@
 
 
             if (channel !== internalTexture._designatedSlot) {
             if (channel !== internalTexture._designatedSlot) {
                 if (internalTexture._designatedSlot > -1) { // Texture is already assigned to a slot
                 if (internalTexture._designatedSlot > -1) { // Texture is already assigned to a slot
-                    channel = internalTexture._designatedSlot;
-                } else { // Not slot for this texture, let's pick a new one
-                    if (this._nextFreeTextureSlot > -1) { // We can use a free slot
-                        channel = this._nextFreeTextureSlot;
-                        this._nextFreeTextureSlot++;
-                        if (this._nextFreeTextureSlot >= this._caps.maxTexturesImageUnits) {
-                            this._nextFreeTextureSlot = -1; // No more free slots, we will recycle
-                        }
-                    } else { // We need to recycle the oldest bound texture, sorry.
-                        channel = this._removeDesignatedSlot(this._boundTexturesOrder[0]);
+                    return internalTexture._designatedSlot;
+                } else {
+                    // No slot for this texture, let's pick a new one (if we find a free slot)
+                    if (this._nextFreeTextureSlots.length) {
+                        return this._nextFreeTextureSlots[0];
                     }
                     }
+
+                    // We need to recycle the oldest bound texture, sorry.
+                    this._textureCollisions.addCount(1, false);
+                    return this._removeDesignatedSlot(this._boundTexturesStack[0]);
                 }
                 }
             }
             }
 
 
@@ -5112,6 +5266,11 @@
             this.onEndFrameObservable.clear();
             this.onEndFrameObservable.clear();
 
 
             Effect.ResetCache();
             Effect.ResetCache();
+
+            // Abort active requests
+            for (let request of this._activeRequests) {
+                request.abort();
+            }
         }
         }
 
 
         // Loading screen
         // Loading screen
@@ -5335,6 +5494,17 @@
             return this._gl.RGBA;
             return this._gl.RGBA;
         };
         };
 
 
+        public _getRGBAMultiSampleBufferFormat(type: number): number {
+            if (type === Engine.TEXTURETYPE_FLOAT) {
+                return this._gl.RGBA32F;
+            }
+            else if (type === Engine.TEXTURETYPE_HALF_FLOAT) {
+                return this._gl.RGBA16F;
+            }
+
+            return this._gl.RGBA8;
+        };
+
         public createQuery(): WebGLQuery {
         public createQuery(): WebGLQuery {
             return this._gl.createQuery();
             return this._gl.createQuery();
         }
         }
@@ -5536,6 +5706,43 @@
             this._gl.bindBufferBase(this._gl.TRANSFORM_FEEDBACK_BUFFER, 0, value);
             this._gl.bindBufferBase(this._gl.TRANSFORM_FEEDBACK_BUFFER, 0, value);
         }
         }
 
 
+        public _loadFile(url: string, onSuccess: (data: string | ArrayBuffer, responseURL?: string) => void, onProgress?: (data: any) => void, database?: Database, useArrayBuffer?: boolean, onError?: (request?: XMLHttpRequest, exception?: any) => void): IFileRequest {
+            let request = Tools.LoadFile(url, onSuccess, onProgress, database, useArrayBuffer, onError);
+            this._activeRequests.push(request);
+            request.onCompleteObservable.add(request => {
+                this._activeRequests.splice(this._activeRequests.indexOf(request), 1);
+            });
+            return request;
+        }
+
+        private _partialLoadFile(url: string, index: number, loadedFiles: (string | ArrayBuffer)[], scene: Nullable<Scene>, onfinish: (files: (string | ArrayBuffer)[]) => void, onErrorCallBack: Nullable<(message?: string, exception?: any) => void> = null): void {
+            var onload = (data: string | ArrayBuffer) => {
+                loadedFiles[index] = data;
+                (<any>loadedFiles)._internalCount++;
+
+                if ((<any>loadedFiles)._internalCount === 6) {
+                    onfinish(loadedFiles);
+                }
+            };
+
+            const onerror = (request?: XMLHttpRequest, exception?: any) => {
+                if (onErrorCallBack && request) {
+                    onErrorCallBack(request.status + " " + request.statusText, exception);
+                }
+            };
+
+            this._loadFile(url, onload, undefined, undefined, true, onerror);
+        }
+
+        private _cascadeLoadFiles(rootUrl: string, scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void> = null): void {
+            var loadedFiles: (string | ArrayBuffer)[] = [];
+            (<any>loadedFiles)._internalCount = 0;
+
+            for (let index = 0; index < 6; index++) {
+                this._partialLoadFile(files[index], index, loadedFiles, scene, onfinish, onError);
+            }
+        }
+
         // Statics
         // Statics
         public static isSupported(): boolean {
         public static isSupported(): boolean {
             try {
             try {

+ 11 - 7
src/Helpers/babylon.environmentHelper.ts

@@ -81,6 +81,11 @@ module BABYLON {
          * Unsigned Int by Default.
          * Unsigned Int by Default.
          */
          */
         groundMirrorTextureType: number;
         groundMirrorTextureType: number;
+        /**
+         * Specifies a bias applied to the ground vertical position to prevent z-fighyting with
+         * the shown objects.
+         */
+        groundYBias: number;
 
 
         /**
         /**
          * Specifies wether or not to create a skybox.
          * Specifies wether or not to create a skybox.
@@ -204,6 +209,8 @@ module BABYLON {
                 groundMirrorFallOffDistance: 0,
                 groundMirrorFallOffDistance: 0,
                 groundMirrorTextureType: Engine.TEXTURETYPE_UNSIGNED_INT,
                 groundMirrorTextureType: Engine.TEXTURETYPE_UNSIGNED_INT,
 
 
+                groundYBias: 0.00001,
+
                 createSkybox: true,
                 createSkybox: true,
                 skyboxSize: 20,
                 skyboxSize: 20,
                 skyboxTexture: this._skyboxTextureCDNUrl,
                 skyboxTexture: this._skyboxTextureCDNUrl,
@@ -471,28 +478,25 @@ module BABYLON {
 
 
             const sceneExtends = this._scene.getWorldExtends();
             const sceneExtends = this._scene.getWorldExtends();
             const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
             const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
-            let bias = 0.0001;
 
 
             if (this._options.sizeAuto) {
             if (this._options.sizeAuto) {
                 if (this._scene.activeCamera instanceof ArcRotateCamera &&
                 if (this._scene.activeCamera instanceof ArcRotateCamera &&
                     this._scene.activeCamera.upperRadiusLimit) {
                     this._scene.activeCamera.upperRadiusLimit) {
                     groundSize = this._scene.activeCamera.upperRadiusLimit * 2;
                     groundSize = this._scene.activeCamera.upperRadiusLimit * 2;
-                }
-
-                if (this._scene.activeCamera) {
-                    bias = (this._scene.activeCamera.maxZ - this._scene.activeCamera.minZ) / 10000;
+                    skyboxSize = groundSize;
                 }
                 }
 
 
                 const sceneDiagonalLenght = sceneDiagonal.length();
                 const sceneDiagonalLenght = sceneDiagonal.length();
                 if (sceneDiagonalLenght > groundSize) {
                 if (sceneDiagonalLenght > groundSize) {
                     groundSize = sceneDiagonalLenght * 2;
                     groundSize = sceneDiagonalLenght * 2;
+                    skyboxSize = groundSize;
                 }
                 }
 
 
                 // 10 % bigger.
                 // 10 % bigger.
                 groundSize *= 1.1;
                 groundSize *= 1.1;
                 skyboxSize *= 1.5;
                 skyboxSize *= 1.5;
                 rootPosition = sceneExtends.min.add(sceneDiagonal.scale(0.5));
                 rootPosition = sceneExtends.min.add(sceneDiagonal.scale(0.5));
-                rootPosition.y = sceneExtends.min.y - bias;
+                rootPosition.y = sceneExtends.min.y - this._options.groundYBias;
             }
             }
 
 
             return { groundSize, skyboxSize, rootPosition };
             return { groundSize, skyboxSize, rootPosition };
@@ -506,7 +510,7 @@ module BABYLON {
                 this._ground = Mesh.CreatePlane("BackgroundPlane", sceneSize.groundSize, this._scene);
                 this._ground = Mesh.CreatePlane("BackgroundPlane", sceneSize.groundSize, this._scene);
                 this._ground.rotation.x = Math.PI / 2; // Face up by default.
                 this._ground.rotation.x = Math.PI / 2; // Face up by default.
                 this._ground.parent = this._rootMesh;
                 this._ground.parent = this._rootMesh;
-                this._ground.onDisposeObservable.add(() => { this._ground = null; })
+                this._ground.onDisposeObservable.add(() => { this._ground = null; });
             }
             }
 
 
             this._ground.receiveShadows = this._options.enableGroundShadow;
             this._ground.receiveShadows = this._options.enableGroundShadow;

+ 109 - 101
src/Instrumentation/babylon.sceneInstrumentation.ts

@@ -4,31 +4,31 @@ module BABYLON {
      */
      */
     export class SceneInstrumentation implements IDisposable {
     export class SceneInstrumentation implements IDisposable {
         private _captureActiveMeshesEvaluationTime = false;
         private _captureActiveMeshesEvaluationTime = false;
-        private _activeMeshesEvaluationTime = new PerfCounter();  
+        private _activeMeshesEvaluationTime = new PerfCounter();
 
 
         private _captureRenderTargetsRenderTime = false;
         private _captureRenderTargetsRenderTime = false;
-        private _renderTargetsRenderTime = new PerfCounter();    
-        
+        private _renderTargetsRenderTime = new PerfCounter();
+
         private _captureFrameTime = false;
         private _captureFrameTime = false;
-        private _frameTime = new PerfCounter();        
+        private _frameTime = new PerfCounter();
 
 
         private _captureRenderTime = false;
         private _captureRenderTime = false;
-        private _renderTime = new PerfCounter();           
+        private _renderTime = new PerfCounter();
 
 
         private _captureInterFrameTime = false;
         private _captureInterFrameTime = false;
-        private _interFrameTime = new PerfCounter();    
-        
+        private _interFrameTime = new PerfCounter();
+
         private _captureParticlesRenderTime = false;
         private _captureParticlesRenderTime = false;
-        private _particlesRenderTime = new PerfCounter();       
-          
+        private _particlesRenderTime = new PerfCounter();
+
         private _captureSpritesRenderTime = false;
         private _captureSpritesRenderTime = false;
-        private _spritesRenderTime = new PerfCounter();   
+        private _spritesRenderTime = new PerfCounter();
 
 
         private _capturePhysicsTime = false;
         private _capturePhysicsTime = false;
-        private _physicsTime = new PerfCounter();     
-        
+        private _physicsTime = new PerfCounter();
+
         private _captureAnimationsTime = false;
         private _captureAnimationsTime = false;
-        private _animationsTime = new PerfCounter();            
+        private _animationsTime = new PerfCounter();
 
 
         // Observers
         // Observers
         private _onBeforeActiveMeshesEvaluationObserver: Nullable<Observer<Scene>> = null;
         private _onBeforeActiveMeshesEvaluationObserver: Nullable<Observer<Scene>> = null;
@@ -39,21 +39,21 @@ module BABYLON {
         private _onAfterRenderObserver: Nullable<Observer<Scene>> = null;
         private _onAfterRenderObserver: Nullable<Observer<Scene>> = null;
 
 
         private _onBeforeDrawPhaseObserver: Nullable<Observer<Scene>> = null;
         private _onBeforeDrawPhaseObserver: Nullable<Observer<Scene>> = null;
-        private _onAfterDrawPhaseObserver: Nullable<Observer<Scene>> = null;        
-        
+        private _onAfterDrawPhaseObserver: Nullable<Observer<Scene>> = null;
+
         private _onBeforeAnimationsObserver: Nullable<Observer<Scene>> = null;
         private _onBeforeAnimationsObserver: Nullable<Observer<Scene>> = null;
 
 
         private _onBeforeParticlesRenderingObserver: Nullable<Observer<Scene>> = null;
         private _onBeforeParticlesRenderingObserver: Nullable<Observer<Scene>> = null;
         private _onAfterParticlesRenderingObserver: Nullable<Observer<Scene>> = null;
         private _onAfterParticlesRenderingObserver: Nullable<Observer<Scene>> = null;
 
 
         private _onBeforeSpritesRenderingObserver: Nullable<Observer<Scene>> = null;
         private _onBeforeSpritesRenderingObserver: Nullable<Observer<Scene>> = null;
-        private _onAfterSpritesRenderingObserver: Nullable<Observer<Scene>> = null;      
-        
+        private _onAfterSpritesRenderingObserver: Nullable<Observer<Scene>> = null;
+
         private _onBeforePhysicsObserver: Nullable<Observer<Scene>> = null;
         private _onBeforePhysicsObserver: Nullable<Observer<Scene>> = null;
-        private _onAfterPhysicsObserver: Nullable<Observer<Scene>> = null;     
-        
-        private _onAfterAnimationsObserver: Nullable<Observer<Scene>> = null;    
-                
+        private _onAfterPhysicsObserver: Nullable<Observer<Scene>> = null;
+
+        private _onAfterAnimationsObserver: Nullable<Observer<Scene>> = null;
+
         // Properties
         // Properties
         /**
         /**
          * Gets the perf counter used for active meshes evaluation time
          * Gets the perf counter used for active meshes evaluation time
@@ -64,28 +64,28 @@ module BABYLON {
 
 
         /**
         /**
          * Gets the active meshes evaluation time capture status
          * Gets the active meshes evaluation time capture status
-         */        
+         */
         public get captureActiveMeshesEvaluationTime(): boolean {
         public get captureActiveMeshesEvaluationTime(): boolean {
             return this._captureActiveMeshesEvaluationTime;
             return this._captureActiveMeshesEvaluationTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the active meshes evaluation time capture
          * Enable or disable the active meshes evaluation time capture
-         */        
+         */
         public set captureActiveMeshesEvaluationTime(value: boolean) {
         public set captureActiveMeshesEvaluationTime(value: boolean) {
             if (value === this._captureActiveMeshesEvaluationTime) {
             if (value === this._captureActiveMeshesEvaluationTime) {
                 return;
                 return;
             }
             }
 
 
-            this._captureActiveMeshesEvaluationTime = value;            
+            this._captureActiveMeshesEvaluationTime = value;
 
 
             if (value) {
             if (value) {
-                this._onBeforeActiveMeshesEvaluationObserver = this.scene.onBeforeActiveMeshesEvaluationObservable.add(()=>{
+                this._onBeforeActiveMeshesEvaluationObserver = this.scene.onBeforeActiveMeshesEvaluationObservable.add(() => {
                     Tools.StartPerformanceCounter("Active meshes evaluation");
                     Tools.StartPerformanceCounter("Active meshes evaluation");
                     this._activeMeshesEvaluationTime.beginMonitoring();
                     this._activeMeshesEvaluationTime.beginMonitoring();
                 });
                 });
 
 
-                this._onAfterActiveMeshesEvaluationObserver = this.scene.onAfterActiveMeshesEvaluationObservable.add(()=>{                    
+                this._onAfterActiveMeshesEvaluationObserver = this.scene.onAfterActiveMeshesEvaluationObservable.add(() => {
                     Tools.EndPerformanceCounter("Active meshes evaluation");
                     Tools.EndPerformanceCounter("Active meshes evaluation");
                     this._activeMeshesEvaluationTime.endMonitoring();
                     this._activeMeshesEvaluationTime.endMonitoring();
                 });
                 });
@@ -110,11 +110,11 @@ module BABYLON {
          */
          */
         public get captureRenderTargetsRenderTime(): boolean {
         public get captureRenderTargetsRenderTime(): boolean {
             return this._captureRenderTargetsRenderTime;
             return this._captureRenderTargetsRenderTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the render targets render time capture
          * Enable or disable the render targets render time capture
-         */        
+         */
         public set captureRenderTargetsRenderTime(value: boolean) {
         public set captureRenderTargetsRenderTime(value: boolean) {
             if (value === this._captureRenderTargetsRenderTime) {
             if (value === this._captureRenderTargetsRenderTime) {
                 return;
                 return;
@@ -123,12 +123,12 @@ module BABYLON {
             this._captureRenderTargetsRenderTime = value;
             this._captureRenderTargetsRenderTime = value;
 
 
             if (value) {
             if (value) {
-                this._onBeforeRenderTargetsRenderObserver = this.scene.OnBeforeRenderTargetsRenderObservable.add(()=>{
+                this._onBeforeRenderTargetsRenderObserver = this.scene.OnBeforeRenderTargetsRenderObservable.add(() => {
                     Tools.StartPerformanceCounter("Render targets rendering");
                     Tools.StartPerformanceCounter("Render targets rendering");
                     this._renderTargetsRenderTime.beginMonitoring();
                     this._renderTargetsRenderTime.beginMonitoring();
                 });
                 });
 
 
-                this._onAfterRenderTargetsRenderObserver = this.scene.OnAfterRenderTargetsRenderObservable.add(()=>{                    
+                this._onAfterRenderTargetsRenderObserver = this.scene.OnAfterRenderTargetsRenderObservable.add(() => {
                     Tools.EndPerformanceCounter("Render targets rendering");
                     Tools.EndPerformanceCounter("Render targets rendering");
                     this._renderTargetsRenderTime.endMonitoring(false);
                     this._renderTargetsRenderTime.endMonitoring(false);
                 });
                 });
@@ -139,7 +139,7 @@ module BABYLON {
                 this.scene.OnAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver);
                 this.scene.OnAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver);
                 this._onAfterRenderTargetsRenderObserver = null;
                 this._onAfterRenderTargetsRenderObserver = null;
             }
             }
-        }        
+        }
 
 
         /**
         /**
          * Gets the perf counter used for particles render time
          * Gets the perf counter used for particles render time
@@ -153,11 +153,11 @@ module BABYLON {
          */
          */
         public get captureParticlesRenderTime(): boolean {
         public get captureParticlesRenderTime(): boolean {
             return this._captureParticlesRenderTime;
             return this._captureParticlesRenderTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the particles render time capture
          * Enable or disable the particles render time capture
-         */        
+         */
         public set captureParticlesRenderTime(value: boolean) {
         public set captureParticlesRenderTime(value: boolean) {
             if (value === this._captureParticlesRenderTime) {
             if (value === this._captureParticlesRenderTime) {
                 return;
                 return;
@@ -166,12 +166,12 @@ module BABYLON {
             this._captureParticlesRenderTime = value;
             this._captureParticlesRenderTime = value;
 
 
             if (value) {
             if (value) {
-                this._onBeforeParticlesRenderingObserver = this.scene.onBeforeParticlesRenderingObservable.add(()=>{                    
+                this._onBeforeParticlesRenderingObserver = this.scene.onBeforeParticlesRenderingObservable.add(() => {
                     Tools.StartPerformanceCounter("Particles");
                     Tools.StartPerformanceCounter("Particles");
                     this._particlesRenderTime.beginMonitoring();
                     this._particlesRenderTime.beginMonitoring();
                 });
                 });
 
 
-                this._onAfterParticlesRenderingObserver = this.scene.onAfterParticlesRenderingObservable.add(()=>{                                        
+                this._onAfterParticlesRenderingObserver = this.scene.onAfterParticlesRenderingObservable.add(() => {
                     Tools.EndPerformanceCounter("Particles");
                     Tools.EndPerformanceCounter("Particles");
                     this._particlesRenderTime.endMonitoring(false);
                     this._particlesRenderTime.endMonitoring(false);
                 });
                 });
@@ -182,7 +182,7 @@ module BABYLON {
                 this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver);
                 this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver);
                 this._onAfterParticlesRenderingObserver = null;
                 this._onAfterParticlesRenderingObserver = null;
             }
             }
-        }        
+        }
 
 
         /**
         /**
          * Gets the perf counter used for sprites render time
          * Gets the perf counter used for sprites render time
@@ -196,11 +196,11 @@ module BABYLON {
          */
          */
         public get captureSpritesRenderTime(): boolean {
         public get captureSpritesRenderTime(): boolean {
             return this._captureSpritesRenderTime;
             return this._captureSpritesRenderTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the sprites render time capture
          * Enable or disable the sprites render time capture
-         */        
+         */
         public set captureSpritesRenderTime(value: boolean) {
         public set captureSpritesRenderTime(value: boolean) {
             if (value === this._captureSpritesRenderTime) {
             if (value === this._captureSpritesRenderTime) {
                 return;
                 return;
@@ -209,12 +209,12 @@ module BABYLON {
             this._captureSpritesRenderTime = value;
             this._captureSpritesRenderTime = value;
 
 
             if (value) {
             if (value) {
-                this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(()=>{                    
+                this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(() => {
                     Tools.StartPerformanceCounter("Sprites");
                     Tools.StartPerformanceCounter("Sprites");
                     this._spritesRenderTime.beginMonitoring();
                     this._spritesRenderTime.beginMonitoring();
                 });
                 });
 
 
-                this._onAfterSpritesRenderingObserver = this.scene.onAfterSpritesRenderingObservable.add(()=>{                                        
+                this._onAfterSpritesRenderingObserver = this.scene.onAfterSpritesRenderingObservable.add(() => {
                     Tools.EndPerformanceCounter("Sprites");
                     Tools.EndPerformanceCounter("Sprites");
                     this._spritesRenderTime.endMonitoring(false);
                     this._spritesRenderTime.endMonitoring(false);
                 });
                 });
@@ -225,7 +225,7 @@ module BABYLON {
                 this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver);
                 this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver);
                 this._onAfterSpritesRenderingObserver = null;
                 this._onAfterSpritesRenderingObserver = null;
             }
             }
-        }      
+        }
 
 
         /**
         /**
          * Gets the perf counter used for physics time
          * Gets the perf counter used for physics time
@@ -239,11 +239,11 @@ module BABYLON {
          */
          */
         public get capturePhysicsTime(): boolean {
         public get capturePhysicsTime(): boolean {
             return this._capturePhysicsTime;
             return this._capturePhysicsTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the physics time capture
          * Enable or disable the physics time capture
-         */        
+         */
         public set capturePhysicsTime(value: boolean) {
         public set capturePhysicsTime(value: boolean) {
             if (value === this._capturePhysicsTime) {
             if (value === this._capturePhysicsTime) {
                 return;
                 return;
@@ -252,12 +252,12 @@ module BABYLON {
             this._capturePhysicsTime = value;
             this._capturePhysicsTime = value;
 
 
             if (value) {
             if (value) {
-                this._onBeforePhysicsObserver = this.scene.onBeforePhysicsObservable.add(()=>{                    
+                this._onBeforePhysicsObserver = this.scene.onBeforePhysicsObservable.add(() => {
                     Tools.StartPerformanceCounter("Physics");
                     Tools.StartPerformanceCounter("Physics");
                     this._physicsTime.beginMonitoring();
                     this._physicsTime.beginMonitoring();
                 });
                 });
 
 
-                this._onAfterPhysicsObserver = this.scene.onAfterPhysicsObservable.add(()=>{                                        
+                this._onAfterPhysicsObserver = this.scene.onAfterPhysicsObservable.add(() => {
                     Tools.EndPerformanceCounter("Physics");
                     Tools.EndPerformanceCounter("Physics");
                     this._physicsTime.endMonitoring();
                     this._physicsTime.endMonitoring();
                 });
                 });
@@ -268,7 +268,7 @@ module BABYLON {
                 this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver);
                 this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver);
                 this._onAfterPhysicsObserver = null;
                 this._onAfterPhysicsObserver = null;
             }
             }
-        }      
+        }
 
 
 
 
         /**
         /**
@@ -283,11 +283,11 @@ module BABYLON {
          */
          */
         public get captureAnimationsTime(): boolean {
         public get captureAnimationsTime(): boolean {
             return this._captureAnimationsTime;
             return this._captureAnimationsTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the animations time capture
          * Enable or disable the animations time capture
-         */        
+         */
         public set captureAnimationsTime(value: boolean) {
         public set captureAnimationsTime(value: boolean) {
             if (value === this._captureAnimationsTime) {
             if (value === this._captureAnimationsTime) {
                 return;
                 return;
@@ -296,74 +296,74 @@ module BABYLON {
             this._captureAnimationsTime = value;
             this._captureAnimationsTime = value;
 
 
             if (value) {
             if (value) {
-                this._onAfterAnimationsObserver = this.scene.onAfterAnimationsObservable.add(()=>{                    
+                this._onAfterAnimationsObserver = this.scene.onAfterAnimationsObservable.add(() => {
                     this._animationsTime.endMonitoring();
                     this._animationsTime.endMonitoring();
                 });
                 });
             } else {
             } else {
                 this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver);
                 this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver);
                 this._onAfterAnimationsObserver = null;
                 this._onAfterAnimationsObserver = null;
             }
             }
-        }              
-        
+        }
+
         /**
         /**
          * Gets the perf counter used for frame time capture
          * Gets the perf counter used for frame time capture
          */
          */
         public get frameTimeCounter(): PerfCounter {
         public get frameTimeCounter(): PerfCounter {
             return this._frameTime;
             return this._frameTime;
-        }               
-       
+        }
+
         /**
         /**
          * Gets the frame time capture status
          * Gets the frame time capture status
-         */        
+         */
         public get captureFrameTime(): boolean {
         public get captureFrameTime(): boolean {
             return this._captureFrameTime;
             return this._captureFrameTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the frame time capture
          * Enable or disable the frame time capture
-         */        
+         */
         public set captureFrameTime(value: boolean) {
         public set captureFrameTime(value: boolean) {
             this._captureFrameTime = value;
             this._captureFrameTime = value;
-        }       
-     
+        }
+
         /**
         /**
          * Gets the perf counter used for inter-frames time capture
          * Gets the perf counter used for inter-frames time capture
          */
          */
         public get interFrameTimeCounter(): PerfCounter {
         public get interFrameTimeCounter(): PerfCounter {
             return this._interFrameTime;
             return this._interFrameTime;
-        }               
-       
+        }
+
         /**
         /**
          * Gets the inter-frames time capture status
          * Gets the inter-frames time capture status
-         */        
+         */
         public get captureInterFrameTime(): boolean {
         public get captureInterFrameTime(): boolean {
             return this._captureInterFrameTime;
             return this._captureInterFrameTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the inter-frames time capture
          * Enable or disable the inter-frames time capture
-         */        
+         */
         public set captureInterFrameTime(value: boolean) {
         public set captureInterFrameTime(value: boolean) {
             this._captureInterFrameTime = value;
             this._captureInterFrameTime = value;
-        }     
+        }
 
 
         /**
         /**
          * Gets the perf counter used for render time capture
          * Gets the perf counter used for render time capture
          */
          */
         public get renderTimeCounter(): PerfCounter {
         public get renderTimeCounter(): PerfCounter {
             return this._renderTime;
             return this._renderTime;
-        }               
-       
+        }
+
         /**
         /**
          * Gets the render time capture status
          * Gets the render time capture status
-         */        
+         */
         public get captureRenderTime(): boolean {
         public get captureRenderTime(): boolean {
             return this._captureRenderTime;
             return this._captureRenderTime;
-        }        
+        }
 
 
         /**
         /**
          * Enable or disable the render time capture
          * Enable or disable the render time capture
-         */        
+         */
         public set captureRenderTime(value: boolean) {
         public set captureRenderTime(value: boolean) {
             if (value === this._captureRenderTime) {
             if (value === this._captureRenderTime) {
                 return;
                 return;
@@ -372,30 +372,37 @@ module BABYLON {
             this._captureRenderTime = value;
             this._captureRenderTime = value;
 
 
             if (value) {
             if (value) {
-                this._onBeforeDrawPhaseObserver = this.scene.onBeforeDrawPhaseObservable.add(()=>{
-                    this._renderTime.beginMonitoring(); 
+                this._onBeforeDrawPhaseObserver = this.scene.onBeforeDrawPhaseObservable.add(() => {
+                    this._renderTime.beginMonitoring();
                     Tools.StartPerformanceCounter("Main render");
                     Tools.StartPerformanceCounter("Main render");
                 });
                 });
 
 
-                this._onAfterDrawPhaseObserver = this.scene.onAfterDrawPhaseObservable.add(()=>{
-                    this._renderTime.endMonitoring(false); 
+                this._onAfterDrawPhaseObserver = this.scene.onAfterDrawPhaseObservable.add(() => {
+                    this._renderTime.endMonitoring(false);
                     Tools.EndPerformanceCounter("Main render");
                     Tools.EndPerformanceCounter("Main render");
-                });                
+                });
             } else {
             } else {
                 this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver);
                 this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver);
                 this._onBeforeDrawPhaseObserver = null;
                 this._onBeforeDrawPhaseObserver = null;
                 this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver);
                 this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver);
                 this._onAfterDrawPhaseObserver = null;
                 this._onAfterDrawPhaseObserver = null;
             }
             }
-        }            
+        }
 
 
         /**
         /**
-         * Gets the perf counter used for frame time capture
+         * Gets the perf counter used for draw calls
          */
          */
         public get drawCallsCounter(): PerfCounter {
         public get drawCallsCounter(): PerfCounter {
             return this.scene.getEngine()._drawCalls;
             return this.scene.getEngine()._drawCalls;
-        }            
-    
+        }
+
+        /**
+         * Gets the perf counter used for texture collisions 
+         */
+        public get textureCollisionsCounter(): PerfCounter {
+            return this.scene.getEngine()._textureCollisions;
+        }
+
         public constructor(public scene: Scene) {
         public constructor(public scene: Scene) {
             // Before render
             // Before render
             this._onBeforeAnimationsObserver = scene.onBeforeAnimationsObservable.add(() => {
             this._onBeforeAnimationsObserver = scene.onBeforeAnimationsObservable.add(() => {
@@ -410,11 +417,11 @@ module BABYLON {
                 if (this._captureFrameTime) {
                 if (this._captureFrameTime) {
                     Tools.StartPerformanceCounter("Scene rendering");
                     Tools.StartPerformanceCounter("Scene rendering");
                     this._frameTime.beginMonitoring();
                     this._frameTime.beginMonitoring();
-                }   
+                }
 
 
                 if (this._captureInterFrameTime) {
                 if (this._captureInterFrameTime) {
-                    this._interFrameTime.endMonitoring(); 
-                }                   
+                    this._interFrameTime.endMonitoring();
+                }
 
 
                 if (this._captureParticlesRenderTime) {
                 if (this._captureParticlesRenderTime) {
                     this._particlesRenderTime.fetchNewFrame();
                     this._particlesRenderTime.fetchNewFrame();
@@ -429,22 +436,23 @@ module BABYLON {
                 }
                 }
 
 
                 this.scene.getEngine()._drawCalls.fetchNewFrame();
                 this.scene.getEngine()._drawCalls.fetchNewFrame();
+                this.scene.getEngine()._textureCollisions.fetchNewFrame();
             });
             });
 
 
             // After render
             // After render
             this._onAfterRenderObserver = scene.onAfterRenderObservable.add(() => {
             this._onAfterRenderObserver = scene.onAfterRenderObservable.add(() => {
                 if (this._captureFrameTime) {
                 if (this._captureFrameTime) {
                     Tools.EndPerformanceCounter("Scene rendering");
                     Tools.EndPerformanceCounter("Scene rendering");
-                    this._frameTime.endMonitoring();                    
+                    this._frameTime.endMonitoring();
                 }
                 }
 
 
                 if (this._captureRenderTime) {
                 if (this._captureRenderTime) {
-                    this._renderTime.endMonitoring(false);                    
-                }                
+                    this._renderTime.endMonitoring(false);
+                }
 
 
                 if (this._captureInterFrameTime) {
                 if (this._captureInterFrameTime) {
-                    this._interFrameTime.beginMonitoring();  
-                }                
+                    this._interFrameTime.beginMonitoring();
+                }
             });
             });
         }
         }
 
 
@@ -459,11 +467,11 @@ module BABYLON {
             this._onAfterActiveMeshesEvaluationObserver = null;
             this._onAfterActiveMeshesEvaluationObserver = null;
 
 
             this.scene.OnBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver);
             this.scene.OnBeforeRenderTargetsRenderObservable.remove(this._onBeforeRenderTargetsRenderObserver);
-            this._onBeforeRenderTargetsRenderObserver = null;   
-            
+            this._onBeforeRenderTargetsRenderObserver = null;
+
             this.scene.OnAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver);
             this.scene.OnAfterRenderTargetsRenderObservable.remove(this._onAfterRenderTargetsRenderObserver);
-            this._onAfterRenderTargetsRenderObserver = null;      
-            
+            this._onAfterRenderTargetsRenderObserver = null;
+
             this.scene.onBeforeAnimationsObservable.remove(this._onBeforeAnimationsObserver);
             this.scene.onBeforeAnimationsObservable.remove(this._onBeforeAnimationsObserver);
             this._onBeforeAnimationsObserver = null;
             this._onBeforeAnimationsObserver = null;
 
 
@@ -471,29 +479,29 @@ module BABYLON {
             this._onBeforeParticlesRenderingObserver = null;
             this._onBeforeParticlesRenderingObserver = null;
 
 
             this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver);
             this.scene.onAfterParticlesRenderingObservable.remove(this._onAfterParticlesRenderingObserver);
-            this._onAfterParticlesRenderingObserver = null;         
-            
+            this._onAfterParticlesRenderingObserver = null;
+
             this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver);
             this.scene.onBeforeSpritesRenderingObservable.remove(this._onBeforeSpritesRenderingObserver);
             this._onBeforeSpritesRenderingObserver = null;
             this._onBeforeSpritesRenderingObserver = null;
 
 
             this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver);
             this.scene.onAfterSpritesRenderingObservable.remove(this._onAfterSpritesRenderingObserver);
-            this._onAfterSpritesRenderingObserver = null;       
-            
+            this._onAfterSpritesRenderingObserver = null;
+
             this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver);
             this.scene.onBeforeDrawPhaseObservable.remove(this._onBeforeDrawPhaseObserver);
             this._onBeforeDrawPhaseObserver = null;
             this._onBeforeDrawPhaseObserver = null;
 
 
             this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver);
             this.scene.onAfterDrawPhaseObservable.remove(this._onAfterDrawPhaseObserver);
-            this._onAfterDrawPhaseObserver = null;    
-            
+            this._onAfterDrawPhaseObserver = null;
+
             this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver);
             this.scene.onBeforePhysicsObservable.remove(this._onBeforePhysicsObserver);
             this._onBeforePhysicsObserver = null;
             this._onBeforePhysicsObserver = null;
 
 
             this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver);
             this.scene.onAfterPhysicsObservable.remove(this._onAfterPhysicsObserver);
-            this._onAfterPhysicsObserver = null;    
-            
+            this._onAfterPhysicsObserver = null;
+
             this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver);
             this.scene.onAfterAnimationsObservable.remove(this._onAfterAnimationsObserver);
-            this._onAfterAnimationsObserver = null;            
-                
+            this._onAfterAnimationsObserver = null;
+
             (<any>this.scene) = null;
             (<any>this.scene) = null;
         }
         }
     }
     }

+ 64 - 25
src/Loading/babylon.sceneLoader.ts

@@ -1,4 +1,13 @@
 module BABYLON {
 module BABYLON {
+    export class SceneLoaderProgressEvent {
+        constructor(public readonly lengthComputable: boolean, public readonly loaded: number, public readonly total: number) {
+        }
+
+        public static FromProgressEvent(event: ProgressEvent): SceneLoaderProgressEvent {
+            return new SceneLoaderProgressEvent(event.lengthComputable, event.loaded, event.total);
+        }
+    }
+
     export interface ISceneLoaderPluginExtensions {
     export interface ISceneLoaderPluginExtensions {
         [extension: string]: {
         [extension: string]: {
             isBinary: boolean;
             isBinary: boolean;
@@ -23,8 +32,8 @@
     export interface ISceneLoaderPluginAsync {
     export interface ISceneLoaderPluginAsync {
         name: string;
         name: string;
         extensions: string | ISceneLoaderPluginExtensions;
         extensions: string | ISceneLoaderPluginExtensions;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
-        loadAsync: (scene: Scene, data: string, rootUrl: string, onSuccess?: () => void, onProgress?: (event: ProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
+        loadAsync: (scene: Scene, data: string, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
         canDirectLoad?: (data: string) => boolean;
         canDirectLoad?: (data: string) => boolean;
         rewriteRootURL?: (rootUrl: string, responseURL?: string) => string;
         rewriteRootURL?: (rootUrl: string, responseURL?: string) => string;
     }
     }
@@ -146,9 +155,9 @@
             return null;
             return null;
         }
         }
 
 
-        private static _loadData(rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: (plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync, data: any, responseURL?: string) => void, onProgress: ((event: ProgressEvent) => void) | undefined, onError: (message: string, exception?: any) => void, pluginExtension: Nullable<string>): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
-            var directLoad = SceneLoader._getDirectLoad(sceneFilename);
-            var registeredPlugin = pluginExtension ? SceneLoader._getPluginForExtension(pluginExtension) : (directLoad ? SceneLoader._getPluginForDirectLoad(sceneFilename) : SceneLoader._getPluginForFilename(sceneFilename));
+        private static _loadData(rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: (plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync, data: any, responseURL?: string) => void, onProgress: ((event: SceneLoaderProgressEvent) => void) | undefined, onError: (message: string, exception?: any) => void, onDispose: () => void, pluginExtension: Nullable<string>): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
+            let directLoad = SceneLoader._getDirectLoad(sceneFilename);
+            let registeredPlugin = pluginExtension ? SceneLoader._getPluginForExtension(pluginExtension) : (directLoad ? SceneLoader._getPluginForDirectLoad(sceneFilename) : SceneLoader._getPluginForFilename(sceneFilename));
 
 
             let plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
             let plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
             if ((registeredPlugin.plugin as ISceneLoaderPluginFactory).createPlugin) {
             if ((registeredPlugin.plugin as ISceneLoaderPluginFactory).createPlugin) {
@@ -158,12 +167,12 @@
                 plugin = <any>registeredPlugin.plugin;
                 plugin = <any>registeredPlugin.plugin;
             }
             }
 
 
-            var useArrayBuffer = registeredPlugin.isBinary;
-            var database: Database;
+            let useArrayBuffer = registeredPlugin.isBinary;
+            let database: Database;
 
 
             SceneLoader.OnPluginActivatedObservable.notifyObservers(plugin);
             SceneLoader.OnPluginActivatedObservable.notifyObservers(plugin);
 
 
-            var dataCallback = (data: any, responseURL?: string) => {
+            let dataCallback = (data: any, responseURL?: string) => {
                 if (scene.isDisposed) {
                 if (scene.isDisposed) {
                     onError("Scene has been disposed");
                     onError("Scene has been disposed");
                     return;
                     return;
@@ -174,11 +183,32 @@
                 onSuccess(plugin, data, responseURL);
                 onSuccess(plugin, data, responseURL);
             };
             };
 
 
-            var manifestChecked = (success: any) => {
-                Tools.LoadFile(rootUrl + sceneFilename, dataCallback, onProgress, database, useArrayBuffer, (request, exception) => {
+            let request: Nullable<IFileRequest> = null;
+            let pluginDisposed = false;
+            let onDisposeObservable = (plugin as any).onDisposeObservable as Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
+            if (onDisposeObservable) {
+                onDisposeObservable.add(() => {
+                    pluginDisposed = true;
+
                     if (request) {
                     if (request) {
-                        onError(request.status + " " + request.statusText, exception);
+                        request.abort();
+                        request = null;
                     }
                     }
+
+                    onDispose();
+                });
+            }
+
+            let manifestChecked = () => {
+                if (pluginDisposed) {
+                    return;
+                }
+
+                let url = rootUrl + sceneFilename;
+                request = Tools.LoadFile(url, dataCallback, onProgress ? event => {
+                    onProgress(SceneLoaderProgressEvent.FromProgressEvent(event));
+                }: undefined, database, useArrayBuffer, (request, exception) => {
+                    onError("Failed to load scene." + (exception ? "" : " " + exception.message), exception);
                 });
                 });
             };
             };
 
 
@@ -193,17 +223,17 @@
                     database = new Database(rootUrl + sceneFilename, manifestChecked);
                     database = new Database(rootUrl + sceneFilename, manifestChecked);
                 }
                 }
                 else {
                 else {
-                    manifestChecked(true);
+                    manifestChecked();
                 }
                 }
             }
             }
             // Loading file from disk via input file or drag'n'drop
             // Loading file from disk via input file or drag'n'drop
             else {
             else {
-                var fileOrString = <any>sceneFilename;
+                let fileOrString = <any>sceneFilename;
 
 
                 if (fileOrString.name) { // File
                 if (fileOrString.name) { // File
-                    Tools.ReadFile(fileOrString, dataCallback, onProgress, useArrayBuffer);
+                    request = Tools.ReadFile(fileOrString, dataCallback, onProgress, useArrayBuffer);
                 } else if (FilesInput.FilesToLoad[sceneFilename]) {
                 } else if (FilesInput.FilesToLoad[sceneFilename]) {
-                    Tools.ReadFile(FilesInput.FilesToLoad[sceneFilename], dataCallback, onProgress, useArrayBuffer);
+                    request = Tools.ReadFile(FilesInput.FilesToLoad[sceneFilename], dataCallback, onProgress, useArrayBuffer);
                 } else {
                 } else {
                     onError("Unable to find file named " + sceneFilename);
                     onError("Unable to find file named " + sceneFilename);
                 }
                 }
@@ -249,7 +279,7 @@
         * @param onProgress a callback with a progress event for each file being loaded
         * @param onProgress a callback with a progress event for each file being loaded
         * @param onError a callback with the scene, a message, and possibly an exception when import fails
         * @param onError a callback with the scene, a message, and possibly an exception when import fails
         */
         */
-        public static ImportMesh(meshNames: any, rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: Nullable<(meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void> = null, onProgress: Nullable<(event: ProgressEvent) => void> = null, onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null, pluginExtension: Nullable<string> = null): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
+        public static ImportMesh(meshNames: any, rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: Nullable<(meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void> = null, onProgress: Nullable<(event: SceneLoaderProgressEvent) => void> = null, onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null, pluginExtension: Nullable<string> = null): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
             if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
             if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
                 Tools.Error("Wrong sceneFilename parameter");
                 Tools.Error("Wrong sceneFilename parameter");
                 return null;
                 return null;
@@ -258,6 +288,10 @@
             var loadingToken = {};
             var loadingToken = {};
             scene._addPendingData(loadingToken);
             scene._addPendingData(loadingToken);
 
 
+            var disposeHandler = () => {
+                scene._removePendingData(loadingToken);
+            };
+
             var errorHandler = (message: string, exception?: any) => {
             var errorHandler = (message: string, exception?: any) => {
                 let errorMessage = "Unable to import meshes from " + rootUrl + sceneFilename + ": " + message;
                 let errorMessage = "Unable to import meshes from " + rootUrl + sceneFilename + ": " + message;
 
 
@@ -268,10 +302,10 @@
                     // should the exception be thrown?
                     // should the exception be thrown?
                 }
                 }
 
 
-                scene._removePendingData(loadingToken);
+                disposeHandler();
             };
             };
 
 
-            var progressHandler = onProgress ? (event: ProgressEvent) => {
+            var progressHandler = onProgress ? (event: SceneLoaderProgressEvent) => {
                 try {
                 try {
                     onProgress(event);
                     onProgress(event);
                 }
                 }
@@ -320,7 +354,7 @@
                         successHandler(meshes, particleSystems, skeletons);
                         successHandler(meshes, particleSystems, skeletons);
                     }, progressHandler, errorHandler);
                     }, progressHandler, errorHandler);
                 }
                 }
-            }, progressHandler, errorHandler, pluginExtension);
+            }, progressHandler, errorHandler, disposeHandler, pluginExtension);
         }
         }
 
 
         /**
         /**
@@ -332,7 +366,7 @@
         * @param onProgress a callback with a progress event for each file being loaded
         * @param onProgress a callback with a progress event for each file being loaded
         * @param onError a callback with the scene, a message, and possibly an exception when import fails
         * @param onError a callback with the scene, a message, and possibly an exception when import fails
         */
         */
-        public static Load(rootUrl: string, sceneFilename: any, engine: Engine, onSuccess: Nullable<(scene: Scene) => void> = null, onProgress: Nullable<(event: ProgressEvent) => void> = null, onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null, pluginExtension: Nullable<string> = null): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
+        public static Load(rootUrl: string, sceneFilename: any, engine: Engine, onSuccess: Nullable<(scene: Scene) => void> = null, onProgress: Nullable<(event: SceneLoaderProgressEvent) => void> = null, onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null, pluginExtension: Nullable<string> = null): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
             return SceneLoader.Append(rootUrl, sceneFilename, new Scene(engine), onSuccess, onProgress, onError, pluginExtension);
             return SceneLoader.Append(rootUrl, sceneFilename, new Scene(engine), onSuccess, onProgress, onError, pluginExtension);
         }
         }
 
 
@@ -345,7 +379,7 @@
         * @param onProgress a callback with a progress event for each file being loaded
         * @param onProgress a callback with a progress event for each file being loaded
         * @param onError a callback with the scene, a message, and possibly an exception when import fails
         * @param onError a callback with the scene, a message, and possibly an exception when import fails
         */
         */
-        public static Append(rootUrl: string, sceneFilename: any, scene: Scene, onSuccess: Nullable<(scene: Scene) => void> = null, onProgress: Nullable<(event: ProgressEvent) => void> = null, onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null, pluginExtension: Nullable<string> = null): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
+        public static Append(rootUrl: string, sceneFilename: any, scene: Scene, onSuccess: Nullable<(scene: Scene) => void> = null, onProgress: Nullable<(event: SceneLoaderProgressEvent) => void> = null, onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null, pluginExtension: Nullable<string> = null): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
             if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
             if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
                 Tools.Error("Wrong sceneFilename parameter");
                 Tools.Error("Wrong sceneFilename parameter");
                 return null;
                 return null;
@@ -358,6 +392,11 @@
             var loadingToken = {};
             var loadingToken = {};
             scene._addPendingData(loadingToken);
             scene._addPendingData(loadingToken);
 
 
+            var disposeHandler = () => {
+                scene._removePendingData(loadingToken);
+                scene.getEngine().hideLoadingUI();
+            };
+
             var errorHandler = (message: Nullable<string>, exception?: any) => {
             var errorHandler = (message: Nullable<string>, exception?: any) => {
                 let errorMessage = "Unable to load from " + rootUrl + sceneFilename + (message ? ": " + message : "");
                 let errorMessage = "Unable to load from " + rootUrl + sceneFilename + (message ? ": " + message : "");
                 if (onError) {
                 if (onError) {
@@ -366,11 +405,11 @@
                     Tools.Error(errorMessage);
                     Tools.Error(errorMessage);
                     // should the exception be thrown?
                     // should the exception be thrown?
                 }
                 }
-                scene._removePendingData(loadingToken);
-                scene.getEngine().hideLoadingUI();
+
+                disposeHandler();
             };
             };
 
 
-            var progressHandler = onProgress ? (event: ProgressEvent) => {
+            var progressHandler = onProgress ? (event: SceneLoaderProgressEvent) => {
                 try {
                 try {
                     onProgress(event);
                     onProgress(event);
                 }
                 }
@@ -414,7 +453,7 @@
                         scene.getEngine().hideLoadingUI();
                         scene.getEngine().hideLoadingUI();
                     });
                     });
                 }
                 }
-            }, progressHandler, errorHandler, pluginExtension);
+            }, progressHandler, errorHandler, disposeHandler, pluginExtension);
         }
         }
     };
     };
 }
 }

+ 8 - 1
src/Materials/Textures/babylon.colorGradingTexture.ts

@@ -169,7 +169,14 @@ module BABYLON {
                 }
                 }
             }
             }
 
 
-            Tools.LoadFile(this.url, callback);
+            let scene = this.getScene();
+            if (scene) {
+                scene._loadFile(this.url, callback);
+            }
+            else {
+                this._engine._loadFile(this.url, callback);
+            }
+
             return this._texture;
             return this._texture;
         }
         }
 
 

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

@@ -47,6 +47,7 @@ module BABYLON {
         public _depthStencilBuffer: Nullable<WebGLRenderbuffer>;
         public _depthStencilBuffer: Nullable<WebGLRenderbuffer>;
         public _MSAAFramebuffer: Nullable<WebGLFramebuffer>;
         public _MSAAFramebuffer: Nullable<WebGLFramebuffer>;
         public _MSAARenderBuffer: Nullable<WebGLRenderbuffer>;
         public _MSAARenderBuffer: Nullable<WebGLRenderbuffer>;
+        public _attachments: Nullable<number[]>;
         public _cachedCoordinatesMode: Nullable<number>;
         public _cachedCoordinatesMode: Nullable<number>;
         public _cachedWrapU: Nullable<number>;
         public _cachedWrapU: Nullable<number>;
         public _cachedWrapV: Nullable<number>;
         public _cachedWrapV: Nullable<number>;
@@ -239,4 +240,4 @@ module BABYLON {
             }
             }
         }
         }
     }
     }
-}
+}

+ 26 - 22
src/Materials/Textures/babylon.multiRenderTarget.ts

@@ -1,12 +1,14 @@
 module BABYLON {
 module BABYLON {
     export interface IMultiRenderTargetOptions {
     export interface IMultiRenderTargetOptions {
-        generateMipMaps: boolean,
-        types: number[],
-        samplingModes: number[],
-        generateDepthBuffer: boolean,
-        generateStencilBuffer: boolean,
-        generateDepthTexture: boolean,
-        textureCount: number
+        generateMipMaps?: boolean,
+        types?: number[],
+        samplingModes?: number[],
+        generateDepthBuffer?: boolean,
+        generateStencilBuffer?: boolean,
+        generateDepthTexture?: boolean,
+        textureCount?: number,
+        doNotChangeAspectRatio?: boolean,
+        defaultType?: number
     };
     };
     export class MultiRenderTarget extends RenderTargetTexture {
     export class MultiRenderTarget extends RenderTargetTexture {
 
 
@@ -43,12 +45,10 @@ module BABYLON {
             }
             }
         }
         }
 
 
-        constructor(name: string, size: any, count: number, scene: Scene, options?: any) {
-            options = options || {};
-
-            var generateMipMaps = options.generateMipMaps ? options.generateMipMaps : false;
-            var generateDepthTexture = options.generateDepthTexture ? options.generateDepthTexture : false;
-            var doNotChangeAspectRatio = options.doNotChangeAspectRatio === undefined ? true : options.doNotChangeAspectRatio;
+        constructor(name: string, size: any, count: number, scene: Scene, options?: IMultiRenderTargetOptions) {
+            var generateMipMaps = options && options.generateMipMaps ? options.generateMipMaps : false;
+            var generateDepthTexture = options && options.generateDepthTexture ? options.generateDepthTexture : false;
+            var doNotChangeAspectRatio = !options || options.doNotChangeAspectRatio === undefined ? true : options.doNotChangeAspectRatio;
 
 
             super(name, size, scene, generateMipMaps, doNotChangeAspectRatio);
             super(name, size, scene, generateMipMaps, doNotChangeAspectRatio);
 
 
@@ -63,21 +63,21 @@ module BABYLON {
             var samplingModes = [];
             var samplingModes = [];
 
 
             for (var i = 0; i < count; i++) {
             for (var i = 0; i < count; i++) {
-                if (options.types && options.types[i]) {
+                if (options && options.types && options.types[i] !== undefined) {
                     types.push(options.types[i]);
                     types.push(options.types[i]);
                 } else {
                 } else {
-                    types.push(Engine.TEXTURETYPE_FLOAT);
+                    types.push(options && options.defaultType ? options.defaultType : Engine.TEXTURETYPE_UNSIGNED_INT);
                 }
                 }
 
 
-                if (options.samplingModes && options.samplingModes[i]) {
+                if (options && options.samplingModes && options.samplingModes[i] !== undefined) {
                     samplingModes.push(options.samplingModes[i]);
                     samplingModes.push(options.samplingModes[i]);
                 } else {
                 } else {
                     samplingModes.push(Texture.BILINEAR_SAMPLINGMODE);
                     samplingModes.push(Texture.BILINEAR_SAMPLINGMODE);
                 }
                 }
             }
             }
 
 
-            var generateDepthBuffer = options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
-            var generateStencilBuffer = options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
+            var generateDepthBuffer = !options || options.generateDepthBuffer === undefined ? true : options.generateDepthBuffer;
+            var generateStencilBuffer = !options || options.generateStencilBuffer === undefined ? false : options.generateStencilBuffer;
 
 
             this._size = size;
             this._size = size;
             this._multiRenderTargetOptions = {
             this._multiRenderTargetOptions = {
@@ -132,9 +132,7 @@ module BABYLON {
                 return;
                 return;
             }
             }
 
 
-            for (var i = 0; i < this._internalTextures.length; i++) {
-                this._samples = this._engine.updateRenderTargetTextureSampleCount(this._internalTextures[i], value);
-            }
+            this._samples = this._engine.updateMultipleRenderTargetTextureSampleCount(this._internalTextures, value);
         }
         }
 
 
         public resize(size: any) {
         public resize(size: any) {
@@ -143,6 +141,12 @@ module BABYLON {
             this._createInternalTextures();
             this._createInternalTextures();
         }
         }
 
 
+        protected unbindFrameBuffer(engine: Engine, faceIndex: number): void {
+            engine.unBindMultiColorAttachmentFramebuffer(this._internalTextures, this.isCube, () => {
+                this.onAfterRenderObservable.notifyObservers(faceIndex);
+            });
+        }
+
         public dispose(): void {
         public dispose(): void {
             this.releaseInternalTextures();
             this.releaseInternalTextures();
 
 
@@ -162,4 +166,4 @@ module BABYLON {
             }
             }
         }
         }
     }
     }
-}
+}

+ 13 - 5
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -445,7 +445,7 @@
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                         for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
                             var subMesh = mesh.subMeshes[subIndex];
                             var subMesh = mesh.subMeshes[subIndex];
                             scene._activeIndices.addCount(subMesh.indexCount, false);
                             scene._activeIndices.addCount(subMesh.indexCount, false);
-                            this._renderingManager.dispatch(subMesh);
+                            this._renderingManager.dispatch(subMesh, mesh);
                         }
                         }
                     }
                     }
                 }
                 }
@@ -495,6 +495,15 @@
             return Math.min(Tools.FloorPOT(renderDimension), curved);
             return Math.min(Tools.FloorPOT(renderDimension), curved);
         }
         }
 
 
+        protected unbindFrameBuffer(engine: Engine, faceIndex: number): void {
+            if (!this._texture) {
+                return;
+            }
+            engine.unBindFramebuffer(this._texture, this.isCube, () => {
+                this.onAfterRenderObservable.notifyObservers(faceIndex);
+            });
+        }
+
         private renderToTarget(faceIndex: number, currentRenderList: AbstractMesh[], currentRenderListLength: number, useCameraPostProcess: boolean, dumpForDebug: boolean): void {
         private renderToTarget(faceIndex: number, currentRenderList: AbstractMesh[], currentRenderListLength: number, useCameraPostProcess: boolean, dumpForDebug: boolean): void {
             var scene = this.getScene();
             var scene = this.getScene();
             
             
@@ -559,9 +568,8 @@
                     }
                     }
                 }
                 }
 
 
-                engine.unBindFramebuffer(this._texture, this.isCube, () => {
-                    this.onAfterRenderObservable.notifyObservers(faceIndex);
-                });
+            this.unbindFrameBuffer(engine, faceIndex);
+
             } else {
             } else {
                 this.onAfterRenderObservable.notifyObservers(faceIndex);
                 this.onAfterRenderObservable.notifyObservers(faceIndex);
             }
             }
@@ -702,4 +710,4 @@
             }
             }
         }
         }
     }
     }
-}
+}

+ 1 - 1
src/Materials/Textures/babylon.videoTexture.ts

@@ -148,7 +148,7 @@
                     if (onReady) {
                     if (onReady) {
                         onReady(new VideoTexture("video", video, scene, true, true));
                         onReady(new VideoTexture("video", video, scene, true, true));
                     }
                     }
-                }, function (e: DOMException) {
+                }, function (e: MediaStreamError) {
                     Tools.Error(e.name);
                     Tools.Error(e.name);
                 });
                 });
             }
             }

+ 22 - 5
src/Materials/babylon.effect.ts

@@ -303,7 +303,7 @@
             }
             }
 
 
             // Vertex shader
             // Vertex shader
-            Tools.LoadFile(vertexShaderUrl + ".vertex.fx", callback);
+            this._engine._loadFile(vertexShaderUrl + ".vertex.fx", callback);
         }
         }
 
 
         public _loadFragmentShader(fragment: any, callback: (data: any) => void): void {
         public _loadFragmentShader(fragment: any, callback: (data: any) => void): void {
@@ -343,7 +343,7 @@
             }
             }
 
 
             // Fragment shader
             // Fragment shader
-            Tools.LoadFile(fragmentShaderUrl + ".fragment.fx", callback);
+            this._engine._loadFile(fragmentShaderUrl + ".fragment.fx", callback);
         }
         }
 
 
         private _dumpShadersSource(vertexCode: string, fragmentCode: string, defines: string): void {
         private _dumpShadersSource(vertexCode: string, fragmentCode: string, defines: string): void {
@@ -491,7 +491,7 @@
                 } else {
                 } else {
                     var includeShaderUrl = Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
                     var includeShaderUrl = Engine.ShadersRepository + "ShadersInclude/" + includeFile + ".fx";
 
 
-                    Tools.LoadFile(includeShaderUrl, (fileContent) => {
+                    this._engine._loadFile(includeShaderUrl, (fileContent) => {
                         Effect.IncludesShadersStore[includeFile] = fileContent as string;
                         Effect.IncludesShadersStore[includeFile] = fileContent as string;
                         this._processIncludes(<string>returnValue, callback);
                         this._processIncludes(<string>returnValue, callback);
                     });
                     });
@@ -544,6 +544,11 @@
             this._prepareEffect();
             this._prepareEffect();
         }
         }
 
 
+        public getSpecificUniformLocations(names: string[]): Nullable<WebGLUniformLocation>[] {
+            let engine = this._engine;
+            return engine.getUniforms(this._program, names);
+        }
+
         public _prepareEffect() {
         public _prepareEffect() {
             let attributesNames = this._attributesNames;
             let attributesNames = this._attributesNames;
             let defines = this.defines;
             let defines = this.defines;
@@ -553,7 +558,7 @@
             var previousProgram = this._program;
             var previousProgram = this._program;
 
 
             try {
             try {
-                var engine = this._engine;
+                let engine = this._engine;
 
 
                 if (this._vertexSourceCodeOverride && this._fragmentSourceCodeOverride) {
                 if (this._vertexSourceCodeOverride && this._fragmentSourceCodeOverride) {
                     this._program = engine.createRawShaderProgram(this._vertexSourceCodeOverride, this._fragmentSourceCodeOverride, undefined, this._transformFeedbackVaryings);
                     this._program = engine.createRawShaderProgram(this._vertexSourceCodeOverride, this._fragmentSourceCodeOverride, undefined, this._transformFeedbackVaryings);
@@ -758,7 +763,7 @@
 
 
         public bindUniformBuffer(buffer: WebGLBuffer, name: string): void {
         public bindUniformBuffer(buffer: WebGLBuffer, name: string): void {
             let bufferName = this._uniformBuffersNames[name];
             let bufferName = this._uniformBuffersNames[name];
-            if (Effect._baseCache[bufferName] === buffer) {
+            if (bufferName === undefined || Effect._baseCache[bufferName] === buffer) {
                 return;
                 return;
             }
             }
             Effect._baseCache[bufferName] = buffer;
             Effect._baseCache[bufferName] = buffer;
@@ -769,6 +774,18 @@
             this._engine.bindUniformBlock(this._program, blockName, index);
             this._engine.bindUniformBlock(this._program, blockName, index);
         }
         }
 
 
+        public setInt(uniformName: string, value: number): Effect {
+            var cache = this._valueCache[uniformName];
+            if (cache !== undefined && cache === value)
+                return this;
+
+            this._valueCache[uniformName] = value;
+
+            this._engine.setInt(this.getUniform(uniformName), value);
+
+            return this;
+        }
+
         public setIntArray(uniformName: string, array: Int32Array): Effect {
         public setIntArray(uniformName: string, array: Int32Array): Effect {
             this._valueCache[uniformName] = null;
             this._valueCache[uniformName] = null;
             this._engine.setIntArray(this.getUniform(uniformName), array);
             this._engine.setIntArray(this.getUniform(uniformName), array);

+ 24 - 23
src/Materials/babylon.materialHelper.ts

@@ -3,10 +3,10 @@
 
 
         public static BindEyePosition(effect: Effect, scene: Scene): void {
         public static BindEyePosition(effect: Effect, scene: Scene): void {
             if (scene._forcedViewPosition) {
             if (scene._forcedViewPosition) {
-                effect.setVector3("vEyePosition", scene._forcedViewPosition);            
+                effect.setVector3("vEyePosition", scene._forcedViewPosition);
                 return;
                 return;
             }
             }
-            effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera!.globalPosition);            
+            effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera!.globalPosition);
         }
         }
 
 
         public static PrepareDefinesForMergedUV(texture: BaseTexture, defines: any, key: string): void {
         public static PrepareDefinesForMergedUV(texture: BaseTexture, defines: any, key: string): void {
@@ -57,13 +57,13 @@
             if (defines["DEPTHPREPASS"] !== !engine.getColorWrite()) {
             if (defines["DEPTHPREPASS"] !== !engine.getColorWrite()) {
                 defines["DEPTHPREPASS"] = !defines["DEPTHPREPASS"];
                 defines["DEPTHPREPASS"] = !defines["DEPTHPREPASS"];
                 changed = true;
                 changed = true;
-            }            
+            }
 
 
             if (defines["INSTANCES"] !== useInstances) {
             if (defines["INSTANCES"] !== useInstances) {
                 defines["INSTANCES"] = useInstances;
                 defines["INSTANCES"] = useInstances;
                 changed = true;
                 changed = true;
             }
             }
-            
+
             if (changed) {
             if (changed) {
                 defines.markAsUnprocessed();
                 defines.markAsUnprocessed();
             }
             }
@@ -76,7 +76,7 @@
 
 
             defines._normals = defines._needNormals;
             defines._normals = defines._needNormals;
             defines._uvs = defines._needUVs;
             defines._uvs = defines._needUVs;
-            
+
             defines["NORMAL"] = (defines._needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind));
             defines["NORMAL"] = (defines._needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind));
 
 
             if (defines._needNormals && mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
             if (defines._needNormals && mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
@@ -111,7 +111,7 @@
                 var manager = (<Mesh>mesh).morphTargetManager;
                 var manager = (<Mesh>mesh).morphTargetManager;
                 if (manager) {
                 if (manager) {
                     defines["MORPHTARGETS_TANGENT"] = manager.supportsTangents && defines["TANGENT"];
                     defines["MORPHTARGETS_TANGENT"] = manager.supportsTangents && defines["TANGENT"];
-                    defines["MORPHTARGETS_NORMAL"] = manager.supportsNormals && defines["NORMAL"] ;
+                    defines["MORPHTARGETS_NORMAL"] = manager.supportsNormals && defines["NORMAL"];
                     defines["MORPHTARGETS"] = (manager.numInfluencers > 0);
                     defines["MORPHTARGETS"] = (manager.numInfluencers > 0);
                     defines["NUM_MORPH_INFLUENCERS"] = manager.numInfluencers;
                     defines["NUM_MORPH_INFLUENCERS"] = manager.numInfluencers;
                 } else {
                 } else {
@@ -146,7 +146,7 @@
                     }
                     }
 
 
                     defines["LIGHT" + lightIndex] = true;
                     defines["LIGHT" + lightIndex] = true;
-                    
+
                     defines["SPOTLIGHT" + lightIndex] = false;
                     defines["SPOTLIGHT" + lightIndex] = false;
                     defines["HEMILIGHT" + lightIndex] = false;
                     defines["HEMILIGHT" + lightIndex] = false;
                     defines["POINTLIGHT" + lightIndex] = false;
                     defines["POINTLIGHT" + lightIndex] = false;
@@ -184,7 +184,7 @@
                         }
                         }
                     }
                     }
 
 
-                    if (light.lightmapMode != Light.LIGHTMAP_DEFAULT ) {
+                    if (light.lightmapMode != Light.LIGHTMAP_DEFAULT) {
                         lightmapMode = true;
                         lightmapMode = true;
                         defines["LIGHTMAPEXCLUDED" + lightIndex] = true;
                         defines["LIGHTMAPEXCLUDED" + lightIndex] = true;
                         defines["LIGHTMAPNOSPECULAR" + lightIndex] = (light.lightmapMode == Light.LIGHTMAP_SHADOWSONLY);
                         defines["LIGHTMAPNOSPECULAR" + lightIndex] = (light.lightmapMode == Light.LIGHTMAP_SHADOWSONLY);
@@ -208,7 +208,7 @@
                     defines["LIGHT" + index] = false;
                     defines["LIGHT" + index] = false;
                     defines["HEMILIGHT" + lightIndex] = false;
                     defines["HEMILIGHT" + lightIndex] = false;
                     defines["POINTLIGHT" + lightIndex] = false;
                     defines["POINTLIGHT" + lightIndex] = false;
-                    defines["DIRLIGHT" + lightIndex] = false;                    
+                    defines["DIRLIGHT" + lightIndex] = false;
                     defines["SPOTLIGHT" + lightIndex] = false;
                     defines["SPOTLIGHT" + lightIndex] = false;
                     defines["SHADOW" + lightIndex] = false;
                     defines["SHADOW" + lightIndex] = false;
                 }
                 }
@@ -220,9 +220,9 @@
                 needRebuild = true;
                 needRebuild = true;
             }
             }
 
 
-            defines["SHADOWFLOAT"] = shadowEnabled && 
-                                    ((caps.textureFloatRender && caps.textureFloatLinearFiltering) ||
-                                         (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering));
+            defines["SHADOWFLOAT"] = shadowEnabled &&
+                ((caps.textureFloatRender && caps.textureFloatLinearFiltering) ||
+                    (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering));
             defines["LIGHTMAPEXCLUDED"] = lightmapMode;
             defines["LIGHTMAPEXCLUDED"] = lightmapMode;
 
 
             if (needRebuild) {
             if (needRebuild) {
@@ -370,29 +370,30 @@
         }
         }
 
 
         public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: any, maxSimultaneousLights = 4, usePhysicalLightFalloff = false) {
         public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: any, maxSimultaneousLights = 4, usePhysicalLightFalloff = false) {
-            var lightIndex = 0;
-            for (var light of mesh._lightSources) {
+            let len = Math.min(mesh._lightSources.length, maxSimultaneousLights);
+
+            for (var i = 0; i < len; i++) {
+
+                let light = mesh._lightSources[i];
+                let iAsString = i.toString();
+
                 let scaledIntensity = light.getScaledIntensity();
                 let scaledIntensity = light.getScaledIntensity();
-                light._uniformBuffer.bindToEffect(effect, "Light" + lightIndex);
+                light._uniformBuffer.bindToEffect(effect, "Light" + i);
 
 
-                MaterialHelper.BindLightProperties(light, effect, lightIndex);
+                MaterialHelper.BindLightProperties(light, effect, i);
 
 
                 light.diffuse.scaleToRef(scaledIntensity, Tmp.Color3[0]);
                 light.diffuse.scaleToRef(scaledIntensity, Tmp.Color3[0]);
-                light._uniformBuffer.updateColor4("vLightDiffuse", Tmp.Color3[0], usePhysicalLightFalloff ? light.radius : light.range, lightIndex + "");
+                light._uniformBuffer.updateColor4("vLightDiffuse", Tmp.Color3[0], usePhysicalLightFalloff ? light.radius : light.range, iAsString);
                 if (defines["SPECULARTERM"]) {
                 if (defines["SPECULARTERM"]) {
                     light.specular.scaleToRef(scaledIntensity, Tmp.Color3[1]);
                     light.specular.scaleToRef(scaledIntensity, Tmp.Color3[1]);
-                    light._uniformBuffer.updateColor3("vLightSpecular", Tmp.Color3[1], lightIndex + "");
+                    light._uniformBuffer.updateColor3("vLightSpecular", Tmp.Color3[1], iAsString);
                 }
                 }
 
 
                 // Shadows
                 // Shadows
                 if (scene.shadowsEnabled) {
                 if (scene.shadowsEnabled) {
-                    this.BindLightShadow(light, scene, mesh, lightIndex + "", effect);
+                    this.BindLightShadow(light, scene, mesh, iAsString, effect);
                 }
                 }
                 light._uniformBuffer.update();
                 light._uniformBuffer.update();
-                lightIndex++;
-
-                if (lightIndex === maxSimultaneousLights)
-                    break;
             }
             }
         }
         }
 
 

+ 14 - 1
src/Materials/babylon.shaderMaterial.ts

@@ -5,6 +5,7 @@
         private _textures: { [name: string]: Texture } = {};
         private _textures: { [name: string]: Texture } = {};
         private _textureArrays: { [name: string]: Texture[] } = {};
         private _textureArrays: { [name: string]: Texture[] } = {};
         private _floats: { [name: string]: number } = {};
         private _floats: { [name: string]: number } = {};
+        private _ints: { [name: string]: number } = {};
         private _floatsArrays: { [name: string]: number[] } = {};
         private _floatsArrays: { [name: string]: number[] } = {};
         private _colors3: { [name: string]: Color3 } = {};
         private _colors3: { [name: string]: Color3 } = {};
         private _colors3Arrays: { [name: string]: number[] } = {};
         private _colors3Arrays: { [name: string]: number[] } = {};
@@ -81,6 +82,13 @@
             return this;
             return this;
         }
         }
 
 
+        public setInt(name: string, value: number): ShaderMaterial {
+            this._checkUniform(name);
+            this._ints[name] = value;
+
+            return this;
+        }
+
         public setFloats(name: string, value: number[]): ShaderMaterial {
         public setFloats(name: string, value: number[]): ShaderMaterial {
             this._checkUniform(name);
             this._checkUniform(name);
             this._floatsArrays[name] = value;
             this._floatsArrays[name] = value;
@@ -322,12 +330,17 @@
                     this._effect.setTextureArray(name, this._textureArrays[name]);
                     this._effect.setTextureArray(name, this._textureArrays[name]);
                 }
                 }
 
 
+                // Int    
+                for (name in this._ints) {
+                    this._effect.setInt(name, this._ints[name]);
+                }
+
                 // Float    
                 // Float    
                 for (name in this._floats) {
                 for (name in this._floats) {
                     this._effect.setFloat(name, this._floats[name]);
                     this._effect.setFloat(name, this._floats[name]);
                 }
                 }
 
 
-                // Float s   
+                // Floats   
                 for (name in this._floatsArrays) {
                 for (name in this._floatsArrays) {
                     this._effect.setArray(name, this._floatsArrays[name]);
                     this._effect.setArray(name, this._floatsArrays[name]);
                 }
                 }

+ 5 - 5
src/Mesh/babylon.abstractMesh.ts

@@ -904,14 +904,14 @@
                 for (var descendant of descendants) {
                 for (var descendant of descendants) {
                     let childMesh = <AbstractMesh>descendant;
                     let childMesh = <AbstractMesh>descendant;
 
 
+                    childMesh.computeWorldMatrix(true);
+
                     //make sure we have the needed params to get mix and max
                     //make sure we have the needed params to get mix and max
                     if (!childMesh.getBoundingInfo || childMesh.getTotalVertices() === 0) {
                     if (!childMesh.getBoundingInfo || childMesh.getTotalVertices() === 0) {
                         continue;
                         continue;
                     }
                     }
 
 
-                    childMesh.computeWorldMatrix(true);
                     let childBoundingInfo = childMesh.getBoundingInfo();
                     let childBoundingInfo = childMesh.getBoundingInfo();
-
                     let boundingBox = childBoundingInfo.boundingBox;
                     let boundingBox = childBoundingInfo.boundingBox;
 
 
                     var minBox = boundingBox.minimumWorld;
                     var minBox = boundingBox.minimumWorld;
@@ -1322,7 +1322,7 @@
             var index: number;
             var index: number;
 
 
             // Action manager
             // Action manager
-            if (this.actionManager) {
+            if (this.actionManager !== undefined && this.actionManager !== null) {
                 this.actionManager.dispose();
                 this.actionManager.dispose();
                 this.actionManager = null;
                 this.actionManager = null;
             }
             }
@@ -1388,8 +1388,8 @@
             }
             }
 
 
             // Octree
             // Octree
-            var sceneOctree = this.getScene().selectionOctree;
-            if (sceneOctree) {
+            const sceneOctree = this.getScene().selectionOctree;
+            if (sceneOctree !== undefined && sceneOctree !== null) {
                 var index = sceneOctree.dynamicContent.indexOf(this);
                 var index = sceneOctree.dynamicContent.indexOf(this);
 
 
                 if (index !== -1) {
                 if (index !== -1) {

+ 5 - 4
src/Mesh/babylon.geometry.ts

@@ -557,7 +557,7 @@
             }
             }
 
 
             scene._addPendingData(this);
             scene._addPendingData(this);
-            Tools.LoadFile(this.delayLoadingFile, data => {
+            scene._loadFile(this.delayLoadingFile, data => {
                 if (!this._delayLoadingFunction) {
                 if (!this._delayLoadingFunction) {
                     return;
                     return;
                 }
                 }
@@ -578,7 +578,7 @@
                 if (onLoaded) {
                 if (onLoaded) {
                     onLoaded();
                     onLoaded();
                 }
                 }
-            }, () => { }, scene.database);
+            }, undefined, true);
         }
         }
 
 
         /**
         /**
@@ -1059,8 +1059,9 @@
             mesh.computeWorldMatrix(true);
             mesh.computeWorldMatrix(true);
 
 
             // Octree
             // Octree
-            if (scene['_selectionOctree']) {
-                scene['_selectionOctree'].addMesh(<AbstractMesh>mesh);
+            const sceneOctree = scene.selectionOctree;
+            if (sceneOctree !== undefined && sceneOctree !== null) {
+                sceneOctree.addMesh(<AbstractMesh>mesh);
             }
             }
         }
         }
 
 

+ 2 - 2
src/Mesh/babylon.mesh.ts

@@ -427,7 +427,7 @@
          * Returns a positive integer : the total number of vertices within the mesh geometry or zero if the mesh has no geometry.
          * Returns a positive integer : the total number of vertices within the mesh geometry or zero if the mesh has no geometry.
          */
          */
         public getTotalVertices(): number {
         public getTotalVertices(): number {
-            if (!this._geometry) {
+            if (this._geometry === null || this._geometry === undefined) {
                 return 0;
                 return 0;
             }
             }
             return this._geometry.getTotalVertices();
             return this._geometry.getTotalVertices();
@@ -2104,7 +2104,7 @@
             serializationObject.scaling = this.scaling.asArray();
             serializationObject.scaling = this.scaling.asArray();
             serializationObject.localMatrix = this.getPivotMatrix().asArray();
             serializationObject.localMatrix = this.getPivotMatrix().asArray();
 
 
-            serializationObject.isEnabled = this.isEnabled();
+            serializationObject.isEnabled = this.isEnabled(false);
             serializationObject.isVisible = this.isVisible;
             serializationObject.isVisible = this.isVisible;
             serializationObject.infiniteDistance = this.infiniteDistance;
             serializationObject.infiniteDistance = this.infiniteDistance;
             serializationObject.pickable = this.isPickable;
             serializationObject.pickable = this.isPickable;

+ 40 - 39
src/Mesh/babylon.mesh.vertexData.ts

@@ -1,6 +1,5 @@
 module BABYLON {
 module BABYLON {
-    export interface IGetSetVerticesData
-    {
+    export interface IGetSetVerticesData {
         isVerticesDataPresent(kind: string): boolean;
         isVerticesDataPresent(kind: string): boolean;
         getVerticesData(kind: string, copyWhenShared?: boolean, forceCopy?: boolean): Nullable<FloatArray>;
         getVerticesData(kind: string, copyWhenShared?: boolean, forceCopy?: boolean): Nullable<FloatArray>;
         getIndices(copyWhenShared?: boolean): Nullable<IndicesArray>;
         getIndices(copyWhenShared?: boolean): Nullable<IndicesArray>;
@@ -346,7 +345,7 @@
                 if (length === 0 || length === other.length) {
                 if (length === 0 || length === other.length) {
                     return other;
                     return other;
                 }
                 }
-                
+
                 var padding = new Float32Array(length - other.length);
                 var padding = new Float32Array(length - other.length);
                 padding.fill(defaultValue);
                 padding.fill(defaultValue);
                 return this._mergeElement(padding, other, length);
                 return this._mergeElement(padding, other, length);
@@ -744,7 +743,7 @@
             var positions32 = new Float32Array(positions);
             var positions32 = new Float32Array(positions);
             var normals32 = new Float32Array(normals);
             var normals32 = new Float32Array(normals);
             var uvs32 = new Float32Array(uvs);
             var uvs32 = new Float32Array(uvs);
-            
+
             vertexData.indices = indices;
             vertexData.indices = indices;
             vertexData.positions = positions32;
             vertexData.positions = positions32;
             vertexData.normals = normals32;
             vertexData.normals = normals32;
@@ -1401,10 +1400,10 @@
          * Creates the VertexData of the TiledGround.  
          * Creates the VertexData of the TiledGround.  
          */
          */
         public static CreateTiledGround(options: { xmin: number, zmin: number, xmax: number, zmax: number, subdivisions?: { w: number; h: number; }, precision?: { w: number; h: number; } }): VertexData {
         public static CreateTiledGround(options: { xmin: number, zmin: number, xmax: number, zmax: number, subdivisions?: { w: number; h: number; }, precision?: { w: number; h: number; } }): VertexData {
-            var xmin = options.xmin || -1.0;
-            var zmin = options.zmin || -1.0;
-            var xmax = options.xmax || 1.0;
-            var zmax = options.zmax || 1.0;
+            var xmin = (options.xmin !== undefined && options.xmin !== null) ? options.xmin : -1.0;
+            var zmin = (options.zmin !== undefined && options.zmin !== null) ? options.zmin : -1.0;
+            var xmax = (options.xmax !== undefined && options.xmax !== null) ? options.xmax : 1.0;
+            var zmax = (options.zmax !== undefined && options.zmax !== null) ? options.zmax : 1.0;
             var subdivisions = options.subdivisions || { w: 1, h: 1 };
             var subdivisions = options.subdivisions || { w: 1, h: 1 };
             var precision = options.precision || { w: 1, h: 1 };
             var precision = options.precision || { w: 1, h: 1 };
 
 
@@ -1494,7 +1493,7 @@
             var uvs = [];
             var uvs = [];
             var row, col;
             var row, col;
             var filter = options.colorFilter || new Color3(0.3, 0.59, 0.11);
             var filter = options.colorFilter || new Color3(0.3, 0.59, 0.11);
-            
+
             // Vertices
             // Vertices
             for (row = 0; row <= options.subdivisions; row++) {
             for (row = 0; row <= options.subdivisions; row++) {
                 for (col = 0; col <= options.subdivisions; col++) {
                 for (col = 0; col <= options.subdivisions; col++) {
@@ -1659,8 +1658,8 @@
         /**
         /**
          * Re-creates the VertexData of the Polygon for sideOrientation.  
          * Re-creates the VertexData of the Polygon for sideOrientation.  
          */
          */
-        public static CreatePolygon(polygon: Mesh, sideOrientation: number, fUV?:Vector4[], fColors?: Color4[], frontUVs?: Vector4, backUVs?: Vector4) {
-			var faceUV: Vector4[] = fUV || new Array<Vector4>(3);
+        public static CreatePolygon(polygon: Mesh, sideOrientation: number, fUV?: Vector4[], fColors?: Color4[], frontUVs?: Vector4, backUVs?: Vector4) {
+            var faceUV: Vector4[] = fUV || new Array<Vector4>(3);
             var faceColors = fColors;
             var faceColors = fColors;
             var colors = [];
             var colors = [];
 
 
@@ -1673,39 +1672,39 @@
                     faceColors[f] = new Color4(1, 1, 1, 1);
                     faceColors[f] = new Color4(1, 1, 1, 1);
                 }
                 }
             }
             }
-            
+
             var positions = <FloatArray>polygon.getVerticesData(VertexBuffer.PositionKind);
             var positions = <FloatArray>polygon.getVerticesData(VertexBuffer.PositionKind);
-			var normals = <FloatArray>polygon.getVerticesData(VertexBuffer.NormalKind);
-			var uvs = <FloatArray>polygon.getVerticesData(VertexBuffer.UVKind);
+            var normals = <FloatArray>polygon.getVerticesData(VertexBuffer.NormalKind);
+            var uvs = <FloatArray>polygon.getVerticesData(VertexBuffer.UVKind);
             var indices = <IndicesArray>polygon.getIndices();
             var indices = <IndicesArray>polygon.getIndices();
-            
+
             // set face colours and textures
             // set face colours and textures
             var idx: number = 0;
             var idx: number = 0;
             var face: number = 0;
             var face: number = 0;
-            for (var index = 0; index < normals.length; index += 3) { 
+            for (var index = 0; index < normals.length; index += 3) {
                 //Edge Face  no. 1
                 //Edge Face  no. 1
-                if(Math.abs(normals[index + 1]) < 0.001) {
-                   face = 1; 
+                if (Math.abs(normals[index + 1]) < 0.001) {
+                    face = 1;
                 }
                 }
                 //Top Face  no. 0
                 //Top Face  no. 0
-                if(Math.abs(normals[index + 1] - 1) < 0.001 ) {
-                   face = 0; 
+                if (Math.abs(normals[index + 1] - 1) < 0.001) {
+                    face = 0;
                 }
                 }
                 //Bottom Face  no. 2
                 //Bottom Face  no. 2
-                if(Math.abs(normals[index + 1] + 1) < 0.001 ) {
-                   face = 2; 
+                if (Math.abs(normals[index + 1] + 1) < 0.001) {
+                    face = 2;
                 }
                 }
                 idx = index / 3;
                 idx = index / 3;
-                uvs[2*idx] = (1 - uvs[2*idx])*faceUV[face].x + uvs[2*idx]*faceUV[face].z;
-                uvs[2*idx + 1] = (1 - uvs[2*idx + 1])*faceUV[face].y + uvs[2*idx + 1]*faceUV[face].w;
+                uvs[2 * idx] = (1 - uvs[2 * idx]) * faceUV[face].x + uvs[2 * idx] * faceUV[face].z;
+                uvs[2 * idx + 1] = (1 - uvs[2 * idx + 1]) * faceUV[face].y + uvs[2 * idx + 1] * faceUV[face].w;
                 if (faceColors) {
                 if (faceColors) {
                     colors.push(faceColors[face].r, faceColors[face].g, faceColors[face].b, faceColors[face].a);
                     colors.push(faceColors[face].r, faceColors[face].g, faceColors[face].b, faceColors[face].a);
                 }
                 }
             }
             }
 
 
-			// sides
+            // sides
             VertexData._ComputeSides(sideOrientation, positions, indices, normals, uvs, frontUVs, backUVs);
             VertexData._ComputeSides(sideOrientation, positions, indices, normals, uvs, frontUVs, backUVs);
-            
+
             // Result
             // Result
             var vertexData = new VertexData();
             var vertexData = new VertexData();
             vertexData.indices = indices;
             vertexData.indices = indices;
@@ -1719,8 +1718,8 @@
             }
             }
 
 
             return vertexData;
             return vertexData;
-			
-		}
+
+        }
 
 
         /**
         /**
          * Creates the VertexData of the IcoSphere.  
          * Creates the VertexData of the IcoSphere.  
@@ -2234,8 +2233,10 @@
          * depthSortedFacets : optional array of depthSortedFacets to store the facet distances from the reference location
          * depthSortedFacets : optional array of depthSortedFacets to store the facet distances from the reference location
          */
          */
         public static ComputeNormals(positions: any, indices: any, normals: any,
         public static ComputeNormals(positions: any, indices: any, normals: any,
-            options?: { facetNormals?: any, facetPositions?: any, facetPartitioning?: any, ratio?: number, bInfo?: any, bbSize?: Vector3, subDiv?: any, 
-                useRightHandedSystem?: boolean, depthSort?: boolean, distanceTo?: Vector3, depthSortedFacets?: any }): void {
+            options?: {
+                facetNormals?: any, facetPositions?: any, facetPartitioning?: any, ratio?: number, bInfo?: any, bbSize?: Vector3, subDiv?: any,
+                useRightHandedSystem?: boolean, depthSort?: boolean, distanceTo?: Vector3, depthSortedFacets?: any
+            }): void {
 
 
             // temporary scalar variables
             // temporary scalar variables
             var index = 0;                      // facet index     
             var index = 0;                      // facet index     
@@ -2276,7 +2277,7 @@
                 if (computeDepthSort) {
                 if (computeDepthSort) {
                     if (distanceTo === undefined) {
                     if (distanceTo === undefined) {
                         distanceTo = Vector3.Zero();
                         distanceTo = Vector3.Zero();
-                    } 
+                    }
                     var depthSortedFacets = options.depthSortedFacets;
                     var depthSortedFacets = options.depthSortedFacets;
                 }
                 }
             }
             }
@@ -2319,7 +2320,7 @@
             }
             }
 
 
             // Loop : 1 indice triplet = 1 facet
             // Loop : 1 indice triplet = 1 facet
-            var nbFaces = (indices.length / 3)|0;
+            var nbFaces = (indices.length / 3) | 0;
             for (index = 0; index < nbFaces; index++) {
             for (index = 0; index < nbFaces; index++) {
 
 
                 // get the indexes of the coordinates of each vertex of the facet
                 // get the indexes of the coordinates of each vertex of the facet
@@ -2386,10 +2387,10 @@
                     block_idx_v3 = b3x + options.subDiv.max * b3y + subSq * b3z;
                     block_idx_v3 = b3x + options.subDiv.max * b3y + subSq * b3z;
                     block_idx_o = ox + options.subDiv.max * oy + subSq * oz;
                     block_idx_o = ox + options.subDiv.max * oy + subSq * oz;
 
 
-                    options.facetPartitioning[block_idx_o] = options.facetPartitioning[block_idx_o] ? options.facetPartitioning[block_idx_o] :new Array();
-                    options.facetPartitioning[block_idx_v1] = options.facetPartitioning[block_idx_v1] ? options.facetPartitioning[block_idx_v1] :new Array();
-                    options.facetPartitioning[block_idx_v2] = options.facetPartitioning[block_idx_v2] ? options.facetPartitioning[block_idx_v2] :new Array();
-                    options.facetPartitioning[block_idx_v3] = options.facetPartitioning[block_idx_v3] ? options.facetPartitioning[block_idx_v3] :new Array();
+                    options.facetPartitioning[block_idx_o] = options.facetPartitioning[block_idx_o] ? options.facetPartitioning[block_idx_o] : new Array();
+                    options.facetPartitioning[block_idx_v1] = options.facetPartitioning[block_idx_v1] ? options.facetPartitioning[block_idx_v1] : new Array();
+                    options.facetPartitioning[block_idx_v2] = options.facetPartitioning[block_idx_v2] ? options.facetPartitioning[block_idx_v2] : new Array();
+                    options.facetPartitioning[block_idx_v3] = options.facetPartitioning[block_idx_v3] ? options.facetPartitioning[block_idx_v3] : new Array();
 
 
                     // push each facet index in each block containing the vertex
                     // push each facet index in each block containing the vertex
                     options.facetPartitioning[block_idx_v1].push(index);
                     options.facetPartitioning[block_idx_v1].push(index);
@@ -2488,12 +2489,12 @@
                     var lu: number = uvs.length;
                     var lu: number = uvs.length;
                     var u: number = 0;
                     var u: number = 0;
                     for (u = 0; u < lu; u++) {
                     for (u = 0; u < lu; u++) {
-                        uvs[u + lu] = uvs[u];                       
+                        uvs[u + lu] = uvs[u];
                     }
                     }
                     frontUVs = frontUVs ? frontUVs : new Vector4(0.0, 0.0, 1.0, 1.0);
                     frontUVs = frontUVs ? frontUVs : new Vector4(0.0, 0.0, 1.0, 1.0);
-                    backUVs = backUVs ? backUVs : new Vector4(0.0, 0.0, 1.0, 1.0); 
+                    backUVs = backUVs ? backUVs : new Vector4(0.0, 0.0, 1.0, 1.0);
                     u = 0;
                     u = 0;
-                    for (i = 0; i < lu / 2; i++) {    
+                    for (i = 0; i < lu / 2; i++) {
                         uvs[u] = frontUVs.x + (frontUVs.z - frontUVs.x) * uvs[u];
                         uvs[u] = frontUVs.x + (frontUVs.z - frontUVs.x) * uvs[u];
                         uvs[u + 1] = frontUVs.y + (frontUVs.w - frontUVs.y) * uvs[u + 1];
                         uvs[u + 1] = frontUVs.y + (frontUVs.w - frontUVs.y) * uvs[u + 1];
                         uvs[u + lu] = backUVs.x + (backUVs.z - backUVs.x) * uvs[u + lu];
                         uvs[u + lu] = backUVs.x + (backUVs.z - backUVs.x) * uvs[u + lu];

+ 3 - 5
src/Mesh/babylon.subMesh.ts

@@ -101,7 +101,9 @@
         public getMaterial(): Nullable<Material> {
         public getMaterial(): Nullable<Material> {
             var rootMaterial = this._renderingMesh.material;
             var rootMaterial = this._renderingMesh.material;
 
 
-            if (rootMaterial && (<MultiMaterial>rootMaterial).getSubMaterial) {
+            if (rootMaterial === null || rootMaterial === undefined) {
+                return this._mesh.getScene().defaultMaterial;
+            } else if ((<MultiMaterial>rootMaterial).getSubMaterial) {
                 var multiMaterial = <MultiMaterial>rootMaterial;
                 var multiMaterial = <MultiMaterial>rootMaterial;
                 var effectiveMaterial = multiMaterial.getSubMaterial(this.materialIndex);
                 var effectiveMaterial = multiMaterial.getSubMaterial(this.materialIndex);
 
 
@@ -113,10 +115,6 @@
                 return effectiveMaterial;
                 return effectiveMaterial;
             }
             }
 
 
-            if (!rootMaterial) {
-                return this._mesh.getScene().defaultMaterial;
-            }
-
             return rootMaterial;
             return rootMaterial;
         }
         }
 
 

+ 5 - 2
src/Mesh/babylon.transformNode.ts

@@ -119,7 +119,7 @@ module BABYLON {
          */
          */
         protected _getWorldMatrixDeterminant(): number {
         protected _getWorldMatrixDeterminant(): number {
             if (this._currentRenderId !== this.getScene().getRenderId()) {
             if (this._currentRenderId !== this.getScene().getRenderId()) {
-                this._worldMatrixDeterminant = this.computeWorldMatrix().determinant();
+                this.computeWorldMatrix();
             }
             }
             return this._worldMatrixDeterminant;
             return this._worldMatrixDeterminant;
         }
         }
@@ -463,7 +463,7 @@ module BABYLON {
          */
          */
         public setParent(node: Nullable<Node>): TransformNode {
         public setParent(node: Nullable<Node>): TransformNode {
 
 
-            if (node == null) {
+            if (node === null) {
                 var rotation = Tmp.Quaternion[0];
                 var rotation = Tmp.Quaternion[0];
                 var position = Tmp.Vector3[0];
                 var position = Tmp.Vector3[0];
                 var scale = Tmp.Vector3[1];
                 var scale = Tmp.Vector3[1];
@@ -835,6 +835,9 @@ module BABYLON {
                 this._poseMatrix = Matrix.Invert(this._worldMatrix);
                 this._poseMatrix = Matrix.Invert(this._worldMatrix);
             }
             }
 
 
+            // Cache the determinant
+            this._worldMatrixDeterminant = this._worldMatrix.determinant();
+
             return this._worldMatrix;
             return this._worldMatrix;
         }
         }
 
 

+ 54 - 0
src/Particles/babylon.boxParticleEmitter.ts

@@ -0,0 +1,54 @@
+module BABYLON {
+    export class BoxParticleEmitter implements IParticleEmitterType {
+
+        public get direction1(): Vector3 {
+            return this._particleSystem.direction1;
+        }
+        public set direction1(value: Vector3) {
+            this._particleSystem.direction1 = value;
+        }
+
+        public get direction2(): Vector3 {
+            return this._particleSystem.direction2;
+        }
+        public set direction2(value: Vector3) {
+            this._particleSystem.direction2 = value;
+        }
+
+        public get minEmitBox(): Vector3 {
+            return this._particleSystem.minEmitBox;
+        }
+        public set minEmitBox(value: Vector3) {
+            this._particleSystem.minEmitBox = value;
+        }
+
+        public get maxEmitBox(): Vector3 {
+            return this._particleSystem.maxEmitBox;
+        }
+        public set maxEmitBox(value: Vector3) {
+            this._particleSystem.maxEmitBox = value;
+        }
+        
+        // to be updated like the rest of emitters when breaking changes.
+        // all property should be come public variables and passed through constructor.
+        constructor(private _particleSystem: ParticleSystem) {
+
+        }
+
+        startDirectionFunction(emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
+            var randX = ParticleSystem.randomNumber(this.direction1.x, this.direction2.x);
+            var randY = ParticleSystem.randomNumber(this.direction1.y, this.direction2.y);
+            var randZ = ParticleSystem.randomNumber(this.direction1.z, this.direction2.z);
+
+            Vector3.TransformNormalFromFloatsToRef(randX * emitPower, randY * emitPower, randZ * emitPower, worldMatrix, directionToUpdate);
+        }
+
+        startPositionFunction(worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void {
+            var randX = ParticleSystem.randomNumber(this.minEmitBox.x, this.maxEmitBox.x);
+            var randY = ParticleSystem.randomNumber(this.minEmitBox.y, this.maxEmitBox.y);
+            var randZ = ParticleSystem.randomNumber(this.minEmitBox.z, this.maxEmitBox.z);
+
+            Vector3.TransformCoordinatesFromFloatsToRef(randX, randY, randZ, worldMatrix, positionToUpdate);
+        }
+    }
+}

+ 29 - 0
src/Particles/babylon.coneParticleEmitter.ts

@@ -0,0 +1,29 @@
+module BABYLON {
+    export class ConeParticleEmitter implements IParticleEmitterType {
+        constructor(public radius: number, public angle: number) {
+        }
+
+        startDirectionFunction(emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
+            if (this.angle === 0) {
+                Vector3.TransformNormalFromFloatsToRef(0, emitPower, 0, worldMatrix, directionToUpdate);
+            }
+            else {
+                var phi = ParticleSystem.randomNumber(0, 2 * Math.PI);
+                var theta = ParticleSystem.randomNumber(0, this.angle);
+                var randX = Math.cos(phi) * Math.sin(theta);
+                var randY = Math.cos(theta);
+                var randZ = Math.sin(phi) * Math.sin(theta);
+                Vector3.TransformNormalFromFloatsToRef(randX * emitPower, randY * emitPower, randZ * emitPower, worldMatrix, directionToUpdate);
+            }
+        }
+
+        startPositionFunction(worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void {
+            var s = ParticleSystem.randomNumber(0, Math.PI * 2);
+            var radius = ParticleSystem.randomNumber(0, this.radius);
+            var randX = radius * Math.sin(s);
+            var randZ = radius * Math.cos(s);
+
+            Vector3.TransformCoordinatesFromFloatsToRef(randX, 0, randZ, worldMatrix, positionToUpdate);
+        }
+    }
+}

+ 6 - 0
src/Particles/babylon.iParticleEmitterType.ts

@@ -0,0 +1,6 @@
+module BABYLON {
+    export interface IParticleEmitterType {
+        startDirectionFunction(emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void;
+        startPositionFunction(worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void;
+    }
+}

+ 60 - 33
src/Particles/babylon.particleSystem.ts

@@ -1,18 +1,8 @@
 module BABYLON {
 module BABYLON {
-    var randomNumber = (min: number, max: number): number => {
-        if (min === max) {
-            return (min);
-        }
-
-        var random = Math.random();
-
-        return ((random * (max - min)) + min);
-    }
-
     export interface IParticleSystem {
     export interface IParticleSystem {
         id: string;
         id: string;
         name: string;
         name: string;
-        emitter: Nullable<AbstractMesh | Vector3>; 
+        emitter: Nullable<AbstractMesh | Vector3>;
         renderingGroupId: number;
         renderingGroupId: number;
         layerMask: number;
         layerMask: number;
         isStarted(): boolean;
         isStarted(): boolean;
@@ -93,6 +83,7 @@
         public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
         public color2 = new Color4(1.0, 1.0, 1.0, 1.0);
         public colorDead = new Color4(0, 0, 0, 1.0);
         public colorDead = new Color4(0, 0, 0, 1.0);
         public textureMask = new Color4(1.0, 1.0, 1.0, 1.0);
         public textureMask = new Color4(1.0, 1.0, 1.0, 1.0);
+        public particleEmitterType: IParticleEmitterType;
         public startDirectionFunction: (emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle) => void;
         public startDirectionFunction: (emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle) => void;
         public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
         public startPositionFunction: (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle) => void;
 
 
@@ -172,21 +163,7 @@
             this._vertexBuffers["options"] = options;
             this._vertexBuffers["options"] = options;
 
 
             // Default behaviors
             // Default behaviors
-            this.startDirectionFunction = (emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void => {
-                var randX = randomNumber(this.direction1.x, this.direction2.x);
-                var randY = randomNumber(this.direction1.y, this.direction2.y);
-                var randZ = randomNumber(this.direction1.z, this.direction2.z);
-
-                Vector3.TransformNormalFromFloatsToRef(randX * emitPower, randY * emitPower, randZ * emitPower, worldMatrix, directionToUpdate);
-            }
-
-            this.startPositionFunction = (worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void => {
-                var randX = randomNumber(this.minEmitBox.x, this.maxEmitBox.x);
-                var randY = randomNumber(this.minEmitBox.y, this.maxEmitBox.y);
-                var randZ = randomNumber(this.minEmitBox.z, this.maxEmitBox.z);
-
-                Vector3.TransformCoordinatesFromFloatsToRef(randX, randY, randZ, worldMatrix, positionToUpdate);
-            }
+            this.particleEmitterType = new BoxParticleEmitter(this);
 
 
             this.updateFunction = (particles: Particle[]): void => {
             this.updateFunction = (particles: Particle[]): void => {
                 for (var index = 0; index < particles.length; index++) {
                 for (var index = 0; index < particles.length; index++) {
@@ -345,18 +322,28 @@
 
 
                 this.particles.push(particle);
                 this.particles.push(particle);
 
 
-                var emitPower = randomNumber(this.minEmitPower, this.maxEmitPower);
+                var emitPower = ParticleSystem.randomNumber(this.minEmitPower, this.maxEmitPower);
 
 
-                this.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
+                if (this.startDirectionFunction) {
+                    this.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
+                }
+                else {
+                    this.particleEmitterType.startDirectionFunction(emitPower, worldMatrix, particle.direction, particle);
+                }
 
 
-                particle.lifeTime = randomNumber(this.minLifeTime, this.maxLifeTime);
+                if (this.startPositionFunction) {
+                    this.startPositionFunction(worldMatrix, particle.position, particle);
+                }
+                else {
+                    this.particleEmitterType.startPositionFunction(worldMatrix, particle.position, particle);
+                }
 
 
-                particle.size = randomNumber(this.minSize, this.maxSize);
-                particle.angularSpeed = randomNumber(this.minAngularSpeed, this.maxAngularSpeed);
+                particle.lifeTime = ParticleSystem.randomNumber(this.minLifeTime, this.maxLifeTime);
 
 
-                this.startPositionFunction(worldMatrix, particle.position, particle);
+                particle.size = ParticleSystem.randomNumber(this.minSize, this.maxSize);
+                particle.angularSpeed = ParticleSystem.randomNumber(this.minAngularSpeed, this.maxAngularSpeed);
 
 
-                var step = randomNumber(0, 1.0);
+                var step = ParticleSystem.randomNumber(0, 1.0);
 
 
                 Color4.LerpToRef(this.color1, this.color2, step, particle.color);
                 Color4.LerpToRef(this.color1, this.color2, step, particle.color);
 
 
@@ -593,6 +580,46 @@
             this.onDisposeObservable.clear();
             this.onDisposeObservable.clear();
         }
         }
 
 
+        public createSphereEmitter(radius = 1): SphereParticleEmitter {
+            var particleEmitter = new SphereParticleEmitter(radius);
+            this.particleEmitterType = particleEmitter;
+            return particleEmitter;
+        }
+
+        public createDirectedSphereEmitter(radius = 1, direction1 = new Vector3(0, 1.0, 0), direction2 = new Vector3(0, 1.0, 0)): SphereDirectedParticleEmitter {
+            var particleEmitter = new SphereDirectedParticleEmitter(radius, direction1, direction2)
+            this.particleEmitterType = particleEmitter;
+            return particleEmitter;
+        }
+
+        public createConeEmitter(radius = 1, angle = Math.PI / 4): ConeParticleEmitter {
+            var particleEmitter = new ConeParticleEmitter(radius, angle);
+            this.particleEmitterType = particleEmitter;
+            return particleEmitter;
+        }
+
+        // this method needs to be changed when breaking changes will be allowed to match the sphere and cone methods and properties direction1,2 and minEmitBox,maxEmitBox to be removed from the system.
+        public createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3): BoxParticleEmitter {
+            var particleEmitter = new BoxParticleEmitter(this);
+            this.direction1 = direction1;
+            this.direction2 = direction2;
+            this.minEmitBox = minEmitBox;
+            this.maxEmitBox = maxEmitBox;
+            this.particleEmitterType = particleEmitter;
+            return particleEmitter;
+        }
+
+        public static randomNumber(min: number, max: number): number {
+            if (min === max) {
+                return (min);
+            }
+
+            var random = Math.random();
+
+            return ((random * (max - min)) + min);
+        }
+
+
         // Clone
         // Clone
         public clone(name: string, newEmitter: any): ParticleSystem {
         public clone(name: string, newEmitter: any): ParticleSystem {
             var custom: Nullable<Effect> = null;
             var custom: Nullable<Effect> = null;

+ 35 - 0
src/Particles/babylon.sphereParticleEmitter.ts

@@ -0,0 +1,35 @@
+module BABYLON {
+    export class SphereParticleEmitter implements IParticleEmitterType {
+        constructor(public radius: number) {
+
+        }
+
+        startDirectionFunction(emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
+            // measure the direction Vector from the emitter to the particle.
+            var direction = particle.position.subtract(worldMatrix.getTranslation());
+            Vector3.TransformNormalFromFloatsToRef(direction.x * emitPower, direction.y * emitPower, direction.z * emitPower, worldMatrix, directionToUpdate);
+        }
+
+        startPositionFunction(worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void {
+            var phi = ParticleSystem.randomNumber(0, 2 * Math.PI);
+            var theta = ParticleSystem.randomNumber(0, Math.PI);
+            var randX = this.radius * Math.cos(phi) * Math.sin(theta);
+            var randY = this.radius * Math.cos(theta);
+            var randZ = this.radius * Math.sin(phi) * Math.sin(theta);
+            Vector3.TransformCoordinatesFromFloatsToRef(randX, randY, randZ, worldMatrix, positionToUpdate);
+        }
+    }
+
+    export class SphereDirectedParticleEmitter extends SphereParticleEmitter {
+        constructor(radius: number, public direction1: Vector3, public direction2: Vector3) {
+            super(radius);
+        }
+
+        startDirectionFunction(emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
+            var randX = ParticleSystem.randomNumber(this.direction1.x, this.direction2.x);
+            var randY = ParticleSystem.randomNumber(this.direction1.y, this.direction2.y);
+            var randZ = ParticleSystem.randomNumber(this.direction1.z, this.direction2.z);
+            Vector3.TransformNormalFromFloatsToRef(randX * emitPower, randY * emitPower, randZ * emitPower, worldMatrix, directionToUpdate);
+        }
+    }
+}

+ 15 - 5
src/Rendering/babylon.geometryBufferRenderer.ts

@@ -108,6 +108,14 @@ module BABYLON {
             return this._multiRenderTarget;
             return this._multiRenderTarget;
         }
         }
 
 
+        public get samples(): number {
+            return this._multiRenderTarget.samples;
+        }
+
+        public set samples(value: number) {
+            this._multiRenderTarget.samples = value;
+        }
+
         // Methods
         // Methods
         public dispose(): void {
         public dispose(): void {
             this.getGBuffer().dispose();
             this.getGBuffer().dispose();
@@ -117,7 +125,9 @@ module BABYLON {
             var engine = this._scene.getEngine();
             var engine = this._scene.getEngine();
             var count = this._enablePosition ? 3 : 2;
             var count = this._enablePosition ? 3 : 2;
 
 
-            this._multiRenderTarget = new MultiRenderTarget("gBuffer", { width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio }, count, this._scene, { generateMipMaps : false, generateDepthTexture: true });
+            this._multiRenderTarget = new MultiRenderTarget("gBuffer",
+                { width: engine.getRenderWidth() * this._ratio, height: engine.getRenderHeight() * this._ratio }, count, this._scene,
+                { generateMipMaps: false, generateDepthTexture: true, defaultType: Engine.TEXTURETYPE_FLOAT });
             if (!this.isSupported) {
             if (!this.isSupported) {
                 return;
                 return;
             }
             }
@@ -126,7 +136,7 @@ module BABYLON {
             this._multiRenderTarget.refreshRate = 1;
             this._multiRenderTarget.refreshRate = 1;
             this._multiRenderTarget.renderParticles = false;
             this._multiRenderTarget.renderParticles = false;
             this._multiRenderTarget.renderList = null;
             this._multiRenderTarget.renderList = null;
-            
+
             // set default depth value to 1.0 (far away)
             // set default depth value to 1.0 (far away)
             this._multiRenderTarget.onClearObservable.add((engine: Engine) => {
             this._multiRenderTarget.onClearObservable.add((engine: Engine) => {
                 engine.clear(new Color4(0.0, 0.0, 0.0, 1.0), true, true, true);
                 engine.clear(new Color4(0.0, 0.0, 0.0, 1.0), true, true, true);
@@ -158,7 +168,7 @@ module BABYLON {
                 if (this.isReady(subMesh, hardwareInstancedRendering)) {
                 if (this.isReady(subMesh, hardwareInstancedRendering)) {
                     engine.enableEffect(this._effect);
                     engine.enableEffect(this._effect);
                     mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
                     mesh._bind(subMesh, this._effect, Material.TriangleFillMode);
-                    
+
 
 
                     this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
                     this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
                     this._effect.setMatrix("view", scene.getViewMatrix());
                     this._effect.setMatrix("view", scene.getViewMatrix());
@@ -188,12 +198,12 @@ module BABYLON {
                 var index;
                 var index;
 
 
                 if (depthOnlySubMeshes.length) {
                 if (depthOnlySubMeshes.length) {
-                    engine.setColorWrite(false);            
+                    engine.setColorWrite(false);
                     for (index = 0; index < depthOnlySubMeshes.length; index++) {
                     for (index = 0; index < depthOnlySubMeshes.length; index++) {
                         renderSubMesh(depthOnlySubMeshes.data[index]);
                         renderSubMesh(depthOnlySubMeshes.data[index]);
                     }
                     }
                     engine.setColorWrite(true);
                     engine.setColorWrite(true);
-                }                  
+                }
 
 
                 for (index = 0; index < opaqueSubMeshes.length; index++) {
                 for (index = 0; index < opaqueSubMeshes.length; index++) {
                     renderSubMesh(opaqueSubMeshes.data[index]);
                     renderSubMesh(opaqueSubMeshes.data[index]);

+ 12 - 5
src/Rendering/babylon.renderingGroup.ts

@@ -315,12 +315,19 @@
         /**
         /**
          * Inserts the submesh in its correct queue depending on its material.
          * Inserts the submesh in its correct queue depending on its material.
          * @param subMesh The submesh to dispatch
          * @param subMesh The submesh to dispatch
+         * @param [mesh] Optional reference to the submeshes's mesh. Provide if you have an exiting reference to improve performance.
+         * @param [material] Optional reference to the submeshes's material. Provide if you have an exiting reference to improve performance.
          */
          */
-        public dispatch(subMesh: SubMesh): void {
-            var material = subMesh.getMaterial();
-            var mesh = subMesh.getMesh();
+        public dispatch(subMesh: SubMesh, mesh?: AbstractMesh, material?: Nullable<Material>): void {
+            // Get mesh and materials if not provided
+            if (mesh === undefined) {
+                mesh = subMesh.getMesh();
+            }
+            if (material === undefined) {
+                material = subMesh.getMaterial();
+            }
 
 
-            if (!material) {
+            if (material === null || material === undefined) {
                 return;
                 return;
             }
             }
 
 
@@ -340,7 +347,7 @@
                 this._opaqueSubMeshes.push(subMesh); // Opaque
                 this._opaqueSubMeshes.push(subMesh); // Opaque
             }
             }
 
 
-            if (mesh._edgesRenderer) {
+            if (mesh._edgesRenderer !== null && mesh._edgesRenderer !== undefined) {
                 this._edgesRenderers.push(mesh._edgesRenderer);
                 this._edgesRenderers.push(mesh._edgesRenderer);
             }
             }
         }
         }

+ 11 - 4
src/Rendering/babylon.renderingManager.ts

@@ -142,7 +142,7 @@
         }
         }
 
 
         private _prepareRenderingGroup(renderingGroupId: number): void {
         private _prepareRenderingGroup(renderingGroupId: number): void {
-            if (!this._renderingGroups[renderingGroupId]) {
+            if (this._renderingGroups[renderingGroupId] === undefined) {
                 this._renderingGroups[renderingGroupId] = new RenderingGroup(renderingGroupId, this._scene,
                 this._renderingGroups[renderingGroupId] = new RenderingGroup(renderingGroupId, this._scene,
                     this._customOpaqueSortCompareFn[renderingGroupId],
                     this._customOpaqueSortCompareFn[renderingGroupId],
                     this._customAlphaTestSortCompareFn[renderingGroupId],
                     this._customAlphaTestSortCompareFn[renderingGroupId],
@@ -167,13 +167,20 @@
             this._renderingGroups[renderingGroupId].dispatchParticles(particleSystem);
             this._renderingGroups[renderingGroupId].dispatchParticles(particleSystem);
         }
         }
 
 
-        public dispatch(subMesh: SubMesh): void {
-            var mesh = subMesh.getMesh();
+        /**
+         * @param subMesh The submesh to dispatch
+         * @param [mesh] Optional reference to the submeshes's mesh. Provide if you have an exiting reference to improve performance.
+         * @param [material] Optional reference to the submeshes's material. Provide if you have an exiting reference to improve performance.
+         */
+        public dispatch(subMesh: SubMesh, mesh?: AbstractMesh, material?: Nullable<Material>): void {
+            if (mesh === undefined) {
+                mesh = subMesh.getMesh();
+            }
             var renderingGroupId = mesh.renderingGroupId || 0;
             var renderingGroupId = mesh.renderingGroupId || 0;
 
 
             this._prepareRenderingGroup(renderingGroupId);
             this._prepareRenderingGroup(renderingGroupId);
 
 
-            this._renderingGroups[renderingGroupId].dispatch(subMesh);
+            this._renderingGroups[renderingGroupId].dispatch(subMesh, mesh, material);
         }
         }
 
 
         /**
         /**

+ 20 - 14
src/Tools/babylon.assetsManager.ts

@@ -7,7 +7,7 @@ module BABYLON {
         ERROR
         ERROR
     }
     }
 
 
-    export abstract class AbstractAssetTask  {
+    export abstract class AbstractAssetTask {
         public onSuccess: (task: any) => void;
         public onSuccess: (task: any) => void;
         public onError: (task: any, message?: string, exception?: any) => void;
         public onError: (task: any, message?: string, exception?: any) => void;
 
 
@@ -112,17 +112,17 @@ module BABYLON {
         public text: string;
         public text: string;
 
 
         public onSuccess: (task: TextFileAssetTask) => void;
         public onSuccess: (task: TextFileAssetTask) => void;
-        public onError: (task: TextFileAssetTask, message?: string, exception?: any) => void;        
+        public onError: (task: TextFileAssetTask, message?: string, exception?: any) => void;
 
 
         constructor(public name: string, public url: string) {
         constructor(public name: string, public url: string) {
             super(name);
             super(name);
         }
         }
 
 
         public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
         public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
-            Tools.LoadFile(this.url, (data) => {
+            scene._loadFile(this.url, (data) => {
                 this.text = data as string;
                 this.text = data as string;
                 onSuccess();
                 onSuccess();
-            }, undefined, scene.database, false, (request, exception) => {
+            }, undefined, false, true, (request, exception) => {
                 if (request) {
                 if (request) {
                     onError(request.status + " " + request.statusText, exception);
                     onError(request.status + " " + request.statusText, exception);
                 }
                 }
@@ -134,18 +134,17 @@ module BABYLON {
         public data: ArrayBuffer;
         public data: ArrayBuffer;
 
 
         public onSuccess: (task: BinaryFileAssetTask) => void;
         public onSuccess: (task: BinaryFileAssetTask) => void;
-        public onError: (task: BinaryFileAssetTask, message?: string, exception?: any) => void;               
+        public onError: (task: BinaryFileAssetTask, message?: string, exception?: any) => void;
 
 
         constructor(public name: string, public url: string) {
         constructor(public name: string, public url: string) {
             super(name);
             super(name);
         }
         }
 
 
         public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
         public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
-            Tools.LoadFile(this.url, (data) => {
-
+            scene._loadFile(this.url, (data) => {
                 this.data = data as ArrayBuffer;
                 this.data = data as ArrayBuffer;
                 onSuccess();
                 onSuccess();
-            }, undefined, scene.database, true, (request, exception) => {
+            }, undefined, true, true, (request, exception) => {
                 if (request) {
                 if (request) {
                     onError(request.status + " " + request.statusText, exception);
                     onError(request.status + " " + request.statusText, exception);
                 }
                 }
@@ -157,7 +156,7 @@ module BABYLON {
         public image: HTMLImageElement;
         public image: HTMLImageElement;
 
 
         public onSuccess: (task: ImageAssetTask) => void;
         public onSuccess: (task: ImageAssetTask) => void;
-        public onError: (task: ImageAssetTask, message?: string, exception?: any) => void;            
+        public onError: (task: ImageAssetTask, message?: string, exception?: any) => void;
 
 
         constructor(public name: string, public url: string) {
         constructor(public name: string, public url: string) {
             super(name);
             super(name);
@@ -187,9 +186,9 @@ module BABYLON {
 
 
     export class TextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<Texture> {
     export class TextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<Texture> {
         public texture: Texture;
         public texture: Texture;
-        
+
         public onSuccess: (task: TextureAssetTask) => void;
         public onSuccess: (task: TextureAssetTask) => void;
-        public onError: (task: TextureAssetTask, message?: string, exception?: any) => void;    
+        public onError: (task: TextureAssetTask, message?: string, exception?: any) => void;
 
 
         constructor(public name: string, public url: string, public noMipmap?: boolean, public invertY?: boolean, public samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
         constructor(public name: string, public url: string, public noMipmap?: boolean, public invertY?: boolean, public samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
             super(name);
             super(name);
@@ -213,7 +212,7 @@ module BABYLON {
         public texture: CubeTexture;
         public texture: CubeTexture;
 
 
         public onSuccess: (task: CubeTextureAssetTask) => void;
         public onSuccess: (task: CubeTextureAssetTask) => void;
-        public onError: (task: CubeTextureAssetTask, message?: string, exception?: any) => void;            
+        public onError: (task: CubeTextureAssetTask, message?: string, exception?: any) => void;
 
 
         constructor(public name: string, public url: string, public extensions?: string[], public noMipmap?: boolean, public files?: string[]) {
         constructor(public name: string, public url: string, public extensions?: string[], public noMipmap?: boolean, public files?: string[]) {
             super(name);
             super(name);
@@ -237,7 +236,7 @@ module BABYLON {
         public texture: HDRCubeTexture;
         public texture: HDRCubeTexture;
 
 
         public onSuccess: (task: HDRCubeTextureAssetTask) => void;
         public onSuccess: (task: HDRCubeTextureAssetTask) => void;
-        public onError: (task: HDRCubeTextureAssetTask, message?: string, exception?: any) => void;               
+        public onError: (task: HDRCubeTextureAssetTask, message?: string, exception?: any) => void;
 
 
         constructor(public name: string, public url: string, public size?: number, public noMipmap = false, public generateHarmonics = true, public useInGammaSpace = false, public usePMREMGenerator = false) {
         constructor(public name: string, public url: string, public size?: number, public noMipmap = false, public generateHarmonics = true, public useInGammaSpace = false, public usePMREMGenerator = false) {
             super(name);
             super(name);
@@ -259,6 +258,7 @@ module BABYLON {
 
 
     export class AssetsManager {
     export class AssetsManager {
         private _scene: Scene;
         private _scene: Scene;
+        private _isLoading = false;
 
 
         protected tasks = new Array<AbstractAssetTask>();
         protected tasks = new Array<AbstractAssetTask>();
         protected waitingTasksCount = 0;
         protected waitingTasksCount = 0;
@@ -366,7 +366,7 @@ module BABYLON {
                     Tools.Error("Error running tasks-done callbacks.");
                     Tools.Error("Error running tasks-done callbacks.");
                     console.log(e);
                     console.log(e);
                 }
                 }
-
+                this._isLoading = false;
                 this._scene.getEngine().hideLoadingUI();
                 this._scene.getEngine().hideLoadingUI();
             }
             }
         }
         }
@@ -402,11 +402,16 @@ module BABYLON {
         }
         }
 
 
         public reset(): AssetsManager {
         public reset(): AssetsManager {
+            this._isLoading = false;
             this.tasks = new Array<AbstractAssetTask>();
             this.tasks = new Array<AbstractAssetTask>();
             return this;
             return this;
         }
         }
 
 
         public load(): AssetsManager {
         public load(): AssetsManager {
+            if (this._isLoading) {
+                return this;
+            }
+            this._isLoading = true;
             this.waitingTasksCount = this.tasks.length;
             this.waitingTasksCount = this.tasks.length;
 
 
             if (this.waitingTasksCount === 0) {
             if (this.waitingTasksCount === 0) {
@@ -414,6 +419,7 @@ module BABYLON {
                     this.onFinish(this.tasks);
                     this.onFinish(this.tasks);
                 }
                 }
                 this.onTasksDoneObservable.notifyObservers(this.tasks);
                 this.onTasksDoneObservable.notifyObservers(this.tasks);
+                this._isLoading = false;
                 return this;
                 return this;
             }
             }
 
 

+ 2 - 2
src/Tools/babylon.filesInput.ts

@@ -7,7 +7,7 @@
         private _engine: Engine;
         private _engine: Engine;
         private _currentScene: Scene;
         private _currentScene: Scene;
         private _sceneLoadedCallback: (sceneFile: File, scene: Scene) => void;
         private _sceneLoadedCallback: (sceneFile: File, scene: Scene) => void;
-        private _progressCallback: (progress: ProgressEvent) => void;
+        private _progressCallback: (progress: SceneLoaderProgressEvent) => void;
         private _additionalRenderLoopLogicCallback: () => void;
         private _additionalRenderLoopLogicCallback: () => void;
         private _textureLoadingCallback: (remaining: number) => void;
         private _textureLoadingCallback: (remaining: number) => void;
         private _startingProcessingFilesCallback: () => void;
         private _startingProcessingFilesCallback: () => void;
@@ -18,7 +18,7 @@
         private _sceneFileToLoad: File;
         private _sceneFileToLoad: File;
         private _filesToLoad: File[];
         private _filesToLoad: File[];
 
 
-        constructor(engine: Engine, scene: Scene, sceneLoadedCallback: (sceneFile: File, scene: Scene) => void, progressCallback: (progress: ProgressEvent) => void, additionalRenderLoopLogicCallback: () => void,
+        constructor(engine: Engine, scene: Scene, sceneLoadedCallback: (sceneFile: File, scene: Scene) => void, progressCallback: (progress: SceneLoaderProgressEvent) => void, additionalRenderLoopLogicCallback: () => void, 
             textureLoadingCallback: (remaining: number) => void, startingProcessingFilesCallback: () => void, onReloadCallback: (sceneFile: File) => void, errorCallback: (sceneFile: File, scene: Scene, message: string) => void) {
             textureLoadingCallback: (remaining: number) => void, startingProcessingFilesCallback: () => void, onReloadCallback: (sceneFile: File) => void, errorCallback: (sceneFile: File, scene: Scene, message: string) => void) {
             this._engine = engine;
             this._engine = engine;
             this._currentScene = scene;
             this._currentScene = scene;

+ 5 - 0
src/Tools/babylon.sceneSerializer.ts

@@ -209,6 +209,11 @@
                 serializationObject.multiMaterials.push(multiMaterial.serialize());
                 serializationObject.multiMaterials.push(multiMaterial.serialize());
             }
             }
 
 
+            // Environment texture
+            if (scene.environmentTexture) {
+                serializationObject.environmentTexture = scene.environmentTexture.name;
+            }
+
             // Skeletons
             // Skeletons
             serializationObject.skeletons = [];
             serializationObject.skeletons = [];
             for (index = 0; index < scene.skeletons.length; index++) {
             for (index = 0; index < scene.skeletons.length; index++) {

+ 144 - 58
src/Tools/babylon.tools.ts

@@ -30,6 +30,18 @@
         }
         }
     }
     }
 
 
+    export interface IFileRequest {
+        /**
+         * Raised when the request is complete (success or error).
+         */
+        onCompleteObservable: Observable<IFileRequest>;
+
+        /**
+         * Aborts the request for a file.
+         */
+        abort: () => void;
+    }
+
     // Screenshots
     // Screenshots
     var screenshotCanvas: HTMLCanvasElement;
     var screenshotCanvas: HTMLCanvasElement;
 
 
@@ -514,88 +526,141 @@
             return img;
             return img;
         }
         }
 
 
-        public static LoadFile(url: string, callback: (data: string | ArrayBuffer, responseURL?: string) => void, progressCallBack?: (data: any) => void, database?: Database, useArrayBuffer?: boolean, onError?: (request?: XMLHttpRequest, exception?: any) => void, onRetry?: (oldRequest: XMLHttpRequest, newRequest: XMLHttpRequest) => void, retryStrategy: Nullable<(url: string, request: XMLHttpRequest, retryIndex: number) => number> = null): Nullable<XMLHttpRequest> {
+        public static LoadFile(url: string, onSuccess: (data: string | ArrayBuffer, responseURL?: string) => void, onProgress?: (data: any) => void, database?: Database, useArrayBuffer?: boolean, onError?: (request?: XMLHttpRequest, exception?: any) => void): IFileRequest {
             url = Tools.CleanUrl(url);
             url = Tools.CleanUrl(url);
 
 
             url = Tools.PreprocessUrl(url);
             url = Tools.PreprocessUrl(url);
 
 
-            let request: Nullable<XMLHttpRequest> = null;
+            // If file and file input are set
+            if (url.indexOf("file:") !== -1) {
+                const fileName = decodeURIComponent(url.substring(5).toLowerCase());
+                if (FilesInput.FilesToLoad[fileName]) {
+                    return Tools.ReadFile(FilesInput.FilesToLoad[fileName], onSuccess, onProgress, useArrayBuffer);
+                }
+            }
 
 
-            let noIndexedDB = (retryIndex?: number) => {
-                let oldRequest = request;
-                request = new XMLHttpRequest();
+            const loadUrl = Tools.BaseUrl + url;
 
 
-                let loadUrl = Tools.BaseUrl + url;
-                request.open('GET', loadUrl, true);
+            let aborted = false;
+            const fileRequest: IFileRequest = {
+                onCompleteObservable: new Observable<IFileRequest>(),
+                abort: () => aborted = true,
+            };
 
 
-                if (useArrayBuffer) {
-                    request.responseType = "arraybuffer";
-                }
+            const requestFile = () => {
+                let request = new XMLHttpRequest();
+                let retryHandle: Nullable<number> = null;
 
 
-                if (progressCallBack) {
-                    request.onprogress = progressCallBack;
-                }
+                fileRequest.abort = () => {
+                    aborted = true;
 
 
-                request.onreadystatechange = () => {
-                    let req = <XMLHttpRequest>request;
-                    // In case of undefined state in some browsers.
-                    if (req.readyState === (XMLHttpRequest.DONE || 4)) {
-                        req.onreadystatechange = () => { };//some browsers have issues where onreadystatechange can be called multiple times with the same value
+                    if (request.readyState !== (XMLHttpRequest.DONE || 4)) {
+                        request.abort();
+                    }
+
+                    if (retryHandle !== null) {
+                        clearTimeout(retryHandle);
+                        retryHandle = null;
+                    }
+                };
 
 
-                        if (req.status >= 200 && req.status < 300 || (!Tools.IsWindowObjectExist() && (req.status === 0))) {
-                            callback(!useArrayBuffer ? req.responseText : <ArrayBuffer>req.response, req.responseURL);
+                const retryLoop = (retryIndex: number) => {
+                    request.open('GET', loadUrl, true);
+
+                    if (useArrayBuffer) {
+                        request.responseType = "arraybuffer";
+                    }
+
+                    if (onProgress) {
+                        request.addEventListener("progress", onProgress);
+                    }
+
+                    const onLoadEnd = () => {
+                        fileRequest.onCompleteObservable.notifyObservers(fileRequest);
+                    };
+
+                    request.addEventListener("loadend", onLoadEnd);
+
+                    const onReadyStateChange = () => {
+                        if (aborted) {
                             return;
                             return;
                         }
                         }
 
 
-                        retryStrategy = retryStrategy || Tools.DefaultRetryStrategy;
-                        if (retryStrategy) {
-                            let waitTime = retryStrategy(loadUrl, req, retryIndex || 0);
-                            if (waitTime !== -1) {
-                                setTimeout(() => noIndexedDB((retryIndex || 0) + 1), waitTime);
+                        // In case of undefined state in some browsers.
+                        if (request.readyState === (XMLHttpRequest.DONE || 4)) {
+                            // Some browsers have issues where onreadystatechange can be called multiple times with the same value.
+                            request.removeEventListener("readystatechange", onReadyStateChange);
+
+                            if (request.status >= 200 && request.status < 300 || (!Tools.IsWindowObjectExist() && (request.status === 0))) {
+                                onSuccess(!useArrayBuffer ? request.responseText : <ArrayBuffer>request.response, request.responseURL);
                                 return;
                                 return;
                             }
                             }
-                        }
 
 
-                        let e = new Error("Error status: " + req.status + " - Unable to load " + loadUrl);
-                        if (onError) {
-                            onError(req, e);
-                        } else {
-                            throw e;
+                            let retryStrategy = Tools.DefaultRetryStrategy;
+                            if (retryStrategy) {
+                                let waitTime = retryStrategy(loadUrl, request, retryIndex);
+                                if (waitTime !== -1) {
+                                    // Prevent the request from completing for retry.
+                                    request.removeEventListener("loadend", onLoadEnd);
+                                    request = new XMLHttpRequest();
+                                    retryHandle = setTimeout(() => retryLoop(retryIndex + 1), waitTime);
+                                    return;
+                                }
+                            }
+
+                            let e = new LoadFileError("Error status: " + request.status + " " + request.statusText + " - Unable to load " + loadUrl, request);
+                            if (onError) {
+                                onError(request, e);
+                            } else {
+                                throw e;
+                            }
                         }
                         }
-                    }
-                };
+                    };
 
 
-                request.send();
+                    request.addEventListener("readystatechange", onReadyStateChange);
 
 
-                if (oldRequest && onRetry) {
-                    onRetry(oldRequest, request);
-                }
-            };
+                    request.send();
+                };
 
 
-            let loadFromIndexedDB = () => {
-                if (database) {
-                    database.loadFileFromDB(url, callback, progressCallBack, noIndexedDB, useArrayBuffer);
-                }
+                retryLoop(0);
             };
             };
 
 
-            // If file and file input are set
-            if (url.indexOf("file:") !== -1) {
-                let fileName = decodeURIComponent(url.substring(5).toLowerCase());
-                if (FilesInput.FilesToLoad[fileName]) {
-                    Tools.ReadFile(FilesInput.FilesToLoad[fileName], callback, progressCallBack, useArrayBuffer);
-                    return request;
-                }
-            }
-
             // Caching all files
             // Caching all files
             if (database && database.enableSceneOffline) {
             if (database && database.enableSceneOffline) {
+                const noIndexedDB = () => {
+                    if (!aborted) {
+                        requestFile();
+                    }
+                };
+
+                const loadFromIndexedDB = () => {
+                    // TODO: database needs to support aborting and should return a IFileRequest
+                    if (aborted) {
+                        return;
+                    }
+
+                    if (database) {
+                        database.loadFileFromDB(url, data => {
+                            if (!aborted) {
+                                onSuccess(data);
+                            }
+
+                            fileRequest.onCompleteObservable.notifyObservers(fileRequest);
+                        }, onProgress ? event => {
+                            if (!aborted) {
+                                onProgress(event);
+                            }
+                        } : undefined, noIndexedDB, useArrayBuffer);
+                    }
+                };
+
                 database.openAsync(loadFromIndexedDB, noIndexedDB);
                 database.openAsync(loadFromIndexedDB, noIndexedDB);
             }
             }
             else {
             else {
-                noIndexedDB();
+                requestFile();
             }
             }
 
 
-            return request;
+            return fileRequest;
         }
         }
 
 
         /** 
         /** 
@@ -623,18 +688,38 @@
             head.appendChild(script);
             head.appendChild(script);
         }
         }
 
 
-        public static ReadFileAsDataURL(fileToLoad: Blob, callback: (data: any) => void, progressCallback: (this: MSBaseReader, ev: ProgressEvent) => any): void {
-            var reader = new FileReader();
+        public static ReadFileAsDataURL(fileToLoad: Blob, callback: (data: any) => void, progressCallback: (this: MSBaseReader, ev: ProgressEvent) => any): IFileRequest {
+            let reader = new FileReader();
+
+            let request: IFileRequest = {
+                onCompleteObservable: new Observable<IFileRequest>(),
+                abort: () => reader.abort(),
+            };
+
+            reader.onloadend = e => {
+                request.onCompleteObservable.notifyObservers(request);
+            };
+
             reader.onload = e => {
             reader.onload = e => {
                 //target doesn't have result from ts 1.3
                 //target doesn't have result from ts 1.3
                 callback((<any>e.target)['result']);
                 callback((<any>e.target)['result']);
             };
             };
+
             reader.onprogress = progressCallback;
             reader.onprogress = progressCallback;
+
             reader.readAsDataURL(fileToLoad);
             reader.readAsDataURL(fileToLoad);
+
+            return request;
         }
         }
 
 
-        public static ReadFile(fileToLoad: File, callback: (data: any) => void, progressCallBack?: (this: MSBaseReader, ev: ProgressEvent) => any, useArrayBuffer?: boolean): void {
-            var reader = new FileReader();
+        public static ReadFile(fileToLoad: File, callback: (data: any) => void, progressCallBack?: (this: MSBaseReader, ev: ProgressEvent) => any, useArrayBuffer?: boolean): IFileRequest {
+            let reader = new FileReader();
+            let request: IFileRequest = {
+                onCompleteObservable: new Observable<IFileRequest>(),
+                abort: () => reader.abort(),
+            };
+
+            reader.onloadend = e => request.onCompleteObservable.notifyObservers(request);
             reader.onerror = e => {
             reader.onerror = e => {
                 Tools.Log("Error while reading file: " + fileToLoad.name);
                 Tools.Log("Error while reading file: " + fileToLoad.name);
                 callback(JSON.stringify({ autoClear: true, clearColor: [1, 0, 0], ambientColor: [0, 0, 0], gravity: [0, -9.807, 0], meshes: [], cameras: [], lights: [] }));
                 callback(JSON.stringify({ autoClear: true, clearColor: [1, 0, 0], ambientColor: [0, 0, 0], gravity: [0, -9.807, 0], meshes: [], cameras: [], lights: [] }));
@@ -643,7 +728,6 @@
                 //target doesn't have result from ts 1.3
                 //target doesn't have result from ts 1.3
                 callback((<any>e.target)['result']);
                 callback((<any>e.target)['result']);
             };
             };
-
             if (progressCallBack) {
             if (progressCallBack) {
                 reader.onprogress = progressCallBack;
                 reader.onprogress = progressCallBack;
             }
             }
@@ -654,6 +738,8 @@
             else {
             else {
                 reader.readAsArrayBuffer(fileToLoad);
                 reader.readAsArrayBuffer(fileToLoad);
             }
             }
+
+            return request;
         }
         }
 
 
         //returns a downloadable url to a file content.
         //returns a downloadable url to a file content.

+ 14 - 43
src/babylon.mixins.ts

@@ -1,18 +1,11 @@
 // Mixins
 // Mixins
 interface Window {
 interface Window {
-    mozIndexedDB(func: any): any;
-    webkitIndexedDB(func: any): any;
+    mozIndexedDB: IDBFactory;
+    webkitIndexedDB: IDBFactory;
     msIndexedDB: IDBFactory;
     msIndexedDB: IDBFactory;
-    IDBTransaction(func: any): any;
-    webkitIDBTransaction(func: any): any;
-    msIDBTransaction(func: any): any;
-    IDBKeyRange(func: any): any;
-    webkitIDBKeyRange(func: any): any;
-    msIDBKeyRange(func: any): any;
-    webkitURL: HTMLURL;
-    webkitRequestAnimationFrame(func: any): any;
-    mozRequestAnimationFrame(func: any): any;
-    oRequestAnimationFrame(func: any): any;
+    webkitURL: typeof URL;
+    mozRequestAnimationFrame(callback: FrameRequestCallback): number;
+    oRequestAnimationFrame(callback: FrameRequestCallback): number;
     WebGLRenderingContext: WebGLRenderingContext;
     WebGLRenderingContext: WebGLRenderingContext;
     MSGesture: MSGesture;
     MSGesture: MSGesture;
     CANNON: any;
     CANNON: any;
@@ -23,8 +16,8 @@ interface Window {
     Math: Math;
     Math: Math;
     Uint8Array: Uint8ArrayConstructor;
     Uint8Array: Uint8ArrayConstructor;
     Float32Array: Float32ArrayConstructor;
     Float32Array: Float32ArrayConstructor;
-    mozURL: any;
-    msURL: any;
+    mozURL: typeof URL;
+    msURL: typeof URL;
     VRFrameData: any; // WebVR, from specs 1.1
     VRFrameData: any; // WebVR, from specs 1.1
 }
 }
 
 
@@ -81,13 +74,7 @@ interface WebGLRenderingContext {
     QUERY_RESULT: number;
     QUERY_RESULT: number;
 }
 }
 
 
-interface HTMLURL {
-    createObjectURL(param1: any, param2?: any): string;
-}
-
 interface Document {
 interface Document {
-    exitFullscreen(): void;
-    webkitCancelFullScreen(): void;
     mozCancelFullScreen(): void;
     mozCancelFullScreen(): void;
     msCancelFullScreen(): void;
     msCancelFullScreen(): void;
     mozFullScreen: boolean;
     mozFullScreen: boolean;
@@ -99,17 +86,12 @@ interface Document {
 }
 }
 
 
 interface HTMLCanvasElement {
 interface HTMLCanvasElement {
-    requestPointerLock(): void;
     msRequestPointerLock?(): void;
     msRequestPointerLock?(): void;
     mozRequestPointerLock?(): void;
     mozRequestPointerLock?(): void;
     webkitRequestPointerLock?(): void;
     webkitRequestPointerLock?(): void;
 }
 }
 
 
 interface CanvasRenderingContext2D {
 interface CanvasRenderingContext2D {
-    imageSmoothingEnabled: boolean;
-    mozImageSmoothingEnabled: boolean;
-    oImageSmoothingEnabled: boolean;
-    webkitImageSmoothingEnabled: boolean;
     msImageSmoothingEnabled: boolean;
     msImageSmoothingEnabled: boolean;
 }
 }
 
 
@@ -133,23 +115,16 @@ interface MouseEvent {
     msMovementY: number;
     msMovementY: number;
 }
 }
 
 
-interface MSStyleCSSProperties {
-    webkitTransform: string;
-    webkitTransition: string;
-}
-
 interface Navigator {
 interface Navigator {
     getVRDisplays: () => any;
     getVRDisplays: () => any;
     mozGetVRDevices: (any: any) => any;
     mozGetVRDevices: (any: any) => any;
-    getUserMedia: any;
-    webkitGetUserMedia: any;
-    mozGetUserMedia: any;
-    msGetUserMedia: any;
-
-    getGamepads(func?: any): any;
-    webkitGetGamepads(func?: any): any
-    msGetGamepads(func?: any): any;
-    webkitGamepads(func?: any): any;
+    webkitGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
+    mozGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
+    msGetUserMedia(constraints: MediaStreamConstraints, successCallback: NavigatorUserMediaSuccessCallback, errorCallback: NavigatorUserMediaErrorCallback): void;
+
+    webkitGetGamepads(): Gamepad[];
+    msGetGamepads(): Gamepad[];
+    webkitGamepads(): Gamepad[];
 }
 }
 
 
 interface HTMLVideoElement {
 interface HTMLVideoElement {
@@ -161,10 +136,6 @@ interface Screen {
     mozOrientation: string;
     mozOrientation: string;
 }
 }
 
 
-interface HTMLMediaElement {
-    crossOrigin: string | null;
-}
-
 interface Math {
 interface Math {
     fround(x: number): number;
     fround(x: number): number;
     imul(a: number, b: number): number;
     imul(a: number, b: number): number;

+ 15 - 7
src/babylon.node.ts

@@ -44,17 +44,20 @@
                 return;
                 return;
             }
             }
 
 
-            if (this._parentNode) {
+            // Remove self from list of children of parent
+            if (this._parentNode && this._parentNode._children !== undefined && this._parentNode._children !== null) {
                 var index = this._parentNode._children.indexOf(this);
                 var index = this._parentNode._children.indexOf(this);
                 if (index !== -1) {
                 if (index !== -1) {
                     this._parentNode._children.splice(index, 1);
                     this._parentNode._children.splice(index, 1);
                 }
                 }
             }
             }
 
 
+            // Store new parent
             this._parentNode = parent;
             this._parentNode = parent;
 
 
+            // Add as child to new parent
             if (this._parentNode) {
             if (this._parentNode) {
-                if (!this._parentNode._children) {
+                if (this._parentNode._children === undefined || this._parentNode._children === null) {
                     this._parentNode._children = new Array<Node>();
                     this._parentNode._children = new Array<Node>();
                 }
                 }
                 this._parentNode._children.push(this);
                 this._parentNode._children.push(this);
@@ -241,17 +244,22 @@
 
 
         /**
         /**
          * Is this node enabled. 
          * Is this node enabled. 
-         * If the node has a parent and is enabled, the parent will be inspected as well.
+         * If the node has a parent, all ancestors will be checked and false will be returned if any are false (not enabled), otherwise will return true.
+         * @param {boolean} [checkAncestors=true] - Indicates if this method should check the ancestors. The default is to check the ancestors. If set to false, the method will return the value of this node without checking ancestors.
          * @return {boolean} whether this node (and its parent) is enabled.
          * @return {boolean} whether this node (and its parent) is enabled.
          * @see setEnabled
          * @see setEnabled
          */
          */
-        public isEnabled(): boolean {
-            if (!this._isEnabled) {
+        public isEnabled(checkAncestors: boolean = true): boolean {
+            if (checkAncestors === false) {
+                return this._isEnabled;
+            }
+
+            if (this._isEnabled === false) {
                 return false;
                 return false;
             }
             }
 
 
-            if (this.parent) {
-                return this.parent.isEnabled();
+            if (this.parent !== undefined && this.parent !== null) {
+                return this.parent.isEnabled(checkAncestors);
             }
             }
 
 
             return true;
             return true;

+ 103 - 24
src/babylon.scene.ts

@@ -3,6 +3,11 @@
         dispose(): void;
         dispose(): void;
     }
     }
 
 
+    export interface IActiveMeshCandidateProvider {
+        getMeshes(scene: Scene): AbstractMesh[];
+        readonly checksIsEnabled: boolean; // Indicates if the meshes have been checked to make sure they are isEnabled().
+    }
+
     class ClickInfo {
     class ClickInfo {
         private _singleClick = false;
         private _singleClick = false;
         private _doubleClick = false;
         private _doubleClick = false;
@@ -870,9 +875,11 @@
         private _alternateProjectionUpdateFlag = -1;
         private _alternateProjectionUpdateFlag = -1;
 
 
         public _toBeDisposed = new SmartArray<Nullable<IDisposable>>(256);
         public _toBeDisposed = new SmartArray<Nullable<IDisposable>>(256);
+        private _activeRequests = new Array<IFileRequest>();
         private _pendingData = new Array();
         private _pendingData = new Array();
         private _isDisposed = false;
         private _isDisposed = false;
 
 
+        public dispatchAllSubMeshesOfActiveMeshes: boolean = false;
         private _activeMeshes = new SmartArray<AbstractMesh>(256);
         private _activeMeshes = new SmartArray<AbstractMesh>(256);
         private _processedMaterials = new SmartArray<Material>(256);
         private _processedMaterials = new SmartArray<Material>(256);
         private _renderTargets = new SmartArrayNoDuplicate<RenderTargetTexture>(256);
         private _renderTargets = new SmartArrayNoDuplicate<RenderTargetTexture>(256);
@@ -2015,6 +2022,17 @@
             return animatable;
             return animatable;
         }
         }
 
 
+        /**
+         * Begin a new animation on a given node
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of created animatables
+         */
         public beginDirectAnimation(target: any, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Animatable {
         public beginDirectAnimation(target: any, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Animatable {
             if (speedRatio === undefined) {
             if (speedRatio === undefined) {
                 speedRatio = 1.0;
                 speedRatio = 1.0;
@@ -2025,6 +2043,28 @@
             return animatable;
             return animatable;
         }
         }
 
 
+        /**
+         * Begin a new animation on a given node and its hierarchy
+         * @param {BABYLON.Node} node defines the root node where the animation will take place
+         * @param {boolean} 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 {BABYLON.Animation[]} defines the list of animations to start
+         * @param {number} from defines the initial value
+         * @param {number} to defines the final value
+         * @param {boolean} loop defines if you want animation to loop (off by default)
+         * @param {number} speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @returns the list of animatables created for all nodes
+         */
+        public beginDirectHierarchyAnimation(target: Node, directDescendantsOnly: boolean, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void): Animatable[] {
+            let children = target.getDescendants(directDescendantsOnly);
+            let result = [];
+            for (var child of children) {
+                result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd));
+            }
+
+            return result;
+        }
+
         public getAnimatableByTarget(target: any): Nullable<Animatable> {
         public getAnimatableByTarget(target: any): Nullable<Animatable> {
             for (var index = 0; index < this._activeAnimatables.length; index++) {
             for (var index = 0; index < this._activeAnimatables.length; index++) {
                 if (this._activeAnimatables[index].target === target) {
                 if (this._activeAnimatables[index].target === target) {
@@ -2035,7 +2075,7 @@
             return null;
             return null;
         }
         }
 
 
-        public get Animatables(): Animatable[] {
+        public get animatables(): Animatable[] {
             return this._activeAnimatables;
             return this._activeAnimatables;
         }
         }
 
 
@@ -2918,18 +2958,18 @@
         }
         }
 
 
         private _evaluateSubMesh(subMesh: SubMesh, mesh: AbstractMesh): void {
         private _evaluateSubMesh(subMesh: SubMesh, mesh: AbstractMesh): void {
-            if (mesh.alwaysSelectAsActiveMesh || mesh.subMeshes.length === 1 || subMesh.isInFrustum(this._frustumPlanes)) {
-                var material = subMesh.getMaterial();
-
+            if (this.dispatchAllSubMeshesOfActiveMeshes || mesh.alwaysSelectAsActiveMesh || mesh.subMeshes.length === 1 || subMesh.isInFrustum(this._frustumPlanes)) {
                 if (mesh.showSubMeshesBoundingBox) {
                 if (mesh.showSubMeshesBoundingBox) {
-                    let boundingInfo = subMesh.getBoundingInfo();
-
-                    this.getBoundingBoxRenderer().renderList.push(boundingInfo.boundingBox);
+                    const boundingInfo = subMesh.getBoundingInfo();
+                    if (boundingInfo !== null && boundingInfo !== undefined) {
+                        this.getBoundingBoxRenderer().renderList.push(boundingInfo.boundingBox);
+                    }
                 }
                 }
 
 
-                if (material) {
+                const material = subMesh.getMaterial();
+                if (material !== null && material !== undefined) {
                     // Render targets
                     // Render targets
-                    if (material.getRenderTargetTextures) {
+                    if (material.getRenderTargetTextures !== undefined) {
                         if (this._processedMaterials.indexOf(material) === -1) {
                         if (this._processedMaterials.indexOf(material) === -1) {
                             this._processedMaterials.push(material);
                             this._processedMaterials.push(material);
 
 
@@ -2939,7 +2979,7 @@
 
 
                     // Dispatch
                     // Dispatch
                     this._activeIndices.addCount(subMesh.indexCount, false);
                     this._activeIndices.addCount(subMesh.indexCount, false);
-                    this._renderingManager.dispatch(subMesh);
+                    this._renderingManager.dispatch(subMesh, mesh, material);
                 }
                 }
             }
             }
         }
         }
@@ -2948,6 +2988,14 @@
             return this._intermediateRendering
             return this._intermediateRendering
         }
         }
 
 
+        private _activeMeshCandidateProvider: IActiveMeshCandidateProvider;
+        public setActiveMeshCandidateProvider(provider: IActiveMeshCandidateProvider): void {
+            this._activeMeshCandidateProvider = provider;
+        }
+        public getActiveMeshCandidateProvider(): IActiveMeshCandidateProvider {
+            return this._activeMeshCandidateProvider;
+        }
+
         private _activeMeshesFrozen = false;
         private _activeMeshesFrozen = false;
 
 
         /**
         /**
@@ -2992,18 +3040,32 @@
             // Meshes
             // Meshes
             var meshes: AbstractMesh[];
             var meshes: AbstractMesh[];
             var len: number;
             var len: number;
-
-            if (this._selectionOctree) { // Octree
+            var checkIsEnabled = true;
+
+            // Determine mesh candidates
+            if (this._activeMeshCandidateProvider !== undefined) {
+                // Use _activeMeshCandidateProvider
+                meshes = this._activeMeshCandidateProvider.getMeshes(this);
+                checkIsEnabled = this._activeMeshCandidateProvider.checksIsEnabled === false;
+                if (meshes !== undefined) {
+                    len = meshes.length;
+                } else {
+                    len = 0;
+                }
+            } else if (this._selectionOctree !== undefined) {
+                // Octree
                 var selection = this._selectionOctree.select(this._frustumPlanes);
                 var selection = this._selectionOctree.select(this._frustumPlanes);
                 meshes = selection.data;
                 meshes = selection.data;
                 len = selection.length;
                 len = selection.length;
-            } else { // Full scene traversal
+            } else {
+                // Full scene traversal
                 len = this.meshes.length;
                 len = this.meshes.length;
                 meshes = this.meshes;
                 meshes = this.meshes;
             }
             }
 
 
-            for (var meshIndex = 0; meshIndex < len; meshIndex++) {
-                var mesh = meshes[meshIndex];
+            // Check each mesh
+            for (var meshIndex = 0, mesh, meshLOD; meshIndex < len; meshIndex++) {
+                mesh = meshes[meshIndex];
 
 
                 if (mesh.isBlocked) {
                 if (mesh.isBlocked) {
                     continue;
                     continue;
@@ -3011,7 +3073,7 @@
 
 
                 this._totalVertices.addCount(mesh.getTotalVertices(), false);
                 this._totalVertices.addCount(mesh.getTotalVertices(), false);
 
 
-                if (!mesh.isReady() || !mesh.isEnabled()) {
+                if (!mesh.isReady() || (checkIsEnabled && !mesh.isEnabled())) {
                     continue;
                     continue;
                 }
                 }
 
 
@@ -3023,9 +3085,9 @@
                 }
                 }
 
 
                 // Switch to current LOD
                 // Switch to current LOD
-                var meshLOD = mesh.getLOD(this.activeCamera);
+                meshLOD = mesh.getLOD(this.activeCamera);
 
 
-                if (!meshLOD) {
+                if (meshLOD === undefined || meshLOD === null) {
                     continue;
                     continue;
                 }
                 }
 
 
@@ -3068,7 +3130,7 @@
         }
         }
 
 
         private _activeMesh(sourceMesh: AbstractMesh, mesh: AbstractMesh): void {
         private _activeMesh(sourceMesh: AbstractMesh, mesh: AbstractMesh): void {
-            if (mesh.skeleton && this.skeletonsEnabled) {
+            if (this.skeletonsEnabled && mesh.skeleton !== null && mesh.skeleton !== undefined) {
                 if (this._activeSkeletons.pushNoDuplicate(mesh.skeleton)) {
                 if (this._activeSkeletons.pushNoDuplicate(mesh.skeleton)) {
                     mesh.skeleton.prepare();
                     mesh.skeleton.prepare();
                 }
                 }
@@ -3084,12 +3146,15 @@
                 this.getBoundingBoxRenderer().renderList.push(boundingInfo.boundingBox);
                 this.getBoundingBoxRenderer().renderList.push(boundingInfo.boundingBox);
             }
             }
 
 
-            if (mesh && mesh.subMeshes) {
+            if (
+                mesh !== undefined && mesh !== null
+                && mesh.subMeshes !== undefined && mesh.subMeshes !== null && mesh.subMeshes.length > 0
+            ) {
                 // Submeshes Octrees
                 // Submeshes Octrees
                 var len: number;
                 var len: number;
                 var subMeshes: SubMesh[];
                 var subMeshes: SubMesh[];
 
 
-                if (mesh._submeshesOctree && mesh.useOctreeForRenderingSelection) {
+                if (mesh.useOctreeForRenderingSelection && mesh._submeshesOctree !== undefined && mesh._submeshesOctree !== null) {
                     var intersections = mesh._submeshesOctree.select(this._frustumPlanes);
                     var intersections = mesh._submeshesOctree.select(this._frustumPlanes);
 
 
                     len = intersections.length;
                     len = intersections.length;
@@ -3099,8 +3164,8 @@
                     len = subMeshes.length;
                     len = subMeshes.length;
                 }
                 }
 
 
-                for (var subIndex = 0; subIndex < len; subIndex++) {
-                    var subMesh = subMeshes[subIndex];
+                for (var subIndex = 0, subMesh; subIndex < len; subIndex++) {
+                    subMesh = subMeshes[subIndex];
 
 
                     this._evaluateSubMesh(subMesh, mesh);
                     this._evaluateSubMesh(subMesh, mesh);
                 }
                 }
@@ -3812,6 +3877,11 @@
             this._meshesForIntersections.dispose();
             this._meshesForIntersections.dispose();
             this._toBeDisposed.dispose();
             this._toBeDisposed.dispose();
 
 
+            // Abort active requests
+            for (let request of this._activeRequests) {
+                request.abort();
+            }
+
             // Debug layer
             // Debug layer
             if (this._debugLayer) {
             if (this._debugLayer) {
                 this._debugLayer.hide();
                 this._debugLayer.hide();
@@ -4441,7 +4511,7 @@
                     camera = freeCamera;
                     camera = freeCamera;
                 }
                 }
                 camera.minZ = radius * 0.01;
                 camera.minZ = radius * 0.01;
-                camera.maxZ = radius * 100;
+                camera.maxZ = radius * 1000;
                 camera.speed = radius * 0.2;
                 camera.speed = radius * 0.2;
                 this.activeCamera = camera;
                 this.activeCamera = camera;
 
 
@@ -4587,5 +4657,14 @@
                 material.markAsDirty(flag);
                 material.markAsDirty(flag);
             }
             }
         }
         }
+
+        public _loadFile(url: string, onSuccess: (data: string | ArrayBuffer, responseURL?: string) => void, onProgress?: (data: any) => void, useDatabase?: boolean, useArrayBuffer?: boolean, onError?: (request?: XMLHttpRequest, exception?: any) => void): IFileRequest {
+            let request = Tools.LoadFile(url, onSuccess, onProgress, useDatabase ? this.database : undefined, useArrayBuffer, onError);
+            this._activeRequests.push(request);
+            request.onCompleteObservable.add(request => {
+                this._activeRequests.splice(this._activeRequests.indexOf(request), 1);
+            });
+            return request;
+        }
     }
     }
 }
 }

BIN
tests/validation/ReferenceImages/Flat2009.png


BIN
tests/validation/ReferenceImages/GLTF LOD.png


BIN
tests/validation/ReferenceImages/SpaceDeK.png


+ 0 - 0
tests/validation/ReferenceImages/instancedBones.png


Some files were not shown because too many files changed in this diff