Преглед на файлове

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

Guide преди 7 години
родител
ревизия
0876a9ccab
променени са 99 файла, в които са добавени 15386 реда и са изтрити 11412 реда
  1. 9683 9264
      Playground/babylon.d.txt
  2. 12 20
      Playground/debug.html
  3. 12 20
      Playground/index-local.html
  4. 12 20
      Playground/index.html
  5. 12 20
      Playground/indexStable.html
  6. 81 80
      Playground/js/index.js
  7. 12 20
      Playground/ts.html
  8. 3 1
      Tools/Gulp/config.json
  9. 16 13
      Viewer/dist/basicExample.html
  10. 3 1
      Viewer/src/configuration/types/default.ts
  11. 2 2
      Viewer/src/viewer/defaultViewer.ts
  12. 591 172
      dist/preview release/babylon.d.ts
  13. 1 1
      dist/preview release/babylon.js
  14. 863 282
      dist/preview release/babylon.max.js
  15. 863 282
      dist/preview release/babylon.no-module.max.js
  16. 1 1
      dist/preview release/babylon.worker.js
  17. 865 284
      dist/preview release/es6.js
  18. 1 1
      dist/preview release/glTF2Interface/package.json
  19. 1 1
      dist/preview release/gui/package.json
  20. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js
  21. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  22. 8 3
      dist/preview release/inspector/babylon.inspector.d.ts
  23. 18 6
      dist/preview release/inspector/babylon.inspector.module.d.ts
  24. 1 1
      dist/preview release/inspector/package.json
  25. 2 2
      dist/preview release/loaders/package.json
  26. 1 1
      dist/preview release/materialsLibrary/package.json
  27. 1 1
      dist/preview release/postProcessesLibrary/package.json
  28. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  29. 2 2
      dist/preview release/serializers/package.json
  30. 19 5
      dist/preview release/viewer/babylon.viewer.d.ts
  31. 3 3
      dist/preview release/viewer/babylon.viewer.js
  32. 3 3
      dist/preview release/viewer/babylon.viewer.max.js
  33. 22 5
      dist/preview release/viewer/babylon.viewer.module.d.ts
  34. 229 218
      dist/preview release/what's new.md
  35. 12 9
      inspector/src/helpers/Helpers.ts
  36. 54 47
      inspector/src/tabs/SceneTab.ts
  37. 208 80
      inspector/src/tabs/TextureTab.ts
  38. 1 1
      inspector/src/tabs/ToolsTab.ts
  39. BIN
      inspector/test/environment.dds
  40. 3 0
      inspector/test/index.js
  41. BIN
      inspector/test/test_1.dds
  42. 31 3
      loaders/src/OBJ/babylon.objFileLoader.ts
  43. 6 3
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  44. 11 8
      loaders/src/glTF/babylon.glTFFileLoader.ts
  45. 1 1
      package.json
  46. 2 0
      src/Actions/babylon.action.ts
  47. 24 5
      src/Actions/babylon.actionManager.ts
  48. 12 12
      src/Animations/babylon.animation.ts
  49. 57 40
      src/Animations/babylon.runtimeAnimation.ts
  50. 11 7
      src/Behaviors/Mesh/babylon.pointerDragBehavior.ts
  51. 4 0
      src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts
  52. 18 0
      src/Cameras/VR/babylon.webVRCamera.ts
  53. 12 14
      src/Culling/babylon.boundingBox.ts
  54. 14 10
      src/Culling/babylon.boundingSphere.ts
  55. 1 1
      src/Engine/babylon.engine.ts
  56. 35 29
      src/Gizmos/babylon.boundingBoxGizmo.ts
  57. 4 0
      src/Instrumentation/babylon.sceneInstrumentation.ts
  58. 9 6
      src/Loading/babylon.sceneLoader.ts
  59. 5 2
      src/Materials/Background/babylon.backgroundMaterial.ts
  60. 5 0
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  61. 4 1
      src/Materials/Textures/Procedurals/babylon.noiseProceduralTexture.ts
  62. 14 1
      src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts
  63. 2 0
      src/Materials/babylon.standardMaterial.ts
  64. 0 10
      src/Mesh/babylon.abstractMesh.ts
  65. 21 30
      src/Mesh/babylon.mesh.ts
  66. 2 2
      src/Mesh/babylon.transformNode.ts
  67. 138 0
      src/Particles/EmitterTypes/babylon.cylinderParticleEmitter.ts
  68. 131 7
      src/Particles/babylon.IParticleSystem.ts
  69. 57 2
      src/Particles/babylon.baseParticleSystem.ts
  70. 94 2
      src/Particles/babylon.gpuParticleSystem.ts
  71. 14 2
      src/Particles/babylon.particle.ts
  72. 9 2
      src/Particles/babylon.particleHelper.ts
  73. 262 18
      src/Particles/babylon.particleSystem.ts
  74. 9 3
      src/Physics/Plugins/babylon.cannonJSPlugin.ts
  75. 19 17
      src/Physics/babylon.physicsJoint.ts
  76. 2 2
      src/PostProcess/babylon.postProcess.ts
  77. 166 8
      src/Rendering/babylon.outlineRenderer.ts
  78. 2 2
      src/Rendering/babylon.renderingGroup.ts
  79. 2 2
      src/Rendering/babylon.renderingManager.ts
  80. 1 1
      src/Rendering/babylon.utilityLayerRenderer.ts
  81. 4 0
      src/Shaders/ShadersInclude/defaultVertexDeclaration.fx
  82. 5 1
      src/Shaders/background.vertex.fx
  83. 5 1
      src/Shaders/default.vertex.fx
  84. 7 1
      src/Shaders/gpuRenderParticles.fragment.fx
  85. 35 25
      src/Shaders/gpuUpdateParticles.vertex.fx
  86. 1 2
      src/Shaders/noise.fragment.fx
  87. 7 2
      src/Shaders/particles.fragment.fx
  88. 14 10
      src/Shaders/pbr.vertex.fx
  89. 2 2
      src/Sprites/babylon.sprite.ts
  90. 46 1
      src/Sprites/babylon.spriteManager.ts
  91. 297 0
      src/Sprites/babylon.spriteSceneComponent.ts
  92. 10 4
      src/Tools/babylon.filesInput.ts
  93. 8 0
      src/Tools/babylon.tools.ts
  94. 75 231
      src/babylon.scene.ts
  95. 25 0
      src/babylon.sceneComponent.ts
  96. BIN
      tests/validation/ReferenceImages/outline.png
  97. BIN
      tests/validation/ReferenceImages/ssao2.png
  98. 8 3
      tests/validation/config.json
  99. 2 1
      what's new.md

Файловите разлики са ограничени, защото са твърде много
+ 9683 - 9264
Playground/babylon.d.txt


+ 12 - 20
Playground/debug.html

@@ -141,7 +141,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1600">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1600">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -150,14 +150,12 @@
                         <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
                         <div class="option" id="formatButton1600">Format code</div>
                         <div class="option" id="minimapToggle1600">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                     </div>
                 </div>
 
-                <div class="button uncheck" id="debugButton1600">Inspector
-                    <i class="fa fa-square-o" aria-hidden="true"></i>
-                </div>
+                <div class="button uncheck" id="debugButton1600">Inspector</div>
                 <div class="button" id="metadataButton1600">Metadata</div>
             </div>
 
@@ -233,7 +231,7 @@
                             </div>
                         </div>
                         <div class="option" id='safemodeToggle1475'>Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1475">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -242,11 +240,9 @@
                         <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
                         <div class="option" id="formatButton1475">Format code</div>
                         <div class="option" id="minimapToggle1475">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1475">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1475">Inspector</div>
                         <div class="option" id="metadataButton1475">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1475">Vers. : Latest</span>
@@ -316,7 +312,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1030">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1030">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -325,11 +321,9 @@
                         <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
                         <div class="option" id="formatButton1030">Format code</div>
                         <div class="option" id="minimapToggle1030">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1030">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1030">Inspector</div>
                         <div class="option" id="metadataButton1030">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1030">Vers. : Latest</span>
@@ -396,7 +390,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle750">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" 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>
@@ -405,11 +399,9 @@
                         <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
                         <div class="option" id="formatButton750">Format code</div>
                         <div class="option" id="minimapToggle750">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton750">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton750">Inspector</div>
                         <div class="option" id="metadataButton750">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion750">Vers. : Latest</span>

+ 12 - 20
Playground/index-local.html

@@ -81,7 +81,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1600">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1600">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -90,14 +90,12 @@
                         <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
                         <div class="option" id="formatButton1600">Format code</div>
                         <div class="option" id="minimapToggle1600">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                     </div>
                 </div>
 
-                <div class="button uncheck" id="debugButton1600">Inspector
-                    <i class="fa fa-square-o" aria-hidden="true"></i>
-                </div>
+                <div class="button uncheck" id="debugButton1600">Inspector</div>
                 <div class="button" id="metadataButton1600">Metadata</div>
             </div>
 
@@ -172,7 +170,7 @@
                             </div>
                         </div>
                         <div class="option" id='safemodeToggle1475'>Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1475">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -181,11 +179,9 @@
                         <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
                         <div class="option" id="formatButton1475">Format code</div>
                         <div class="option" id="minimapToggle1475">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1475">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1475">Inspector</div>
                         <div class="option" id="metadataButton1475">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1475">Vers. : Latest</span>
@@ -255,7 +251,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1030">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1030">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -264,11 +260,9 @@
                         <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
                         <div class="option" id="formatButton1030">Format code</div>
                         <div class="option" id="minimapToggle1030">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1030">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1030">Inspector</div>
                         <div class="option" id="metadataButton1030">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1030">Vers. : Latest</span>
@@ -335,7 +329,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle750">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" 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>
@@ -344,11 +338,9 @@
                         <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
                         <div class="option" id="formatButton750">Format code</div>
                         <div class="option" id="minimapToggle750">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton750">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton750">Inspector</div>
                         <div class="option" id="metadataButton750">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion750">Vers. : Latest</span>

+ 12 - 20
Playground/index.html

@@ -116,7 +116,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1600">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1600">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -125,14 +125,12 @@
                         <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
                         <div class="option" id="formatButton1600">Format code</div>
                         <div class="option" id="minimapToggle1600">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                     </div>
                 </div>
 
-                <div class="button uncheck" id="debugButton1600">Inspector
-                    <i class="fa fa-square-o" aria-hidden="true"></i>
-                </div>
+                <div class="button uncheck" id="debugButton1600">Inspector</div>
                 <div class="button" id="metadataButton1600">Metadata</div>
             </div>
 
@@ -207,7 +205,7 @@
                             </div>
                         </div>
                         <div class="option" id='safemodeToggle1475'>Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1475">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -216,11 +214,9 @@
                         <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
                         <div class="option" id="formatButton1475">Format code</div>
                         <div class="option" id="minimapToggle1475">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1475">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1475">Inspector</div>
                         <div class="option" id="metadataButton1475">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1475">Vers. : Latest</span>
@@ -290,7 +286,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1030">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1030">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -299,11 +295,9 @@
                         <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
                         <div class="option" id="formatButton1030">Format code</div>
                         <div class="option" id="minimapToggle1030">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1030">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1030">Inspector</div>
                         <div class="option" id="metadataButton1030">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1030">Vers. : Latest</span>
@@ -370,7 +364,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle750">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" 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>
@@ -379,11 +373,9 @@
                         <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
                         <div class="option" id="formatButton750">Format code</div>
                         <div class="option" id="minimapToggle750">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton750">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton750">Inspector</div>
                         <div class="option" id="metadataButton750">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion750">Vers. : Latest</span>

+ 12 - 20
Playground/indexStable.html

@@ -109,7 +109,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1600">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1600">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -118,14 +118,12 @@
                         <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
                         <div class="option" id="formatButton1600">Format code</div>
                         <div class="option" id="minimapToggle1600">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                     </div>
                 </div>
 
-                <div class="button uncheck" id="debugButton1600">Inspector
-                    <i class="fa fa-square-o" aria-hidden="true"></i>
-                </div>
+                <div class="button uncheck" id="debugButton1600">Inspector</div>
                 <div class="button" id="metadataButton1600">Metadata</div>
             </div>
 
@@ -198,7 +196,7 @@
                             </div>
                         </div>
                         <div class="option" id='safemodeToggle1475'>Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1475">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -207,11 +205,9 @@
                         <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
                         <div class="option" id="formatButton1475">Format code</div>
                         <div class="option" id="minimapToggle1475">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1475">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1475">Inspector</div>
                         <div class="option" id="metadataButton1475">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1475">Vers. : Latest</span>
@@ -281,7 +277,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1030">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1030">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -290,11 +286,9 @@
                         <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
                         <div class="option" id="formatButton1030">Format code</div>
                         <div class="option" id="minimapToggle1030">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1030">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1030">Inspector</div>
                         <div class="option" id="metadataButton1030">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1030">Vers. : Latest</span>
@@ -361,7 +355,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle750">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" 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>
@@ -370,11 +364,9 @@
                         <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
                         <div class="option" id="formatButton750">Format code</div>
                         <div class="option" id="minimapToggle750">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton750">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton750">Inspector</div>
                         <div class="option" id="metadataButton750">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion750">Vers. : Latest</span>

+ 81 - 80
Playground/js/index.js

@@ -83,7 +83,7 @@ function showError(errorMessage, errorEvent) {
 
         if (examplesButton && examplesButton.length > 0) {
             var isExamplesDisplayed = false;
-            for(var i = 0; i < examplesButton.length; i++) {
+            for (var i = 0; i < examplesButton.length; i++) {
                 examplesButton[i].parentElement.onclick = function () {
                     isExamplesDisplayed = !isExamplesDisplayed;
                     if (isExamplesDisplayed) {
@@ -94,10 +94,10 @@ function showError(errorMessage, errorEvent) {
                         document.getElementById("exampleList").style.display = "none";
                         document.getElementsByClassName("wrapper")[0].style.width = "100%";
                     }
-                } 
+                }
             }
         }
-        
+
 
         var filterBar = document.getElementById("filterBar");
         if (filterBar) {
@@ -151,7 +151,7 @@ function showError(errorMessage, errorEvent) {
 
             // setToMultipleID("currentScript", "innerHTML", "Custom");
             setToMultipleID("safemodeToggle", "addClass", "checked");
-            setToMultipleID("minimapToggle", "addClass", "checked");
+            // setToMultipleID("minimapToggle", "addClass", "checked"); // Why ?!
             setToMultipleID('safemodeToggle', 'innerHTML', 'Safe mode <i class="fa fa-check-square" aria-hidden="true"></i>');
         }
 
@@ -176,10 +176,10 @@ function showError(errorMessage, errorEvent) {
             setToMultipleID("currentVersion", "innerHTML", "Version: Latest");
         }
 
-        var checkTypescriptSupport = function(xhr) {
+        var checkTypescriptSupport = function (xhr) {
             var filename = location.pathname.substring(location.pathname.lastIndexOf('/') + 1);
             if (xhr.responseText.indexOf("class Playground") !== -1) {// Typescript content
-                if(!filename) {
+                if (!filename) {
                     window.location.href = location.protocol + "//" + location.host + "/ts.html" + window.location.hash;
                     return false;
                 }
@@ -191,7 +191,7 @@ function showError(errorMessage, errorEvent) {
                 if (filename === "ts.html") {
                     window.location.href = location.protocol + "//" + location.host + location.pathname.replace(filename, "index.html") + window.location.hash;
                     return false;
-                }  
+                }
             }
 
             return true;
@@ -243,10 +243,10 @@ function showError(errorMessage, errorEvent) {
         var loadScriptsList = function () {
 
             var exampleList = document.getElementById("exampleList");
-           
+
             var xhr = new XMLHttpRequest();
             //Open Typescript or Javascript examples
-            if(exampleList.className != 'typescript') {
+            if (exampleList.className != 'typescript') {
                 xhr.open('GET', 'https://raw.githubusercontent.com/BabylonJS/Documentation/master/examples/list.json', true);
             }
             else {
@@ -265,7 +265,7 @@ function showError(errorMessage, errorEvent) {
                         }
                         scripts.sort(sortScriptsList);
 
-                                                
+
 
                         if (exampleList) {
                             for (var i = 0; i < scripts.length; i++) {
@@ -347,41 +347,41 @@ function showError(errorMessage, errorEvent) {
                                     // Old examples
                                     //loadScriptFromIndex(index);
                                     var newPG = "";
-                                    switch(index) {
-                                        case 1 : newPG = "#TAZ2CB#0"; break; // Basic scene
-                                        case 2 : newPG = "#A1210C#0"; break; // Basic elements
-                                        case 3 : newPG = "#CURCZC#0"; break; // Rotation and scaling
-                                        case 4 : newPG = "#DXARSP#0"; break; // Materials
-                                        case 5 : newPG = "#1A3M5C#0"; break; // Cameras
-                                        case 6 : newPG = "#AQRDKW#0"; break; // Lights
-                                        case 7 : newPG = "#QYFDDP#1"; break; // Animations
-                                        case 8 : newPG = "#9RI8CG#0"; break; // Sprites
-                                        case 9 : newPG = "#U8MEB0#0"; break; // Collisions
-                                        case 10 : newPG = "#KQV9SA#0"; break; // Intersections
-                                        case 11 : newPG = "#NU4F6Y#0"; break; // Picking
-                                        case 12 : newPG = "#EF9X5R#0"; break; // Particles
-                                        case 13 : newPG = "#7G0IQW#0"; break; // Environment
-                                        case 14 : newPG = "#95PXRY#0"; break; // Height map
-                                        case 15 : newPG = "#IFYDRS#0"; break; // Shadows
-                                        case 16 : newPG = "#AQZJ4C#0"; break; // Import meshes
-                                        case 17 : newPG = "#J19GYK#0"; break; // Actions
-                                        case 18 : newPG = "#UZ23UH#0"; break; // Drag and drop
-                                        case 19 : newPG = "#AQZJ4C#0"; break; // Fresnel
-                                        case 20 : newPG = "#8ZNVGR#0"; break; // Easing functions
-                                        case 21 : newPG = "#B2ZXG6#0"; break; // Procedural texture
-                                        case 22 : newPG = "#DXAEUY#0"; break; // Basic sounds
-                                        case 23 : newPG = "#EDVU95#0"; break; // Sound on mesh
-                                        case 24 : newPG = "#N96NXC#0"; break; // SSAO rendering pipeline
-                                        case 25 : newPG = "#7D2QDD#0"; break; // SSAO 2
-                                        case 26 : newPG = "#V2DAKC#0"; break; // Volumetric light scattering
-                                        case 27 : newPG = "#XH85A9#0"; break; // Refraction and reflection
-                                        case 28 : newPG = "#8MGKWK#0"; break; // PBR
-                                        case 29 : newPG = "#0K8EYN#0"; break; // Instanced bones
-                                        case 30 : newPG = "#C245A1#0"; break; // Pointer events handling
-                                        case 31 : newPG = "#TAFSN0#2"; break; // WebVR
-                                        case 32 : newPG = "#3VMTI9#0"; break; // GUI
-                                        case 33 : newPG = "#7149G4#0"; break; // Physics
-                                        
+                                    switch (index) {
+                                        case 1: newPG = "#TAZ2CB#0"; break; // Basic scene
+                                        case 2: newPG = "#A1210C#0"; break; // Basic elements
+                                        case 3: newPG = "#CURCZC#0"; break; // Rotation and scaling
+                                        case 4: newPG = "#DXARSP#0"; break; // Materials
+                                        case 5: newPG = "#1A3M5C#0"; break; // Cameras
+                                        case 6: newPG = "#AQRDKW#0"; break; // Lights
+                                        case 7: newPG = "#QYFDDP#1"; break; // Animations
+                                        case 8: newPG = "#9RI8CG#0"; break; // Sprites
+                                        case 9: newPG = "#U8MEB0#0"; break; // Collisions
+                                        case 10: newPG = "#KQV9SA#0"; break; // Intersections
+                                        case 11: newPG = "#NU4F6Y#0"; break; // Picking
+                                        case 12: newPG = "#EF9X5R#0"; break; // Particles
+                                        case 13: newPG = "#7G0IQW#0"; break; // Environment
+                                        case 14: newPG = "#95PXRY#0"; break; // Height map
+                                        case 15: newPG = "#IFYDRS#0"; break; // Shadows
+                                        case 16: newPG = "#AQZJ4C#0"; break; // Import meshes
+                                        case 17: newPG = "#J19GYK#0"; break; // Actions
+                                        case 18: newPG = "#UZ23UH#0"; break; // Drag and drop
+                                        case 19: newPG = "#AQZJ4C#0"; break; // Fresnel
+                                        case 20: newPG = "#8ZNVGR#0"; break; // Easing functions
+                                        case 21: newPG = "#B2ZXG6#0"; break; // Procedural texture
+                                        case 22: newPG = "#DXAEUY#0"; break; // Basic sounds
+                                        case 23: newPG = "#EDVU95#0"; break; // Sound on mesh
+                                        case 24: newPG = "#N96NXC#0"; break; // SSAO rendering pipeline
+                                        case 25: newPG = "#7D2QDD#0"; break; // SSAO 2
+                                        case 26: newPG = "#V2DAKC#0"; break; // Volumetric light scattering
+                                        case 27: newPG = "#XH85A9#0"; break; // Refraction and reflection
+                                        case 28: newPG = "#8MGKWK#0"; break; // PBR
+                                        case 29: newPG = "#0K8EYN#0"; break; // Instanced bones
+                                        case 30: newPG = "#C245A1#0"; break; // Pointer events handling
+                                        case 31: newPG = "#TAFSN0#2"; break; // WebVR
+                                        case 32: newPG = "#3VMTI9#0"; break; // GUI
+                                        case 33: newPG = "#7149G4#0"; break; // Physics
+
                                         default: newPG = ""; break;
                                     }
                                     window.location.href = location.protocol + "//" + location.host + location.pathname + "#" + newPG;
@@ -552,18 +552,18 @@ function showError(errorMessage, errorEvent) {
                 var createEngineFunction = "createDefaultEngine";
                 var createSceneFunction;
 
-                getRunCode(jsEditor, function(code) {
+                getRunCode(jsEditor, function (code) {
                     var createDefaultEngine = function () {
                         return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true });
                     }
-    
+
                     var scene;
                     var defaultEngineZip = "new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true })";
 
                     if (code.indexOf("createEngine") !== -1) {
                         createEngineFunction = "createEngine";
                     }
-    
+
                     if (code.indexOf("delayCreateScene") !== -1) { // createScene
                         createSceneFunction = "delayCreateScene";
                         checkCamera = false;
@@ -574,14 +574,14 @@ function showError(errorMessage, errorEvent) {
                     } else if (code.indexOf("createscene") !== -1) { // createscene
                         createSceneFunction = "createscene";
                     }
-    
+
                     if (!createSceneFunction) {
                         // just pasted code.
                         engine = createDefaultEngine();
                         scene = new BABYLON.Scene(engine);
                         eval("runScript = function(scene, canvas) {" + code + "}");
                         runScript(scene, canvas);
-    
+
                         zipCode = "var engine = " + defaultEngineZip + ";\r\nvar scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
                     } else {
                         //execute the code
@@ -592,59 +592,59 @@ function showError(errorMessage, errorEvent) {
                             showError("createEngine function must return an engine.", null);
                             return;
                         }
-    
+
                         //create scene
                         eval("scene = " + createSceneFunction + "()");
-    
+
                         if (!scene) {
                             showError(createSceneFunction + " function must return a scene.", null);
                             return;
                         }
-    
+
                         var createEngineZip = (createEngineFunction === "createEngine")
                             ? "createEngine()"
                             : defaultEngineZip
 
-                        zipCode = 
+                        zipCode =
                             code + "\r\n\r\n" +
                             "var engine = " + createEngineZip + ";\r\n" +
                             "var scene = " + createSceneFunction + "();"
 
                     }
-    
+
                     engine.runRenderLoop(function () {
                         if (engine.scenes.length === 0) {
                             return;
                         }
-    
+
                         if (canvas.width !== canvas.clientWidth) {
                             engine.resize();
                         }
-    
+
                         var scene = engine.scenes[0];
-    
+
                         if (scene.activeCamera || scene.activeCameras.length > 0) {
                             scene.render();
                         }
-    
+
                         fpsLabel.style.right = document.body.clientWidth - (jsEditor.domElement.clientWidth + canvas.clientWidth) + "px";
                         fpsLabel.innerHTML = engine.getFps().toFixed() + " fps";
                     });
-    
+
                     if (engine.scenes.length === 0) {
                         showError("You must at least create a scene.", null);
                         return;
                     }
-    
+
                     if (checkCamera && engine.scenes[0].activeCamera == null) {
                         showError("You must at least create a camera.", null);
                         return;
                     }
-    
+
                     engine.scenes[0].executeWhenReady(function () {
                         document.getElementById("statusBar").innerHTML = "";
                     });
-    
+
                     if (scene) {
                         if (showInspector) {
                             scene.debugLayer.show({ initialTab: initialTabIndex });
@@ -655,7 +655,7 @@ function showError(errorMessage, errorEvent) {
                             scene.debugLayer.show();
                         }
                     }
-                });              
+                });
 
             } catch (e) {
                 showError(e.message, e);
@@ -899,7 +899,7 @@ function showError(errorMessage, errorEvent) {
             if (editorButton.classList.contains('checked')) {
                 setToMultipleID("editorButton", "removeClass", 'checked');
                 splitInstance.collapse(0);
-                setToMultipleID("editorButton", "innerHTML", 'Editor <i class="fa fa-square-o" aria-hidden="true"></i>');
+                setToMultipleID("editorButton", "innerHTML", 'Editor <i class="far fa-square" aria-hidden="true"></i>');
             } else {
                 setToMultipleID("editorButton", "addClass", 'checked');
                 splitInstance.setSizes([50, 50]);  // Reset
@@ -908,7 +908,7 @@ function showError(errorMessage, errorEvent) {
             engine.resize();
 
             if (scene.debugLayer.isVisible()) {
-                scene.debugLayer.hide();
+                scene.debugLayer.hide();  // Because when you close it with the cross, it doesn't call hide(), so you have to do it in code
                 scene.debugLayer.show();
             }
         }
@@ -941,8 +941,12 @@ function showError(errorMessage, errorEvent) {
                 contextmenu: false,
                 folding: true,
                 showFoldingControls: "always",
-                renderIndentGuides: true
-            };            
+                renderIndentGuides: true,
+                minimap: {
+                    enabled: true
+                }
+            };
+            editorOptions.minimap.enabled = document.getElementById("minimapToggle1600").classList.contains('checked');
             jsEditor = monaco.editor.create(document.getElementById('jsEditor'), editorOptions);
             jsEditor.setValue(oldCode);
             setFontSize(fontSize);
@@ -963,22 +967,18 @@ function showError(errorMessage, errorEvent) {
             }
 
             localStorage.setItem("bjs-playground-theme", theme);
-
         }
 
         var toggleDebug = function () {
-            var debugButton = document.getElementById("debugButton1600");
+            // Always showing the debug layer, because you can close it by itself
             var scene = engine.scenes[0];
-
-            if (debugButton.classList.contains('uncheck')) {
-                setToMultipleID("debugButton", "removeClass", 'uncheck');
-                setToMultipleID("debugButton", "innerHTML", 'Inspector<i class="fa fa-check-square" aria-hidden="true"></i>');
-                scene.debugLayer.show();
-            } else {
-                setToMultipleID("debugButton", "addClass", 'uncheck');
-                setToMultipleID("debugButton", "innerHTML", 'Inspector<i class="fa fa-square-o" aria-hidden="true"></i>');
+            if (document.getElementsByClassName("insp-right-panel")[0]) {
                 scene.debugLayer.hide();
             }
+            else {
+                scene.debugLayer.hide(); // Because when you close it with the cross, it doesn't call hide(), so you have to do it in code
+                scene.debugLayer.show();
+            }
         }
 
         var toggleMetadata = function () {
@@ -994,7 +994,7 @@ function showError(errorMessage, errorEvent) {
             var minimapToggle = document.getElementById("minimapToggle1600");
             if (minimapToggle.classList.contains('checked')) {
                 jsEditor.updateOptions({ minimap: { enabled: false } });
-                setToMultipleID("minimapToggle", "innerHTML", 'Minimap <i class="fa fa-square-o" aria-hidden="true"></i>');
+                setToMultipleID("minimapToggle", "innerHTML", 'Minimap <i class="far fa-square" aria-hidden="true"></i>');
             } else {
                 jsEditor.updateOptions({ minimap: { enabled: true } });
                 setToMultipleID("minimapToggle", "innerHTML", 'Minimap <i class="fa fa-check-square" aria-hidden="true"></i>');
@@ -1202,7 +1202,7 @@ function showError(errorMessage, errorEvent) {
             if (document.getElementById("safemodeToggle1600").classList.contains('checked')) {
                 setToMultipleID("safemodeToggle", "innerHTML", 'Safe mode <i class="fa fa-check-square" aria-hidden="true"></i>');
             } else {
-                setToMultipleID("safemodeToggle", "innerHTML", 'Safe mode <i class="fa fa-square-o" aria-hidden="true"></i>');
+                setToMultipleID("safemodeToggle", "innerHTML", 'Safe mode <i class="far fa-square" aria-hidden="true"></i>');
             }
         });
         // Editor
@@ -1224,6 +1224,7 @@ function showError(errorMessage, errorEvent) {
         // Restore theme
         var theme = localStorage.getItem("bjs-playground-theme") || 'light';
         toggleTheme(theme);
+        toggleMinimap();
     }
 
     // Monaco

+ 12 - 20
Playground/ts.html

@@ -116,7 +116,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1600">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1600">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -125,14 +125,12 @@
                         <div class="option" id="editorFullscreenButton1600">Editor Fullscreen</div>
                         <div class="option" id="formatButton1600">Format code</div>
                         <div class="option" id="minimapToggle1600">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                     </div>
                 </div>
 
-                <div class="button uncheck" id="debugButton1600">Inspector
-                    <i class="fa fa-square-o" aria-hidden="true"></i>
-                </div>
+                <div class="button uncheck" id="debugButton1600">Inspector</div>
                 <div class="button" id="metadataButton1600">Metadata</div>
             </div>
 
@@ -207,7 +205,7 @@
                             </div>
                         </div>
                         <div class="option" id='safemodeToggle1475'>Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1475">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -216,11 +214,9 @@
                         <div class="option" id="editorFullscreenButton1475">Editor Fullscreen</div>
                         <div class="option" id="formatButton1475">Format code</div>
                         <div class="option" id="minimapToggle1475">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1475">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1475">Inspector</div>
                         <div class="option" id="metadataButton1475">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1475">Vers. : Latest</span>
@@ -290,7 +286,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle1030">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
                         <div class="option checked" id="editorButton1030">Editor
                             <i class="fa fa-check-square" aria-hidden="true"></i>
@@ -299,11 +295,9 @@
                         <div class="option" id="editorFullscreenButton1030">Editor Fullscreen</div>
                         <div class="option" id="formatButton1030">Format code</div>
                         <div class="option" id="minimapToggle1030">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton1030">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton1030">Inspector</div>
                         <div class="option" id="metadataButton1030">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion1030">Vers. : Latest</span>
@@ -370,7 +364,7 @@
                             </div>
                         </div>
                         <div class="option" id="safemodeToggle750">Safe mode
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" 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>
@@ -379,11 +373,9 @@
                         <div class="option" id="editorFullscreenButton750">Editor Fullscreen</div>
                         <div class="option" id="formatButton750">Format code</div>
                         <div class="option" id="minimapToggle750">Minimap
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
-                        </div>
-                        <div class="option" id="debugButton750">Inspector
-                            <i class="fa fa-square-o" aria-hidden="true"></i>
+                            <i class="far fa-square" aria-hidden="true"></i>
                         </div>
+                        <div class="option" id="debugButton750">Inspector</div>
                         <div class="option" id="metadataButton750">Metadata</div>
                         <div class="option subSelect">
                             <span id="currentVersion750">Vers. : Latest</span>

+ 3 - 1
Tools/Gulp/config.json

@@ -294,6 +294,7 @@
                 "../../src/Particles/babylon.baseParticleSystem.js",
                 "../../src/Particles/babylon.particleSystem.js",
                 "../../src/Particles/EmitterTypes/babylon.boxParticleEmitter.js",
+                "../../src/Particles/EmitterTypes/babylon.cylinderParticleEmitter.js",
                 "../../src/Particles/EmitterTypes/babylon.coneParticleEmitter.js",
                 "../../src/Particles/EmitterTypes/babylon.sphereParticleEmitter.js",
                 "../../src/Particles/EmitterTypes/babylon.hemisphericParticleEmitter.js",
@@ -410,7 +411,8 @@
         "sprites": {
             "files": [
                 "../../src/Sprites/babylon.spriteManager.js",
-                "../../src/Sprites/babylon.sprite.js"
+                "../../src/Sprites/babylon.sprite.js",
+                "../../src/Sprites/babylon.spriteSceneComponent.js"
             ],
             "dependUpon": [
                 "core"

+ 16 - 13
Viewer/dist/basicExample.html

@@ -8,27 +8,30 @@
         <title>BabylonJS Viewer - Basic usage</title>
         <style>
             babylon {
-                max-width: 800px;
-                max-height: 500px;
-                width: 100%;
-                height: 600px;
+                width: 300px;
+                height: 200px;
             }
         </style>
     </head>
 
     <body>
-        <babylon id="babylon-viewer" configuration="config.json" model.title="Damaged Helmet" model.subtitle="BabylonJS" model.thumbnail="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png"
-            model.url="https://www.babylonjs.com/Assets/DamagedHelmet/glTF/DamagedHelmet.gltf" camera.behaviors.auto-rotate="0"
-            templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
+        <babylon id="babylon-viewer" model="https://models.babylonjs.com/boombox.glb">
+            <!-- Setting the vr object (or vr="true" in the <babylon tag) is enough to enable VR.
+                 The object-scale-factor is optional an can be used to scale the model in VR mode.
+                 If setting "2", the model will be twice the size of its normalized size.
+            -->
+            <vr object-scale-factor="2">
+            </vr>
+            <!-- This is needed in order to show the VR button. It is also possible to toggleVR() using javascript -->
+            <templates>
+                <nav-bar>
+                    <params hide-vr="false"></params>
+                </nav-bar>
+            </templates>
+        </babylon>
         <script src="viewer.js"></script>
         <script>
-            // The following lines are redundant. 
-            // They are only here to show how you could achive the tag initialization on your own.
 
-            // a simple way of disabling auto init 
-            BabylonViewer.disableInit = true;
-            // Initializing the viewer on specific HTML tags.
-            BabylonViewer.InitTags('babylon');
         </script>
     </body>
 

+ 3 - 1
Viewer/src/configuration/types/default.ts

@@ -62,8 +62,10 @@ export let defaultConfiguration: ViewerConfiguration = {
                 }
             },
             events: {
+                click: {
+                    '.navbar-control': true
+                },
                 pointerdown: {
-                    '.navbar-control': true,
                     '.help-button': true
                 },
                 input: {

+ 2 - 2
Viewer/src/viewer/defaultViewer.ts

@@ -120,7 +120,7 @@ export class DefaultViewer extends AbstractViewer {
         let navbar = this.templateManager.getTemplate('navBar');
         if (navbar) {
             this.onFrameRenderedObservable.add(this._updateProgressBar);
-            this.templateManager.eventManager.registerCallback('navBar', this._handlePointerDown, 'pointerdown');
+            this.templateManager.eventManager.registerCallback('navBar', this._handlePointerClick, 'click');
             // an example how to trigger the help button. publiclly available
             this.templateManager.eventManager.registerCallback("navBar", () => {
                 // do your thing
@@ -158,7 +158,7 @@ export class DefaultViewer extends AbstractViewer {
     private _isAnimationPaused: boolean;
     private _resumePlay: boolean;
 
-    private _handlePointerDown = (event: EventCallback) => {
+    private _handlePointerClick = (event: EventCallback) => {
 
         let pointerDown = <PointerEvent>event.event;
         if (pointerDown.button !== 0) return;

Файловите разлики са ограничени, защото са твърде много
+ 591 - 172
dist/preview release/babylon.d.ts


Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/babylon.js


Файловите разлики са ограничени, защото са твърде много
+ 863 - 282
dist/preview release/babylon.max.js


Файловите разлики са ограничени, защото са твърде много
+ 863 - 282
dist/preview release/babylon.no-module.max.js


Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/babylon.worker.js


Файловите разлики са ограничени, защото са твърде много
+ 865 - 284
dist/preview release/es6.js


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

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

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

@@ -4,7 +4,7 @@
     },
     "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.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js


Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js.map


+ 8 - 3
dist/preview release/inspector/babylon.inspector.d.ts

@@ -590,12 +590,12 @@ declare module INSPECTOR {
             /**
                 * Useful function used to create a div
                 */
-            static CreateDiv(className?: BABYLON.Nullable<string>, parent?: HTMLElement): HTMLDivElement;
+            static CreateDiv(className?: BABYLON.Nullable<string>, parent?: HTMLElement, tooltip?: string): HTMLDivElement;
             /**
                 * Useful function used to create a input
                 */
-            static CreateInput(className?: string, parent?: HTMLElement): HTMLInputElement;
-            static CreateElement(element: string, className?: BABYLON.Nullable<string>, parent?: HTMLElement): HTMLElement;
+            static CreateInput(className?: string, parent?: HTMLElement, tooltip?: string): HTMLInputElement;
+            static CreateElement(element: string, className?: BABYLON.Nullable<string>, parent?: HTMLElement, tooltip?: string): HTMLElement;
             /**
                 * Removes all children of the given div.
                 */
@@ -804,6 +804,7 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     export class TextureTab extends Tab {
+        static DDSPreview: DDSPreview;
         /** The panel containing a list of items */
         protected _treePanel: HTMLElement;
         protected _treeItems: Array<TreeItem>;
@@ -817,6 +818,10 @@ declare module INSPECTOR {
         /** Set the given item as active in the tree */
         activateNode(item: TreeItem): void;
     }
+    class DDSPreview {
+        constructor(AdapterItem: TextureAdapter);
+        insertPreview(AdapterItem: TextureAdapter): void;
+    }
 }
 declare module INSPECTOR {
     export class ToolsTab extends Tab {

+ 18 - 6
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -756,12 +756,12 @@ declare module 'babylonjs-inspector/helpers/Helpers' {
             /**
                 * Useful function used to create a div
                 */
-            static CreateDiv(className?: Nullable<string>, parent?: HTMLElement): HTMLDivElement;
+            static CreateDiv(className?: Nullable<string>, parent?: HTMLElement, tooltip?: string): HTMLDivElement;
             /**
                 * Useful function used to create a input
                 */
-            static CreateInput(className?: string, parent?: HTMLElement): HTMLInputElement;
-            static CreateElement(element: string, className?: Nullable<string>, parent?: HTMLElement): HTMLElement;
+            static CreateInput(className?: string, parent?: HTMLElement, tooltip?: string): HTMLInputElement;
+            static CreateElement(element: string, className?: Nullable<string>, parent?: HTMLElement, tooltip?: string): HTMLElement;
             /**
                 * Removes all children of the given div.
                 */
@@ -1040,11 +1040,13 @@ declare module 'babylonjs-inspector/tabs/TabBar' {
 }
 
 declare module 'babylonjs-inspector/tabs/TextureTab' {
+    import { TextureAdapter } from "babylonjs-inspector/adapters/TextureAdapter";
     import { Inspector } from "babylonjs-inspector/Inspector";
     import { TreeItem } from "babylonjs-inspector/tree/TreeItem";
     import { Tab } from "babylonjs-inspector/tabs/Tab";
     import { TabBar } from "babylonjs-inspector/tabs/TabBar";
     export class TextureTab extends Tab {
+        static DDSPreview: DDSPreview;
         /** The panel containing a list of items */
         protected _treePanel: HTMLElement;
         protected _treeItems: Array<TreeItem>;
@@ -1058,6 +1060,11 @@ declare module 'babylonjs-inspector/tabs/TextureTab' {
         /** Set the given item as active in the tree */
         activateNode(item: TreeItem): void;
     }
+    class DDSPreview {
+        constructor(AdapterItem: TextureAdapter);
+        insertPreview(AdapterItem: TextureAdapter): void;
+    }
+    export {};
 }
 
 declare module 'babylonjs-inspector/tabs/ToolsTab' {
@@ -1921,12 +1928,12 @@ declare module INSPECTOR {
             /**
                 * Useful function used to create a div
                 */
-            static CreateDiv(className?: BABYLON.Nullable<string>, parent?: HTMLElement): HTMLDivElement;
+            static CreateDiv(className?: BABYLON.Nullable<string>, parent?: HTMLElement, tooltip?: string): HTMLDivElement;
             /**
                 * Useful function used to create a input
                 */
-            static CreateInput(className?: string, parent?: HTMLElement): HTMLInputElement;
-            static CreateElement(element: string, className?: BABYLON.Nullable<string>, parent?: HTMLElement): HTMLElement;
+            static CreateInput(className?: string, parent?: HTMLElement, tooltip?: string): HTMLInputElement;
+            static CreateElement(element: string, className?: BABYLON.Nullable<string>, parent?: HTMLElement, tooltip?: string): HTMLElement;
             /**
                 * Removes all children of the given div.
                 */
@@ -2135,6 +2142,7 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     export class TextureTab extends Tab {
+        static DDSPreview: DDSPreview;
         /** The panel containing a list of items */
         protected _treePanel: HTMLElement;
         protected _treeItems: Array<TreeItem>;
@@ -2148,6 +2156,10 @@ declare module INSPECTOR {
         /** Set the given item as active in the tree */
         activateNode(item: TreeItem): void;
     }
+    class DDSPreview {
+        constructor(AdapterItem: TextureAdapter);
+        insertPreview(AdapterItem: TextureAdapter): void;
+    }
 }
 declare module INSPECTOR {
     export class ToolsTab extends Tab {

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -4,7 +4,7 @@
     },
     "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.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.3.0-beta.3"
+        "babylonjs-gltf2interface": "3.3.0-beta.4"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "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",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "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",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "3.3.0-beta.3",
+    "version": "3.3.0-beta.4",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "3.3.0-beta.3"
+        "babylonjs-gltf2interface": "3.3.0-beta.4"
     },
     "peerDependencies": {
         "babylonjs": ">=3.2.0-alpha"

+ 19 - 5
dist/preview release/viewer/babylon.viewer.d.ts

@@ -168,11 +168,11 @@ declare module BabylonViewer {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
+            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<string> | Promise<Template>;
+            hideOverlayScreen(): Promise<Template> | Promise<string>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -189,11 +189,11 @@ declare module BabylonViewer {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<string> | Promise<Template>;
+            showLoadingScreen(): Promise<Template> | Promise<string>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<string> | Promise<Template>;
+            hideLoadingScreen(): Promise<Template> | Promise<string>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -924,7 +924,7 @@ declare module BabylonViewer {
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
@@ -1558,6 +1558,20 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 declare module BabylonViewer {
+    /**
+        * A custom upgrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedUpgrade(sceneManager: SceneManager): boolean;
+    /**
+        * A custom degrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedDegrade(sceneManager: SceneManager): boolean;
+}
+declare module BabylonViewer {
 }
 declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {

Файловите разлики са ограничени, защото са твърде много
+ 3 - 3
dist/preview release/viewer/babylon.viewer.js


Файловите разлики са ограничени, защото са твърде много
+ 3 - 3
dist/preview release/viewer/babylon.viewer.max.js


+ 22 - 5
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -200,11 +200,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
+            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<string> | Promise<Template>;
+            hideOverlayScreen(): Promise<Template> | Promise<string>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -221,11 +221,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<string> | Promise<Template>;
+            showLoadingScreen(): Promise<Template> | Promise<string>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<string> | Promise<Template>;
+            hideLoadingScreen(): Promise<Template> | Promise<string>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -985,13 +985,14 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 
 declare module 'babylonjs-viewer/optimizer/custom' {
+    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     /**
       *
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 
@@ -1662,6 +1663,22 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 
+declare module 'babylonjs-viewer/optimizer/custom/extended' {
+    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
+    /**
+        * A custom upgrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedUpgrade(sceneManager: SceneManager): boolean;
+    /**
+        * A custom degrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedDegrade(sceneManager: SceneManager): boolean;
+}
+
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';

+ 229 - 218
dist/preview release/what's new.md

@@ -1,218 +1,229 @@
-# 3.3.0
-
-## Major updates
-
-- GUI
-  - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
-  - New GUI control: [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
-  - New GUI control: [InputPassword](https://doc.babylonjs.com/how_to/gui#inputpassword) ([theom](https://github.com/theom))
-- Gizmo Support ([TrevorDev](https://github.com/TrevorDev))
-  - Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, scale, rotation and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
-  - New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh. [Doc](http://doc.babylonjs.com/how_to/meshbehavior) ([TrevorDev](https://github.com/TrevorDev))
-  - Added attachToBoxBehavior to attach UI to a bounding box ([TrevorDev](https://github.com/TrevorDev))
-  - Gizmo manager's internal gizmos are now public ([TrevorDev](https://github.com/TrevorDev))
-  - Ability to customize meshes on gizmos ([TrevorDev](https://github.com/TrevorDev))
-  - Added ignoreChildren field to bounding box to save performance when using heavily nested meshes ([TrevorDev](https://github.com/TrevorDev))
-  - Add uniform scaling drag support to scale gizmo ([TrevorDev](https://github.com/TrevorDev))
-  - Support interacting with child elements ([TrevorDev](https://github.com/TrevorDev))
-- Particle system improvements ([Deltakosh](https://github.com/deltakosh))
-  - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
-  - Improved CPU particles rendering performance (up to x2 on low end devices)
-  - Added support for `isBillboardBased`. [Doc](http://doc.babylonjs.com/babylon101/particles#alignment)
-  - Added support for billboard mode. [Doc](https://doc.babylonjs.com/babylon101/particles#alignment)
-  - Added support for `minScaleX`, `minScaleY`, `maxScaleX`, `maxScaleY`. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
-  - Added support for `radiusRange` for sphere emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#sphere-emitter)
-  - Added support for `radiusRange` and `heightRange` for cone emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#cone-emitter)
-  - Added new point emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#point-emitter)
-  - Added new hemispheric emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#hemispheric-emitter)
-  - Added support for `ParticleSystem.BLENDMODE_ADD` alpha mode. [Doc](https://doc.babylonjs.com/babylon101/particles#particle-blending)
-  - Added support for color gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#particle-colors)
-  - Added support for pre-warming. [Doc](https://doc.babylonjs.com/babylon101/particles#pre-warming)
-  - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
-  - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
-  - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
-  - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
-  - Added support for velocty gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#velocity-over-time)
-  - Added support for limit velocty gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time)
-  - Added support for noise textures. [Doc](http://doc.babylonjs.com/babylon101/particles#noise-texture)
-- Added SceneComponent to help decoupling Scene from its components. ([sebavan](http://www.github.com/sebavan))
-- Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
-- Playground can now be used with TypeScript directly!. [Demo](https://www.babylonjs-playground.com/ts.html) ([Deltakosh](https://github.com/deltakosh), [NasimiAsl](https://github.com/NasimiAsl))
-- GUI and Inspector are now ES-Modules ([RaananW](https://github.com/RaananW))
-- Added support for noise procedural textures. [Doc](http://doc.babylonjs.com/how_to/how_to_use_procedural_textures#noise-procedural-texture) ([Deltakosh](https://github.com/deltakosh))
-- Added new `PhotoDome` object to display 360 photos. [Demo](https://www.babylonjs-playground.com/#14KRGG#0) ([SzeyinLee](https://github.com/SzeyinLee))
-- Added Video Recorder [Documentation](http://localhost:8080/How_To/Render_Scene_on_a_Video) ([sebavan](http://www.github.com/sebavan))
-
-## Updates
-
-- Updated TypeScript version to new major 3.0.1 ([christopherstock](https://github.com/christopherstock))
-- All NPM packages have `latest`and `preview` streams [#3055](https://github.com/BabylonJS/Babylon.js/issues/3055) ([RaananW](https://github.com/RaananW))
-- Added New Tools Tab in the inspector (env texture and screenshot tools so far) ([sebavan](http://www.github.com/sebavan))
-- Moved to gulp 4, updated dependencies to latest ([RaananW](https://github.com/RaananW))
-
-### GUI
-- Added dead key support and before key add observable to InputText. [Doc](https://doc.babylonjs.com/how_to/gui#using-onbeforekeyaddobservable-for-extended-keyboard-layouts-and-input-masks)([theom](https://github.com/theom))
-- Added `TextBlock.computeExpectedHeight`, added `TextWrapping.Ellipsis` as `TextBlock.wordWrapping` possible value ([adrientetar](https://github.com/adrientetar))
-- New vertical mode for sliders in 2D GUI. [Demo](https://www.babylonjs-playground.com/#U9AC0N#53) ([Saket Saurabh](https://github.com/ssaket))
-- Added `isEnabled` and `disabledColor` property to Gui Control ([barteq100](https://github.com/barteq100))
-
-### Core Engine
-
-- Added support for muyltiple clip planes. [Demo](https://www.babylonjs-playground.com/#Y6W087) ([Deltakosh](https://github.com/deltakosh))
-- Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
-- Added new `BoundingInfo.scale()` function to let users control the size of the bounding info ([Deltakosh](https://github.com/deltakosh))
-- Added new `Animatable.waitAsync` function to use Promises with animations. Demo [Here](https://www.babylonjs-playground.com/#HZBCXR) ([Deltakosh](https://github.com/deltakosh))
-- Added the choice of [forming a closed loop](http://doc.babylonjs.com/how_to/how_to_use_curve3#catmull-rom-spline) to the catmull-rom-spline curve3 ([johnk](https://github.com/babylonjsguide))
-- Added support for specifying the center of rotation to textures ([bghgary](http://www.github.com/bghgary))
-- Added webVR support for Oculus Go ([TrevorDev](https://github.com/TrevorDev))
-- Added ability to not generate polynomials harmonics upon prefiltered texture creation ([sebavan](http://www.github.com/sebavan))
-- Added predicate function to customize the list of mesh included in the computation of bounding vectors in the ```getHierarchyBoundingVectors``` method ([sebavan](http://www.github.com/sebavan))
-- Added webVR constructor options: disable laser pointer toggle, teleportation floor meshes ([TrevorDev](https://github.com/TrevorDev))
-- Get a root mesh from an asset container, load a mesh from a file with a single string url ([TrevorDev](https://github.com/TrevorDev))
-- UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
-- AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
-- New `serialize` and `Parse` functions to serialize and parse all procedural textures from the Procedural Textures Library ([julien-moreau](https://github.com/julien-moreau))
-- Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
-- AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
-- Added sprite isVisible field ([TrevorDev](https://github.com/TrevorDev))
-- EnvironmentHelper will recreate ground and skybox meshes if force-disposed ([RaananW](https://github.com/RaananW))
-- Added viewport caching mechanism in engine ([sebavan](http://www.github.com/sebavan))
-- Added unpackFlipY caching mechanism in engine ([sebavan](http://www.github.com/sebavan))
-- Added rebind optimization of video texture ([sebavan](http://www.github.com/sebavan))
-- Fix Background Material effect caching ([sebavan](http://www.github.com/sebavan))
-- Prevent texture ```getSize``` to generate garbage collection ([sebavan](http://www.github.com/sebavan))
-- Prevent ```lodGenerationScale``` and ```lodGenerationOffset``` to force rebind ([sebavan](http://www.github.com/sebavan))
-- Added poster property on VideoTexture ([sebavan](http://www.github.com/sebavan))
-- Added ```onUserActionRequestedObservable``` to workaround and detect autoplay video policy restriction on VideoTexture ([sebavan](http://www.github.com/sebavan))
-- `Sound` now accepts `MediaStream` as source to enable easier WebAudio and WebRTC integrations ([menduz](https://github.com/menduz))
-- Vector x, y and z constructor parameters are now optional and default to 0 ([TrevorDev](https://github.com/TrevorDev))
-- Added and removed camera methods in the default pipeline ([TrevorDev](https://github.com/TrevorDev))
-- Added internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
-- Added canvas toBlob polyfill in tools ([sebavan](http://www.github.com/sebavan))
-- Added `RawCubeTexture` class with RGBD and mipmap support ([bghgary](http://www.github.com/bghgary))
-- Added effect layer per rendering group addressing [Issue 4463](https://github.com/BabylonJS/Babylon.js/issues/4463) ([sebavan](http://www.github.com/sebavan))
-- Added predicate function `targetMask` argument to `scene.beginWeightedAnimation`, `scene.beginAnimation`, `scene.stopAnimation`, and `animatable.stop` to allow for selective application of animations.  ([fmmoret](http://github.com/fmmoret))
-- Oculus GO and GearVR 3dof controllers will now rotate with the user's head if they turn around in their room ([TrevorDev](https://github.com/TrevorDev))
-- Added onPoseUpdatedFromDeviceObservable to webVRCamera to detect when the camera's pose has been updated ([TrevorDev](https://github.com/TrevorDev))
-- Added gltf light falloff [Issue 4148](https://github.com/BabylonJS/Babylon.js/issues/4148) ([sebavan](http://www.github.com/sebavan))
-- Added falloff type per light to prevent material only inconsistencies [Issue 4148](https://github.com/BabylonJS/Babylon.js/issues/4148) ([sebavan](http://www.github.com/sebavan))
-- Added WeightedSound; selects one from many Sounds with random weight for playback. ([najadojo](https://github.com/najadojo))
-- Added HDR support to ReflectionProbe ([Deltakosh](https://github.com/deltakosh))
-- Added ACES ToneMapping to the image processing to help getting more parity with other engines ([sebavan](http://www.github.com/sebavan))
-- Added Image Processing to the particle system to allow consistency in one pass forward rendering scenes ([sebavan](http://www.github.com/sebavan))
-- Added Video Recorder [Issue 4708](https://github.com/BabylonJS/Babylon.js/issues/4708) ([sebavan](http://www.github.com/sebavan))
-- Added support for main WebGL2 texture formats ([PeapBoy](https://github.com/NicolasBuecher))
-- Added fadeInOutBehavior and tooltipText for holographic buttons ([TrevorDev](https://github.com/TrevorDev))
-- StartDrag method added to pointerDragBehavior to simulate the start of a drag ([TrevorDev](https://github.com/TrevorDev))
-- Added EdgesLineRenderer to address [#4919](https://github.com/BabylonJS/Babylon.js/pull/4919) ([barteq100](https://github.com/barteq100))
-- Added ```ambientTextureImpactOnAnalyticalLights``` in PBRMaterial to allow fine grained control of the AmbientTexture on the analytical diffuse light ([sebavan](http://www.github.com/sebavan))
-- BoundingBoxGizmo scalePivot field that can be used to always scale objects from the bottom ([TrevorDev](https://github.com/TrevorDev))
-
-### glTF Loader
-
-- Added support for KHR_texture_transform ([bghgary](http://www.github.com/bghgary))
-- Added `onNodeLODsLoadedObservable` and `onMaterialLODsLoadedObservable` to MSFT_lod loader extension ([bghgary](http://www.github.com/bghgary))
-- Added glTF loader settings to the GLTF tab in the debug layer ([bghgary](http://www.github.com/bghgary))
-- Added debug logging and performance counters ([bghgary](http://www.github.com/bghgary))
-- Added support for EXT_lights_imageBased ([bghgary](http://www.github.com/bghgary))
-- Added support for MSFT_audio_emitter ([najadojo](http://www.github.com/najadojo))
-- Added support for custom loader extensions ([bghgary](http://www.github.com/bghgary))
-
-### Viewer
-
-- No fullscreen button on small devices ([RaananW](https://github.com/RaananW))
-- Nav-Bar is now displayed on fullscreen per default ([RaananW](https://github.com/RaananW))
-- Viewer configuration supports deprecated values using the new configurationCompatibility processor  ([RaananW](https://github.com/RaananW))
-- Shadows will only render while models are entering the scene or animating ([RaananW](https://github.com/RaananW))
-- Support for model drag and drop onto the canvas ([RaananW](https://github.com/RaananW))
-- New lab feature - global light rotation [#4347](https://github.com/BabylonJS/Babylon.js/issues/4347) ([RaananW](https://github.com/RaananW))
-- New NPM package - babylonjs-viewer-assets, to separate the binary assets and the code of the viewer ([RaananW](https://github.com/RaananW))
-- A new HD-Toggler button allows setting a better hardware scaling rate ([RaananW](https://github.com/RaananW))
-- An initial support for WebVR is implemented ([RaananW](https://github.com/RaananW))
-- It is now possible to choose the element that goes fullscreen in the default viewer ([RaananW](https://github.com/RaananW))
-- The default viewer has a plugin system with which new buttons can be added externally ([RaananW](https://github.com/RaananW))
-- The extended configuration is now the default when not providing the "extended" parameter ([RaananW](https://github.com/RaananW))
-- viewer.updateConfiguration also accepts a URL to download configuration remotely ([RaananW](https://github.com/RaananW))
-- Viewer supports 3D printing on windows 10 ([RaananW](https://github.com/RaananW))
-- The viewer's environment map is using the new .env feature ([RaananW](https://github.com/RaananW))
-
-### Materials Library
-
-- Added ```unlit``` mode to lava material ([sebavan](http://www.github.com/sebavan))
-
-### Documentation
-
-- Added all code comments for GUI
-
-## Bug fixes
-
-- VR experience helper will now fire pointer events even when no mesh is currently hit ([TrevorDev](https://github.com/TrevorDev))
-- RawTexture.CreateAlphaTexture no longer fails to create a usable texture ([TrevorDev](https://github.com/TrevorDev))
-- SceneSerializer.SerializeMesh now serializes all materials kinds (not only StandardMaterial) ([julien-moreau](https://github.com/julien-moreau))
-- WindowsMotionController's trackpad field will be updated prior to it's onTrackpadChangedObservable event ([TrevorDev](https://github.com/TrevorDev))
-- VR experience helper's controllers will not fire pointer events when laser's are disabled, instead the camera ray pointer event will be used ([TrevorDev](https://github.com/TrevorDev))
-- Node's setParent(node.parent) will no longer throw an exception when parent is undefined and will behave the same as setParent(null) ([TrevorDev](https://github.com/TrevorDev))
-- Mesh.MergeMeshes flips triangles on meshes with negative scaling ([SvenFrankson](http://svenfrankson.com))
-- Avoid firing button events multiple times when calling vrController.attachMesh() ([TrevorDev](https://github.com/TrevorDev))
-- Parse geometry when load binary mesh ([SinhNQ](https://github.com/quocsinh))
-- Removing observers during observable notify should not skip over valid observers ([TrevorDev](https://github.com/TrevorDev))
-- Initializing gamepadManager should register the gamepad update events ([TrevorDev](https://github.com/TrevorDev))
-- Do not generate mipmaps for RawCubeTexture if OES_texture_float_linear and/or EXT_color_buffer_float extensions are not supported ([PeapBoy](https://github.com/NicolasBuecher))
-- Do not modify passed camera array parameter when creating a default pipeline ([TrevorDev](https://github.com/TrevorDev))
-- Fixed issue where gaze trackers were appearing even after leaving VR ([atulyar](https://github.com/atulyar))
-- AdvancedDynamicTexture should not overwrite skipOnPointerObservable to false ([TrevorDev](https://github.com/TrevorDev))
-- Fixed issue where VRExperienceHelper.onExitingVR observable was being fired twice ([atulyar](https://github.com/atulyar))
-- Avoid firing onExitingVR observable multiple times when calling exitVR() and add observables to Viewer that can be used instead of the ones in VRExperienceHelper ([atulyar](https://github.com/atulyar))
-- GizmoManager should hide existing gizmos if a non-attachable mesh is selected ([TrevorDev](https://github.com/TrevorDev))
-- Ignore isPickable = false for vr ray casting if the mesh's name matches the specified floorMeshName to maintain backwards compatability ([TrevorDev](https://github.com/TrevorDev))
-- Fix File Loading if hosted from `file:`-Protocol ([ltetzlaff](https://github.com/ltetzlaff))
-- Do not throw error when updating a controller with no left stick ([TrevorDev](https://github.com/TrevorDev))
-- Exiting VR can result in messed up view ([TrevorDev](https://github.com/TrevorDev))
-- Dispose existing gazeTrackers when setting a new one, remove pivot matrix of meshes using boundingBoxGizmo ([TrevorDev](https://github.com/TrevorDev))
-- Set missing parentId in Mesh.serialize() for instances ([julien-moreau](https://github.com/julien-moreau))
-
-### Core Engine
-
-- Fixed ```shadowEnabled``` property on lights. Shadows are not visible anymore when disabled ([sebavan](http://www.github.com/sebavan))
-- Physics `unregisterOnPhysicsCollide` didn't remove callback correctly [#4291](https://github.com/BabylonJS/Babylon.js/issues/4291) ([RaananW](https://github.com/RaananW))
-- Added missing getter and setter for global exposure in ColorCurves ([RaananW](https://github.com/RaananW))
-- Fixed an issue with view matrix when `ArcRotateCamera` was used with collisions ([Deltakosh](https://github.com/deltakosh))
-- Fixed a bug with setting `unlit` on `PBRMaterial` after the material is ready (Wrong dirty flags) ([bghgary](http://www.github.com/bghgary))
-- Fixed `HighlightLayer` support on browsers not supporting HalfFloat ([sebavan](http://www.github.com/sebavan))
-- Fixed support for R and RG texture formats ([sebavan](http://www.github.com/sebavan))
-- Fixed `updatable` parameter setting in the SPS ([jerome](https://github.com/jbousquie))
-- Angular and linear velocity were using the wrong method to copy values to the physics engine ([RaananW](https://github.com/RaananW))
-- Fixed env texture generation in Byte Mode ([sebavan](http://www.github.com/sebavan))
-- Oimo.js now receives quaternion and not euler when a body is being constructed ([RaananW](https://github.com/RaananW))
-- Improving visual quality on SSAO2 shader ([CraigFeldspar](https://github.com/CraigFeldspar))
-- Fixed a bug where changing the sample count on `PostProcess` would not update the WebGL Texture ([CraigFeldspar](https://github.com/CraigFeldspar))
-
-### Viewer
-
-- Fix Navbar Interaction on Mozilla/Firefox ([SzeyinLee](https://github.com/SzeyinLee))
-- Fix Animation Slider Interaction on Mozilla/Firefox ([sebavan](http://www.github.com/sebavan))
-- Fix Animation Slider Clickable area size Cross Plat ([sebavan](http://www.github.com/sebavan))
-- Ground material didn't take the default main color is no material definition was provided ([RaananW](https://github.com/RaananW))
-- Model configuration was not extended correctly if loaded more than one model ([RaananW](https://github.com/RaananW))
-- It wasn't possible to disable camera behavior(s) using configuration  [#4348](https://github.com/BabylonJS/Babylon.js/issues/4348) ([RaananW](https://github.com/RaananW))
-- Animation blending was always set to true, ignoring configuration [#4412](https://github.com/BabylonJS/Babylon.js/issues/4412) ([RaananW](https://github.com/RaananW))
-- Animation navbar now updates correctly when a new model is loaded [#4441](https://github.com/BabylonJS/Babylon.js/issues/4441) ([RaananW](https://github.com/RaananW))
-- Non-normalized meshes didn't center and focus correctly ([RaananW](https://github.com/RaananW))
-- Meshes with skeletons could have incorrect animations ([RaananW](https://github.com/RaananW))
-- Removed element IDs from viewer's templates to allow muitiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
-- Viewer is not using Engine.LastCreatedScene anymore, to support multiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
-- Template location was ignored if html was defined ([RaananW](https://github.com/RaananW))
-- Drag and Drop only worked if a model was already loaded before ([RaananW](https://github.com/RaananW))
-- It was not possible to add new custom optimizers, only use existing ones ([RaananW](https://github.com/RaananW))
-- Button texts were truncated incorrectly ([RaananW](https://github.com/RaananW))
-- Animation names with more than one word didn't work correctly ([RaananW](https://github.com/RaananW))
-
-### Loaders
-
-- STL Loader only supported binary downloads and no data: urls [#4473](https://github.com/BabylonJS/Babylon.js/issues/4473) ([RaananW](https://github.com/RaananW))
-- OBJ Loader is now an async loader [#4571](https://github.com/BabylonJS/Babylon.js/issues/4571) ([RaananW](https://github.com/RaananW))
-
-## Breaking changes
-
-- Fixing support for R and RG texture formats made us remove TextureFormat_R32F and TextureFormat_RG32F as they were mixing formats and types. Please, use the respective TextureFormat_R and TextureFormat_RG with the Float types ([sebavan](http://www.github.com/sebavan))
-- Replacing `scene.onRenderingGroupObservable` by `onBeforeRenderingGroupObservable` and `onAfterRenderingGroupObservable` to prevent the stage check ([sebavan](http://www.github.com/sebavan))
+# 3.3.0
+
+## Major updates
+
+- GUI
+  - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
+  - New GUI control: [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
+  - New GUI control: [InputPassword](https://doc.babylonjs.com/how_to/gui#inputpassword) ([theom](https://github.com/theom))
+- Gizmo Support ([TrevorDev](https://github.com/TrevorDev))
+  - Gizmo and GizmoManager classes used to manipulate meshes in a scene. Gizmo types include: position, scale, rotation and bounding box. [Doc](http://doc.babylonjs.com/how_to/gizmo) ([TrevorDev](https://github.com/TrevorDev))
+  - New behaviors: PointerDragBehavior, SixDofDragBehavior and MultiPointerScaleBehavior to enable smooth drag and drop/scaling with mouse or 6dof controller on a mesh. [Doc](http://doc.babylonjs.com/how_to/meshbehavior) ([TrevorDev](https://github.com/TrevorDev))
+  - Added attachToBoxBehavior to attach UI to a bounding box ([TrevorDev](https://github.com/TrevorDev))
+  - Gizmo manager's internal gizmos are now public ([TrevorDev](https://github.com/TrevorDev))
+  - Ability to customize meshes on gizmos ([TrevorDev](https://github.com/TrevorDev))
+  - Added ignoreChildren field to bounding box to save performance when using heavily nested meshes ([TrevorDev](https://github.com/TrevorDev))
+  - Add uniform scaling drag support to scale gizmo ([TrevorDev](https://github.com/TrevorDev))
+  - Support interacting with child elements ([TrevorDev](https://github.com/TrevorDev))
+- Particle system improvements ([Deltakosh](https://github.com/deltakosh))
+  - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
+  - Improved CPU particles rendering performance (up to x2 on low end devices)
+  - Added support for `isBillboardBased`. [Doc](http://doc.babylonjs.com/babylon101/particles#alignment)
+  - Added support for billboard mode. [Doc](https://doc.babylonjs.com/babylon101/particles#alignment)
+  - Added support for `minScaleX`, `minScaleY`, `maxScaleX`, `maxScaleY`. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
+  - Added support for `radiusRange` for sphere emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#sphere-emitter)
+  - Added support for `radiusRange` and `heightRange` for cone emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#cone-emitter)
+  - Added new point emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#point-emitter)
+  - Added new hemispheric emitter. [Doc](https://doc.babylonjs.com/babylon101/particles#hemispheric-emitter)
+  - Added support for `ParticleSystem.BLENDMODE_ADD` alpha mode. [Doc](https://doc.babylonjs.com/babylon101/particles#particle-blending)
+  - Added support for color gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#particle-colors)
+  - Added support for pre-warming. [Doc](https://doc.babylonjs.com/babylon101/particles#pre-warming)
+  - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
+  - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
+  - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
+  - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
+  - Added support for velocty gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#velocity-over-time)
+  - Added support for limit velocty gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time)
+  - Added support for noise textures. [Doc](http://doc.babylonjs.com/babylon101/particles#noise-texture)
+  - Added support for emit rate gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#emit-rate-over-time)
+  - Start size gradient support for particles [Doc](http://doc.babylonjs.com/babylon101/particles#start-size-over-time) ([TrevorDev](https://github.com/TrevorDev))
+  - Cylinder particle emitter and constructor in baseParticle [Doc](https://doc.babylonjs.com/babylon101/particles#cylinder-emitter) ([TrevorDev](https://github.com/TrevorDev))
+- Added SceneComponent to help decoupling Scene from its components. ([sebavan](http://www.github.com/sebavan))
+- Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
+- Playground can now be used with TypeScript directly!. [Demo](https://www.babylonjs-playground.com/ts.html) ([Deltakosh](https://github.com/deltakosh), [NasimiAsl](https://github.com/NasimiAsl))
+- GUI and Inspector are now ES-Modules ([RaananW](https://github.com/RaananW))
+- Added support for noise procedural textures. [Doc](http://doc.babylonjs.com/how_to/how_to_use_procedural_textures#noise-procedural-texture) ([Deltakosh](https://github.com/deltakosh))
+- Added new `PhotoDome` object to display 360 photos. [Demo](https://www.babylonjs-playground.com/#14KRGG#0) ([SzeyinLee](https://github.com/SzeyinLee))
+- Added Video Recorder [Documentation](http://localhost:8080/How_To/Render_Scene_on_a_Video) ([sebavan](http://www.github.com/sebavan))
+
+## Updates
+
+- Updated TypeScript version to new major 3.0.1 ([christopherstock](https://github.com/christopherstock))
+- All NPM packages have `latest`and `preview` streams [#3055](https://github.com/BabylonJS/Babylon.js/issues/3055) ([RaananW](https://github.com/RaananW))
+- Added New Tools Tab in the inspector (env texture and screenshot tools so far) ([sebavan](http://www.github.com/sebavan))
+- Moved to gulp 4, updated dependencies to latest ([RaananW](https://github.com/RaananW))
+
+### GUI
+- Added dead key support and before key add observable to InputText. [Doc](https://doc.babylonjs.com/how_to/gui#using-onbeforekeyaddobservable-for-extended-keyboard-layouts-and-input-masks)([theom](https://github.com/theom))
+- Added `TextBlock.computeExpectedHeight`, added `TextWrapping.Ellipsis` as `TextBlock.wordWrapping` possible value ([adrientetar](https://github.com/adrientetar))
+- New vertical mode for sliders in 2D GUI. [Demo](https://www.babylonjs-playground.com/#U9AC0N#53) ([Saket Saurabh](https://github.com/ssaket))
+- Added `isEnabled` and `disabledColor` property to Gui Control ([barteq100](https://github.com/barteq100))
+
+### Core Engine
+
+- Added `scene.pickSpriteWithRay` function ([Deltakosh](https://github.com/deltakosh))
+- Added support for muyltiple clip planes. [Demo](https://www.babylonjs-playground.com/#Y6W087) ([Deltakosh](https://github.com/deltakosh))
+- Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
+- Added new `BoundingInfo.scale()` function to let users control the size of the bounding info ([Deltakosh](https://github.com/deltakosh))
+- Added new `Animatable.waitAsync` function to use Promises with animations. Demo [Here](https://www.babylonjs-playground.com/#HZBCXR) ([Deltakosh](https://github.com/deltakosh))
+- Added the choice of [forming a closed loop](http://doc.babylonjs.com/how_to/how_to_use_curve3#catmull-rom-spline) to the catmull-rom-spline curve3 ([johnk](https://github.com/babylonjsguide))
+- Added support for specifying the center of rotation to textures ([bghgary](http://www.github.com/bghgary))
+- Added webVR support for Oculus Go ([TrevorDev](https://github.com/TrevorDev))
+- Added ability to not generate polynomials harmonics upon prefiltered texture creation ([sebavan](http://www.github.com/sebavan))
+- Added predicate function to customize the list of mesh included in the computation of bounding vectors in the ```getHierarchyBoundingVectors``` method ([sebavan](http://www.github.com/sebavan))
+- Added webVR constructor options: disable laser pointer toggle, teleportation floor meshes ([TrevorDev](https://github.com/TrevorDev))
+- Get a root mesh from an asset container, load a mesh from a file with a single string url ([TrevorDev](https://github.com/TrevorDev))
+- UtilityLayer class to render another scene as a layer on top of an existing scene ([TrevorDev](https://github.com/TrevorDev))
+- AnimationGroup has now onAnimationGroupEnd observable ([RaananW](https://github.com/RaananW))
+- New `serialize` and `Parse` functions to serialize and parse all procedural textures from the Procedural Textures Library ([julien-moreau](https://github.com/julien-moreau))
+- Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
+- AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
+- Added sprite isVisible field ([TrevorDev](https://github.com/TrevorDev))
+- EnvironmentHelper will recreate ground and skybox meshes if force-disposed ([RaananW](https://github.com/RaananW))
+- Added viewport caching mechanism in engine ([sebavan](http://www.github.com/sebavan))
+- Added unpackFlipY caching mechanism in engine ([sebavan](http://www.github.com/sebavan))
+- Added rebind optimization of video texture ([sebavan](http://www.github.com/sebavan))
+- Fix Background Material effect caching ([sebavan](http://www.github.com/sebavan))
+- Prevent texture ```getSize``` to generate garbage collection ([sebavan](http://www.github.com/sebavan))
+- Prevent ```lodGenerationScale``` and ```lodGenerationOffset``` to force rebind ([sebavan](http://www.github.com/sebavan))
+- Added poster property on VideoTexture ([sebavan](http://www.github.com/sebavan))
+- Added ```onUserActionRequestedObservable``` to workaround and detect autoplay video policy restriction on VideoTexture ([sebavan](http://www.github.com/sebavan))
+- `Sound` now accepts `MediaStream` as source to enable easier WebAudio and WebRTC integrations ([menduz](https://github.com/menduz))
+- Vector x, y and z constructor parameters are now optional and default to 0 ([TrevorDev](https://github.com/TrevorDev))
+- Added and removed camera methods in the default pipeline ([TrevorDev](https://github.com/TrevorDev))
+- Added internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
+- Added canvas toBlob polyfill in tools ([sebavan](http://www.github.com/sebavan))
+- Added `RawCubeTexture` class with RGBD and mipmap support ([bghgary](http://www.github.com/bghgary))
+- Added effect layer per rendering group addressing [Issue 4463](https://github.com/BabylonJS/Babylon.js/issues/4463) ([sebavan](http://www.github.com/sebavan))
+- Added predicate function `targetMask` argument to `scene.beginWeightedAnimation`, `scene.beginAnimation`, `scene.stopAnimation`, and `animatable.stop` to allow for selective application of animations.  ([fmmoret](http://github.com/fmmoret))
+- Oculus GO and GearVR 3dof controllers will now rotate with the user's head if they turn around in their room ([TrevorDev](https://github.com/TrevorDev))
+- Added onPoseUpdatedFromDeviceObservable to webVRCamera to detect when the camera's pose has been updated ([TrevorDev](https://github.com/TrevorDev))
+- Added gltf light falloff [Issue 4148](https://github.com/BabylonJS/Babylon.js/issues/4148) ([sebavan](http://www.github.com/sebavan))
+- Added falloff type per light to prevent material only inconsistencies [Issue 4148](https://github.com/BabylonJS/Babylon.js/issues/4148) ([sebavan](http://www.github.com/sebavan))
+- Added WeightedSound; selects one from many Sounds with random weight for playback. ([najadojo](https://github.com/najadojo))
+- Added HDR support to ReflectionProbe ([Deltakosh](https://github.com/deltakosh))
+- Added ACES ToneMapping to the image processing to help getting more parity with other engines ([sebavan](http://www.github.com/sebavan))
+- Added Image Processing to the particle system to allow consistency in one pass forward rendering scenes ([sebavan](http://www.github.com/sebavan))
+- Added Video Recorder [Issue 4708](https://github.com/BabylonJS/Babylon.js/issues/4708) ([sebavan](http://www.github.com/sebavan))
+- Added support for main WebGL2 texture formats ([PeapBoy](https://github.com/NicolasBuecher))
+- Added fadeInOutBehavior and tooltipText for holographic buttons ([TrevorDev](https://github.com/TrevorDev))
+- StartDrag method added to pointerDragBehavior to simulate the start of a drag ([TrevorDev](https://github.com/TrevorDev))
+- Added EdgesLineRenderer to address [#4919](https://github.com/BabylonJS/Babylon.js/pull/4919) ([barteq100](https://github.com/barteq100))
+- Added ```ambientTextureImpactOnAnalyticalLights``` in PBRMaterial to allow fine grained control of the AmbientTexture on the analytical diffuse light ([sebavan](http://www.github.com/sebavan))
+- BoundingBoxGizmo scalePivot field that can be used to always scale objects from the bottom ([TrevorDev](https://github.com/TrevorDev))
+- Improved _isSyncronized performance and reduced GC in TransformNode.computeWorldMatrix by directly reading property. ([Bolloxim](https://github.com/Bolloxim))
+- Added supports for reflectionMatrix in Skybox Mode Cube Texture allowing offsetting the world center or rotating the matrix ([sebavan](http://www.github.com/sebavan))
+
+### glTF Loader
+
+- Added support for KHR_texture_transform ([bghgary](http://www.github.com/bghgary))
+- Added `onNodeLODsLoadedObservable` and `onMaterialLODsLoadedObservable` to MSFT_lod loader extension ([bghgary](http://www.github.com/bghgary))
+- Added glTF loader settings to the GLTF tab in the debug layer ([bghgary](http://www.github.com/bghgary))
+- Added debug logging and performance counters ([bghgary](http://www.github.com/bghgary))
+- Added support for EXT_lights_imageBased ([bghgary](http://www.github.com/bghgary))
+- Added support for MSFT_audio_emitter ([najadojo](http://www.github.com/najadojo))
+- Added support for custom loader extensions ([bghgary](http://www.github.com/bghgary))
+
+### Viewer
+
+- No fullscreen button on small devices ([RaananW](https://github.com/RaananW))
+- Nav-Bar is now displayed on fullscreen per default ([RaananW](https://github.com/RaananW))
+- Viewer configuration supports deprecated values using the new configurationCompatibility processor  ([RaananW](https://github.com/RaananW))
+- Shadows will only render while models are entering the scene or animating ([RaananW](https://github.com/RaananW))
+- Support for model drag and drop onto the canvas ([RaananW](https://github.com/RaananW))
+- New lab feature - global light rotation [#4347](https://github.com/BabylonJS/Babylon.js/issues/4347) ([RaananW](https://github.com/RaananW))
+- New NPM package - babylonjs-viewer-assets, to separate the binary assets and the code of the viewer ([RaananW](https://github.com/RaananW))
+- A new HD-Toggler button allows setting a better hardware scaling rate ([RaananW](https://github.com/RaananW))
+- An initial support for WebVR is implemented ([RaananW](https://github.com/RaananW))
+- It is now possible to choose the element that goes fullscreen in the default viewer ([RaananW](https://github.com/RaananW))
+- The default viewer has a plugin system with which new buttons can be added externally ([RaananW](https://github.com/RaananW))
+- The extended configuration is now the default when not providing the "extended" parameter ([RaananW](https://github.com/RaananW))
+- viewer.updateConfiguration also accepts a URL to download configuration remotely ([RaananW](https://github.com/RaananW))
+- Viewer supports 3D printing on windows 10 ([RaananW](https://github.com/RaananW))
+- The viewer's environment map is using the new .env feature ([RaananW](https://github.com/RaananW))
+
+### Materials Library
+
+- Added ```unlit``` mode to lava material ([sebavan](http://www.github.com/sebavan))
+
+### Documentation
+
+- Added all code comments for GUI
+
+## Bug fixes
+
+- VR experience helper will now fire pointer events even when no mesh is currently hit ([TrevorDev](https://github.com/TrevorDev))
+- RawTexture.CreateAlphaTexture no longer fails to create a usable texture ([TrevorDev](https://github.com/TrevorDev))
+- SceneSerializer.SerializeMesh now serializes all materials kinds (not only StandardMaterial) ([julien-moreau](https://github.com/julien-moreau))
+- WindowsMotionController's trackpad field will be updated prior to it's onTrackpadChangedObservable event ([TrevorDev](https://github.com/TrevorDev))
+- VR experience helper's controllers will not fire pointer events when laser's are disabled, instead the camera ray pointer event will be used ([TrevorDev](https://github.com/TrevorDev))
+- Node's setParent(node.parent) will no longer throw an exception when parent is undefined and will behave the same as setParent(null) ([TrevorDev](https://github.com/TrevorDev))
+- Mesh.MergeMeshes flips triangles on meshes with negative scaling ([SvenFrankson](http://svenfrankson.com))
+- Avoid firing button events multiple times when calling vrController.attachMesh() ([TrevorDev](https://github.com/TrevorDev))
+- Parse geometry when load binary mesh ([SinhNQ](https://github.com/quocsinh))
+- Removing observers during observable notify should not skip over valid observers ([TrevorDev](https://github.com/TrevorDev))
+- Initializing gamepadManager should register the gamepad update events ([TrevorDev](https://github.com/TrevorDev))
+- Do not generate mipmaps for RawCubeTexture if OES_texture_float_linear and/or EXT_color_buffer_float extensions are not supported ([PeapBoy](https://github.com/NicolasBuecher))
+- Do not modify passed camera array parameter when creating a default pipeline ([TrevorDev](https://github.com/TrevorDev))
+- Fixed issue where gaze trackers were appearing even after leaving VR ([atulyar](https://github.com/atulyar))
+- AdvancedDynamicTexture should not overwrite skipOnPointerObservable to false ([TrevorDev](https://github.com/TrevorDev))
+- Fixed issue where VRExperienceHelper.onExitingVR observable was being fired twice ([atulyar](https://github.com/atulyar))
+- Avoid firing onExitingVR observable multiple times when calling exitVR() and add observables to Viewer that can be used instead of the ones in VRExperienceHelper ([atulyar](https://github.com/atulyar))
+- GizmoManager should hide existing gizmos if a non-attachable mesh is selected ([TrevorDev](https://github.com/TrevorDev))
+- Ignore isPickable = false for vr ray casting if the mesh's name matches the specified floorMeshName to maintain backwards compatability ([TrevorDev](https://github.com/TrevorDev))
+- Fix File Loading if hosted from `file:`-Protocol ([ltetzlaff](https://github.com/ltetzlaff))
+- Do not throw error when updating a controller with no left stick ([TrevorDev](https://github.com/TrevorDev))
+- Exiting VR can result in messed up view ([TrevorDev](https://github.com/TrevorDev))
+- Dispose existing gazeTrackers when setting a new one ([TrevorDev](https://github.com/TrevorDev))
+- Set missing parentId in Mesh.serialize() for instances ([julien-moreau](https://github.com/julien-moreau))
+- Do not modify pivot point when using bounding box gizmo or behaviors ([TrevorDev](https://github.com/TrevorDev))
+- GPUParticleSystem does not get stuck in burst loop when stopped and started ([TrevorDev](https://github.com/TrevorDev))
+- trackPosition:false not working in webVRCamera ([TrevorDev](https://github.com/TrevorDev))
+- Spring Joint could not be removed ([TrevorDev](https://github.com/TrevorDev))
+
+### Core Engine
+
+- Fixed ```shadowEnabled``` property on lights. Shadows are not visible anymore when disabled ([sebavan](http://www.github.com/sebavan))
+- Physics `unregisterOnPhysicsCollide` didn't remove callback correctly [#4291](https://github.com/BabylonJS/Babylon.js/issues/4291) ([RaananW](https://github.com/RaananW))
+- Added missing getter and setter for global exposure in ColorCurves ([RaananW](https://github.com/RaananW))
+- Fixed an issue with view matrix when `ArcRotateCamera` was used with collisions ([Deltakosh](https://github.com/deltakosh))
+- Fixed a bug with setting `unlit` on `PBRMaterial` after the material is ready (Wrong dirty flags) ([bghgary](http://www.github.com/bghgary))
+- Fixed `HighlightLayer` support on browsers not supporting HalfFloat ([sebavan](http://www.github.com/sebavan))
+- Fixed support for R and RG texture formats ([sebavan](http://www.github.com/sebavan))
+- Fixed `updatable` parameter setting in the SPS ([jerome](https://github.com/jbousquie))
+- Angular and linear velocity were using the wrong method to copy values to the physics engine ([RaananW](https://github.com/RaananW))
+- Fixed env texture generation in Byte Mode ([sebavan](http://www.github.com/sebavan))
+- Oimo.js now receives quaternion and not euler when a body is being constructed ([RaananW](https://github.com/RaananW))
+- Improving visual quality on SSAO2 shader ([CraigFeldspar](https://github.com/CraigFeldspar))
+- Fixed a bug where changing the sample count on `PostProcess` would not update the WebGL Texture ([CraigFeldspar](https://github.com/CraigFeldspar))
+
+### Viewer
+
+- Fix Navbar Interaction on Mozilla/Firefox ([SzeyinLee](https://github.com/SzeyinLee))
+- Fix Animation Slider Interaction on Mozilla/Firefox ([sebavan](http://www.github.com/sebavan))
+- Fix Animation Slider Clickable area size Cross Plat ([sebavan](http://www.github.com/sebavan))
+- Ground material didn't take the default main color is no material definition was provided ([RaananW](https://github.com/RaananW))
+- Model configuration was not extended correctly if loaded more than one model ([RaananW](https://github.com/RaananW))
+- It wasn't possible to disable camera behavior(s) using configuration  [#4348](https://github.com/BabylonJS/Babylon.js/issues/4348) ([RaananW](https://github.com/RaananW))
+- Animation blending was always set to true, ignoring configuration [#4412](https://github.com/BabylonJS/Babylon.js/issues/4412) ([RaananW](https://github.com/RaananW))
+- Animation navbar now updates correctly when a new model is loaded [#4441](https://github.com/BabylonJS/Babylon.js/issues/4441) ([RaananW](https://github.com/RaananW))
+- Non-normalized meshes didn't center and focus correctly ([RaananW](https://github.com/RaananW))
+- Meshes with skeletons could have incorrect animations ([RaananW](https://github.com/RaananW))
+- Removed element IDs from viewer's templates to allow muitiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
+- Viewer is not using Engine.LastCreatedScene anymore, to support multiple viewers in a single page [#4500](https://github.com/BabylonJS/Babylon.js/issues/4500) ([RaananW](https://github.com/RaananW))
+- Template location was ignored if html was defined ([RaananW](https://github.com/RaananW))
+- Drag and Drop only worked if a model was already loaded before ([RaananW](https://github.com/RaananW))
+- It was not possible to add new custom optimizers, only use existing ones ([RaananW](https://github.com/RaananW))
+- Button texts were truncated incorrectly ([RaananW](https://github.com/RaananW))
+- Animation names with more than one word didn't work correctly ([RaananW](https://github.com/RaananW))
+
+### Loaders
+
+- STL Loader only supported binary downloads and no data: urls [#4473](https://github.com/BabylonJS/Babylon.js/issues/4473) ([RaananW](https://github.com/RaananW))
+- OBJ Loader is now an async loader [#4571](https://github.com/BabylonJS/Babylon.js/issues/4571) ([RaananW](https://github.com/RaananW))
+- GLTF Loader does not have texture conflicts on subsequent loads anymore [#5030](https://github.com/BabylonJS/Babylon.js/issues/5030) ([sebavan](http://www.github.com/sebavan))
+
+## Breaking changes
+
+- Fixing support for R and RG texture formats made us remove TextureFormat_R32F and TextureFormat_RG32F as they were mixing formats and types. Please, use the respective TextureFormat_R and TextureFormat_RG with the Float types ([sebavan](http://www.github.com/sebavan))
+- Replacing `scene.onRenderingGroupObservable` by `onBeforeRenderingGroupObservable` and `onAfterRenderingGroupObservable` to prevent the stage check ([sebavan](http://www.github.com/sebavan))

+ 12 - 9
inspector/src/helpers/Helpers.ts

@@ -114,23 +114,26 @@ export class Helpers {
     /**
      * Useful function used to create a div
      */
-    public static CreateDiv(className: Nullable<string> = null, parent?: HTMLElement): HTMLDivElement {
-        return <HTMLDivElement>Helpers.CreateElement('div', className, parent);
+    public static CreateDiv(className: Nullable<string> = null, parent?: HTMLElement, tooltip?: string): HTMLDivElement {
+        return <HTMLDivElement>Helpers.CreateElement('div', className, parent, tooltip);
     }
 
     /**
      * Useful function used to create a input
      */
-    public static CreateInput(className?: string, parent?: HTMLElement): HTMLInputElement {
-        return <HTMLInputElement>Helpers.CreateElement('input', className, parent);
+    public static CreateInput(className?: string, parent?: HTMLElement, tooltip?: string): HTMLInputElement {
+        return <HTMLInputElement>Helpers.CreateElement('input', className, parent, tooltip);
     }
 
-    public static CreateElement(element: string, className: Nullable<string> = null, parent?: HTMLElement): HTMLElement {
+    public static CreateElement(element: string, className: Nullable<string> = null, parent?: HTMLElement, tooltip?: string): HTMLElement {
         let elem = Inspector.DOCUMENT.createElement(element);
 
         if (className) {
             elem.className = className;
         }
+        if(tooltip && tooltip != '') {
+            elem.title = tooltip;
+        }
         if (parent) {
             parent.appendChild(elem);
         }
@@ -153,7 +156,7 @@ export class Helpers {
      */
     public static Css(elem: HTMLElement, cssAttribute: string): string {
         let clone = elem.cloneNode(true) as HTMLElement;
-        let div = Helpers.CreateDiv('', Inspector.DOCUMENT.body);
+        let div = Helpers.CreateDiv('', Inspector.DOCUMENT.body, '');
         div.style.display = 'none';
         div.appendChild(clone);
         let value = (<any>Inspector.WINDOW.getComputedStyle(clone))[cssAttribute];
@@ -165,17 +168,17 @@ export class Helpers {
 
     public static LoadScript() {
         Tools.LoadFile("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js", (elem) => {
-            let script = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body);
+            let script = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body, '');
             script.textContent = elem as string;
 
             // Load glsl detection
             Tools.LoadFile("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/languages/glsl.min.js", (elem) => {
-                let script = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body);
+                let script = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body, '');
                 script.textContent = elem as string;
 
                 // Load css style
                 Tools.LoadFile("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/zenburn.min.css", (elem) => {
-                    let style = Helpers.CreateElement('style', '', Inspector.DOCUMENT.body);
+                    let style = Helpers.CreateElement('style', '', Inspector.DOCUMENT.body, '');
                     style.textContent = elem as string;
                 });
             }, undefined, undefined, undefined, () => {

+ 54 - 47
inspector/src/tabs/SceneTab.ts

@@ -28,7 +28,7 @@ export class SceneTab extends Tab {
         // Build the properties panel : a div that will contains the tree and the detail panel
         this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
 
-        this._actions = Helpers.CreateDiv('scene-actions', this._panel) as HTMLDivElement;
+        this._actions = Helpers.CreateDiv('scene-actions', this._panel, '') as HTMLDivElement;
 
         this._detailsPanel = new DetailPanel();
         this._panel.appendChild(this._detailsPanel.toHtml());
@@ -54,15 +54,17 @@ export class SceneTab extends Tab {
         // Build actions
         {
 
-            // Rendering mode
-            let title = Helpers.CreateDiv('actions-title', this._actions);
+            // --------------------- Rendering mode ---------------------
+
+            let title = Helpers.CreateDiv('actions-title', this._actions, 'Choose the way of rendering the scene.');
             title.textContent = 'Rendering mode';
-            let point = Helpers.CreateDiv('action-radio', this._actions);
-            let wireframe = Helpers.CreateDiv('action-radio', this._actions);
-            let solid = Helpers.CreateDiv('action-radio', this._actions);
+            let point = Helpers.CreateDiv('action-radio', this._actions, 'Force scene rendering to points cloud : scene.forcePointsCloud');
             point.textContent = 'Point';
+            let wireframe = Helpers.CreateDiv('action-radio', this._actions, 'Force scene rendering to wireframe : scene.forceWireframe');
             wireframe.textContent = 'Wireframe';
+            let solid = Helpers.CreateDiv('action-radio', this._actions, 'Force scene rendering to solid.');
             solid.textContent = 'Solid';
+
             if (this._inspector.scene.forcePointsCloud) {
                 point.classList.add('active');
             } else if (this._inspector.scene.forceWireframe) {
@@ -75,47 +77,51 @@ export class SceneTab extends Tab {
             wireframe.addEventListener('click', () => { this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = true; });
             solid.addEventListener('click', () => { this._inspector.scene.forcePointsCloud = false; this._inspector.scene.forceWireframe = false; });
 
-            // Textures
-            title = Helpers.CreateDiv('actions-title', this._actions);
+            // --------------------- Textures ---------------------
+
+            title = Helpers.CreateDiv('actions-title', this._actions, 'Choose which textures channels to display or not on materials. (Check to display)');
             title.textContent = 'Textures channels';
-            this._generateActionLine('Diffuse Texture', StandardMaterial.DiffuseTextureEnabled, (b: boolean) => { StandardMaterial.DiffuseTextureEnabled = b });
-            this._generateActionLine('Ambient Texture', StandardMaterial.AmbientTextureEnabled, (b: boolean) => { StandardMaterial.AmbientTextureEnabled = b });
-            this._generateActionLine('Specular Texture', StandardMaterial.SpecularTextureEnabled, (b: boolean) => { StandardMaterial.SpecularTextureEnabled = b });
-            this._generateActionLine('Emissive Texture', StandardMaterial.EmissiveTextureEnabled, (b: boolean) => { StandardMaterial.EmissiveTextureEnabled = b });
-            this._generateActionLine('Bump Texture', StandardMaterial.BumpTextureEnabled, (b: boolean) => { StandardMaterial.BumpTextureEnabled = b });
-            this._generateActionLine('Opacity Texture', StandardMaterial.OpacityTextureEnabled, (b: boolean) => { StandardMaterial.OpacityTextureEnabled = b });
-            this._generateActionLine('Reflection Texture', StandardMaterial.ReflectionTextureEnabled, (b: boolean) => { StandardMaterial.ReflectionTextureEnabled = b });
-            this._generateActionLine('Refraction Texture', StandardMaterial.RefractionTextureEnabled, (b: boolean) => { StandardMaterial.RefractionTextureEnabled = b });
-            this._generateActionLine('ColorGrading', StandardMaterial.ColorGradingTextureEnabled, (b: boolean) => { StandardMaterial.ColorGradingTextureEnabled = b });
-            this._generateActionLine('Lightmap Texture', StandardMaterial.LightmapTextureEnabled, (b: boolean) => { StandardMaterial.LightmapTextureEnabled = b });
-            this._generateActionLine('Fresnel', StandardMaterial.FresnelEnabled, (b: boolean) => { StandardMaterial.FresnelEnabled = b });
-
-            // Options
-            title = Helpers.CreateDiv('actions-title', this._actions);
+            this._generateActionLine('Diffuse Texture', StandardMaterial.DiffuseTextureEnabled, (b: boolean) => { StandardMaterial.DiffuseTextureEnabled = b }, 'StandardMaterial.DiffuseTextureEnabled');
+            this._generateActionLine('Ambient Texture', StandardMaterial.AmbientTextureEnabled, (b: boolean) => { StandardMaterial.AmbientTextureEnabled = b }, 'StandardMaterial.AmbientTextureEnabled');
+            this._generateActionLine('Specular Texture', StandardMaterial.SpecularTextureEnabled, (b: boolean) => { StandardMaterial.SpecularTextureEnabled = b }, 'StandardMaterial.SpecularTextureEnabled');
+            this._generateActionLine('Emissive Texture', StandardMaterial.EmissiveTextureEnabled, (b: boolean) => { StandardMaterial.EmissiveTextureEnabled = b }, 'StandardMaterial.EmissiveTextureEnabled');
+            this._generateActionLine('Bump Texture', StandardMaterial.BumpTextureEnabled, (b: boolean) => { StandardMaterial.BumpTextureEnabled = b }, 'StandardMaterial.BumpTextureEnabled');
+            this._generateActionLine('Opacity Texture', StandardMaterial.OpacityTextureEnabled, (b: boolean) => { StandardMaterial.OpacityTextureEnabled = b }, 'StandardMaterial.OpacityTextureEnabled');
+            this._generateActionLine('Reflection Texture', StandardMaterial.ReflectionTextureEnabled, (b: boolean) => { StandardMaterial.ReflectionTextureEnabled = b }, 'StandardMaterial.ReflectionTextureEnabled');
+            this._generateActionLine('Refraction Texture', StandardMaterial.RefractionTextureEnabled, (b: boolean) => { StandardMaterial.RefractionTextureEnabled = b }, 'StandardMaterial.RefractionTextureEnabled');
+            this._generateActionLine('ColorGrading', StandardMaterial.ColorGradingTextureEnabled, (b: boolean) => { StandardMaterial.ColorGradingTextureEnabled = b }, 'StandardMaterial.ColorGradingTextureEnabled');
+            this._generateActionLine('Lightmap Texture', StandardMaterial.LightmapTextureEnabled, (b: boolean) => { StandardMaterial.LightmapTextureEnabled = b }, 'StandardMaterial.LightmapTextureEnabled');
+            this._generateActionLine('Fresnel', StandardMaterial.FresnelEnabled, (b: boolean) => { StandardMaterial.FresnelEnabled = b }, 'StandardMaterial.FresnelEnabled');
+
+            // --------------------- Options ---------------------
+
+            title = Helpers.CreateDiv('actions-title', this._actions, 'Choose which options to enable / disable on the scene. (Uncheck to disable).');
             title.textContent = 'Options';
-            this._generateActionLine('Animations', this._inspector.scene.animationsEnabled, (b: boolean) => { this._inspector.scene.animationsEnabled = b });
-            this._generateActionLine('Collisions', this._inspector.scene.collisionsEnabled, (b: boolean) => { this._inspector.scene.collisionsEnabled = b });
-            this._generateActionLine('Fog', this._inspector.scene.fogEnabled, (b: boolean) => { this._inspector.scene.fogEnabled = b });
-            this._generateActionLine('Lens flares', this._inspector.scene.lensFlaresEnabled, (b: boolean) => { this._inspector.scene.lensFlaresEnabled = b });
-            this._generateActionLine('Lights', this._inspector.scene.lightsEnabled, (b: boolean) => { this._inspector.scene.lightsEnabled = b });
-            this._generateActionLine('Particles', this._inspector.scene.particlesEnabled, (b: boolean) => { this._inspector.scene.particlesEnabled = b });
-            this._generateActionLine('Post-processes', this._inspector.scene.postProcessesEnabled, (b: boolean) => { this._inspector.scene.postProcessesEnabled = b });
-            this._generateActionLine('Probes', this._inspector.scene.probesEnabled, (b: boolean) => { this._inspector.scene.probesEnabled = b });
-            this._generateActionLine('Procedural textures', this._inspector.scene.proceduralTexturesEnabled, (b: boolean) => { this._inspector.scene.proceduralTexturesEnabled = b });
-            this._generateActionLine('Render targets', this._inspector.scene.renderTargetsEnabled, (b: boolean) => { this._inspector.scene.renderTargetsEnabled = b });
-            this._generateActionLine('Shadows', this._inspector.scene.shadowsEnabled, (b: boolean) => { this._inspector.scene.shadowsEnabled = b });
-            this._generateActionLine('Skeletons', this._inspector.scene.skeletonsEnabled, (b: boolean) => { this._inspector.scene.skeletonsEnabled = b });
-            this._generateActionLine('Sprites', this._inspector.scene.spritesEnabled, (b: boolean) => { this._inspector.scene.spritesEnabled = b });
-            this._generateActionLine('Textures', this._inspector.scene.texturesEnabled, (b: boolean) => { this._inspector.scene.texturesEnabled = b });
-
-            // Audio
-            title = Helpers.CreateDiv('actions-title', this._actions);
+            this._generateActionLine('Animations', this._inspector.scene.animationsEnabled, (b: boolean) => { this._inspector.scene.animationsEnabled = b }, 'scene.animationsEnabled');
+            this._generateActionLine('Collisions', this._inspector.scene.collisionsEnabled, (b: boolean) => { this._inspector.scene.collisionsEnabled = b }, 'scene.collisionsEnabled');
+            this._generateActionLine('Fog', this._inspector.scene.fogEnabled, (b: boolean) => { this._inspector.scene.fogEnabled = b }, 'scene.fogEnabled(boolean)');
+            this._generateActionLine('Lens flares', this._inspector.scene.lensFlaresEnabled, (b: boolean) => { this._inspector.scene.lensFlaresEnabled = b }, 'scene.lensFlaresEnabled');
+            this._generateActionLine('Lights', this._inspector.scene.lightsEnabled, (b: boolean) => { this._inspector.scene.lightsEnabled = b }, 'scene.lightsEnabled');
+            this._generateActionLine('Particles', this._inspector.scene.particlesEnabled, (b: boolean) => { this._inspector.scene.particlesEnabled = b }, 'scene.particlesEnabled');
+            this._generateActionLine('Post-processes', this._inspector.scene.postProcessesEnabled, (b: boolean) => { this._inspector.scene.postProcessesEnabled = b }, 'scene.postProcessesEnabled');
+            this._generateActionLine('Probes', this._inspector.scene.probesEnabled, (b: boolean) => { this._inspector.scene.probesEnabled = b }, 'scene.probesEnabled');
+            this._generateActionLine('Procedural textures', this._inspector.scene.proceduralTexturesEnabled, (b: boolean) => { this._inspector.scene.proceduralTexturesEnabled = b }, 'scene.proceduralTexturesEnabled');
+            this._generateActionLine('Render targets', this._inspector.scene.renderTargetsEnabled, (b: boolean) => { this._inspector.scene.renderTargetsEnabled = b }, 'scene.renderTargetsEnabled');
+            this._generateActionLine('Shadows', this._inspector.scene.shadowsEnabled, (b: boolean) => { this._inspector.scene.shadowsEnabled = b }, 'scene.shadowsEnabled');
+            this._generateActionLine('Skeletons', this._inspector.scene.skeletonsEnabled, (b: boolean) => { this._inspector.scene.skeletonsEnabled = b }, 'scene.skeletonsEnabled');
+            this._generateActionLine('Sprites', this._inspector.scene.spritesEnabled, (b: boolean) => { this._inspector.scene.spritesEnabled = b }, 'scene.spritesEnabled');
+            this._generateActionLine('Textures', this._inspector.scene.texturesEnabled, (b: boolean) => { this._inspector.scene.texturesEnabled = b }, 'scene.texturesEnabled');
+
+            // --------------------- Audio ---------------------
+
+            title = Helpers.CreateDiv('actions-title', this._actions, 'Choose which audio rendering should be used.');
             title.textContent = 'Audio';
-            let headphones = Helpers.CreateDiv('action-radio', this._actions);
-            let normalSpeaker = Helpers.CreateDiv('action-radio', this._actions);
-            this._generateActionLine('Disable audio', !this._inspector.scene.audioEnabled, (b: boolean) => { this._inspector.scene.audioEnabled = !b });
+            let headphones = Helpers.CreateDiv('action-radio', this._actions, 'Use Headphones mode.');
             headphones.textContent = 'Headphones';
+            let normalSpeaker = Helpers.CreateDiv('action-radio', this._actions, 'Use Normal speakers mode.');
             normalSpeaker.textContent = 'Normal speakers';
+            this._generateActionLine('Disable audio', !this._inspector.scene.audioEnabled, (b: boolean) => { this._inspector.scene.audioEnabled = !b }, 'Disable audio on the scene.');
+            
             this._generateRadioAction([headphones, normalSpeaker]);
             if (this._inspector.scene.headphone) {
                 headphones.classList.add('active');
@@ -125,8 +131,9 @@ export class SceneTab extends Tab {
             headphones.addEventListener('click', () => { this._inspector.scene.headphone = true; });
             normalSpeaker.addEventListener('click', () => { this._inspector.scene.headphone = false; });
 
-            // Viewers
-            title = Helpers.CreateDiv('actions-title', this._actions);
+            // --------------------- Viewer ---------------------
+
+            title = Helpers.CreateDiv('actions-title', this._actions, 'Viewer');
             title.textContent = 'Viewer';
             this._generateActionLine('Skeletons', false, (b: boolean) => {
                 if (b) {
@@ -154,7 +161,7 @@ export class SceneTab extends Tab {
                     }
                     this._skeletonViewers = [];
                 }
-            });
+            }, 'Enable to see Skeletons on the scene : Debug.SkeletonViewer');
         }
     }
 
@@ -164,8 +171,8 @@ export class SceneTab extends Tab {
     }
 
     /** generates a div which correspond to an option that can be activated/deactivated */
-    private _generateActionLine(name: string, initValue: boolean, action: (b: boolean) => void) {
-        let div = Helpers.CreateDiv('scene-actions', this._actions);
+    private _generateActionLine(name: string, initValue: boolean, action: (b: boolean) => void, tooltip?: string) {
+        let div = Helpers.CreateDiv('scene-actions', this._actions, tooltip);
         div.textContent = name;
         div.classList.add('action');
         if (initValue) {

+ 208 - 80
inspector/src/tabs/TextureTab.ts

@@ -1,4 +1,4 @@
-import { CubeTexture, RenderTargetTexture, Tools } from "babylonjs";
+import { CubeTexture, RenderTargetTexture, Tools, Vector3 } from "babylonjs";
 import { TextureAdapter } from "../adapters/TextureAdapter";
 import { Helpers } from "../helpers/Helpers";
 import { Inspector } from "../Inspector";
@@ -10,6 +10,8 @@ import * as Split from "Split";
 
 export class TextureTab extends Tab {
 
+    static DDSPreview: DDSPreview;
+
     private _inspector: Inspector;
     /** The panel containing a list of items */
     protected _treePanel: HTMLElement;
@@ -39,7 +41,7 @@ export class TextureTab extends Tab {
     }
 
     public dispose() {
-        // Nothing to dispose
+        TextureTab.DDSPreview.dispose();
     }
 
     public update(_items?: Array<TreeItem>) {
@@ -85,95 +87,127 @@ export class TextureTab extends Tab {
         Helpers.CleanDiv(this._imagePanel);
         // Get the texture object
         let texture = item.adapter.object;
-        let imgs: HTMLImageElement[] = [];
-        let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
-        imgs.push(img);
-        //Create five other images elements
-        for (let i = 0; i < 5; i++) {
-            imgs.push(Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement);
-        }
-
-        if (texture instanceof RenderTargetTexture) {
-            // RenderTarget textures
-            let scene = this._inspector.scene;
-            let engine = scene.getEngine();
-            let size = texture.getSize();
-
-            // Clone the texture
-            let screenShotTexture = texture.clone();
-            screenShotTexture.activeCamera = texture.activeCamera;
-            screenShotTexture.onBeforeRender = texture.onBeforeRender;
-            screenShotTexture.onAfterRender = texture.onAfterRender;
-            screenShotTexture.onBeforeRenderObservable = texture.onBeforeRenderObservable;
-
-            // To display the texture after rendering
-            screenShotTexture.onAfterRenderObservable.add((faceIndex: number) => {
-                Tools.DumpFramebuffer(size.width, size.height, engine,
-                    (data) => imgs[faceIndex].src = data);
-            });
-
-            // Render the texture
-            scene.incrementRenderId();
-            scene.resetCachedMaterial();
-            screenShotTexture.render();
-            screenShotTexture.dispose();
-        } else if (texture instanceof CubeTexture) {
-            // Cannot open correctly DDS File
-            // Display all textures of the CubeTexture
-            let pixels = <ArrayBufferView>texture.readPixels();
-            let canvas = document.createElement('canvas');
-            canvas.id = "MyCanvas";
-
-            if (img.parentElement) {
-                img.parentElement.appendChild(canvas);
-            }
-            let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
-            let size = texture.getSize();
+        let imageExtension = item.adapter.object.name.split('.').pop();
+        //In case the texture is a standard image format
+        if (imageExtension == "png" || imageExtension == "jpg" || imageExtension == "gif" || imageExtension == "svg") {
 
-            let tmp = pixels.buffer.slice(0, size.height * size.width * 4);
-            let u = new Uint8ClampedArray(tmp)
+            let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
 
-            let colors = new ImageData(size.width * 6, size.height);
+            img.style.width = this._imagePanel.style.width;
 
-            colors.data.set(u);
-            let imgData = ctx.createImageData(size.width * 6, size.height);
+            img.style.height = "auto";
 
-            imgData.data.set(u);
+            img.src = (<BABYLON.Texture>texture).name;
 
-            // let data = imgData.data;
+        } else if (imageExtension == "dds") {
+            //In case the texture is a dds format
 
-            // for(let i = 0, len = size.height * size.width; i < len; i++) {
-            //     data[i] = pixels[i];
-            // }
-            ctx.putImageData(imgData, 0, 0);
-            // let i: number = 0;
-            // for(let filename of (texture as CubeTexture)['_files']){
-            //     imgs[i].src = filename;
-            //     i++;
-            // }
+            if (TextureTab.DDSPreview != null && TextureTab.DDSPreview.canvas != null) {
+                this._imagePanel.appendChild(<Node>TextureTab.DDSPreview.canvas);
+                TextureTab.DDSPreview.insertPreview(item.adapter);
+            }
+            else {
+                //Create a canvas to load BJS if it don't exists
+                let previewCanvas = Helpers.CreateElement('canvas', '', this._imagePanel);
+                previewCanvas.style.outline = "none";
+                previewCanvas.style.webkitTapHighlightColor = "rgba(255,255,255,0)";
+                previewCanvas.id = "babylonjs-inspector-textures-preview";
+
+                TextureTab.DDSPreview = new DDSPreview(item.adapter);
+            }
         }
-        else if (texture['_canvas']) {
-            // Dynamic texture
-            let base64Image = texture['_canvas'].toDataURL("image/png");
-            img.src = base64Image;
-        } else if (texture.url) {
-            let pixels = texture.readPixels();
-            let canvas = document.createElement('canvas');
-            canvas.id = "MyCanvas";
-
-            if (img.parentElement) {
-                img.parentElement.appendChild(canvas);
+        else {
+            let imgs: HTMLImageElement[] = [];
+            let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
+            imgs.push(img);
+            //Create five other images elements
+            for (let i = 0; i < 5; i++) {
+                imgs.push(Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement);
+            }
+
+            if (texture instanceof RenderTargetTexture) {
+                // RenderTarget textures
+                let scene = this._inspector.scene;
+                let engine = scene.getEngine();
+                let size = texture.getSize();
+
+                // Clone the texture
+                let screenShotTexture = texture.clone();
+                screenShotTexture.activeCamera = texture.activeCamera;
+                screenShotTexture.onBeforeRender = texture.onBeforeRender;
+                screenShotTexture.onAfterRender = texture.onAfterRender;
+                screenShotTexture.onBeforeRenderObservable = texture.onBeforeRenderObservable;
+
+                // To display the texture after rendering
+                screenShotTexture.onAfterRenderObservable.add((faceIndex: number) => {
+                    Tools.DumpFramebuffer(size.width, size.height, engine,
+                        (data) => imgs[faceIndex].src = data);
+                });
+
+                // Render the texture
+                scene.incrementRenderId();
+                scene.resetCachedMaterial();
+                screenShotTexture.render();
+                screenShotTexture.dispose();
+            } else if (texture instanceof CubeTexture) {
+                // Cannot open correctly DDS File
+                // Display all textures of the CubeTexture
+                let pixels = <ArrayBufferView>texture.readPixels();
+                let canvas = document.createElement('canvas');
+                canvas.id = "MyCanvas";
+
+                if (img.parentElement) {
+                    img.parentElement.appendChild(canvas);
+                }
+                let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
+                let size = texture.getSize();
+
+                let tmp = pixels.buffer.slice(0, size.height * size.width * 4);
+                let u = new Uint8ClampedArray(tmp)
+
+                let colors = new ImageData(size.width * 6, size.height);
+
+                colors.data.set(u);
+                let imgData = ctx.createImageData(size.width * 6, size.height);
+
+                imgData.data.set(u);
+
+                // let data = imgData.data;
+
+                // for(let i = 0, len = size.height * size.width; i < len; i++) {
+                //     data[i] = pixels[i];
+                // }
+                ctx.putImageData(imgData, 0, 0);
+                // let i: number = 0;
+                // for(let filename of (texture as CubeTexture)['_files']){
+                //     imgs[i].src = filename;
+                //     i++;
+                // }
             }
-            let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
-            let size = texture.getSize();
+            else if (texture['_canvas']) {
+                // Dynamic texture
+                let base64Image = texture['_canvas'].toDataURL("image/png");
+                img.src = base64Image;
+            } else if (texture.url) {
+                let pixels = texture.readPixels();
+                let canvas = document.createElement('canvas');
+                canvas.id = "MyCanvas";
+
+                if (img.parentElement) {
+                    img.parentElement.appendChild(canvas);
+                }
+                let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
+                let size = texture.getSize();
+
+                let imgData = ctx.createImageData(size.width, size.height);
 
-            let imgData = ctx.createImageData(size.width, size.height);
+                imgData.data.set(pixels);
 
-            imgData.data.set(pixels);
+                ctx.putImageData(imgData, 0, 0);
+                // If an url is present, the texture is an image
+                // img.src = texture.url;
 
-            ctx.putImageData(imgData, 0, 0);
-            // If an url is present, the texture is an image
-            // img.src = texture.url;
+            }
 
         }
 
@@ -196,4 +230,98 @@ export class TextureTab extends Tab {
         }
         item.active(true);
     }
+
+}
+
+class DDSPreview {
+
+    public canvas: HTMLCanvasElement | null;
+    private _engine: BABYLON.Engine;
+    private _scene: BABYLON.Scene;
+    private _camera: BABYLON.ArcRotateCamera;
+    private _mat: BABYLON.StandardMaterial;
+    private _tex: BABYLON.Texture;
+    private _cubeTex: BABYLON.CubeTexture;
+    private _mesh: BABYLON.Mesh;
+
+    constructor(AdapterItem: TextureAdapter) {
+
+        this.canvas = document.getElementById("babylonjs-inspector-textures-preview") as HTMLCanvasElement;
+        this._engine = new BABYLON.Engine(this.canvas, true);
+
+        this._run();
+        this.insertPreview(AdapterItem);
+    }
+
+    private _run() {
+        this._scene = new BABYLON.Scene(this._engine);
+        this._scene.clearColor = new BABYLON.Color4(0.1412, 0.1412, 0.1412, 1);
+
+        let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), this._scene);
+        light.intensity = 1;
+
+        this._camera = new BABYLON.ArcRotateCamera("Camera", 0, 1.57, 5, Vector3.Zero(), this._scene);
+        this._scene.activeCamera = this._camera;
+        this._camera.attachControl(this.canvas as HTMLCanvasElement);
+
+        window.addEventListener("resize", () => {
+            this._engine.resize();
+        });
+
+        this._scene.executeWhenReady(() => {
+            this._engine.runRenderLoop(() => {
+                this._scene.render();
+            });
+        });
+    }
+
+    public insertPreview(AdapterItem: TextureAdapter) {
+        if (this._tex) this._tex.dispose();
+        if (this._mat) this._mat.dispose();
+        if (this._mesh) this._mesh.dispose();
+
+        this._mat = new BABYLON.StandardMaterial("customMat", this._scene);
+
+        if (AdapterItem.type() == "Texture") {
+            //If the dds is not a cube format render it on a plane
+
+            var previewMeshPlane = BABYLON.Mesh.CreatePlane("previewPlane", 3, this._scene);
+            previewMeshPlane.rotate(new BABYLON.Vector3(1, 0, 0), 3.14);
+            previewMeshPlane.rotate(new BABYLON.Vector3(0, 1, 0), -1.57);
+            this._mesh = previewMeshPlane;
+
+            this._tex = new BABYLON.Texture(AdapterItem.object.name, this._scene);
+            this._tex.invertZ = true;
+            this._tex.uScale = -1;
+
+            this._mat.diffuseTexture = this._tex;
+            this._mat.emissiveTexture = this._tex;
+            this._mat.specularTexture = this._tex;
+            this._mat.disableLighting = true;
+
+            previewMeshPlane.material = this._mat;
+
+        }
+        else if (AdapterItem.type() == "BaseTexture") {
+            //Else if the dds is a cube format render it on a box
+
+            var previewMeshBox = BABYLON.Mesh.CreateBox("previewBox", 3, this._scene);
+            previewMeshBox.rotate(new BABYLON.Vector3(0, 1, 0), -0.5);
+            this._mesh = previewMeshBox;
+
+            this._cubeTex = new BABYLON.CubeTexture(AdapterItem.object.name, this._scene);
+            this._mat.reflectionTexture = this._cubeTex;
+            (<BABYLON.CubeTexture>this._mat.reflectionTexture).coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
+            this._mat.disableLighting = true;
+
+            previewMeshBox.material = this._mat;
+        }
+
+        this._engine.resize();
+    }
+
+    public dispose() {
+        this._engine.dispose();
+        this.canvas = null;
+    }
 }

+ 1 - 1
inspector/src/tabs/ToolsTab.ts

@@ -141,7 +141,7 @@ export class ToolsTab extends Tab {
             };
             elemValue.appendChild(inputElement);
 
-            if (VideoRecorder.IsSupported(this._scene.getEngine())) {
+            if (VideoRecorder && VideoRecorder.IsSupported(this._scene.getEngine())) {
                 let videoRecorderElement = Inspector.DOCUMENT.createElement('input');
                 videoRecorderElement.value = "Start Recording Video";
                 videoRecorderElement.type = "button";

BIN
inspector/test/environment.dds


+ 3 - 0
inspector/test/index.js

@@ -56,6 +56,9 @@ var Test = (function () {
 
         var tn = new BABYLON.TransformNode("transform node");
 
+        let DDSTexture = new BABYLON.CubeTexture("test/environment.dds", scene);
+        let DDSTexture2 = new BABYLON.Texture("test/test_1.dds", scene);
+
         // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
         var ground = BABYLON.Mesh.CreateGround("node_damagedHelmet_-6514", 6, 6, 2, scene);
         ground.parent = tn;

BIN
inspector/test/test_1.dds


+ 31 - 3
loaders/src/OBJ/babylon.objFileLoader.ts

@@ -254,7 +254,17 @@ module BABYLON {
                 () => { console.warn("Error - Unable to load " + pathOfFile); });
         }
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             //get the meshes from OBJ file
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(meshes => {
                 return {
@@ -266,14 +276,32 @@ module BABYLON {
             });
         }
 
-        public loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        public loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void> {
             //Get the 3D model
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(() => {
                 // return void
             });
         }
 
-        public loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer> {
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        public loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer> {
             return this.importMeshAsync(null, scene, data, rootUrl).then(result => {
                 var container = new AssetContainer(scene);
                 result.meshes.forEach(mesh => container.meshes.push(mesh));

+ 6 - 3
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -64,6 +64,7 @@ module BABYLON.GLTF2 {
         private _state: Nullable<GLTFLoaderState> = null;
         private _extensions: { [name: string]: IGLTFLoaderExtension } = {};
         private _rootUrl: string;
+        private _fullName: string;
         private _rootBabylonMesh: Mesh;
         private _defaultBabylonMaterialData: { [drawMode: number]: Material } = {};
         private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
@@ -156,10 +157,11 @@ module BABYLON.GLTF2 {
         }
 
         /** @hidden */
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
                 this.babylonScene = scene;
                 this._rootUrl = rootUrl;
+                this._fullName = fullName || `${Date.now()}`;
                 this._progressCallback = onProgress;
                 this._loadData(data);
 
@@ -198,10 +200,11 @@ module BABYLON.GLTF2 {
         }
 
         /** @hidden */
-        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
+        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void> {
             return Promise.resolve().then(() => {
                 this.babylonScene = scene;
                 this._rootUrl = rootUrl;
+                this._fullName = fullName || `${Date.now()}`;
                 this._progressCallback = onProgress;
                 this._loadData(data);
                 return this._loadAsync(null, () => undefined);
@@ -1648,7 +1651,7 @@ module BABYLON.GLTF2 {
 
             const image = ArrayItem.Get(`${context}/source`, this.gltf.images, texture.source);
             promises.push(this.loadImageAsync(`#/images/${image.index}`, image).then(data => {
-                const dataUrl = `data:${this._rootUrl}${image.uri || `image${image.index}`}`;
+                const dataUrl = `data:${this._fullName}${image.uri || `image${image.index}`}`;
                 babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
             }));
 

+ 11 - 8
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -89,8 +89,8 @@ module BABYLON {
     /** @hidden */
     export interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
 
     /**
@@ -445,13 +445,14 @@ module BABYLON {
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
-                return this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress);
+                return this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fullName);
             });
         }
 
@@ -461,13 +462,14 @@ module BABYLON {
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          */
-        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
+        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void> {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
-                return this._loader.loadAsync(scene, loaderData, rootUrl, onProgress);
+                return this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fullName);
             });
         }
 
@@ -477,13 +479,14 @@ module BABYLON {
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          */
-        public loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer> {
+        public loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer> {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
-                return this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(result => {
+                return this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fullName).then(result => {
                     const container = new AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);

+ 1 - 1
package.json

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

+ 2 - 0
src/Actions/babylon.action.ts

@@ -37,6 +37,8 @@
             if (triggerOptions.parameter) {
                 this.trigger = triggerOptions.trigger;
                 this._triggerParameter = triggerOptions.parameter;
+            } else if(triggerOptions.trigger) {
+                this.trigger = triggerOptions.trigger;
             } else {
                 this.trigger = triggerOptions;
             }

+ 24 - 5
src/Actions/babylon.actionManager.ts

@@ -15,15 +15,15 @@
          */
         constructor(
             /** The mesh or sprite that triggered the action */
-            public source: any, 
+            public source: any,
             /** The X mouse cursor position at the time of the event */
-            public pointerX: number, 
+            public pointerX: number,
             /** The Y mouse cursor position at the time of the event */
-            public pointerY: number, 
+            public pointerY: number,
             /** The mesh that is currently pointed at (can be null) */
-            public meshUnderPointer: Nullable<AbstractMesh>, 
+            public meshUnderPointer: Nullable<AbstractMesh>,
             /** the original (browser) event that triggered the ActionEvent */
-            public sourceEvent?: any, 
+            public sourceEvent?: any,
             /** additional data for the event */
             public additionalData?: any) {
 
@@ -307,6 +307,25 @@
         }
 
         /**
+         * Does this action manager handles actions of any of the given triggers. This function takes two arguments for 
+         * speed.
+         * @param triggerA defines the trigger to be tested
+         * @param triggerB defines the trigger to be tested
+         * @return a boolean indicating whether one (or more) of the triggers is handled 
+         */
+        public hasSpecificTriggers2(triggerA: number, triggerB: number): boolean {
+            for (var index = 0; index < this.actions.length; index++) {
+                var action = this.actions[index];
+
+                if (triggerA == action.trigger || triggerB == action.trigger) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        /**
          * Does this action manager handles actions of a given trigger
          * @param trigger defines the trigger to be tested
          * @param parameterPredicate defines an optional predicate to filter triggers by parameter

+ 12 - 12
src/Animations/babylon.animation.ts

@@ -11,9 +11,9 @@
          */
         constructor(
             /**The name of the animation range**/
-            public name: string, 
+            public name: string,
             /**The starting frame of the animation */
-            public from: number, 
+            public from: number,
             /**The ending frame of the animation*/
             public to: number) {
         }
@@ -46,9 +46,9 @@
             /** The frame for which the event is triggered **/
             public frame: number,
             /** The event to perform when triggered **/
-            public action: (currentFrame: number) => void , 
+            public action: (currentFrame: number) => void,
             /** Specifies if the event should be triggered only once**/
-            public onlyOnce?: boolean ) {
+            public onlyOnce?: boolean) {
         }
 
         /** @hidden */
@@ -472,15 +472,15 @@
          */
         constructor(
             /**Name of the animation */
-            public name: string, 
+            public name: string,
             /**Property to animate */
-            public targetProperty: string, 
+            public targetProperty: string,
             /**The frames per second of the animation */
-            public framePerSecond: number, 
+            public framePerSecond: number,
             /**The data type of the animation */
             public dataType: number,
-            /**The loop mode of the animation */ 
-            public loopMode?: number, 
+            /**The loop mode of the animation */
+            public loopMode?: number,
             /**Specifies if blending should be enabled */
             public enableBlending?: boolean) {
             this.targetPropertyPath = targetProperty.split(".");
@@ -757,7 +757,7 @@
             }
 
             return value;
-        } 
+        }
 
         /**
          * @hidden Internal use only
@@ -896,7 +896,7 @@
         public matrixInterpolateFunction(startValue: Matrix, endValue: Matrix, gradient: number, result?: Matrix): Matrix {
             if (Animation.AllowMatrixDecomposeForInterpolation) {
                 if (result) {
-                    Matrix.DecomposeLerpToRef(startValue, endValue, gradient, result);    
+                    Matrix.DecomposeLerpToRef(startValue, endValue, gradient, result);
                     return result;
                 }
                 return Matrix.DecomposeLerp(startValue, endValue, gradient);
@@ -1121,7 +1121,7 @@
             } else if (constructor.Slerp) { // Slerp supported
                 return constructor.Slerp(left, right, amount);
             } else if (left.toFixed) { // Number
-                return left * (1.0 - amount) + amount* right;
+                return left * (1.0 - amount) + amount * right;
             } else { // Blending not supported
                 return right;
             }

+ 57 - 40
src/Animations/babylon.runtimeAnimation.ts

@@ -1,5 +1,22 @@
 module BABYLON {
 
+    // Static values to help the garbage collector
+
+    // Quaternion
+    const _staticOffsetValueQuaternion: Readonly<Quaternion> = Object.freeze(new Quaternion(0, 0, 0, 0));
+
+    // Vector3
+    const _staticOffsetValueVector3: Readonly<Vector3> = Object.freeze(Vector3.Zero());
+
+    // Vector2
+    const _staticOffsetValueVector2: Readonly<Vector2> = Object.freeze(Vector2.Zero());
+
+    // Size
+    const _staticOffsetValueSize: Readonly<Size> = Object.freeze(Size.Zero());
+
+    // Color3
+    const _staticOffsetValueColor3: Readonly<Color3> = Object.freeze(Color3.Black());
+
     /**
      * Defines a runtime animation
      */
@@ -15,7 +32,7 @@
          * The animation used by the runtime animation
          */
         private _animation: Animation;
-        
+
         /**
          * The target of the runtime animation
          */
@@ -25,37 +42,37 @@
          * The initiating animatable
          */
         private _host: Animatable;
-        
+
         /**
          * The original value of the runtime animation
          */
         private _originalValue = new Array<any>();
-        
+
         /**
          * The original blend value of the runtime animation
          */
         private _originalBlendValue: any;
-        
+
         /**
          * The offsets cache of the runtime animation
          */
-        private _offsetsCache: {[key: string]: any} = {};
-        
+        private _offsetsCache: { [key: string]: any } = {};
+
         /**
          * The high limits cache of the runtime animation
          */
-        private _highLimitsCache: {[key: string]: any} = {};
-        
+        private _highLimitsCache: { [key: string]: any } = {};
+
         /**
          * Specifies if the runtime animation has been stopped
          */
         private _stopped = false;
-        
+
         /**
          * The blending factor of the runtime animation
          */
         private _blendingFactor = 0;
-        
+
         /**
          * The BabylonJS scene
          */
@@ -65,20 +82,20 @@
          * The current value of the runtime animation
          */
         private _currentValue: any;
-        
+
         /** @hidden */
         public _workValue: any;
-        
+
         /**
          * The active target of the runtime animation
          */
         private _activeTarget: any;
-        
+
         /**
          * The target path of the runtime animation
          */
         private _targetPath: string = "";
-        
+
         /**
          * The weight of the runtime animation
          */
@@ -93,7 +110,7 @@
          * The previous delay of the runtime animation
          */
         private _previousDelay: number = 0;
-        
+
         /**
          * The previous ratio of the runtime animation
          */
@@ -111,7 +128,7 @@
          */
         public get weight(): number {
             return this._weight;
-        }              
+        }
 
         /**
          * Gets the current value of the runtime animation
@@ -196,7 +213,7 @@
             // Events
             for (var index = 0; index < this._events.length; index++) {
                 this._events[index].isDone = false;
-            }                  
+            }
         }
 
         /**
@@ -205,7 +222,7 @@
          */
         public isStopped(): boolean {
             return this._stopped;
-        }        
+        }
 
         /**
          * Disposes of the runtime animation
@@ -217,7 +234,7 @@
                 this._animation.runtimeAnimations.splice(index, 1);
             }
         }
-        
+
         /**
          * Interpolates the animation from the current frame
          * @param currentFrame The frame to interpolate the animation to
@@ -269,7 +286,7 @@
                     property = property[targetPropertyPath[index]];
                 }
 
-                path =  targetPropertyPath[targetPropertyPath.length - 1];
+                path = targetPropertyPath[targetPropertyPath.length - 1];
                 destination = property;
             } else {
                 path = targetPropertyPath[0];
@@ -323,7 +340,7 @@
                             this._currentValue = Matrix.Lerp(this._originalBlendValue, currentValue, this._blendingFactor);
                         }
                     }
-                } else { 
+                } else {
                     this._currentValue = Animation._UniversalLerp(this._originalBlendValue, currentValue, this._blendingFactor);
                 }
 
@@ -349,7 +366,7 @@
          * @returns Loop Mode
          */
         private _getCorrectLoopMode(): number | undefined {
-            if ( this._target && this._target.animationPropertiesOverride) {
+            if (this._target && this._target.animationPropertiesOverride) {
                 return this._target.animationPropertiesOverride.loopMode;
             }
 
@@ -424,7 +441,7 @@
             }
 
             //to and from cannot be the same key
-            if(from === to) {
+            if (from === to) {
                 if (from > keys[0].frame) {
                     from--;
                 } else if (to < keys[keys.length - 1].frame) {
@@ -434,7 +451,7 @@
 
             // Compute ratio
             var range = to - from;
-            var offsetValue;
+            var offsetValue: any;
             // ratio represents the frame delta between from and to
             var ratio = (delay * (this._animation.framePerSecond * speedRatio) / 1000.0) + this._ratioOffset;
             var highLimitValue = 0;
@@ -495,30 +512,30 @@
                         break;
                     // Quaternion
                     case Animation.ANIMATIONTYPE_QUATERNION:
-                        offsetValue = new Quaternion(0, 0, 0, 0);
+                        offsetValue = _staticOffsetValueQuaternion;
                         break;
                     // Vector3
                     case Animation.ANIMATIONTYPE_VECTOR3:
-                        offsetValue = Vector3.Zero();
+                        offsetValue = _staticOffsetValueVector3;
                         break;
                     // Vector2
                     case Animation.ANIMATIONTYPE_VECTOR2:
-                        offsetValue = Vector2.Zero();
+                        offsetValue = _staticOffsetValueVector2;
                         break;
                     // Size
                     case Animation.ANIMATIONTYPE_SIZE:
-                        offsetValue = Size.Zero();
+                        offsetValue = _staticOffsetValueSize;
                         break;
                     // Color3
                     case Animation.ANIMATIONTYPE_COLOR3:
-                        offsetValue = Color3.Black();
+                        offsetValue = _staticOffsetValueColor3;
                 }
             }
 
             // Compute value
             var repeatCount = (ratio / range) >> 0;
             var currentFrame = returnValue ? from + ratio % range : to;
-            
+
             // Need to normalize?
             if (this._host && this._host.syncRoot) {
                 let syncRoot = this._host.syncRoot;
@@ -528,16 +545,16 @@
 
             // Reset events if looping
             let events = this._events;
-            if (range > 0 && this.currentFrame > currentFrame || 
+            if (range > 0 && this.currentFrame > currentFrame ||
                 range < 0 && this.currentFrame < currentFrame) {
-                    // Need to reset animation events
-                    for (var index = 0; index < events.length; index++) {
-                        if (!events[index].onlyOnce) {
-                            // reset event, the animation is looping
-                            events[index].isDone = false;
-                        }
-                    }                    
+                // Need to reset animation events
+                for (var index = 0; index < events.length; index++) {
+                    if (!events[index].onlyOnce) {
+                        // reset event, the animation is looping
+                        events[index].isDone = false;
+                    }
                 }
+            }
 
             var currentValue = this._interpolate(currentFrame, repeatCount, this._getCorrectLoopMode(), offsetValue, highLimitValue);
 
@@ -551,7 +568,7 @@
                 if (
                     (range > 0 && currentFrame >= events[index].frame && events[index].frame >= from) ||
                     (range < 0 && currentFrame <= events[index].frame && events[index].frame <= from)
-                ){
+                ) {
                     var event = events[index];
                     if (!event.isDone) {
                         // If event should be done only once, remove it.
@@ -571,6 +588,6 @@
             return returnValue;
         }
     }
-} 
+}
 
 

+ 11 - 7
src/Behaviors/Mesh/babylon.pointerDragBehavior.ts

@@ -3,7 +3,7 @@ module BABYLON {
      * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
      */
     export class PointerDragBehavior implements Behavior<Mesh> {
-        private _attachedNode: Node; 
+        private _attachedNode: Mesh; 
         private _dragPlane: Mesh;
         private _scene:Scene;
         private _pointerObserver:Nullable<Observer<PointerInfo>>;
@@ -177,11 +177,13 @@ module BABYLON {
 
             this._beforeRenderObserver = this._scene.onBeforeRenderObservable.add(()=>{
                 if(this._moving && this.moveAttached){
+                    BoundingBoxGizmo._RemoveAndStorePivotPoint(this._attachedNode);
                     // Slowly move mesh to avoid jitter
-                    this._targetPosition.subtractToRef((<Mesh>this._attachedNode).absolutePosition, this._tmpVector);
+                    this._targetPosition.subtractToRef((this._attachedNode).absolutePosition, this._tmpVector);
                     this._tmpVector.scaleInPlace(this.dragDeltaRatio);
-                    (<Mesh>this._attachedNode).getAbsolutePosition().addToRef(this._tmpVector, this._tmpVector);
-                    (<Mesh>this._attachedNode).setAbsolutePosition(this._tmpVector);
+                    (this._attachedNode).getAbsolutePosition().addToRef(this._tmpVector, this._tmpVector);
+                    (this._attachedNode).setAbsolutePosition(this._tmpVector);
+                    BoundingBoxGizmo._RestorePivotPoint(this._attachedNode);
                 }
             });
         }
@@ -219,6 +221,7 @@ module BABYLON {
                 return;
             }
             
+            BoundingBoxGizmo._RemoveAndStorePivotPoint(this._attachedNode);
             // Create start ray from the camera to the object
             if(fromRay){
                 this._startDragRay.direction.copyFrom(fromRay.direction)
@@ -237,7 +240,7 @@ module BABYLON {
                 this.currentDraggingPointerID = 1;
                 this.lastDragPosition.copyFrom(pickedPoint);
                 this.onDragStartObservable.notifyObservers({dragPlanePoint: pickedPoint, pointerId: this.currentDraggingPointerID});
-                this._targetPosition.copyFrom((<Mesh>this._attachedNode).absolutePosition)
+                this._targetPosition.copyFrom((this._attachedNode).absolutePosition)
 
                 // Detatch camera controls
                 if(this.detachCameraControls && this._scene.activeCamera && !this._scene.activeCamera.leftCamera){
@@ -249,6 +252,7 @@ module BABYLON {
                     }
                 }
             }
+            BoundingBoxGizmo._RestorePivotPoint(this._attachedNode);
         }
 
         private _dragDelta = new BABYLON.Vector3();
@@ -298,7 +302,7 @@ module BABYLON {
                 if(this._useAlternatePickedPointAboveMaxDragAngle){
                     // Invert ray direction along the towards object axis
                     this._tmpVector.copyFrom(ray.direction);
-                    (<Mesh>this._attachedNode).absolutePosition.subtractToRef(ray.origin, this._alternatePickedPoint);
+                    (this._attachedNode).absolutePosition.subtractToRef(ray.origin, this._alternatePickedPoint);
                     this._alternatePickedPoint.normalize();
                     this._alternatePickedPoint.scaleInPlace(-2*Vector3.Dot(this._alternatePickedPoint, this._tmpVector));
                     this._tmpVector.addInPlace(this._alternatePickedPoint);
@@ -307,7 +311,7 @@ module BABYLON {
                     var dot = Vector3.Dot(this._dragPlane.forward, this._tmpVector);
                     this._dragPlane.forward.scaleToRef(-dot, this._alternatePickedPoint);
                     this._alternatePickedPoint.addInPlace(this._tmpVector);
-                    this._alternatePickedPoint.addInPlace((<Mesh>this._attachedNode).absolutePosition);
+                    this._alternatePickedPoint.addInPlace((this._attachedNode).absolutePosition);
                     return this._alternatePickedPoint
                 }else{
                     return null;

+ 4 - 0
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -83,6 +83,7 @@ module BABYLON {
                         }
                         
                         pickedMesh = this._ownerNode;
+                        BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
                         lastSixDofOriginPosition.copyFrom(pointerInfo.pickInfo.ray.origin);
 
                         // Set position and orientation of the controller
@@ -115,6 +116,7 @@ module BABYLON {
                                 attachedElement = null;
                             }
                         }
+                        BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
                     }
                 }else if(pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP){
                     if(this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId){
@@ -172,6 +174,7 @@ module BABYLON {
             // On every frame move towards target scaling to avoid jitter caused by vr controllers
             this._sceneRenderObserver = ownerNode.getScene().onBeforeRenderObservable.add(()=>{
                 if(this.dragging && this._moving && pickedMesh){
+                    BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
                     // Slowly move mesh to avoid jitter
                     pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(this.dragDeltaRatio));
                     
@@ -189,6 +192,7 @@ module BABYLON {
                     pickedMesh.setParent(null);
                     Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
                     pickedMesh.setParent(oldParent);
+                    BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
                 }
             });
         }

+ 18 - 0
src/Cameras/VR/babylon.webVRCamera.ts

@@ -525,6 +525,17 @@ module BABYLON {
 
         private updateCacheCalled: boolean;
 
+        // Remove translation from 6dof headset if trackposition is set to false
+        private _correctPositionIfNotTrackPosition(matrix:Matrix, isViewMatrix = false){
+            if(this.rawPose && this.rawPose.position && !this.webVROptions.trackPosition){
+                Matrix.TranslationToRef(this.rawPose.position[0], this.rawPose.position[1], -this.rawPose.position[2], this._tmpMatrix);
+                if(!isViewMatrix){
+                    this._tmpMatrix.invert();
+                }
+                this._tmpMatrix.multiplyToRef(matrix, matrix);
+            }
+        }
+
         /**
          * @hidden
          * Updates the cached values of the camera
@@ -559,6 +570,7 @@ module BABYLON {
                 // Update the gamepad to ensure the mesh is updated on the same frame as camera
                 this.controllers.forEach((controller) => {
                     controller._deviceToWorld.copyFrom(this._deviceToWorld);
+                    this._correctPositionIfNotTrackPosition(controller._deviceToWorld);
                     controller.update();
                 });
             }
@@ -596,6 +608,7 @@ module BABYLON {
             return Matrix.Identity();
         }
 
+        private _tmpMatrix = new BABYLON.Matrix();
         /**
          * This function is called by the two RIG cameras.
          * 'this' is the left or right camera (and NOT (!!!) the WebVRFreeCamera instance)
@@ -635,6 +648,9 @@ module BABYLON {
 
                 this._webvrViewMatrix.invert();
             }
+             
+            // Remove translation from 6dof headset if trackposition is set to false
+            parentCamera._correctPositionIfNotTrackPosition(this._webvrViewMatrix, true);
 
             parentCamera._worldToDevice.multiplyToRef(this._webvrViewMatrix, this._webvrViewMatrix);
 
@@ -704,6 +720,8 @@ module BABYLON {
                     let webVrController: WebVRController = <WebVRController>gamepad;
                     webVrController.deviceScaleFactor = this.deviceScaleFactor;
                     webVrController._deviceToWorld.copyFrom(this._deviceToWorld);
+                    this._correctPositionIfNotTrackPosition(webVrController._deviceToWorld);
+
                     if (this.webVROptions.controllerMeshes) {
                         if (webVrController.defaultModel) {
                             webVrController.defaultModel.setEnabled(true);

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

@@ -33,27 +33,24 @@
         public reConstruct(min: Vector3, max: Vector3) {
             this.minimum = min.clone();
             this.maximum = max.clone()
+
             // Bounding vectors
-            this.vectors = new Array<Vector3>();
-            this.vectors.push(this.minimum.clone());
-            this.vectors.push(this.maximum.clone());
+            this.vectors = [
+                this.minimum.clone(),
+                this.maximum.clone(),
+                this.minimum.clone(),
+                this.minimum.clone(),
+                this.minimum.clone(),
+                this.maximum.clone(),
+                this.maximum.clone(),
+                this.maximum.clone()
+            ];
 
-            this.vectors.push(this.minimum.clone());
             this.vectors[2].x = this.maximum.x;
-
-            this.vectors.push(this.minimum.clone());
             this.vectors[3].y = this.maximum.y;
-
-            this.vectors.push(this.minimum.clone());
             this.vectors[4].z = this.maximum.z;
-
-            this.vectors.push(this.maximum.clone());
             this.vectors[5].z = this.minimum.z;
-
-            this.vectors.push(this.maximum.clone());
             this.vectors[6].x = this.minimum.x;
-
-            this.vectors.push(this.maximum.clone());
             this.vectors[7].y = this.minimum.y;
 
             // OBB
@@ -65,6 +62,7 @@
             for (var index = 0; index < this.vectors.length; index++) {
                 this.vectorsWorld[index] = Vector3.Zero();
             }
+
             this.minimumWorld = Vector3.Zero();
             this.maximumWorld = Vector3.Zero();
             this.centerWorld = Vector3.Zero();

+ 14 - 10
src/Culling/babylon.boundingSphere.ts

@@ -1,4 +1,8 @@
 module BABYLON {
+    // This matrix is used as a value to reset the bounding box.
+    const _identityMatrix = Matrix.Identity();
+    const _tempRadiusVector = new Vector3(0, 0, 0);
+
     export class BoundingSphere {
         public center: Vector3;
         public radius: number;
@@ -7,14 +11,14 @@
         public minimum: Vector3;
         public maximum: Vector3;
 
-        private _tempRadiusVector = Vector3.Zero();
-
         /**
          * Creates a new bounding sphere
          * @param min defines the minimum vector (in local space)
          * @param max defines the maximum vector (in local space)
          */
         constructor(min: Vector3, max: Vector3) {
+            this.center = Vector3.Zero();
+            this.centerWorld = Vector3.Zero();
             this.reConstruct(min, max);
         }
 
@@ -29,11 +33,11 @@
 
             var distance = Vector3.Distance(min, max);
 
-            this.center = Vector3.Lerp(min, max, 0.5);
+            Vector3.LerpToRef(min, max, 0.5, this.center);
             this.radius = distance * 0.5;
 
-            this.centerWorld = Vector3.Zero();
-            this._update(Matrix.Identity());
+            this.centerWorld.set(0, 0, 0);
+            this._update(_identityMatrix);
         }
 
         /**
@@ -43,10 +47,10 @@
          */
         public scale(factor: number): BoundingSphere {
             let newRadius = this.radius * factor;
-            let newRadiusVector = new Vector3(newRadius, newRadius, newRadius);
+            _tempRadiusVector.set(newRadius, newRadius, newRadius)
 
-            let min = this.center.subtract(newRadiusVector);
-            let max = this.center.add(newRadiusVector);
+            let min = this.center.subtract(_tempRadiusVector);
+            let max = this.center.add(_tempRadiusVector);
 
             this.reConstruct(min, max);
 
@@ -57,8 +61,8 @@
         /** @hidden */
         public _update(world: Matrix): void {
             Vector3.TransformCoordinatesToRef(this.center, world, this.centerWorld);
-            Vector3.TransformNormalFromFloatsToRef(1.0, 1.0, 1.0, world, this._tempRadiusVector);
-            this.radiusWorld = Math.max(Math.abs(this._tempRadiusVector.x), Math.abs(this._tempRadiusVector.y), Math.abs(this._tempRadiusVector.z)) * this.radius;
+            Vector3.TransformNormalFromFloatsToRef(1.0, 1.0, 1.0, world, _tempRadiusVector);
+            this.radiusWorld = Math.max(Math.abs(_tempRadiusVector.x), Math.abs(_tempRadiusVector.y), Math.abs(_tempRadiusVector.z)) * this.radius;
         }
 
         public isInFrustum(frustumPlanes: Plane[]): boolean {

+ 1 - 1
src/Engine/babylon.engine.ts

@@ -475,7 +475,7 @@
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "3.3.0-beta.3";
+            return "3.3.0-beta.4";
         }
 
         // Updatable statics so stick with vars here

+ 35 - 29
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -63,32 +63,38 @@ module BABYLON {
         private _anchorMesh: AbstractMesh;
         private _existingMeshScale = new Vector3();
 
-        private _oldPivotPoint = new Vector3();
-        private _pivotTranslation = new Vector3();
-        private removeAndStorePivotPoint(){
-            if(this.attachedMesh){
+        // Stores the state of the pivot cache (_oldPivotPoint, _pivotTranslation)
+        // store/remove pivot point should only be applied during their outermost calls
+        private static _PivotCached = 0;
+        private static _OldPivotPoint = new Vector3();
+        private static _PivotTranslation = new Vector3();
+        private static _PivotTmpVector = new Vector3();
+        /** @hidden */
+        public static _RemoveAndStorePivotPoint(mesh:AbstractMesh){
+            if(mesh && BoundingBoxGizmo._PivotCached === 0){
                 // Save old pivot and set pivot to 0,0,0
-                this.attachedMesh.getPivotPointToRef(this._oldPivotPoint);
-                if(this._oldPivotPoint.equalsToFloats(0,0,0)){
-                    return;
+                mesh.getPivotPointToRef(BoundingBoxGizmo._OldPivotPoint);
+                if(!BoundingBoxGizmo._OldPivotPoint.equalsToFloats(0,0,0)){
+                    mesh.setPivotMatrix(Matrix.IdentityReadOnly);
+                    BoundingBoxGizmo._OldPivotPoint.subtractToRef(mesh.getPivotPoint(), BoundingBoxGizmo._PivotTranslation);
+                    BoundingBoxGizmo._PivotTmpVector.copyFromFloats(1,1,1);
+                    BoundingBoxGizmo._PivotTmpVector.subtractInPlace(mesh.scaling);
+                    BoundingBoxGizmo._PivotTmpVector.multiplyInPlace(BoundingBoxGizmo._PivotTranslation);
+                    mesh.position.addInPlace(BoundingBoxGizmo._PivotTmpVector);
                 }
-                this.attachedMesh.setPivotMatrix(Matrix.IdentityReadOnly);
-                this._oldPivotPoint.subtractToRef(this.attachedMesh.getPivotPoint(), this._pivotTranslation);
-                this._tmpVector.copyFromFloats(1,1,1);
-                this._tmpVector.subtractInPlace(this.attachedMesh.scaling);
-                this._tmpVector.multiplyInPlace(this._pivotTranslation);
-                this.attachedMesh.position.addInPlace(this._tmpVector);
             }
+            BoundingBoxGizmo._PivotCached++;
         }
-        private restorePivotPoint(){
-            if(this.attachedMesh && !this._oldPivotPoint.equalsToFloats(0,0,0)){
-                this.attachedMesh.setPivotPoint(this._oldPivotPoint);    
-                this._oldPivotPoint.subtractToRef(this.attachedMesh.getPivotPoint(), this._pivotTranslation);
-                this._tmpVector.copyFromFloats(1,1,1);
-                this._tmpVector.subtractInPlace(this.attachedMesh.scaling);
-                this._tmpVector.multiplyInPlace(this._pivotTranslation);
-                this.attachedMesh.position.subtractInPlace(this._tmpVector);
+        /** @hidden */
+        public static _RestorePivotPoint(mesh:AbstractMesh){
+            if(mesh && !BoundingBoxGizmo._OldPivotPoint.equalsToFloats(0,0,0) && BoundingBoxGizmo._PivotCached === 1){
+                mesh.setPivotPoint(BoundingBoxGizmo._OldPivotPoint);
+                BoundingBoxGizmo._PivotTmpVector.copyFromFloats(1,1,1);
+                BoundingBoxGizmo._PivotTmpVector.subtractInPlace(mesh.scaling);
+                BoundingBoxGizmo._PivotTmpVector.multiplyInPlace(BoundingBoxGizmo._PivotTranslation);
+                mesh.position.subtractInPlace(BoundingBoxGizmo._PivotTmpVector);
             }
+            this._PivotCached--;
         }
 
         /**
@@ -157,7 +163,7 @@ module BABYLON {
                 _dragBehavior.onDragObservable.add((event) => {
                     this.onRotationSphereDragObservable.notifyObservers({});
                     if (this.attachedMesh) {
-                        this.removeAndStorePivotPoint();
+                        BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
 
                         var worldDragDirection = startingTurnDirection;
 
@@ -197,7 +203,7 @@ module BABYLON {
                         }
                         this.updateBoundingBox();
 
-                        this.restorePivotPoint();
+                        BoundingBoxGizmo._RestorePivotPoint(this.attachedMesh);
                     }
                 });
 
@@ -232,13 +238,11 @@ module BABYLON {
                         _dragBehavior.onDragObservable.add((event) => {
                             this.onScaleBoxDragObservable.notifyObservers({});
                             if(this.attachedMesh){
-                                this.removeAndStorePivotPoint();
-                                
+                                BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
                                 var relativeDragDistance = (event.dragDistance / this._boundingDimensions.length())*this._anchorMesh.scaling.length();
                                 var deltaScale = new Vector3(relativeDragDistance,relativeDragDistance,relativeDragDistance);
                                 deltaScale.scaleInPlace(this._scaleDragSpeed);
                                 this.updateBoundingBox();
-
                                 if(this.scalePivot){
                                     this.attachedMesh.getWorldMatrix().getRotationMatrixToRef(this._tmpRotationMatrix);
                                     // Move anchor to desired pivot point (Bottom left corner + dimension/2)
@@ -261,7 +265,7 @@ module BABYLON {
                                 }
                                 this._anchorMesh.removeChild(this.attachedMesh);
 
-                                this.restorePivotPoint();
+                                BoundingBoxGizmo._RestorePivotPoint(this.attachedMesh);
                             }
                         })
 
@@ -313,8 +317,10 @@ module BABYLON {
             if (value) {
                 // Reset anchor mesh to match attached mesh's scale
                 // This is needed to avoid invalid box/sphere position on first drag
+                BoundingBoxGizmo._RemoveAndStorePivotPoint(value);
                 this._anchorMesh.addChild(value);
                 this._anchorMesh.removeChild(value);
+                BoundingBoxGizmo._RestorePivotPoint(value);
                 this.updateBoundingBox();
             }
         }
@@ -340,7 +346,7 @@ module BABYLON {
          */
         public updateBoundingBox(){
             if(this.attachedMesh){
-                this.removeAndStorePivotPoint();
+                BoundingBoxGizmo._RemoveAndStorePivotPoint(this.attachedMesh);
                 this._update();
                 // Rotate based on axis
                 if (!this.attachedMesh.rotationQuaternion) {
@@ -435,7 +441,7 @@ module BABYLON {
             }
             if (this.attachedMesh) {
                 this._existingMeshScale.copyFrom(this.attachedMesh.scaling);   
-                this.restorePivotPoint();
+                BoundingBoxGizmo._RestorePivotPoint(this.attachedMesh);
             }
         }
 

+ 4 - 0
src/Instrumentation/babylon.sceneInstrumentation.ts

@@ -214,6 +214,10 @@ module BABYLON {
 
             this._captureSpritesRenderTime = value;
 
+            if (!this.scene.spriteManagers) {
+                return;
+            }
+
             if (value) {
                 this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(() => {
                     Tools.StartPerformanceCounter("Sprites");

+ 9 - 6
src/Loading/babylon.sceneLoader.ts

@@ -94,9 +94,10 @@
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded meshes, particle systems, skeletons, and animation groups
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
 
         /**
          * Load into a scene.
@@ -104,9 +105,10 @@
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns Nothing
          */
-        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
 
         /**
          * The callback that returns true if the data can be directly loaded.
@@ -124,9 +126,10 @@
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          */
-        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
     }
 
     interface IRegisteredPlugin {
@@ -473,7 +476,7 @@
                 }
                 else {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                    asyncedPlugin.importMeshAsync(meshNames, scene, data, rootUrl, progressHandler).then(result => {
+                    asyncedPlugin.importMeshAsync(meshNames, scene, data, rootUrl, progressHandler, rootUrl + sceneFilename).then(result => {
                         scene.loadingPluginName = plugin.name;
                         successHandler(result.meshes, result.particleSystems, result.skeletons, result.animationGroups);
                     }).catch(error => {
@@ -631,7 +634,7 @@
                     successHandler();
                 } else {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                    asyncedPlugin.loadAsync(scene, data, rootUrl, progressHandler).then(() => {
+                    asyncedPlugin.loadAsync(scene, data, rootUrl, progressHandler, rootUrl + sceneFilename).then(() => {
                         scene.loadingPluginName = plugin.name;
                         successHandler();
                     }).catch(error => {
@@ -755,7 +758,7 @@
                     successHandler(assetContainer);
                 } else if ((<any>plugin).loadAssetContainerAsync) {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                    asyncedPlugin.loadAssetContainerAsync(scene, data, rootUrl, progressHandler).then(assetContainer => {
+                    asyncedPlugin.loadAssetContainerAsync(scene, data, rootUrl, progressHandler, rootUrl + sceneFilename).then(assetContainer => {
                         scene.loadingPluginName = plugin.name;
                         successHandler(assetContainer);
                     }).catch(error => {

+ 5 - 2
src/Materials/Background/babylon.backgroundMaterial.ts

@@ -99,6 +99,7 @@
         public REFLECTIONMAP_CUBIC = false;
         public REFLECTIONMAP_PROJECTION = false;
         public REFLECTIONMAP_SKYBOX = false;
+        public REFLECTIONMAP_SKYBOX_TRANSFORMED = false;
         public REFLECTIONMAP_EXPLICIT = false;
         public REFLECTIONMAP_EQUIRECTANGULAR = false;
         public REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false;
@@ -687,6 +688,7 @@
                                 break;
                             case Texture.SKYBOX_MODE:
                                 defines.REFLECTIONMAP_SKYBOX = true;
+                                defines.REFLECTIONMAP_SKYBOX_TRANSFORMED = !reflectionTexture.getReflectionTextureMatrix().isIdentity();
                                 break;
                             case Texture.SPHERICAL_MODE:
                                 defines.REFLECTIONMAP_SPHERICAL = true;
@@ -703,8 +705,8 @@
                             case Texture.CUBIC_MODE:
                             case Texture.INVCUBIC_MODE:
                             default:
-                                    defines.REFLECTIONMAP_CUBIC = true;
-                                    break;                        }
+                                defines.REFLECTIONMAP_CUBIC = true;
+                                break;                        }
 
                         if (this.reflectionFresnel) {
                             defines.REFLECTIONFRESNEL = true;
@@ -730,6 +732,7 @@
                         defines.REFLECTIONMAP_CUBIC = false;
                         defines.REFLECTIONMAP_PROJECTION = false;
                         defines.REFLECTIONMAP_SKYBOX = false;
+                        defines.REFLECTIONMAP_SKYBOX_TRANSFORMED = false;
                         defines.REFLECTIONMAP_EXPLICIT = false;
                         defines.REFLECTIONMAP_EQUIRECTANGULAR = false;
                         defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false;

+ 5 - 0
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -76,6 +76,7 @@
         public USE_LOCAL_REFLECTIONMAP_CUBIC = false;
         public REFLECTIONMAP_PROJECTION = false;
         public REFLECTIONMAP_SKYBOX = false;
+        public REFLECTIONMAP_SKYBOX_TRANSFORMED = false;
         public REFLECTIONMAP_EXPLICIT = false;
         public REFLECTIONMAP_EQUIRECTANGULAR = false;
         public REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false;
@@ -1134,6 +1135,9 @@
                                 }
                             }
                         }
+                        else {
+                            defines.REFLECTIONMAP_SKYBOX_TRANSFORMED = !reflectionTexture.getReflectionTextureMatrix().isIdentity();
+                        }
                     } else {
                         defines.REFLECTION = false;
                         defines.REFLECTIONMAP_3D = false;
@@ -1143,6 +1147,7 @@
                         defines.USE_LOCAL_REFLECTIONMAP_CUBIC = false;
                         defines.REFLECTIONMAP_PROJECTION = false;
                         defines.REFLECTIONMAP_SKYBOX = false;
+                        defines.REFLECTIONMAP_SKYBOX_TRANSFORMED = false;
                         defines.REFLECTIONMAP_EXPLICIT = false;
                         defines.REFLECTIONMAP_EQUIRECTANGULAR = false;
                         defines.REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false;

+ 4 - 1
src/Materials/Textures/Procedurals/babylon.noiseProceduralTexture.ts

@@ -40,11 +40,14 @@ module BABYLON {
             this._time += scene.getAnimationRatio() * this.animationSpeedFactor * 0.01;
 
             this.setFloat("brightness", this.brightness);
-            this.setInt("octaves", this.octaves);
             this.setFloat("persistence", this.persistence);
             this.setFloat("timeScale", this._time);
         }
 
+        protected _getDefines(): string {
+            return "#define OCTAVES " + this.octaves;
+        }
+
         /** Generate the current state of the procedural texture */
         public render(useCameraPostProcess?: boolean) {
             this._updateShaderUniforms();

+ 14 - 1
src/Materials/Textures/Procedurals/babylon.proceduralTexture.ts

@@ -38,6 +38,8 @@
         private _fallbackTextureUsed = false;
         private _engine: Engine;
 
+        private _cachedDefines = "";
+
         constructor(name: string, size: any, fragment: any, scene: Nullable<Scene>, fallbackTexture: Nullable<Texture> = null, generateMipMaps = true, public isCube = false) {
             super(null, scene, !generateMipMaps);
 
@@ -116,6 +118,10 @@
             engine._releaseEffect(this._effect);
         }
 
+        protected _getDefines(): string {
+            return "";
+        }
+
 
         public isReady(): boolean {
             var engine = this._engine;
@@ -129,6 +135,11 @@
                 return true;
             }
 
+            let defines = this._getDefines();
+            if (this._effect && defines === this._cachedDefines && this._effect.isReady()) {
+                return true;
+            }
+
             if (this._fragment.fragmentElement !== undefined) {
                 shaders = { vertex: "procedural", fragmentElement: this._fragment.fragmentElement };
             }
@@ -136,11 +147,13 @@
                 shaders = { vertex: "procedural", fragment: this._fragment };
             }
 
+            this._cachedDefines = defines;
+
             this._effect = engine.createEffect(shaders,
                 [VertexBuffer.PositionKind],
                 this._uniforms,
                 this._samplers,
-                "", undefined, undefined, () => {
+                defines, undefined, undefined, () => {
                     this.releaseInternalTexture();
 
                     if (this._fallbackTexture) {

+ 2 - 0
src/Materials/babylon.standardMaterial.ts

@@ -60,6 +60,7 @@ module BABYLON {
         public USE_LOCAL_REFLECTIONMAP_CUBIC = false;
         public REFLECTIONMAP_PROJECTION = false;
         public REFLECTIONMAP_SKYBOX = false;
+        public REFLECTIONMAP_SKYBOX_TRANSFORMED = false;
         public REFLECTIONMAP_EXPLICIT = false;
         public REFLECTIONMAP_EQUIRECTANGULAR = false;
         public REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false;
@@ -631,6 +632,7 @@ module BABYLON {
                                     break;
                                 case Texture.SKYBOX_MODE:
                                     defines.setReflectionMode("REFLECTIONMAP_SKYBOX");
+                                    defines.REFLECTIONMAP_SKYBOX_TRANSFORMED = !this._reflectionTexture.getReflectionTextureMatrix().isIdentity();
                                     break;
                                 case Texture.SPHERICAL_MODE:
                                     defines.setReflectionMode("REFLECTIONMAP_SPHERICAL");

+ 0 - 10
src/Mesh/babylon.abstractMesh.ts

@@ -341,21 +341,11 @@
             this._markSubMeshesAsLightDirty();
         }
 
-        /**
-         * Gets or sets a boolean indicating if the outline must be rendered as well
-         * @see https://www.babylonjs-playground.com/#10WJ5S#3
-         */
-        public renderOutline = false;
         /** Defines color to use when rendering outline */
         public outlineColor = Color3.Red();
         /** Define width to use when rendering outline */
         public outlineWidth = 0.02;
 
-        /**
-         * Gets or sets a boolean indicating if the overlay must be rendered as well
-         * @see https://www.babylonjs-playground.com/#10WJ5S#2
-         */        
-        public renderOverlay = false;
         /** Defines color to use when rendering overlay */
         public overlayColor = Color3.Red();
         /** Defines alpha to use when rendering overlay */

+ 21 - 30
src/Mesh/babylon.mesh.ts

@@ -1415,12 +1415,8 @@
                 engine.setAlphaMode(this._effectiveMaterial.alphaMode);
             }
 
-            // Outline - step 1
-            var savedDepthWrite = engine.getDepthWrite();
-            if (this.renderOutline) {
-                engine.setDepthWrite(false);
-                scene.getOutlineRenderer().render(subMesh, batch);
-                engine.setDepthWrite(savedDepthWrite);
+            for (let step of scene._beforeRenderingMeshStage) {
+                step.action(this, subMesh, batch);
             }
 
             var effect: Nullable<Effect>;
@@ -1475,20 +1471,8 @@
             // Unbind
             this._effectiveMaterial.unbind();
 
-            // Outline - step 2
-            if (this.renderOutline && savedDepthWrite) {
-                engine.setDepthWrite(true);
-                engine.setColorWrite(false);
-                scene.getOutlineRenderer().render(subMesh, batch);
-                engine.setColorWrite(true);
-            }
-
-            // Overlay
-            if (this.renderOverlay) {
-                var currentMode = engine.getAlphaMode();
-                engine.setAlphaMode(Engine.ALPHA_COMBINE);
-                scene.getOutlineRenderer().render(subMesh, batch, true);
-                engine.setAlphaMode(currentMode);
+            for (let step of scene._afterRenderingMeshStage) {
+                step.action(this, subMesh, batch);
             }
 
             if (this._onAfterRenderObservable) {
@@ -1718,7 +1702,7 @@
         /**
          * Modifies the mesh geometry according to the passed transformation matrix.  
          * This method returns nothing but it really modifies the mesh even if it's originally not set as updatable. 
-         * The mesh normals are modified accordingly the same transformation.  
+         * The mesh normals are modified using the same transformation.  
          * tuto : http://doc.babylonjs.com/resources/baking_transformations  
          * Note that, under the hood, this method sets a new VertexBuffer each call.  
          * Returns the Mesh.  
@@ -1744,15 +1728,14 @@
             this.setVerticesData(VertexBuffer.PositionKind, temp, (<VertexBuffer>this.getVertexBuffer(VertexBuffer.PositionKind)).isUpdatable());
 
             // Normals
-            if (!this.isVerticesDataPresent(VertexBuffer.NormalKind)) {
-                return this;
-            }
-            data = <FloatArray>this.getVerticesData(VertexBuffer.NormalKind);
-            temp = [];
-            for (index = 0; index < data.length; index += 3) {
-                Vector3.TransformNormal(Vector3.FromArray(data, index), transform).normalize().toArray(temp, index);
+            if (this.isVerticesDataPresent(VertexBuffer.NormalKind)) {
+                data = <FloatArray>this.getVerticesData(VertexBuffer.NormalKind);
+                temp = [];
+                for (index = 0; index < data.length; index += 3) {
+                    Vector3.TransformNormal(Vector3.FromArray(data, index), transform).normalize().toArray(temp, index);
+                }
+                this.setVerticesData(VertexBuffer.NormalKind, temp, (<VertexBuffer>this.getVertexBuffer(VertexBuffer.NormalKind)).isUpdatable());
             }
-            this.setVerticesData(VertexBuffer.NormalKind, temp, (<VertexBuffer>this.getVertexBuffer(VertexBuffer.NormalKind)).isUpdatable());
 
             // flip faces?
             if (transform.m[0] * transform.m[5] * transform.m[10] < 0) { this.flipFaces(); }
@@ -2355,6 +2338,10 @@
             serializationObject.instances = [];
             for (var index = 0; index < this.instances.length; index++) {
                 var instance = this.instances[index];
+                if (instance.doNotSerialize) {
+                    continue;
+                }
+                
                 var serializationInstance: any = {
                     name: instance.name,
                     id: instance.id,
@@ -2684,7 +2671,11 @@
                     }
 
                     if (Tags) {
-                        Tags.AddTagsTo(instance, parsedInstance.tags);
+                        if (parsedInstance.tags) {
+                            Tags.AddTagsTo(instance, parsedInstance.tags);
+                        } else {
+                            Tags.AddTagsTo(instance, parsedMesh.tags);
+                        }
                     }
 
                     instance.position = Vector3.FromArray(parsedInstance.position);

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

@@ -229,8 +229,8 @@ module BABYLON {
             if (!this._cache.position.equals(this.position))
                 return false;
 
-            if (this.rotationQuaternion) {
-                if (!this._cache.rotationQuaternion.equals(this.rotationQuaternion))
+            if (this._rotationQuaternion) {
+                if (!this._cache.rotationQuaternion.equals(this._rotationQuaternion))
                     return false;
             }
 

+ 138 - 0
src/Particles/EmitterTypes/babylon.cylinderParticleEmitter.ts

@@ -0,0 +1,138 @@
+module BABYLON {
+    /**
+     * Particle emitter emitting particles from the inside of a cylinder.
+     * It emits the particles alongside the cylinder radius. The emission direction might be randomized.
+     */
+    export class CylinderParticleEmitter implements IParticleEmitterType {
+         /**
+         * Creates a new instance CylinderParticleEmitter
+         * @param radius the radius of the emission cylinder (1 by default)
+         * @param height the height of the emission cylinder (1 by default)
+         * @param radiusRange the range of the emission cylinder [0-1] 0 Surface only, 1 Entire Radius (1 by default) 
+         * @param directionRandomizer defines how much to randomize the particle direction [0-1]
+         */
+        constructor(
+            /**
+             * The radius of the emission cylinder.
+             */
+            public radius = 1,
+            /**
+             * The height of the emission cylinder.
+             */
+            public height = 1,
+            /**
+             * The range of emission [0-1] 0 Surface only, 1 Entire Radius.
+             */
+            public radiusRange = 1,
+            /**
+             * How much to randomize the particle direction [0-1].
+             */
+            public directionRandomizer = 0) {
+        }
+
+        /**
+         * Called by the particle System when the direction is computed for the created particle.
+         * @param worldMatrix is the world matrix of the particle system
+         * @param directionToUpdate is the direction vector to update with the result
+         * @param particle is the particle we are computed the direction for
+         */
+        public startDirectionFunction(worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
+            var direction = particle.position.subtract(worldMatrix.getTranslation()).normalize();
+            var randY = Scalar.RandomRange(-this.directionRandomizer/2, this.directionRandomizer/2);
+            
+            var angle = Math.atan2(direction.x, direction.z);
+            angle += Scalar.RandomRange(-Math.PI/2, Math.PI/2)*this.directionRandomizer;
+            
+            direction.y = randY; // set direction y to rand y to mirror normal of cylinder surface
+            direction.x = Math.sin(angle);
+            direction.z = Math.cos(angle);
+            direction.normalize();
+
+            Vector3.TransformNormalFromFloatsToRef(direction.x, direction.y, direction.z, worldMatrix, directionToUpdate);
+        }
+
+        /**
+         * Called by the particle System when the position is computed for the created particle.
+         * @param worldMatrix is the world matrix of the particle system
+         * @param positionToUpdate is the position vector to update with the result
+         * @param particle is the particle we are computed the position for
+         */
+        public startPositionFunction(worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void {
+            var yPos = Scalar.RandomRange(-this.height/2, this.height/2);
+            var angle = Scalar.RandomRange(0, 2 * Math.PI);
+
+            // Pick a properly distributed point within the circle https://programming.guide/random-point-within-circle.html
+            var radiusDistribution = Scalar.RandomRange((1-this.radiusRange)*(1-this.radiusRange), 1);
+            var positionRadius = Math.sqrt(radiusDistribution)*this.radius;
+            var xPos = positionRadius*Math.cos(angle);
+            var zPos = positionRadius*Math.sin(angle);
+
+            Vector3.TransformCoordinatesFromFloatsToRef(xPos, yPos, zPos, worldMatrix, positionToUpdate);
+        }
+
+        /**
+         * Clones the current emitter and returns a copy of it
+         * @returns the new emitter
+         */
+        public clone(): CylinderParticleEmitter {
+            let newOne = new CylinderParticleEmitter(this.radius, this.directionRandomizer);
+
+            Tools.DeepCopy(this, newOne);
+
+            return newOne;
+        }    
+        
+        /**
+         * Called by the {BABYLON.GPUParticleSystem} to setup the update shader
+         * @param effect defines the update shader
+         */        
+        public applyToShader(effect: Effect): void {
+            effect.setFloat("radius", this.radius);
+            effect.setFloat("height", this.height);
+            effect.setFloat("radiusRange", this.radiusRange);
+            effect.setFloat("directionRandomizer", this.directionRandomizer);
+        }    
+        
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns a string containng the defines string
+         */
+        public getEffectDefines(): string {
+            return "#define CYLINDEREMITTER"
+        }   
+        
+        /**
+         * Returns the string "CylinderParticleEmitter"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "CylinderParticleEmitter";
+        }         
+        
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        public serialize(): any {
+            var serializationObject: any = {};
+            serializationObject.type = this.getClassName();
+            serializationObject.radius = this.radius;
+            serializationObject.height = this.height;
+            serializationObject.radiusRange = this.radiusRange;
+            serializationObject.directionRandomizer = this.directionRandomizer;
+
+            return serializationObject;
+        }    
+        
+        /**
+         * Parse properties from a JSON object
+         * @param serializationObject defines the JSON object
+         */
+        public parse(serializationObject: any): void {
+            this.radius = serializationObject.radius;
+            this.height = serializationObject.height;
+            this.radiusRange = serializationObject.radiusRange;
+            this.directionRandomizer = serializationObject.directionRandomizer;
+        }          
+    }
+}

+ 131 - 7
src/Particles/babylon.IParticleSystem.ts

@@ -107,7 +107,7 @@ module BABYLON {
         /**
          * The maximum number of particles to emit per frame until we reach the activeParticleCount value
          */
-        emitRate: number; 
+        emitRate: number;
         
         /**
          * You can use gravity if you want to give an orientation to your particles.
@@ -200,17 +200,12 @@ module BABYLON {
         getCapacity(): number;
 
         /**
-         * Gets Wether the system has been started.
+         * Gets if the system has been started. (Note: this will still be true after stop is called)
          * @returns True if it has been started, otherwise false.
          */
         isStarted(): boolean;
 
         /**
-         * Gets if the particle system has been started.
-         * @return true if the system has been started, otherwise false.
-         */
-        isStarted(): boolean;
-        /**
          * Animates the particle system for this frame.
          */
         animate(): void;
@@ -362,5 +357,134 @@ module BABYLON {
          * @returns the current particle system
          */
         removeLimitVelocityGradient(gradient: number): IParticleSystem;        
+        /**
+         * Adds a new drag gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the drag to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addDragGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific drag gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeDragGradient(gradient: number): IParticleSystem;   
+        /**
+         * Gets the current list of drag gradients.
+         * You must use addDragGradient and removeDragGradient to udpate this list
+         * @returns the list of drag gradients
+         */
+        getDragGradients(): Nullable<Array<FactorGradient>>;                    
+        /**
+         * Adds a new emit rate gradient (please note that this will only work if you set the targetStopDuration property)
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the emit rate to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addEmitRateGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific emit rate gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeEmitRateGradient(gradient: number): IParticleSystem;    
+        /**
+         * Gets the current list of emit rate gradients.
+         * You must use addEmitRateGradient and removeEmitRateGradient to udpate this list
+         * @returns the list of emit rate gradients
+         */
+        getEmitRateGradients(): Nullable<Array<FactorGradient>>;   
+        
+        /**
+         * Adds a new start size gradient (please note that this will only work if you set the targetStopDuration property)
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the start size to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific start size gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeStartSizeGradient(gradient: number): IParticleSystem;    
+        /**
+         * Gets the current list of start size gradients.
+         * You must use addStartSizeGradient and removeStartSizeGradient to udpate this list
+         * @returns the list of start size gradients
+         */
+        getStartSizeGradients(): Nullable<Array<FactorGradient>>;  
+        
+
+        /**
+         * Creates a Point Emitter for the particle system (emits directly from the emitter position)
+         * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
+         * @param direction2 Particles are emitted between the direction1 and direction2 from within the box
+         * @returns the emitter
+         */
+        createPointEmitter(direction1: Vector3, direction2: Vector3): PointParticleEmitter;
+
+        /**
+         * Creates a Hemisphere Emitter for the particle system (emits along the hemisphere radius)
+         * @param radius The radius of the hemisphere to emit from
+         * @param radiusRange The range of the hemisphere to emit from [0-1] 0 Surface Only, 1 Entire Radius
+         * @returns the emitter
+         */
+        createHemisphericEmitter(radius: number, radiusRange: number): HemisphericParticleEmitter;
+
+        /**
+         * Creates a Sphere Emitter for the particle system (emits along the sphere radius)
+         * @param radius The radius of the sphere to emit from
+         * @param radiusRange The range of the sphere to emit from [0-1] 0 Surface Only, 1 Entire Radius
+         * @returns the emitter
+         */
+        createSphereEmitter(radius: number, radiusRange: number): SphereParticleEmitter;
+
+        /**
+         * Creates a Directed Sphere Emitter for the particle system (emits between direction1 and direction2)
+         * @param radius The radius of the sphere to emit from
+         * @param direction1 Particles are emitted between the direction1 and direction2 from within the sphere
+         * @param direction2 Particles are emitted between the direction1 and direction2 from within the sphere
+         * @returns the emitter
+         */
+        createDirectedSphereEmitter(radius: number, direction1:Vector3, direction2: Vector3): SphereDirectedParticleEmitter;
+
+        /**
+         * Creates a Cylinder Emitter for the particle system (emits from the cylinder to the particle position)
+         * @param radius The radius of the emission cylinder
+         * @param height The height of the emission cylinder
+         * @param radiusRange The range of emission [0-1] 0 Surface only, 1 Entire Radius
+         * @param directionRandomizer How much to randomize the particle direction [0-1]
+         * @returns the emitter
+         */
+        createCylinderEmitter(radius: number, height: number, radiusRange: number, directionRandomizer: number): CylinderParticleEmitter;
+
+        /**
+         * Creates a Cone Emitter for the particle system (emits from the cone to the particle position)
+         * @param radius The radius of the cone to emit from
+         * @param angle The base angle of the cone
+         * @returns the emitter
+         */
+        createConeEmitter(radius: number, angle: number): ConeParticleEmitter;
+
+        /**
+         * Creates a Box Emitter for the particle system. (emits between direction1 and direction2 from withing the box defined by minEmitBox and maxEmitBox)
+         * @param direction1 Particles are emitted between the direction1 and direction2 from within the box
+         * @param direction2 Particles are emitted between the direction1 and direction2 from within the box
+         * @param minEmitBox Particles are emitted from the box between minEmitBox and maxEmitBox
+         * @param maxEmitBox  Particles are emitted from the box between minEmitBox and maxEmitBox
+         * @returns the emitter
+         */
+        createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3): BoxParticleEmitter;   
+        
+        /**
+         * Get hosting scene
+         * @returns the scene
+         */
+        getScene(): Scene;
     }  
 }

+ 57 - 2
src/Particles/babylon.baseParticleSystem.ts

@@ -14,11 +14,14 @@ module BABYLON {
          * Blend current color and particle color using particle’s alpha.
          */
         public static BLENDMODE_STANDARD = 1;
-
         /**
          * Add current color and particle color multiplied by particle’s alpha.
          */
         public static BLENDMODE_ADD = 2;
+        /**
+         * Multiply current color with particle color 
+         */
+        public static BLENDMODE_MULTIPLY = 3;        
 
         /**
          * List of animations used by the particle system.
@@ -228,6 +231,14 @@ module BABYLON {
 
             this._reset();
         }
+        
+        /**
+         * Get hosting scene
+         * @returns the scene
+         */
+        public getScene(): Scene {
+            return this._scene;
+        }    
 
         /**
          * You can use gravity if you want to give an orientation to your particles.
@@ -240,10 +251,22 @@ module BABYLON {
         protected _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
         protected _velocityGradients: Nullable<Array<FactorGradient>> = null;
         protected _limitVelocityGradients: Nullable<Array<FactorGradient>> = null;
+        protected _dragGradients: Nullable<Array<FactorGradient>> = null;
+        protected _emitRateGradients: Nullable<Array<FactorGradient>> = null;
+        protected _startSizeGradients: Nullable<Array<FactorGradient>> = null;
+
+        /**
+         * Gets the current list of drag gradients.
+         * You must use addDragGradient and removeDragGradient to udpate this list
+         * @returns the list of drag gradients
+         */
+        public getDragGradients(): Nullable<Array<FactorGradient>> {
+            return this._dragGradients;
+        }   
 
         /** Gets or sets a value indicating the damping to apply if the limit velocity factor is reached */
         public limitVelocityDamping = 0.4;
-
+        
         /**
          * Gets the current list of limit velocity gradients.
          * You must use addLimitVelocityGradient and removeLimitVelocityGradient to udpate this list
@@ -299,6 +322,24 @@ module BABYLON {
         }         
 
         /**
+         * Gets the current list of start size gradients.
+         * You must use addStartSizeGradient and removeStartSizeGradient to udpate this list
+         * @returns the list of start size gradients
+         */
+        public getStartSizeGradients(): Nullable<Array<FactorGradient>> {
+            return this._startSizeGradients;
+        }
+        
+        /**
+         * Gets the current list of emit rate gradients.
+         * You must use addEmitRateGradient and removeEmitRateGradient to udpate this list
+         * @returns the list of emit rate gradients
+         */
+        public getEmitRateGradients(): Nullable<Array<FactorGradient>> {
+            return this._emitRateGradients;
+        }      
+
+        /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
@@ -533,6 +574,20 @@ module BABYLON {
         }
 
         /**
+         * Creates a Cylinder Emitter for the particle system (emits from the cylinder to the particle position)
+         * @param radius The radius of the emission cylinder
+         * @param height The height of the emission cylinder
+         * @param radiusRange The range of emission [0-1] 0 Surface only, 1 Entire Radius
+         * @param directionRandomizer How much to randomize the particle direction [0-1]
+         * @returns the emitter
+         */
+        public createCylinderEmitter(radius = 1, height = 1, radiusRange = 1, directionRandomizer = 0): CylinderParticleEmitter {
+            var particleEmitter = new CylinderParticleEmitter(radius, height, radiusRange, directionRandomizer);
+            this.particleEmitterType = particleEmitter;
+            return particleEmitter;
+        }
+
+        /**
          * Creates a Cone Emitter for the particle system (emits from the cone to the particle position)
          * @param radius The radius of the cone to emit from
          * @param angle The base angle of the cone

+ 94 - 2
src/Particles/babylon.gpuParticleSystem.ts

@@ -108,7 +108,7 @@
         }        
 
         /**
-         * Gets Wether the system has been started.
+         * Gets if the system has been started. (Note: this will still be true after stop is called)
          * @returns True if it has been started, otherwise false.
          */
         public isStarted(): boolean {
@@ -234,6 +234,7 @@
         private _sizeGradientsTexture: RawTexture;             
         private _velocityGradientsTexture: RawTexture;    
         private _limitVelocityGradientsTexture: RawTexture;    
+        private _dragGradientsTexture: RawTexture;  
 
         private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number) {
             let valueGradient = new FactorGradient();
@@ -395,6 +396,85 @@
         }
 
         /**
+         * Adds a new drag gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the drag value to affect to the specified gradient    
+         * @returns the current particle system     
+         */
+        public addDragGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._dragGradients) {
+                this._dragGradients = [];
+            }
+
+            this._addFactorGradient(this._dragGradients, gradient, factor);
+
+            if (this._dragGradientsTexture) {
+                this._dragGradientsTexture.dispose();
+                (<any>this._dragGradientsTexture) = null;
+            }
+
+            this._releaseBuffers();   
+
+            return this;
+        }
+
+        /**
+         * Remove a specific drag gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeDragGradient(gradient: number): GPUParticleSystem {
+            this._removeGradient(gradient, this._dragGradients, this._dragGradientsTexture);
+            (<any>this._dragGradientsTexture) = null;
+
+            return this;
+        }   
+        
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the emit rate value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addEmitRateGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            // Do nothing as emit rate is not supported by GPUParticleSystem
+            return this;
+        }
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeEmitRateGradient(gradient: number): IParticleSystem {
+            // Do nothing as emit rate is not supported by GPUParticleSystem
+            return this;
+        } 
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the start size value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+            return this;
+        }
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeStartSizeGradient(gradient: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+            return this;
+        } 
+
+        /**
          * Instantiates a GPU particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * @param name The name of the particle system
@@ -745,6 +825,10 @@
                 defines = "\n#define CLIPPLANE4";
             }
 
+            if (this.blendMode === ParticleSystem.BLENDMODE_MULTIPLY) {
+                defines = "\n#define BLENDMULTIPLYMODE";
+            }
+
             if (this._isBillboardBased) {
                 defines += "\n#define BILLBOARD";
 
@@ -1028,6 +1112,9 @@
                     case ParticleSystem.BLENDMODE_STANDARD:
                         this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
                         break;
+                    case ParticleSystem.BLENDMODE_MULTIPLY:
+                        this._engine.setAlphaMode(Engine.ALPHA_MULTIPLY);
+                        break; 
                 }      
 
                 if (this.forceDepthWrite) {
@@ -1129,7 +1216,12 @@
             if (this._limitVelocityGradientsTexture) {
                 this._limitVelocityGradientsTexture.dispose();
                 (<any>this._limitVelocityGradientsTexture) = null;
-            }         
+            }                   
+
+            if (this._dragGradientsTexture) {
+                this._dragGradientsTexture.dispose();
+                (<any>this._dragGradientsTexture) = null;
+            }               
          
             if (this._randomTexture) {
                 this._randomTexture.dispose();

+ 14 - 2
src/Particles/babylon.particle.ts

@@ -100,7 +100,14 @@
         /** @hidden */
         public _currentLimitVelocity1 = 0;
         /** @hidden */
-        public _currentLimitVelocity2 = 0;            
+        public _currentLimitVelocity2 = 0;       
+        
+        /** @hidden */
+        public _currentDragGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentDrag1 = 0;
+        /** @hidden */
+        public _currentDrag2 = 0;  
 
         /**
          * Creates a new instance Particle
@@ -182,7 +189,12 @@
                 other._currentLimitVelocityGradient = this._currentLimitVelocityGradient;
                 other._currentLimitVelocity1 = this._currentLimitVelocity1;
                 other._currentLimitVelocity2 = this._currentLimitVelocity2;
-            }                           
+            }     
+            if (this._currentDragGradient) {
+                other._currentDragGradient = this._currentDragGradient;
+                other._currentDrag1 = this._currentDrag1;
+                other._currentDrag2 = this._currentDrag2;
+            }                                   
             if (this.particleSystem.isAnimationSheetEnabled) {
                 other._initialStartSpriteCellID = this._initialStartSpriteCellID;
                 other._initialEndSpriteCellID = this._initialEndSpriteCellID;

+ 9 - 2
src/Particles/babylon.particleHelper.ts

@@ -13,10 +13,17 @@ module BABYLON {
          * @param emitter defines the emitter to use
          * @param capacity defines the system capacity (default is 500 particles)
          * @param scene defines the hosting scene
+         * @param useGPU defines if a GPUParticleSystem must be created (default is false)
          * @returns the new Particle system
          */
-        public static CreateDefault(emitter: Nullable<AbstractMesh | Vector3>, capacity = 500, scene?: Scene): ParticleSystem {
-            var system = new ParticleSystem("default system", capacity, scene!);
+        public static CreateDefault(emitter: Nullable<AbstractMesh | Vector3>, capacity = 500, scene?: Scene, useGPU = false): IParticleSystem {
+            var system: IParticleSystem;
+            
+            if (useGPU) {
+                system= new GPUParticleSystem("default system", {capacity: capacity}, scene!);
+            } else {
+                system= new ParticleSystem("default system", capacity, scene!);
+            }
         
             system.emitter = emitter;
             system.particleTexture = new Texture("https://www.babylonjs.com/assets/Flare.png", system.getScene());

+ 262 - 18
src/Particles/babylon.particleSystem.ts

@@ -43,16 +43,6 @@
             this._onDisposeObserver = this.onDisposeObservable.add(callback);
         }
 
-
-
-        /**
-         * Get hosting scene
-         * @returns the scene
-         */
-        public getScene(): Scene {
-            return this._scene;
-        }    
-
         private _particles = new Array<Particle>();
         private _epsilon: number;
         private _capacity: number;
@@ -80,6 +70,20 @@
         private _scaledUpdateSpeed: number;
         private _vertexBufferSize: number;
 
+        /** @hidden */
+        public _currentEmitRateGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentEmitRate1 = 0;
+        /** @hidden */
+        public _currentEmitRate2 = 0;   
+        
+        /** @hidden */
+        public _currentStartSizeGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentStartSize1 = 0;
+        /** @hidden */
+        public _currentStartSize2 = 0;   
+
         // end of sheet animation
 
         // Sub-emitters
@@ -235,6 +239,21 @@
                             });
                         }   
 
+                        /// Drag
+                        if (this._dragGradients && this._dragGradients.length > 0) {                  
+                            Tools.GetCurrentGradient(ratio, this._dragGradients, (currentGradient, nextGradient, scale) => {
+                                if (currentGradient !== particle._currentDragGradient) {
+                                    particle._currentDrag1 = particle._currentDrag2;
+                                    particle._currentDrag2 = (<FactorGradient>nextGradient).getFactor();    
+                                    particle._currentDragGradient = (<FactorGradient>currentGradient);
+                                }                                
+                                
+                                let drag = Scalar.Lerp(particle._currentDrag1, particle._currentDrag2, scale);
+
+                                this._scaledDirection.scaleInPlace(drag);
+                            });
+                        }                           
+
                         particle.position.addInPlace(this._scaledDirection);
 
                         // Noise
@@ -260,7 +279,7 @@
 
                         // Gravity
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
-                        particle.direction.addInPlace(this._scaledGravity);
+                        particle.direction.addInPlace(this._scaledGravity);                       
 
                         // Size
                         if (this._sizeGradients && this._sizeGradients.length > 0) {                  
@@ -398,8 +417,8 @@
             this._removeFactorGradient(this._angularSpeedGradients, gradient);
 
             return this;
-        }          
-        
+        }     
+                   
         /**
          * Adds a new velocity gradient
          * @param gradient defines the gradient to use (between 0 and 1)
@@ -454,7 +473,111 @@
             this._removeFactorGradient(this._limitVelocityGradients, gradient);
 
             return this;
-        }            
+        }       
+        
+        /**
+         * Adds a new drag gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the drag value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addDragGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            if (!this._dragGradients) {
+                this._dragGradients = [];
+            }
+
+            this._addFactorGradient(this._dragGradients, gradient, factor, factor2);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific drag gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeDragGradient(gradient: number): IParticleSystem {
+            this._removeFactorGradient(this._dragGradients, gradient);
+
+            return this;
+        }         
+        
+        /**
+         * Adds a new emit rate gradient (please note that this will only work if you set the targetStopDuration property)
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the emit rate value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addEmitRateGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            if (!this._emitRateGradients) {
+                this._emitRateGradients = [];
+            }
+
+            this._addFactorGradient(this._emitRateGradients, gradient, factor, factor2);
+
+            if (!this._currentEmitRateGradient) {
+                this._currentEmitRateGradient = this._emitRateGradients[0];
+                this._currentEmitRate1 = this._currentEmitRateGradient.getFactor();
+                this._currentEmitRate2 = this._currentEmitRate1;
+            }
+
+            if (this._emitRateGradients.length === 2) {
+                this._currentEmitRate2 = this._emitRateGradients[1].getFactor();
+            }
+
+            return this;
+        }
+
+        /**
+         * Remove a specific emit rate gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeEmitRateGradient(gradient: number): IParticleSystem {
+            this._removeFactorGradient(this._emitRateGradients, gradient);
+
+            return this;
+        }    
+        
+        /**
+         * Adds a new start size gradient (please note that this will only work if you set the targetStopDuration property)
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the start size value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            if (!this._startSizeGradients) {
+                this._startSizeGradients = [];
+            }
+
+            this._addFactorGradient(this._startSizeGradients, gradient, factor, factor2);
+
+            if (!this._currentStartSizeGradient) {
+                this._currentStartSizeGradient = this._startSizeGradients[0];
+                this._currentStartSize1 = this._currentStartSizeGradient.getFactor();
+                this._currentStartSize2 = this._currentStartSize1;
+            }
+
+            if (this._startSizeGradients.length === 2) {
+                this._currentStartSize2 = this._startSizeGradients[1].getFactor();
+            }
+
+            return this;
+        }
+
+        /**
+         * Remove a specific start size gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeStartSizeGradient(gradient: number): IParticleSystem {
+            this._removeFactorGradient(this._emitRateGradients, gradient);
+
+            return this;
+        } 
 
         /**
          * Adds a new color gradient
@@ -628,7 +751,7 @@
         }
 
         /**
-         * Gets whether the system has been started.
+         * Gets if the system has been started. (Note: this will still be true after stop is called)
          * @returns True if it has been started, otherwise false.
          */
         public isStarted(): boolean {
@@ -883,6 +1006,21 @@
                 }
                 // Size and scale
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
+                
+                // Adjust scale by start size
+                if(this._startSizeGradients && this._startSizeGradients[0]){
+                    const ratio = this._actualFrame / this.targetStopDuration;            
+                    Tools.GetCurrentGradient(ratio, this._startSizeGradients, (currentGradient, nextGradient, scale) => {
+                        if (currentGradient !== this._currentStartSizeGradient) {
+                            this._currentStartSize1 = this._currentStartSize2;
+                            this._currentStartSize2 = (<FactorGradient>nextGradient).getFactor();    
+                            this._currentStartSizeGradient = (<FactorGradient>currentGradient);
+                        }                                
+                        
+                        var value = Scalar.Lerp(this._currentStartSize1, this._currentStartSize2, scale);
+                        particle.scale.scaleInPlace(value);
+                    });
+                }
 
                 // Angle
                 if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
@@ -924,6 +1062,18 @@
                     }
                 }                   
 
+                // Drag
+                if (this._dragGradients && this._dragGradients.length > 0) {
+                    particle._currentDragGradient = this._dragGradients[0];
+                    particle._currentDrag1 = particle._currentDragGradient.getFactor();
+
+                    if (this._dragGradients.length > 1) {
+                        particle._currentDrag2 = this._dragGradients[1].getFactor();
+                    } else {
+                        particle._currentDrag2 = particle._currentDrag1;
+                    }
+                }            
+
                 // Color
                 if (!this._colorGradients || this._colorGradients.length === 0) {
                     var step = Scalar.RandomRange(0, 1.0);
@@ -1004,6 +1154,10 @@
                 defines.push("#define ANIMATESHEET");
             }
 
+            if (this.blendMode === ParticleSystem.BLENDMODE_MULTIPLY) {
+                defines.push("#define BLENDMULTIPLYMODE");
+            }
+
             if (this._isBillboardBased) {
                 defines.push("#define BILLBOARD");
 
@@ -1070,7 +1224,7 @@
 
             this._scaledUpdateSpeed = this.updateSpeed * (preWarmOnly ? this.preWarmStepOffset : this._scene.getAnimationRatio());
 
-            // determine the number of particles we need to create
+            // Determine the number of particles we need to create
             var newParticles;
 
             if (this.manualEmitCount > -1) {
@@ -1078,8 +1232,23 @@
                 this._newPartsExcess = 0;
                 this.manualEmitCount = 0;
             } else {
-                newParticles = ((this.emitRate * this._scaledUpdateSpeed) >> 0);
-                this._newPartsExcess += this.emitRate * this._scaledUpdateSpeed - newParticles;
+                let rate = this.emitRate;
+
+                if (this._emitRateGradients && this._emitRateGradients.length > 0 && this.targetStopDuration) {   
+                    const ratio = this._actualFrame / this.targetStopDuration;               
+                    Tools.GetCurrentGradient(ratio, this._emitRateGradients, (currentGradient, nextGradient, scale) => {
+                        if (currentGradient !== this._currentEmitRateGradient) {
+                            this._currentEmitRate1 = this._currentEmitRate2;
+                            this._currentEmitRate2 = (<FactorGradient>nextGradient).getFactor();    
+                            this._currentEmitRateGradient = (<FactorGradient>currentGradient);
+                        }                                
+                        
+                        rate = Scalar.Lerp(this._currentEmitRate1, this._currentEmitRate2, scale);
+                    });
+                }                   
+
+                newParticles = ((rate * this._scaledUpdateSpeed) >> 0);
+                this._newPartsExcess += rate * this._scaledUpdateSpeed - newParticles;
             }
 
             if (this._newPartsExcess > 1.0) {
@@ -1226,6 +1395,9 @@
                 case ParticleSystem.BLENDMODE_STANDARD:
                     engine.setAlphaMode(Engine.ALPHA_COMBINE);
                     break;
+                case ParticleSystem.BLENDMODE_MULTIPLY:
+                    engine.setAlphaMode(Engine.ALPHA_MULTIPLY);
+                    break;                    
             }
 
             if (this.forceDepthWrite) {
@@ -1475,6 +1647,60 @@
                 }
             }    
 
+            let dragGradients = particleSystem.getDragGradients();
+            if (dragGradients) {
+                serializationObject.dragyGradients = [];
+                for (var dragGradient of dragGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: dragGradient.gradient,
+                        factor1: dragGradient.factor1
+                    };
+
+                    if (dragGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = dragGradient.factor2;
+                    }
+
+                    serializationObject.dragGradients.push(serializedGradient);
+                }
+            }    
+            
+            let emitRateGradients = particleSystem.getEmitRateGradients();
+            if (emitRateGradients) {
+                serializationObject.emitRateGradients = [];
+                for (var emitRateGradient of emitRateGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: emitRateGradient.gradient,
+                        factor1: emitRateGradient.factor1
+                    };
+
+                    if (emitRateGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = emitRateGradient.factor2;
+                    }
+
+                    serializationObject.emitRateGradients.push(serializedGradient);
+                }
+            } 
+            
+            let startSizeGradients = particleSystem.getStartSizeGradients();
+            if (startSizeGradients) {
+                serializationObject.startSizeGradients = [];
+                for (var startSizeGradient of startSizeGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: startSizeGradient.gradient,
+                        factor1: startSizeGradient.factor1
+                    };
+
+                    if (startSizeGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = startSizeGradient.factor2;
+                    }
+
+                    serializationObject.startSizeGradients.push(serializedGradient);
+                }
+            } 
+
             let limitVelocityGradients = particleSystem.getLimitVelocityGradients();
             if (limitVelocityGradients) {
                 serializationObject.limitVelocityGradients = [];
@@ -1604,6 +1830,24 @@
                 }
             }     
 
+            if (parsedParticleSystem.dragGradients) {
+                for (var dragGradient of parsedParticleSystem.dragGradients) {
+                    particleSystem.addDragGradient(dragGradient.gradient, dragGradient.factor1 !== undefined ?  dragGradient.factor1 : dragGradient.factor, dragGradient.factor2);
+                }
+            }        
+            
+            if (parsedParticleSystem.emitRateGradients) {
+                for (var emitRateGradient of parsedParticleSystem.emitRateGradients) {
+                    particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ?  emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
+                }
+            } 
+            
+            if (parsedParticleSystem.startSizeGradients) {
+                for (var startSizeGradient of parsedParticleSystem.startSizeGradients) {
+                    particleSystem.addStartSizeGradient(startSizeGradient.gradient, startSizeGradient.factor1 !== undefined ?  startSizeGradient.factor1 : startSizeGradient.factor, startSizeGradient.factor2);
+                }
+            }
+
             if (parsedParticleSystem.limitVelocityGradients) {
                 for (var limitVelocityGradient of parsedParticleSystem.limitVelocityGradients) {
                     particleSystem.addLimitVelocityGradient(limitVelocityGradient.gradient, limitVelocityGradient.factor1 !== undefined ?  limitVelocityGradient.factor1 : limitVelocityGradient.factor, limitVelocityGradient.factor2);

+ 9 - 3
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -205,14 +205,20 @@
             if (impostorJoint.joint.type !== PhysicsJoint.SpringJoint) {
                 this.world.addConstraint(constraint);
             } else {
-                impostorJoint.mainImpostor.registerAfterPhysicsStep(function () {
+                (<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback = (<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback || function () {
                     constraint.applyForce();
-                });
+                };
+                impostorJoint.mainImpostor.registerAfterPhysicsStep((<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback);
             }
         }
 
         public removeJoint(impostorJoint: PhysicsImpostorJoint) {
-            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            if (impostorJoint.joint.type !== PhysicsJoint.SpringJoint) {
+                this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            } else {
+                impostorJoint.mainImpostor.unregisterAfterPhysicsStep((<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback);
+            }
+
         }
 
         private _addMaterial(name: string, friction: number, restitution: number) {

+ 19 - 17
src/Physics/babylon.physicsJoint.ts

@@ -40,13 +40,13 @@ module BABYLON {
         public set physicsPlugin(physicsPlugin: IPhysicsEnginePlugin) {
             this._physicsPlugin = physicsPlugin;
         }
-        
+
         /**
          * Execute a function that is physics-plugin specific.
          * @param {Function} func the function that will be executed. 
          *                        It accepts two parameters: the physics world and the physics joint.
          */
-        public executeNativeFunction(func : (world: any, physicsJoint:any) => void) {
+        public executeNativeFunction(func: (world: any, physicsJoint: any) => void) {
             func(this._physicsPlugin.world, this._physicsJoint)
         }
 
@@ -87,13 +87,13 @@ module BABYLON {
             this._physicsPlugin.updateDistanceJoint(this, maxDistance, minDistance);
         }
     }
-    
+
     export class MotorEnabledJoint extends PhysicsJoint implements IMotorEnabledJoint {
-        
-        constructor(type: number, jointData:PhysicsJointData) {
+
+        constructor(type: number, jointData: PhysicsJointData) {
             super(type, jointData);
         }
-        
+
         /**
          * Set the motor values.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -103,7 +103,7 @@ module BABYLON {
         public setMotor(force?: number, maxForce?: number) {
             this._physicsPlugin.setMotor(this, force || 0, maxForce);
         }
-        
+
         /**
          * Set the motor's limits.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -117,11 +117,11 @@ module BABYLON {
      * This class represents a single hinge physics joint
      */
     export class HingeJoint extends MotorEnabledJoint {
-        
-        constructor(jointData:PhysicsJointData) {
+
+        constructor(jointData: PhysicsJointData) {
             super(PhysicsJoint.HingeJoint, jointData);
         }
-        
+
         /**
          * Set the motor values.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -131,7 +131,7 @@ module BABYLON {
         public setMotor(force?: number, maxForce?: number) {
             this._physicsPlugin.setMotor(this, force || 0, maxForce);
         }
-        
+
         /**
          * Set the motor's limits.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -140,16 +140,16 @@ module BABYLON {
             this._physicsPlugin.setLimit(this, upperLimit, lowerLimit);
         }
     }
-    
+
     /**
      * This class represents a dual hinge physics joint (same as wheel joint)
      */
     export class Hinge2Joint extends MotorEnabledJoint {
-        
-        constructor(jointData:PhysicsJointData) {
+
+        constructor(jointData: PhysicsJointData) {
             super(PhysicsJoint.Hinge2Joint, jointData);
         }
-        
+
         /**
          * Set the motor values.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -160,7 +160,7 @@ module BABYLON {
         public setMotor(force?: number, maxForce?: number, motorIndex: number = 0) {
             this._physicsPlugin.setMotor(this, force || 0, maxForce, motorIndex);
         }
-        
+
         /**
          * Set the motor limits.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -189,5 +189,7 @@ module BABYLON {
         length: number;
         stiffness: number;
         damping: number;
+        /** this callback will be called when applying the force to the impostors. */
+        forceApplicationCallback: () => void;
     }
-}
+}

+ 2 - 2
src/PostProcess/babylon.postProcess.ts

@@ -489,8 +489,8 @@
             this.onActivateObservable.notifyObservers(camera);
 
             // Clear
-            if (scene._allowPostProcessClear && this.autoClear && this.alphaMode === Engine.ALPHA_DISABLE) {
-                this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, true, true, true);
+            if (this.autoClear && this.alphaMode === Engine.ALPHA_DISABLE) {
+                this._engine.clear(this.clearColor ? this.clearColor : scene.clearColor, scene._allowPostProcessClearColor, true, true);
             }
 
             if (this._reusable) {

+ 166 - 8
src/Rendering/babylon.outlineRenderer.ts

@@ -1,18 +1,141 @@
 module BABYLON {
-    export class OutlineRenderer {
-        private _scene: Scene;
-        private _effect: Effect;
-        private _cachedDefines: string;
+    export interface Scene {
+        /** @hidden */
+        _outlineRenderer: OutlineRenderer;
 
+        /** 
+         * Gets the outline renderer associated with the scene
+         * @returns a OutlineRenderer
+         */
+        getOutlineRenderer(): OutlineRenderer;
+    }
+
+    /** 
+     * Gets the outline renderer associated with the scene
+     * @returns a OutlineRenderer
+     */
+    Scene.prototype.getOutlineRenderer = function(): OutlineRenderer {
+        if (!this._outlineRenderer) {
+            this._outlineRenderer = new OutlineRenderer(this);
+        }
+        return this._outlineRenderer;
+    }
+
+    export interface AbstractMesh {
+        /** @hidden (Backing field) */
+        _renderOutline: boolean;
+        /**
+         * Gets or sets a boolean indicating if the outline must be rendered as well
+         * @see https://www.babylonjs-playground.com/#10WJ5S#3
+         */
+        renderOutline: boolean;
+
+        /** @hidden (Backing field) */
+        _renderOverlay: boolean;
+        /**
+         * Gets or sets a boolean indicating if the overlay must be rendered as well
+         * @see https://www.babylonjs-playground.com/#10WJ5S#2
+         */
+        renderOverlay: boolean;
+    }
+
+    Object.defineProperty(AbstractMesh.prototype, "renderOutline", {
+        get: function (this:AbstractMesh) {
+            return this._renderOutline;
+        },
+        set: function (this:AbstractMesh, value: boolean) {
+            if (value) {
+                // Lazy Load the component.
+                this.getScene().getOutlineRenderer();
+            }
+            this._renderOutline = value;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    Object.defineProperty(AbstractMesh.prototype, "renderOverlay", {
+        get: function (this:AbstractMesh) {
+            return this._renderOverlay;
+        },
+        set: function (this:AbstractMesh, value: boolean) {
+            if (value) {
+                // Lazy Load the component.
+                this.getScene().getOutlineRenderer();
+            }
+            this._renderOverlay = value;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    /**
+     * This class is responsible to draw bothe outline/overlay of meshes.
+     * It should not be used directly but through the available method on mesh.
+     */
+    export class OutlineRenderer implements ISceneComponent {
+        /**
+         * The name of the component. Each component must have a unique name.
+         */
+        public name = SceneComponentConstants.NAME_OUTLINERENDERER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /**
+         * Defines a zOffset to prevent zFighting between the overlay and the mesh.
+         */
         public zOffset = 1;
 
+        private _engine: Engine;
+        private _effect: Effect;
+        private _cachedDefines: string;
+        private _savedDepthWrite: boolean;
+
+        /**
+         * Instantiates a new outline renderer. (There could be only one per scene).
+         * @param scene Defines the scene it belongs to
+         */
         constructor(scene: Scene) {
-            this._scene = scene;
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this.scene._addComponent(this);
+        }
+
+        /**
+         * Register the component to one instance of a scene.
+         */
+        public register(): void {
+            this.scene._beforeRenderingMeshStage.registerStep(SceneComponentConstants.STEP_BEFORERENDERINGMESH_OUTLINE, this, this._beforeRenderingMesh);
+            this.scene._afterRenderingMeshStage.registerStep(SceneComponentConstants.STEP_AFTERRENDERINGMESH_OUTLINE, this, this._afterRenderingMesh);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            // Nothing to do here.
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            // Nothing to do here.
         }
 
+        /**
+         * Renders the outline in the canvas.
+         * @param subMesh Defines the sumesh to render
+         * @param batch Defines the batch of meshes in case of instances
+         * @param useOverlay Defines if the rendering is for the overlay or the outline
+         */
         public render(subMesh: SubMesh, batch: _InstancesBatch, useOverlay: boolean = false): void {
-            var scene = this._scene;
-            var engine = this._scene.getEngine();
+            var scene = this.scene;
+            var engine = scene.getEngine();
 
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
 
@@ -63,6 +186,13 @@
             engine.setZOffset(0);
         }
 
+        /**
+         * Returns whether or not the outline renderer is ready for a given submesh.
+         * All the dependencies e.g. submeshes, texture, effect... mus be ready
+         * @param subMesh Defines the submesh to check readyness for
+         * @param useInstances Defines wheter wee are trying to render instances or not
+         * @returns true if ready otherwise false
+         */
         public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
             var defines = [];
             var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
@@ -117,7 +247,7 @@
             var join = defines.join("\n");
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
-                this._effect = this._scene.getEngine().createEffect("outline",
+                this._effect = this.scene.getEngine().createEffect("outline",
                     attribs,
                     ["world", "mBones", "viewProjection", "diffuseMatrix", "offset", "color", "logarithmicDepthConstant"],
                     ["diffuseSampler"], join);
@@ -125,5 +255,33 @@
 
             return this._effect.isReady();
         }
+
+        private _beforeRenderingMesh(mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch): void {
+            // Outline - step 1
+            this._savedDepthWrite = this._engine.getDepthWrite();
+            if (mesh.renderOutline) {
+                this._engine.setDepthWrite(false);
+                this.render(subMesh, batch);
+                this._engine.setDepthWrite(this._savedDepthWrite);
+            }
+        }
+
+        private _afterRenderingMesh(mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch): void {
+            // Outline - step 2
+            if (mesh.renderOutline && this._savedDepthWrite) {
+                this._engine.setDepthWrite(true);
+                this._engine.setColorWrite(false);
+                this.render(subMesh, batch);
+                this._engine.setColorWrite(true);
+            }
+
+            // Overlay
+            if (mesh.renderOverlay) {
+                var currentMode = this._engine.getAlphaMode();
+                this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
+                this.render(subMesh, batch, true);
+                this._engine.setAlphaMode(currentMode);
+            }
+        }
     }
 } 

+ 2 - 2
src/Rendering/babylon.renderingGroup.ts

@@ -6,7 +6,7 @@
         private _alphaTestSubMeshes = new SmartArray<SubMesh>(256);
         private _depthOnlySubMeshes = new SmartArray<SubMesh>(256);
         private _particleSystems = new SmartArray<IParticleSystem>(256);
-        private _spriteManagers = new SmartArray<SpriteManager>(256);
+        private _spriteManagers = new SmartArray<ISpriteManager>(256);
 
         private _opaqueSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
         private _alphaTestSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
@@ -350,7 +350,7 @@
             }
         }
 
-        public dispatchSprites(spriteManager: SpriteManager) {
+        public dispatchSprites(spriteManager: ISpriteManager) {
             this._spriteManagers.push(spriteManager);
         }
 

+ 2 - 2
src/Rendering/babylon.renderingManager.ts

@@ -76,7 +76,7 @@
             info.camera = this._scene.activeCamera;
 
             // Dispatch sprites
-            if (renderSprites) {
+            if (this._scene.spriteManagers && renderSprites) {
                 for (let index = 0; index < this._scene.spriteManagers.length; index++) {
                     var manager = this._scene.spriteManagers[index];
                     this.dispatchSprites(manager);
@@ -158,7 +158,7 @@
             }
         }
 
-        public dispatchSprites(spriteManager: SpriteManager) {
+        public dispatchSprites(spriteManager: ISpriteManager) {
             var renderingGroupId = spriteManager.renderingGroupId || 0;
 
             this._prepareRenderingGroup(renderingGroupId);

+ 1 - 1
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -72,7 +72,7 @@ module BABYLON {
             public originalScene: Scene){
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
-            this.utilityLayerScene._allowPostProcessClear = false;
+            this.utilityLayerScene._allowPostProcessClearColor = false;
             originalScene.getEngine().scenes.pop();
       
             // Detach controls on utility scene, events will be fired by logic below to handle picking priority

+ 4 - 0
src/Shaders/ShadersInclude/defaultVertexDeclaration.fx

@@ -37,6 +37,10 @@ uniform vec3 vBumpInfos;
 uniform mat4 bumpMatrix;
 #endif
 
+#ifdef REFLECTION
+uniform mat4 reflectionMatrix;
+#endif
+
 #ifdef POINTSIZE
 	uniform float pointSize;
 #endif

+ 5 - 1
src/Shaders/background.vertex.fx

@@ -57,7 +57,11 @@ varying vec3 vDirectionW;
 void main(void) {
 
 #ifdef REFLECTIONMAP_SKYBOX
-    vPositionUVW = position;
+    #ifdef REFLECTIONMAP_SKYBOX_TRANSFORMED
+        vPositionUVW = (reflectionMatrix * vec4(position, 1.0)).xyz;
+    #else
+        vPositionUVW = position;
+    #endif
 #endif 
 
 #include<instancesVertex>

+ 5 - 1
src/Shaders/default.vertex.fx

@@ -109,7 +109,11 @@ void main(void) {
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #ifdef REFLECTIONMAP_SKYBOX
-	vPositionUVW = positionUpdated;
+	#ifdef REFLECTIONMAP_SKYBOX_TRANSFORMED
+		vPositionUVW = (reflectionMatrix * vec4(position, 1.0)).xyz;
+	#else
+		vPositionUVW = position;
+	#endif
 #endif 
 
 #define CUSTOM_VERTEX_UPDATE_POSITION

+ 7 - 1
src/Shaders/gpuRenderParticles.fragment.fx

@@ -17,7 +17,13 @@ out vec4 outFragColor;
 
 void main() {
 	#include<clipPlaneFragment> 
-  	outFragColor = texture(textureSampler, vUV) * vColor;
+	vec4 textureColor = texture(textureSampler, vUV);
+  	outFragColor = textureColor * vColor;
+
+	#ifdef BLENDMULTIPLYMODE
+	float alpha = vColor.a * textureColor.a;
+	outFragColor.rgb = outFragColor.rgb * alpha + vec3(1.0) * (1.0 - alpha);	
+	#endif	  
 
 // Apply image processing if relevant. As this applies in linear space, 
 // We first move from gamma to linear.

+ 35 - 25
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -48,6 +48,13 @@ uniform float radiusRange;
 #endif
 #endif
 
+#ifdef CYLINDEREMITTER
+uniform float radius;
+uniform float height;
+uniform float radiusRange;
+uniform float directionRandomizer;
+#endif
+
 #ifdef CONEEMITTER
 uniform vec2 radius;
 uniform float coneAngle;
@@ -130,26 +137,12 @@ vec4 getRandomVec4(float offset) {
 }
 
 void main() {
-  if (age >= life) {
-    if (stopFactor == 0.) {
-      outPosition = position;
-      outAge = life;
-      outLife = life;
-      outSeed = seed;
-#ifndef COLORGRADIENTS      
-      outColor = vec4(0.,0.,0.,0.);
-#endif
-      outSize = vec3(0., 0., 0.);
-#ifndef BILLBOARD        
-      outInitialDirection = initialDirection;
-#endif      
-      outDirection = direction;
-      outAngle = angle;
-#ifdef ANIMATESHEET      
-      outCellIndex = cellIndex;
-#endif
-      return;
-    }
+  float newAge = age + timeDelta;
+
+    
+
+  // If particle is dead and system is not stopped, spawn as new particle
+  if (newAge >= life && stopFactor != 0.) {
     vec3 position;
     vec3 direction;
 
@@ -157,8 +150,8 @@ void main() {
     vec4 randoms = getRandomVec4(seed.x);
 
     // Age and life
-    outAge = 0.0;
     outLife = lifeTime.x + (lifeTime.y - lifeTime.x) * randoms.r;
+    outAge = mod(newAge, outLife);
 
     // Seed
     outSeed = seed;
@@ -232,6 +225,23 @@ void main() {
       // Direction
       direction = position + directionRandomizer * randoms3;
     #endif
+#elif defined(CYLINDEREMITTER)
+    vec3 randoms2 = getRandomVec3(seed.y);
+    vec3 randoms3 = getRandomVec3(seed.z);
+
+    // Position on the cylinder
+    float yPos = (randoms2.x - 0.5)*height;
+    float angle = randoms2.y * PI * 2.;
+    float inverseRadiusRangeSquared = ((1.-radiusRange) * (1.-radiusRange));
+    float positionRadius = radius*sqrt(inverseRadiusRangeSquared + (randoms2.z * (1.-inverseRadiusRangeSquared)));
+    float xPos = positionRadius * cos(angle);
+    float zPos = positionRadius * sin(angle);
+    position = vec3(xPos, yPos, zPos);
+
+    // Direction
+    angle = angle + ((randoms3.x-0.5) * PI);
+    direction = vec3(cos(angle), randoms3.y-0.5, sin(angle));
+    direction = normalize(direction);
 #elif defined(CONEEMITTER)
     vec3 randoms2 = getRandomVec3(seed.y);
 
@@ -282,16 +292,16 @@ void main() {
     outCellIndex = cellInfos.x;
 #endif
 
-  } else {   
+  } else {
     float directionScale = timeDelta;
-    float ageGradient = age / life;
+    outAge = newAge;
+    float ageGradient = newAge / life;
 
 #ifdef VELOCITYGRADIENTS
     directionScale *= texture(velocityGradientSampler, vec2(ageGradient, 0)).r;
 #endif
-
     outPosition = position + direction * directionScale;
-    outAge = age + timeDelta;
+    
     outLife = life;
     outSeed = seed;
 #ifndef COLORGRADIENTS    

+ 1 - 2
src/Shaders/noise.fragment.fx

@@ -2,7 +2,6 @@
 
 // Uniforms
 uniform float brightness;
-uniform int octaves;
 uniform float persistence;
 uniform float timeScale;
 
@@ -42,7 +41,7 @@ float perlinNoise2D(float x,float y)
     float sum = 0.0;
     float frequency = 0.0;
     float amplitude = 0.0;
-    for(int i = 0; i < octaves; i++)
+    for(int i = 0; i < OCTAVES; i++)
     {
         frequency = pow(2.0, float(i));
         amplitude = pow(persistence, float(i));

+ 7 - 2
src/Shaders/particles.fragment.fx

@@ -15,8 +15,13 @@ uniform sampler2D diffuseSampler;
 void main(void) {
 	#include<clipPlaneFragment>
 
-	vec4 baseColor = texture2D(diffuseSampler, vUV);
-	baseColor = (baseColor * textureMask + (vec4(1., 1., 1., 1.) - textureMask)) * vColor;
+	vec4 textureColor = texture2D(diffuseSampler, vUV);
+	vec4 baseColor = (textureColor * textureMask + (vec4(1., 1., 1., 1.) - textureMask)) * vColor;
+
+	#ifdef BLENDMULTIPLYMODE
+	float alpha = vColor.a * textureColor.a;
+	baseColor.rgb = baseColor.rgb * alpha + vec3(1.0) * (1.0 - alpha);
+	#endif
 
 // Apply image processing if relevant. As this applies in linear space, 
 // We first move from gamma to linear.

+ 14 - 10
src/Shaders/pbr.vertex.fx

@@ -98,9 +98,9 @@ varying vec3 vDirectionW;
 #include<logDepthDeclaration>
 
 void main(void) {
-	vec3 positionUpdated = position;
+    vec3 positionUpdated = position;
 #ifdef NORMAL
-	vec3 normalUpdated = normal;
+    vec3 normalUpdated = normal;
 #endif
 #ifdef TANGENT
     vec4 tangentUpdated = tangent;
@@ -109,7 +109,11 @@ void main(void) {
 #include<morphTargetsVertex>[0..maxSimultaneousMorphTargets]
 
 #ifdef REFLECTIONMAP_SKYBOX
-    vPositionUVW = positionUpdated;
+    #ifdef REFLECTIONMAP_SKYBOX_TRANSFORMED
+        vPositionUVW = (reflectionMatrix * vec4(positionUpdated, 1.0)).xyz;
+    #else
+        vPositionUVW = positionUpdated;
+    #endif
 #endif 
 
 #include<instancesVertex>
@@ -121,13 +125,13 @@ void main(void) {
     vPositionW = vec3(worldPos);
 
 #ifdef NORMAL
-	mat3 normalWorld = mat3(finalWorld);
+    mat3 normalWorld = mat3(finalWorld);
 
-	#ifdef NONUNIFORMSCALING
-		normalWorld = transposeMat3(inverseMat3(normalWorld));
-	#endif
+    #ifdef NONUNIFORMSCALING
+        normalWorld = transposeMat3(inverseMat3(normalWorld));
+    #endif
 
-	vNormalW = normalize(normalWorld * normalUpdated);
+    vNormalW = normalize(normalWorld * normalUpdated);
 
     #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(USESPHERICALINVERTEX)
         vec3 reflectionVector = vec3(reflectionMatrix * vec4(vNormalW, 0)).xyz;
@@ -151,11 +155,11 @@ void main(void) {
 #endif
 
 #ifdef MAINUV1
-	vMainUV1 = uv;
+    vMainUV1 = uv;
 #endif 
 
 #ifdef MAINUV2
-	vMainUV2 = uv2;
+    vMainUV2 = uv2;
 #endif 
 
 #if defined(ALBEDO) && ALBEDODIRECTUV == 0 

+ 2 - 2
src/Sprites/babylon.sprite.ts

@@ -19,7 +19,7 @@
         private _toIndex = 0;
         private _delay = 0;
         private _direction = 1;
-        private _manager: SpriteManager;
+        private _manager: ISpriteManager;
         private _time = 0;
         private _onAnimationEnd: () => void;
         /**
@@ -36,7 +36,7 @@
             this.height = value;
         }
 
-        constructor(public name: string, manager: SpriteManager) {
+        constructor(public name: string, manager: ISpriteManager) {
             this._manager = manager;
 
             this._manager.sprites.push(this);

+ 46 - 1
src/Sprites/babylon.spriteManager.ts

@@ -1,5 +1,47 @@
 module BABYLON {
-    export class SpriteManager {
+    /**
+     * Defines the minimum interface to fullfil in order to be a sprite manager.
+     */
+    export interface ISpriteManager extends IDisposable {
+        /**
+         * Restricts the camera to viewing objects with the same layerMask.
+         * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
+         */
+        layerMask: number;
+
+        /**
+         * Gets or sets a boolean indicating if the mesh can be picked (by scene.pick for instance or through actions). Default is true
+         */
+        isPickable: boolean;
+
+        /** 
+         * Specifies the rendering group id for this mesh (0 by default) 
+         * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
+         */
+        renderingGroupId: number;
+
+        /**
+         * Defines the list of sprites managed by the manager.
+         */
+        sprites: Array<Sprite>;
+
+        /**
+         * Tests the intersection of a sprite with a specific ray.
+         * @param ray The ray we are sending to test the collision
+         * @param camera The camera space we are sending rays in
+         * @param predicate A predicate allowing excluding sprites from the list of object to test
+         * @param fastCheck Is the hit test done in a OOBB or AOBB fashion the faster, the less precise
+         * @returns picking info or null.
+         */
+        intersects(ray: Ray, camera:Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
+
+        /**
+         * Renders the list of sprites on screen.
+         */
+        render(): void;
+    }
+
+    export class SpriteManager implements ISpriteManager {
         public sprites = new Array<Sprite>();
         public renderingGroupId = 0;
         public layerMask: number = 0x0FFFFFFF;
@@ -43,6 +85,9 @@
         }
 
         constructor(public name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon: number = 0.01, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
+            if (!scene._getComponent(SceneComponentConstants.NAME_SPRITE)) {
+                scene._addComponent(new SpriteSceneComponent(scene));
+            }
             this._capacity = capacity;
             this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
             this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;

+ 297 - 0
src/Sprites/babylon.spriteSceneComponent.ts

@@ -0,0 +1,297 @@
+module BABYLON {
+    export interface Scene {
+        /** @hidden */
+        _pointerOverSprite: Nullable<Sprite>;
+
+        /** @hidden */
+        _pickedDownSprite: Nullable<Sprite>;
+
+        /** @hidden */
+        _tempSpritePickingRay: Nullable<Ray>;
+
+        /**
+         * All of the sprite managers added to this scene
+         * @see http://doc.babylonjs.com/babylon101/sprites
+         */
+        spriteManagers: Array<ISpriteManager>;
+
+        /**
+         * An event triggered when sprites rendering is about to start
+         * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
+         */
+        onBeforeSpritesRenderingObservable: Observable<Scene>;
+
+        /**
+         * An event triggered when sprites rendering is done
+         * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
+         */
+        onAfterSpritesRenderingObservable: Observable<Scene>;
+
+        /** @hidden */
+        _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** Launch a ray to try to pick a sprite in the scene
+         * @param x position on screen
+         * @param y position on screen
+         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
+         * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
+         * @returns a PickingInfo
+         */
+        pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** Use the given ray to pick a sprite in the scene
+         * @param ray The ray (in world space) to use to pick meshes
+         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
+         * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
+         * @returns a PickingInfo
+         */
+        pickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** 
+         * Force the sprite under the pointer
+         * @param sprite defines the sprite to use
+         */
+        setPointerOverSprite(sprite: Nullable<Sprite>): void;
+
+        /** 
+         * Gets the sprite under the pointer
+         * @returns a Sprite or null if no sprite is under the pointer
+         */
+        getPointerOverSprite(): Nullable<Sprite>;
+    }
+
+    Scene.prototype._internalPickSprites = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        if (!PickingInfo) {
+            return null;
+        }
+
+        var pickingInfo = null;
+
+        if (!camera) {
+            if (!this.activeCamera) {
+                return null;
+            }
+            camera = this.activeCamera;
+        }
+
+        if (this.spriteManagers.length > 0) {
+            for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
+                var spriteManager = this.spriteManagers[spriteIndex];
+
+                if (!spriteManager.isPickable) {
+                    continue;
+                }
+
+                var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
+                if (!result || !result.hit)
+                    continue;
+
+                if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance)
+                    continue;
+
+                pickingInfo = result;
+
+                if (fastCheck) {
+                    break;
+                }
+            }
+        }
+
+        return pickingInfo || new PickingInfo();
+    }
+
+    Scene.prototype.pickSprite = function(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        this.createPickingRayInCameraSpaceToRef(x, y, this._tempSpritePickingRay!, camera);
+
+        return this._internalPickSprites(this._tempSpritePickingRay!, predicate, fastCheck, camera);
+    }
+
+    Scene.prototype.pickSpriteWithRay = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        if (!this._tempSpritePickingRay) {
+            return null;
+        }
+
+        if (!camera) {
+            if (!this.activeCamera) {
+                return null;
+            }
+            camera = this.activeCamera;
+        }
+
+        Ray.TransformToRef(ray, camera.getViewMatrix(), this._tempSpritePickingRay);
+
+        return this._internalPickSprites(this._tempSpritePickingRay, predicate, fastCheck, camera);
+    }
+
+    Scene.prototype.setPointerOverSprite = function(sprite: Nullable<Sprite>): void {
+        if (this._pointerOverSprite === sprite) {
+            return;
+        }
+
+        if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
+            this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
+        }
+
+        this._pointerOverSprite = sprite;
+        if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
+            this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
+        }
+    }
+
+    Scene.prototype.getPointerOverSprite = function(): Nullable<Sprite> {
+        return this._pointerOverSprite;
+    }
+
+    /**
+     * Defines the sprite scene component responsible to manage sprites
+     * in a given scene.
+     */
+    export class SpriteSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_SPRITE;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /** @hidden */
+        private _spritePredicate: (sprite: Sprite) => boolean;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this.scene.spriteManagers = new Array<ISpriteManager>();
+            this.scene._tempSpritePickingRay = Ray ? Ray.Zero() : null;
+            this.scene.onBeforeSpritesRenderingObservable = new Observable<Scene>();
+            this.scene.onAfterSpritesRenderingObservable = new Observable<Scene>();
+            this._spritePredicate = (sprite: Sprite): boolean => {
+                return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
+            };
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._pointerMoveStage.registerStep(SceneComponentConstants.STEP_POINTERMOVE_SPRITE, this, this._pointerMove);
+            this.scene._pointerDownStage.registerStep(SceneComponentConstants.STEP_POINTERDOWN_SPRITE, this, this._pointerDown);
+            this.scene._pointerUpStage.registerStep(SceneComponentConstants.STEP_POINTERUP_SPRITE, this, this._pointerUp);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            /** Nothing to do for sprites */
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            this.scene.onBeforeSpritesRenderingObservable.clear();
+            this.scene.onAfterSpritesRenderingObservable.clear();
+
+            let spriteManagers = this.scene.spriteManagers;
+            while (spriteManagers.length) {
+                spriteManagers[0].dispose();
+            }
+        }
+
+        private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+            var result = this.scene.pickSprite(x, y, this._spritePredicate, fastCheck, camera);
+            if (result) {
+                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
+            }
+            return result;
+        }
+
+        private _pointerMove(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, canvas: HTMLCanvasElement): Nullable<PickingInfo> {
+            var scene = this.scene;
+            if (isMeshPicked) {
+                scene.setPointerOverSprite(null);
+            } else {
+                pickResult = this._pickSpriteButKeepRay(pickResult, unTranslatedPointerX, unTranslatedPointerY, false, scene.cameraToUseForPointers || undefined);
+
+                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
+                    scene.setPointerOverSprite(pickResult.pickedSprite);
+                    if (scene._pointerOverSprite && scene._pointerOverSprite.actionManager && scene._pointerOverSprite.actionManager.hoverCursor) {
+                        canvas.style.cursor = scene._pointerOverSprite.actionManager.hoverCursor;
+                    } else {
+                        canvas.style.cursor = scene.hoverCursor;
+                    }
+                } else {
+                    scene.setPointerOverSprite(null);
+                    // Restore pointer
+                    canvas.style.cursor = scene.defaultCursor;
+                }
+            }
+
+            return pickResult;
+        }
+
+        private _pointerDown(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): Nullable<PickingInfo> {
+            var scene = this.scene;
+            scene._pickedDownSprite = null;
+            if (scene.spriteManagers.length > 0) {
+                pickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
+
+                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
+                    if (pickResult.pickedSprite.actionManager) {
+                        scene._pickedDownSprite = pickResult.pickedSprite;
+                        switch (evt.button) {
+                            case 0:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                            case 1:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                            case 2:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                        }
+                        if (pickResult.pickedSprite.actionManager) {
+                            pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                        }
+                    }
+                }
+            }
+
+            return pickResult;
+        }
+
+        private _pointerUp(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): Nullable<PickingInfo> {
+            var scene = this.scene;
+            if (scene.spriteManagers.length > 0) {
+                let spritePickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
+
+                if (spritePickResult) {
+                    if (spritePickResult.hit && spritePickResult.pickedSprite) {
+                        if (spritePickResult.pickedSprite.actionManager) {
+                            spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
+                            if (spritePickResult.pickedSprite.actionManager) {
+                                if (!this.scene._isPointerSwiping()) {
+                                    spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
+                                }
+                            }
+                        }
+                    }
+                    if (scene._pickedDownSprite && scene._pickedDownSprite.actionManager && scene._pickedDownSprite !== spritePickResult.pickedSprite) {
+                        scene._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(scene._pickedDownSprite, scene, evt));
+                    }
+                }
+            }
+
+            return pickResult;
+        }
+    }
+}

+ 10 - 4
src/Tools/babylon.filesInput.ts

@@ -10,7 +10,7 @@
         private _progressCallback: (progress: SceneLoaderProgressEvent) => void;
         private _additionalRenderLoopLogicCallback: () => void;
         private _textureLoadingCallback: (remaining: number) => void;
-        private _startingProcessingFilesCallback: () => void;
+        private _startingProcessingFilesCallback: (files?: File[]) => void;
         private _onReloadCallback: (sceneFile: File) => void;
         private _errorCallback: (sceneFile: File, scene: Scene, message: string) => void;
         private _elementToMonitor: HTMLElement;
@@ -19,7 +19,7 @@
         private _filesToLoad: File[];
 
         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: (files?: File[]) => void, onReloadCallback: (sceneFile: File) => void, errorCallback: (sceneFile: File, scene: Scene, message: string) => void) {
             this._engine = engine;
             this._currentScene = scene;
 
@@ -136,8 +136,6 @@
         }
 
         public loadFiles(event: any): void {
-            if (this._startingProcessingFilesCallback) this._startingProcessingFilesCallback();
-
             // Handling data transfer via drag'n'drop
             if (event && event.dataTransfer && event.dataTransfer.files) {
                 this._filesToLoad = event.dataTransfer.files;
@@ -148,6 +146,14 @@
                 this._filesToLoad = event.target.files;
             }
 
+            if (!this._filesToLoad || this._filesToLoad.length === 0) {
+                return;
+            }
+
+            if (this._startingProcessingFilesCallback) {
+                this._startingProcessingFilesCallback(this._filesToLoad);
+            }
+
             if (this._filesToLoad && this._filesToLoad.length > 0) {
                 let files = new Array<File>();
                 let folders = [];

+ 8 - 0
src/Tools/babylon.tools.ts

@@ -836,6 +836,9 @@
          * content of this file is added into a new script element, attached to the DOM (body element)
          */
         public static LoadScript(scriptUrl: string, onSuccess: () => void, onError?: (message?: string, exception?: any) => void) {
+            if (!Tools.IsWindowObjectExist()) {
+                return;
+            }
             var head = document.getElementsByTagName('head')[0];
             var script = document.createElement('script');
             script.type = 'text/javascript';
@@ -1748,8 +1751,13 @@
                 if (ratio >= currentGradient.gradient && ratio <= nextGradient.gradient) {
                     let scale =  (ratio - currentGradient.gradient) / (nextGradient.gradient - currentGradient.gradient);
                     updateFunc(currentGradient, nextGradient, scale);
+                    return;
                }
             }
+
+            // Use last index if over
+            const lastIndex = gradients.length - 1;
+            updateFunc(gradients[lastIndex], gradients[lastIndex], 1.0);
         }
     }
 

+ 75 - 231
src/babylon.scene.ts

@@ -207,18 +207,18 @@
          * Gets or sets the active clipplane 2
          */
         public clipPlane2: Nullable<Plane>;
-        
+
 
         /**
          * Gets or sets the active clipplane 3
          */
         public clipPlane3: Nullable<Plane>;
-        
+
 
         /**
          * Gets or sets the active clipplane 4
          */
-        public clipPlane4: Nullable<Plane>;        
+        public clipPlane4: Nullable<Plane>;
 
         /**
          * Gets or sets a boolean indicating if animations are enabled
@@ -278,10 +278,6 @@
          */
         public disableOfflineSupportExceptionRules = new Array<RegExp>();
 
-        // Events
-
-        private _spritePredicate: (sprite: Sprite) => boolean;
-
         /**
         * An event triggered when the scene is disposed.
         */
@@ -416,18 +412,6 @@
         public onAfterParticlesRenderingObservable = new Observable<Scene>();
 
         /**
-        * An event triggered when sprites rendering is about to start
-        * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
-        */
-        public onBeforeSpritesRenderingObservable = new Observable<Scene>();
-
-        /**
-        * An event triggered when sprites rendering is done
-        * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
-        */
-        public onAfterSpritesRenderingObservable = new Observable<Scene>();
-
-        /**
         * An event triggered when SceneLoader.Append or SceneLoader.Load or SceneLoader.ImportMesh were successfully executed
         */
         public onDataLoadedObservable = new Observable<Scene>();
@@ -805,11 +789,6 @@
         * Gets or sets a boolean indicating if sprites are enabled on this scene
         */
         public spritesEnabled = true;
-        /**
-        * All of the sprite managers added to this scene
-        * @see http://doc.babylonjs.com/babylon101/sprites
-        */
-        public spriteManagers = new Array<SpriteManager>();
 
         // Skeletons
         private _skeletonsEnabled = true;
@@ -1021,7 +1000,6 @@
         private _alternateSceneUbo: UniformBuffer;
 
         private _pickWithRayInverseMatrix: Matrix;
-        private _outlineRenderer: OutlineRenderer;
 
         private _viewMatrix: Matrix;
         private _projectionMatrix: Matrix;
@@ -1055,13 +1033,11 @@
         private _selectionOctree: Octree<AbstractMesh>;
 
         private _pointerOverMesh: Nullable<AbstractMesh>;
-        private _pointerOverSprite: Nullable<Sprite>;
 
         private _debugLayer: DebugLayer;
 
         private _pickedDownMesh: Nullable<AbstractMesh>;
         private _pickedUpMesh: Nullable<AbstractMesh>;
-        private _pickedDownSprite: Nullable<Sprite>;
         private _externalData: StringDictionary<Object>;
         private _uid: Nullable<string>;
 
@@ -1169,11 +1145,22 @@
          */
         public _beforeCameraDrawStage = Stage.Create<CameraStageAction>();
         /**
+         * @hidden
          * Defines the actions happening just before a rendering group is drawing.
          */
         public _beforeRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
         /**
          * @hidden
+         * Defines the actions happening just before a mesh is drawing.
+         */
+        public _beforeRenderingMeshStage = Stage.Create<RenderingMeshStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening just after a mesh has been drawn.
+         */
+        public _afterRenderingMeshStage = Stage.Create<RenderingMeshStageAction>();
+        /**
+         * @hidden
          * Defines the actions happening just after a rendering group has been drawn.
          */
         public _afterRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
@@ -1187,6 +1174,21 @@
          * Defines the actions happening when Geometries are rebuilding.
          */
         public _rebuildGeometryStage = Stage.Create<SimpleStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer move event happens.
+         */
+        public _pointerMoveStage = Stage.Create<PointerMoveStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer down event happens.
+         */
+        public _pointerDownStage = Stage.Create<PointerUpDownStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer up event happens.
+         */
+        public _pointerUpStage = Stage.Create<PointerUpDownStageAction>();
 
         /**
          * Creates a new Scene
@@ -1205,10 +1207,6 @@
                 this.postProcessManager = new PostProcessManager(this);
             }
 
-            if (OutlineRenderer) {
-                this._outlineRenderer = new OutlineRenderer(this);
-            }
-
             if (Tools.IsWindowObjectExist()) {
                 this.attachControl();
             }
@@ -1326,14 +1324,6 @@
         }
 
         /** 
-         * Gets the outline renderer associated with the scene
-         * @returns a OutlineRenderer
-         */
-        public getOutlineRenderer(): OutlineRenderer {
-            return this._outlineRenderer;
-        }
-
-        /** 
          * Gets the engine associated with the scene
          * @returns an Engine
          */
@@ -1539,14 +1529,6 @@
         }
 
         // Pointers handling
-        private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            var result = this.pickSprite(x, y, predicate, fastCheck, camera);
-            if (result) {
-                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
-            }
-            return result;
-        }
-
         private _setRayOnPointerInfo(pointerInfo: PointerInfo) {
             if (pointerInfo.pickInfo) {
                 if (!pointerInfo.pickInfo.ray) {
@@ -1579,37 +1561,15 @@
                 return this;
             }
 
-            if (pickResult && pickResult.hit && pickResult.pickedMesh) {
-                this.setPointerOverSprite(null);
-
-                this.setPointerOverMesh(pickResult.pickedMesh);
-
-                if (this._pointerOverMesh && this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                    if (this._pointerOverMesh.actionManager.hoverCursor) {
-                        canvas.style.cursor = this._pointerOverMesh.actionManager.hoverCursor;
-                    } else {
-                        canvas.style.cursor = this.hoverCursor;
-                    }
-                } else {
-                    canvas.style.cursor = this.defaultCursor;
-                }
+            var isMeshPicked = (pickResult && pickResult.hit && pickResult.pickedMesh) ? true : false;
+            if (isMeshPicked) {
+                this.setPointerOverMesh(pickResult!.pickedMesh);
             } else {
                 this.setPointerOverMesh(null);
-                // Sprites
-                pickResult = this._pickSpriteButKeepRay(pickResult, this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
+            }
 
-                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
-                    this.setPointerOverSprite(pickResult.pickedSprite);
-                    if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
-                        canvas.style.cursor = this._pointerOverSprite.actionManager.hoverCursor;
-                    } else {
-                        canvas.style.cursor = this.hoverCursor;
-                    }
-                } else {
-                    this.setPointerOverSprite(null);
-                    // Restore pointer
-                    canvas.style.cursor = this.defaultCursor;
-                }
+            for (let step of this._pointerMoveStage) {
+                pickResult = step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, isMeshPicked, canvas);
             }
 
             if (pickResult) {
@@ -1688,8 +1648,7 @@
                             if (pickResult && pickResult.hit && pickResult.pickedMesh && actionManager) {
                                 if (this._totalPointersPressed !== 0 &&
                                     ((Date.now() - this._startingPointerTime) > Scene.LongPressDelay) &&
-                                    (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold &&
-                                        Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold)) {
+                                    !this._isPointerSwiping()) {
                                     this._startingPointerTime = 0;
                                     actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                 }
@@ -1698,6 +1657,11 @@
                     }
                 }
             }
+            else {
+                for (let step of this._pointerDownStage) {
+                    pickResult = step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, evt);
+                }
+            }
 
             if (pickResult) {
                 let type = PointerEventTypes.POINTERDOWN;
@@ -1762,6 +1726,14 @@
                     }
                 }
             }
+            else {
+                if (!clickInfo.ignore) {
+                    for (let step of this._pointerUpStage) {
+                        pickResult = step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, evt);
+                    }
+                }
+            }
+
             if (this._pickedDownMesh &&
                 this._pickedDownMesh.actionManager &&
                 this._pickedDownMesh.actionManager.hasSpecificTrigger(ActionManager.OnPickOutTrigger) &&
@@ -1810,6 +1782,12 @@
             return this._pointerCaptures[pointerId];
         }
 
+        /** @hidden */
+        public _isPointerSwiping(): boolean {
+            return Math.abs(this._startingPointerPosition.x - this._pointerX) > Scene.DragMovementThreshold ||
+                   Math.abs(this._startingPointerPosition.y - this._pointerY) > Scene.DragMovementThreshold;
+        }
+
         /**
         * Attach events to the canvas (To handle actionManagers triggers and raise onPointerMove, onPointerDown and onPointerUp
         * @param attachUp defines if you want to attach events to pointerup
@@ -1855,8 +1833,7 @@
                 }
                 if (checkPicking) {
                     let btn = evt.button;
-                    clickInfo.hasSwiped = Math.abs(this._startingPointerPosition.x - this._pointerX) > Scene.DragMovementThreshold ||
-                        Math.abs(this._startingPointerPosition.y - this._pointerY) > Scene.DragMovementThreshold;
+                    clickInfo.hasSwiped = this._isPointerSwiping();
 
                     if (!clickInfo.hasSwiped) {
                         let checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
@@ -1903,8 +1880,7 @@
                             ) {
                                 // pointer has not moved for 2 clicks, it's a double click
                                 if (!clickInfo.hasSwiped &&
-                                    Math.abs(this._previousStartingPointerPosition.x - this._startingPointerPosition.x) < Scene.DragMovementThreshold &&
-                                    Math.abs(this._previousStartingPointerPosition.y - this._startingPointerPosition.y) < Scene.DragMovementThreshold) {
+                                    !this._isPointerSwiping()) {
                                     this._previousStartingPointerTime = 0;
                                     this._doubleClickOccured = true;
                                     clickInfo.doubleClick = true;
@@ -1950,10 +1926,6 @@
                 cb(clickInfo, this._currentPickResult);
             };
 
-            this._spritePredicate = (sprite: Sprite): boolean => {
-                return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
-            };
-
             this._onPointerMove = (evt: PointerEvent) => {
 
                 this._updatePointerPosition(evt);
@@ -2014,32 +1986,6 @@
                 var pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY, this.pointerDownPredicate, false, this.cameraToUseForPointers);
 
                 this._processPointerDown(pickResult, evt);
-
-                // Sprites
-                this._pickedDownSprite = null;
-                if (this.spriteManagers.length > 0) {
-                    pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
-
-                    if (pickResult && pickResult.hit && pickResult.pickedSprite) {
-                        if (pickResult.pickedSprite.actionManager) {
-                            this._pickedDownSprite = pickResult.pickedSprite;
-                            switch (evt.button) {
-                                case 0:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                                case 1:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                                case 2:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                            }
-                            if (pickResult.pickedSprite.actionManager) {
-                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                            }
-                        }
-                    }
-                }
             };
 
             this._onPointerUp = (evt: PointerEvent) => {
@@ -2098,29 +2044,6 @@
 
                     this._processPointerUp(pickResult, evt, clickInfo);
 
-                    // Sprites
-                    if (!clickInfo.ignore) {
-                        if (this.spriteManagers.length > 0) {
-                            let spritePickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
-
-                            if (spritePickResult) {
-                                if (spritePickResult.hit && spritePickResult.pickedSprite) {
-                                    if (spritePickResult.pickedSprite.actionManager) {
-                                        spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, this, evt));
-                                        if (spritePickResult.pickedSprite.actionManager) {
-                                            if (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold) {
-                                                spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, this, evt));
-                                            }
-                                        }
-                                    }
-                                }
-                                if (this._pickedDownSprite && this._pickedDownSprite.actionManager && this._pickedDownSprite !== spritePickResult.pickedSprite) {
-                                    this._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(this._pickedDownSprite, this, evt));
-                                }
-                            }
-                        }
-                    }
-
                     this._previousPickResult = this._currentPickResult;
                 });
             };
@@ -2742,12 +2665,13 @@
             totalWeight: number,
             animations: RuntimeAnimation[],
             originalValue: Quaternion
-        }): Quaternion {
+        }, refQuaternion: Quaternion): Quaternion {
             let originalAnimation = holder.animations[0];
             let originalValue = holder.originalValue;
 
             if (holder.animations.length === 1) {
-                return Quaternion.Slerp(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight));
+                Quaternion.SlerpToRef(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight), refQuaternion);
+                return refQuaternion;
             }
 
             let normalizer = 1.0;
@@ -2764,7 +2688,8 @@
                 weights.push(scale);
             } else {
                 if (holder.animations.length === 2) { // Slerp as soon as we can
-                    return Quaternion.Slerp(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight);
+                    Quaternion.SlerpToRef(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight, refQuaternion);
+                    return refQuaternion;
                 }
                 quaternions = [];
                 weights = [];
@@ -2783,7 +2708,8 @@
             let cumulativeQuaternion: Nullable<Quaternion> = null;
             for (var index = 0; index < quaternions.length;) {
                 if (!cumulativeQuaternion) {
-                    cumulativeQuaternion = Quaternion.Slerp(quaternions[index], quaternions[index + 1], weights[index + 1] / (weights[index] + weights[index + 1]));
+                    Quaternion.SlerpToRef(quaternions[index], quaternions[index + 1], weights[index + 1] / (weights[index] + weights[index + 1]), refQuaternion);
+                    cumulativeQuaternion = refQuaternion;
                     cumulativeAmount = weights[index] + weights[index + 1];
                     index += 2;
                     continue;
@@ -2810,13 +2736,13 @@
 
                     let matrixDecomposeMode = Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
 
-                    let finalValue: any;
+                    let finalValue: any = target[path];
                     if (matrixDecomposeMode) {
                         finalValue = this._processLateAnimationBindingsForMatrices(holder);
                     } else {
                         let quaternionMode = originalValue.w !== undefined;
                         if (quaternionMode) {
-                            finalValue = this._processLateAnimationBindingsForQuaternions(holder);
+                            finalValue = this._processLateAnimationBindingsForQuaternions(holder, finalValue || Quaternion.Identity());
                         } else {
 
                             let startIndex = 0;
@@ -4186,7 +4112,7 @@
                 mesh.computeWorldMatrix();
 
                 // Intersections
-                if (mesh.actionManager && mesh.actionManager.hasSpecificTriggers([ActionManager.OnIntersectionEnterTrigger, ActionManager.OnIntersectionExitTrigger])) {
+                if (mesh.actionManager && mesh.actionManager.hasSpecificTriggers2(ActionManager.OnIntersectionEnterTrigger, ActionManager.OnIntersectionExitTrigger)) {
                     this._meshesForIntersections.pushNoDuplicate(mesh);
                 }
 
@@ -4295,7 +4221,7 @@
             this._setAlternateTransformMatrix(alternateCamera.getViewMatrix(), alternateCamera.getProjectionMatrix());
         }
         /** @hidden */
-        public _allowPostProcessClear = true;
+        public _allowPostProcessClearColor = true;
         private _renderForCamera(camera: Camera, rigParent?: Camera): void {
             if (camera && camera._skipRendering) {
                 return;
@@ -4878,11 +4804,17 @@
             this._cameraDrawRenderTargetStage.clear();
             this._beforeCameraDrawStage.clear();
             this._beforeRenderingGroupDrawStage.clear();
+            this._beforeRenderingMeshStage.clear();
+            this._afterRenderingMeshStage.clear();
             this._afterRenderingGroupDrawStage.clear();
             this._afterCameraDrawStage.clear();
             this._beforeCameraUpdateStage.clear();
             this._gatherRenderTargetsStage.clear();
             this._rebuildGeometryStage.clear();
+            this._pointerMoveStage.clear();
+            this._pointerDownStage.clear();
+            this._pointerUpStage.clear();
+
             for (let component of this._components) {
                 component.dispose();
             }
@@ -4933,8 +4865,6 @@
             this.onAfterActiveMeshesEvaluationObservable.clear();
             this.onBeforeParticlesRenderingObservable.clear();
             this.onAfterParticlesRenderingObservable.clear();
-            this.onBeforeSpritesRenderingObservable.clear();
-            this.onAfterSpritesRenderingObservable.clear();
             this.onBeforeDrawPhaseObservable.clear();
             this.onAfterDrawPhaseObservable.clear();
             this.onBeforePhysicsObservable.clear();
@@ -5006,11 +4936,6 @@
                 this.particleSystems[0].dispose();
             }
 
-            // Release sprites
-            while (this.spriteManagers.length) {
-                this.spriteManagers[0].dispose();
-            }
-
             // Release postProcesses
             while (this.postProcesses.length) {
                 this.postProcesses[0].dispose();
@@ -5326,46 +5251,6 @@
             return pickingInfos;
         }
 
-        private _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            if (!PickingInfo) {
-                return null;
-            }
-
-            var pickingInfo = null;
-
-            if (!camera) {
-                if (!this.activeCamera) {
-                    return null;
-                }
-                camera = this.activeCamera;
-            }
-
-            if (this.spriteManagers.length > 0) {
-                for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
-                    var spriteManager = this.spriteManagers[spriteIndex];
-
-                    if (!spriteManager.isPickable) {
-                        continue;
-                    }
-
-                    var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
-                    if (!result || !result.hit)
-                        continue;
-
-                    if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance)
-                        continue;
-
-                    pickingInfo = result;
-
-                    if (fastCheck) {
-                        break;
-                    }
-                }
-            }
-
-            return pickingInfo || new PickingInfo();
-        }
-
         private _tempPickingRay: Nullable<Ray> = Ray ? Ray.Zero() : null;
 
         /** Launch a ray to try to pick a mesh in the scene
@@ -5390,25 +5275,11 @@
             return result;
         }
 
-        /** Launch a ray to try to pick a sprite in the scene
-         * @param x position on screen
-         * @param y position on screen
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
-         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
-         * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
-         * @returns a PickingInfo
-         */
-        public pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            this.createPickingRayInCameraSpaceToRef(x, y, this._tempPickingRay!, camera);
-
-            return this._internalPickSprites(this._tempPickingRay!, predicate, fastCheck, camera);
-        }
-
         private _cachedRayForTransform: Ray;
 
         /** Use the given ray to pick a mesh in the scene
          * @param ray The ray to use to pick meshes
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param predicate Predicate function used to determine eligible meshes. Can be set to null. In this case, a mesh must have isPickable set to true
          * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null
          * @returns a PickingInfo
          */
@@ -5493,33 +5364,6 @@
             return this._pointerOverMesh;
         }
 
-        /** 
-         * Force the sprite under the pointer
-         * @param sprite defines the sprite to use
-         */
-        public setPointerOverSprite(sprite: Nullable<Sprite>): void {
-            if (this._pointerOverSprite === sprite) {
-                return;
-            }
-
-            if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
-                this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
-            }
-
-            this._pointerOverSprite = sprite;
-            if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
-                this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
-            }
-        }
-
-        /** 
-         * Gets the sprite under the pointer
-         * @returns a Sprite or null if no sprite is under the pointer
-         */
-        public getPointerOverSprite(): Nullable<Sprite> {
-            return this._pointerOverSprite;
-        }
-
         // Physics
 
         /** 

+ 25 - 0
src/babylon.sceneComponent.ts

@@ -14,6 +14,8 @@
         public static readonly NAME_GEOMETRYBUFFERRENDERER = "GeometryBufferRenderer";
         public static readonly NAME_DEPTHRENDERER = "DepthRenderer";
         public static readonly NAME_POSTPROCESSRENDERPIPELINEMANAGER = "PostProcessRenderPipelineManager";
+        public static readonly NAME_SPRITE = "Sprite";
+        public static readonly NAME_OUTLINERENDERER = "Outline";
 
         public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;
 
@@ -28,6 +30,10 @@
         public static readonly STEP_BEFORECAMERADRAW_EFFECTLAYER = 0;
         public static readonly STEP_BEFORECAMERADRAW_LAYER = 1;
 
+        public static readonly STEP_BEFORERENDERINGMESH_OUTLINE = 0;
+
+        public static readonly STEP_AFTERRENDERINGMESH_OUTLINE = 0;
+
         public static readonly STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW = 0;
 
         public static readonly STEP_BEFORECAMERAUPDATE_SIMPLIFICATIONQUEUE = 0;
@@ -44,6 +50,10 @@
         public static readonly STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER = 2;
 
         public static readonly STEP_REBUILDGEOMETRY_POSTPROCESSRENDERPIPELINEMANAGER = 0;
+
+        public static readonly STEP_POINTERMOVE_SPRITE = 0;
+        public static readonly STEP_POINTERDOWN_SPRITE = 0;
+        public static readonly STEP_POINTERUP_SPRITE = 0;
     }
 
     /**
@@ -131,6 +141,11 @@
     export type RenderingGroupStageAction = (renderingGroupId: number) => void;
 
     /** 
+     * Strong typing of a Mesh Render related stage step action 
+     */
+    export type RenderingMeshStageAction = (mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch) => void;
+
+    /** 
      * Strong typing of a simple stage step action 
      */
     export type SimpleStageAction = () => void;
@@ -141,6 +156,16 @@
     export type RenderTargetsStageAction = (renderTargets: SmartArrayNoDuplicate<RenderTargetTexture>) => void;
 
     /** 
+     * Strong typing of a pointer move action.
+     */
+    export type PointerMoveStageAction = (unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, canvas: HTMLCanvasElement) => Nullable<PickingInfo>;
+
+    /** 
+     * Strong typing of a pointer up/down action.
+     */
+    export type PointerUpDownStageAction = (unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent) => Nullable<PickingInfo>;
+
+    /** 
      * Repressentation of a stage in the scene (Basically a list of ordered steps) 
      * @hidden
      */

BIN
tests/validation/ReferenceImages/outline.png


BIN
tests/validation/ReferenceImages/ssao2.png


+ 8 - 3
tests/validation/config.json

@@ -1,16 +1,21 @@
 {
   "root": "https://rawgit.com/BabylonJS/Website/master",
-  "tests": [  
+  "tests": [
+    {
+      "title": "Outline",
+      "playgroundId": "#10WJ5S#6",
+      "referenceImage": "outline.png"
+    },
     {
       "title": "Clip planes",
       "playgroundId": "#Y6W087#0",
       "referenceImage": "clipplanes.png"
-    },         
+    },
     {
       "title": "ShadowOnlyMaterial",
       "playgroundId": "#1KF7V1#18",
       "referenceImage": "shadowOnlyMaterial.png"
-    },     
+    },
     {
       "title": "Gizmos",
       "playgroundId": "#8GY6J8#48",

+ 2 - 1
what's new.md

@@ -121,6 +121,7 @@
 - Added `alphaCutOff` support for StandardMaterial ([deltakosh](https://github.com/deltakosh))
 - New `serialize` and `Parse` functions for SSAO2 Rendering Pipeline ([julien-moreau](https://github.com/julien-moreau))
 - Added `furOcclusion` property to FurMaterial to control the occlusion strength ([julien-moreau](https://github.com/julien-moreau))
+- Optimize ephimeral object creation to help GC ([menduz](https://github.com/menduz))
 
 ## Bug fixes
 
@@ -154,4 +155,4 @@
 - `Bone.setScale` does not support scaleChildren property anymore. You can use `Bone.scale` to achieve the same effect ([deltakosh](https://github.com/deltakosh))
 - Vector3 &amp; Vector4:
   - `MinimizeInPlace` has been renamed to `minimizeInPlace`
-  - `MaximizeInPlace` has been renamed to `maximizeInPlace`
+  - `MaximizeInPlace` has been renamed to `maximizeInPlace`