Pārlūkot izejas kodu

Merge remote-tracking branch 'BabylonJS/master'

MackeyK24 8 gadi atpakaļ
vecāks
revīzija
e7f2c244c6
99 mainītis faili ar 11233 papildinājumiem un 7652 dzēšanām
  1. 1 0
      Playground/debug.html
  2. 1 0
      Playground/frame.html
  3. 1 0
      Playground/index.html
  4. 82 0
      Playground/scripts/pointer events handling.js
  5. 2 1
      Playground/scripts/scripts.txt
  6. 19 1
      Tools/Gulp/config.json
  7. BIN
      assets/sounds/explosion.wav
  8. BIN
      assets/sounds/jump.wav
  9. 44 33
      canvas2D/src/Engine/babylon.canvas2d.ts
  10. 4 4
      canvas2D/src/Engine/babylon.ellipse2d.ts
  11. 5 3
      canvas2D/src/Engine/babylon.fontTexture.ts
  12. 35 15
      canvas2D/src/Engine/babylon.group2d.ts
  13. 46 15
      canvas2D/src/Engine/babylon.prim2dBase.ts
  14. 4 4
      canvas2D/src/Engine/babylon.rectangle2d.ts
  15. 5 1
      canvas2D/src/Engine/babylon.renderablePrim2d.ts
  16. 3 3
      canvas2D/src/Engine/babylon.shape2d.ts
  17. 10 2
      canvas2D/src/Engine/babylon.text2d.ts
  18. 10 8
      canvas2D/src/shaders/sprite2d.vertex.fx
  19. 6 5
      canvas2D/src/shaders/text2d.vertex.fx
  20. 28 27
      dist/preview release/babylon.core.js
  21. 3270 3190
      dist/preview release/babylon.d.ts
  22. 36 36
      dist/preview release/babylon.js
  23. 696 178
      dist/preview release/babylon.max.js
  24. 3270 3190
      dist/preview release/babylon.module.d.ts
  25. 36 36
      dist/preview release/babylon.noworker.js
  26. 6 4
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  27. 148 76
      dist/preview release/canvas2D/babylon.canvas2d.js
  28. 12 12
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  29. 6 6
      dist/preview release/inspector/babylon.inspector.bundle.js
  30. 3 0
      dist/preview release/inspector/babylon.inspector.css
  31. 148 3
      dist/preview release/inspector/babylon.inspector.d.ts
  32. 558 50
      dist/preview release/inspector/babylon.inspector.js
  33. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  34. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js
  35. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  36. 21 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.d.ts
  37. 209 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js
  38. 1 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  39. 0 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.d.ts
  40. 0 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.js
  41. 32 3
      dist/preview release/what's new.md
  42. 4 0
      inspector/sass/_tabPanel.scss
  43. 1 0
      inspector/sass/_tree.scss
  44. 140 107
      inspector/src/Inspector.ts
  45. 25 18
      inspector/src/adapters/Adapter.ts
  46. 55 0
      inspector/src/adapters/CameraAdapter.ts
  47. 54 0
      inspector/src/adapters/SoundAdapter.ts
  48. 37 0
      inspector/src/adapters/TextureAdapter.ts
  49. 197 131
      inspector/src/properties.ts
  50. 22 0
      inspector/src/tabs/CameraTab.ts
  51. 49 85
      inspector/src/tabs/ShaderTab.ts
  52. 26 0
      inspector/src/tabs/SoundTab.ts
  53. 42 31
      inspector/src/tabs/Tab.ts
  54. 45 35
      inspector/src/tabs/TabBar.ts
  55. 143 0
      inspector/src/tabs/TextureTab.ts
  56. 49 42
      inspector/src/tree/TreeItem.ts
  57. 39 0
      inspector/src/treetools/CameraPOV.ts
  58. 42 0
      inspector/src/treetools/SoundInteractions.ts
  59. 240 91
      inspector/test/index.js
  60. 1 1
      loaders/src/glTF/babylon.glTFFileLoader.ts
  61. 1 1
      localDev/index.html
  62. 6 2
      materialsLibrary/index.html
  63. 240 0
      materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts
  64. 50 0
      materialsLibrary/src/shadowOnly/shadowOnly.fragment.fx
  65. 62 0
      materialsLibrary/src/shadowOnly/shadowOnly.vertex.fx
  66. 0 1
      materialsLibrary/src/water/babylon.waterMaterial.ts
  67. 78 13
      src/Actions/babylon.actionManager.ts
  68. 28 8
      src/Cameras/VR/babylon.webVRCamera.ts
  69. 16 10
      src/Cameras/babylon.camera.ts
  70. 8 0
      src/Cameras/babylon.targetCamera.ts
  71. 12 9
      src/Debug/babylon.debugLayer.ts
  72. 8 0
      src/Culling/babylon.rayHelper.ts
  73. 5 1
      src/Loading/babylon.sceneLoader.ts
  74. 2 2
      src/Materials/Textures/babylon.mapTexture.ts
  75. 10 2
      src/Materials/Textures/babylon.videoTexture.ts
  76. 10 0
      src/Materials/babylon.pbrMaterial.ts
  77. 10 0
      src/Materials/babylon.standardMaterial.ts
  78. 37 3
      src/Math/babylon.math.ts
  79. 7 10
      src/Mesh/babylon.abstractMesh.ts
  80. 51 8
      src/Mesh/babylon.mesh.vertexData.ts
  81. 6 0
      src/Mesh/babylon.vertexBuffer.ts
  82. 2 2
      src/PostProcess/babylon.postProcess.ts
  83. 2 0
      src/Rendering/babylon.boundingBoxRenderer.ts
  84. 8 0
      src/Rendering/babylon.renderingGroup.ts
  85. 11 0
      src/Rendering/babylon.renderingManager.ts
  86. 5 1
      src/Shaders/ShadersInclude/bumpFragment.fx
  87. 3 0
      src/Shaders/ShadersInclude/bumpFragmentFunctions.fx
  88. 8 0
      src/Shaders/ShadersInclude/bumpVertex.fx
  89. 5 0
      src/Shaders/ShadersInclude/bumpVertexDeclaration.fx
  90. 6 0
      src/Shaders/default.vertex.fx
  91. 7 0
      src/Shaders/pbr.vertex.fx
  92. 91 19
      src/Tools/babylon.extendedGamepad.ts
  93. 14 0
      src/Tools/babylon.observable.ts
  94. 65 24
      src/Tools/babylon.rectPackingMap.ts
  95. 5 0
      src/Tools/babylon.smartArray.ts
  96. 12 0
      src/Tools/babylon.tools.ts
  97. 16 4
      src/babylon.engine.ts
  98. 350 77
      src/babylon.scene.ts
  99. 1 0
      what's new.md

+ 1 - 0
Playground/debug.html

@@ -32,6 +32,7 @@
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.gradientMaterial.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.furMaterial.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.gridMaterial.js"></script>
+    <script src="https://babylonjs.azurewebsites.net/lib/babylon.shadowOnlyMaterial.js"></script>    
 
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.brickProceduralTexture.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.cloudProceduralTexture.js"></script>

+ 1 - 0
Playground/frame.html

@@ -21,6 +21,7 @@
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.gradientMaterial.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.furMaterial.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.gridMaterial.min.js"></script>
+    <script src="https://babylonjs.azurewebsites.net/lib/babylon.shadowOnlyMaterial.min.js"></script>
 
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.brickProceduralTexture.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.cloudProceduralTexture.min.js"></script>

+ 1 - 0
Playground/index.html

@@ -33,6 +33,7 @@
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.gradientMaterial.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.furMaterial.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.gridMaterial.min.js"></script>
+    <script src="https://babylonjs.azurewebsites.net/lib/babylon.shadowOnlyMaterial.min.js"></script>
 
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.brickProceduralTexture.min.js"></script>
     <script src="https://babylonjs.azurewebsites.net/lib/babylon.cloudProceduralTexture.min.js"></script>

+ 82 - 0
Playground/scripts/pointer events handling.js

@@ -0,0 +1,82 @@
+var createScene = function () {
+
+    var scene = new BABYLON.Scene(engine);
+    var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
+    camera.setTarget(BABYLON.Vector3.Zero());
+    camera.attachControl(canvas, true);
+    var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
+    light.intensity = 0.7;
+    var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
+    sphere.position.y = 1;
+    var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
+
+    scene.exclusiveDoubleMode = false;
+
+    scene.onPrePointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PrePointerObservable: pointer down', 'background: red; color: white');
+        //pointerInfo.skipOnPointerObservable = true;
+    }, BABYLON.PointerEventTypes.POINTERDOWN, false);
+    scene.onPrePointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PrePointerObservable: pointer up', 'background: red; color: white');
+        // pointerInfo.skipOnPointerObservable = true;
+    }, BABYLON.PointerEventTypes.POINTERUP, false);
+    scene.onPrePointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PrePointerObservable: pointer pick: ' + pointerInfo.pickInfo.pickedMesh.name, 'background: red; color: white');
+    }, BABYLON.PointerEventTypes.POINTERPICK, false);
+    scene.onPrePointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PrePointerObservable: pointer tap', 'background: red; color: white');
+    }, BABYLON.PointerEventTypes.POINTERTAP, false);
+    scene.onPrePointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PrePointerObservable: pointer double tap', 'background: red; color: white');
+    }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
+    scene.onPointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PointerObservable: pointer down', 'background: blue; color: white');
+    }, BABYLON.PointerEventTypes.POINTERDOWN, false);
+    scene.onPointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PointerObservable: pointer up', 'background: blue; color: white');
+    }, BABYLON.PointerEventTypes.POINTERUP, false);
+    scene.onPointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PointerObservable: pointer pick: ' + pointerInfo.pickInfo.pickedMesh.name, 'background: blue; color: white');
+    }, BABYLON.PointerEventTypes.POINTERPICK, false);
+    scene.onPointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PointerObservable: pointer tap', 'background: blue; color: white');
+    }, BABYLON.PointerEventTypes.POINTERTAP, false);
+    scene.onPointerObservable.add( function(pointerInfo, eventState) {
+        console.log('%c PointerObservable: pointer double tap', 'background: blue; color: white');
+    }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
+
+    var meshes = [sphere, ground];
+    for (var i=0; i< meshes.length; i++) {
+        let mesh = meshes[i];
+        mesh.actionManager = new BABYLON.ActionManager(scene);
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnLongPressTrigger, (function(mesh) {
+            console.log("%c ActionManager: long press : " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnLeftPickTrigger, (function(mesh) {
+            console.log("%c ActionManager: left pick: " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnRightPickTrigger, (function(mesh) {
+            console.log("%c ActionManager: right pick: " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnCenterPickTrigger, (function(mesh) {
+            console.log("%c ActionManager: center pick: " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickTrigger, (function(mesh) {
+            console.log("%c ActionManager: pick : " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickDownTrigger, (function(mesh) {
+            console.log("%c ActionManager: pick down : " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickUpTrigger, (function(mesh) {
+            console.log("%c ActionManager: pick up : " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnDoublePickTrigger, (function(mesh) {
+            console.log("%c ActionManager: double pick : " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+        mesh.actionManager.registerAction(new BABYLON.ExecuteCodeAction(BABYLON.ActionManager.OnPickOutTrigger, (function(mesh) {
+            console.log("%c ActionManager: pick out : " + mesh.name, 'background: green; color: white');
+        }).bind(this, mesh)));
+    }
+
+    return scene;
+};

+ 2 - 1
Playground/scripts/scripts.txt

@@ -26,4 +26,5 @@ Volumetric Light Scattering
 HDR Rendering Pipeline
 Refraction and Reflection
 PBR
-Instanced bones
+Instanced bones
+Pointer events handling

+ 19 - 1
Tools/Gulp/config.json

@@ -35,7 +35,7 @@
             "../../src/Culling/babylon.boundingBox.js",
             "../../src/Culling/babylon.boundingInfo.js",
             "../../src/Culling/babylon.ray.js",
-            "../../src/Culling/babylon.rayHelper.js",
+            "../../src/Debug/babylon.rayHelper.js",
             "../../src/Mesh/babylon.abstractMesh.js",
             "../../src/Lights/babylon.light.js",
             "../../src/Lights/babylon.pointLight.js",
@@ -243,6 +243,16 @@
         "libraries": [
             {
                 "files": [
+                    "../../materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts"
+                ],
+                "shaderFiles": [
+                    "../../materialsLibrary/src/shadowOnly/shadowOnly.vertex.fx",
+                    "../../materialsLibrary/src/shadowOnly/shadowOnly.fragment.fx"
+                ],
+                "output": "babylon.shadowOnlyMaterial.js"
+            },            
+            {
+                "files": [
                     "../../materialsLibrary/src/gradient/babylon.gradientMaterial.ts"
                 ],
                 "shaderFiles": [
@@ -577,6 +587,9 @@
                     "../../inspector/src/properties.ts",
                     "../../inspector/src/gui/BasicElement.ts",
                     "../../inspector/src/adapters/Adapter.ts",
+                    "../../inspector/src/adapters/CameraAdapter.ts",
+                    "../../inspector/src/adapters/SoundAdapter.ts",
+                    "../../inspector/src/adapters/TextureAdapter.ts",
                     "../../inspector/src/adapters/Canvas2DAdapter.ts",
                     "../../inspector/src/adapters/LightAdapter.ts",
                     "../../inspector/src/adapters/MaterialAdapter.ts",
@@ -594,6 +607,9 @@
                     "../../inspector/src/scheduler/Scheduler.ts",
                     "../../inspector/src/tabs/Tab.ts",
                     "../../inspector/src/tabs/PropertyTab.ts",
+                    "../../inspector/src/tabs/CameraTab.ts",
+                    "../../inspector/src/tabs/SoundTab.ts",
+                    "../../inspector/src/tabs/TextureTab.ts",
                     "../../inspector/src/tabs/Canvas2DTab.ts",
                     "../../inspector/src/tabs/LightTab.ts",
                     "../../inspector/src/tabs/MaterialTab.ts",
@@ -614,6 +630,8 @@
                     "../../inspector/src/tree/TreeItem.ts",
                     "../../inspector/src/treetools/AbstractTreeTool.ts",
                     "../../inspector/src/treetools/BoundingBox.ts",
+                    "../../inspector/src/treetools/CameraPOV.ts",
+                    "../../inspector/src/treetools/SoundInteractions.ts",
                     "../../inspector/src/treetools/Checkbox.ts",
                     "../../inspector/src/treetools/DebugArea.ts",
                     "../../inspector/src/treetools/Info.ts",

BIN
assets/sounds/explosion.wav


BIN
assets/sounds/jump.wav


+ 44 - 33
canvas2D/src/Engine/babylon.canvas2d.ts

@@ -156,7 +156,7 @@
             this._engine = engine;
             this._renderingSize = new Size(0, 0);
             this._curHWScale = 0;
-            this._canvasLevelScale = new Vector3(1, 1, 1);
+            this._canvasLevelScale = new Vector2(1, 1);
             this._designSize = settings.designSize || null;
             this._designUseHorizAxis = settings.designUseHorizAxis === true;
             if (!this._trackedGroups) {
@@ -359,9 +359,7 @@
                     if (this.isVisible === false) {
                         return;
                     }
-                    let hs = 1 / this.engine.getHardwareScalingLevel();
-                    let localPos = e.localPosition.multiplyByFloats(hs, hs);
-                    this._handlePointerEventForInteraction(e, localPos, s);
+                    this._handlePointerEventForInteraction(e, e.localPosition, s);
                 });
             }
 
@@ -555,7 +553,7 @@
         }
 
         private _updatePointerInfo(eventData: PointerInfoBase, localPosition: Vector2): boolean {
-            let s = this.scale;
+            let s = this._canvasLevelScale.multiplyByFloats(this.scaleX, this.scaleY);
             let pii = this._primPointerInfo;
             pii.cancelBubble = false;
             if (!pii.canvasPointerPos) {
@@ -570,17 +568,20 @@
 
             if (this._isScreenSpace) {
                 var cameraViewport = camera.viewport;
-                var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+                let renderWidth = engine.getRenderWidth();
+                let renderHeight = engine.getRenderHeight();
+//                console.log(`Render Width: ${renderWidth} Height: ${renderHeight}, localX: ${localPosition.x}, localY: ${localPosition.y}`);
+                var viewport = cameraViewport.toGlobal(renderWidth, renderHeight);
 
                 // Moving coordinates to local viewport world
                 var x = localPosition.x - viewport.x;
                 var y = localPosition.y - viewport.y;
 
-                pii.canvasPointerPos.x = (x - this.actualPosition.x) / s;
-                pii.canvasPointerPos.y = (engine.getRenderHeight() - y - this.actualPosition.y) / s;
+                pii.canvasPointerPos.x = (x - this.actualPosition.x) / s.x;
+                pii.canvasPointerPos.y = (renderHeight - y - this.actualPosition.y) / s.y;
             } else {
-                pii.canvasPointerPos.x = localPosition.x / s;
-                pii.canvasPointerPos.y = localPosition.y / s;
+                pii.canvasPointerPos.x = localPosition.x / s.x;
+                pii.canvasPointerPos.y = localPosition.y / s.x;
             }
             //console.log(`UpdatePointerInfo for ${this.id}, X:${pii.canvasPointerPos.x}, Y:${pii.canvasPointerPos.y}`);
             pii.mouseWheelDelta = 0;
@@ -830,13 +831,13 @@
 
                             if (ii.isPrimIntersected(prim) !== null) {
                                 if (prim.actionManager) {
-                                    if (this._pickStartingTime !== 0 && ((new Date().getTime() - this._pickStartingTime) > ActionManager.LongPressDelay) && (Math.abs(this._pickStartingPosition.x - ii.pickPosition.x) < ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ii.pickPosition.y) < ActionManager.DragMovementThreshold)) {
+                                    if (this._pickStartingTime !== 0 && ((new Date().getTime() - this._pickStartingTime) > Scene.LongPressDelay) && (Math.abs(this._pickStartingPosition.x - ii.pickPosition.x) < Scene.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ii.pickPosition.y) < Scene.DragMovementThreshold)) {
                                         this._pickStartingTime = 0;
                                         prim.actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNewFromPrimitive(prim, ppi.primitivePointerPos, eventData));
                                     }
                                 }
                             }
-                        }, ActionManager.LongPressDelay);
+                        }, Scene.LongPressDelay);
                     }
                 }
             }
@@ -851,7 +852,7 @@
                     prim.actionManager.processTrigger(ActionManager.OnPickUpTrigger, actionEvent);
 
                     // OnPickTrigger
-                    if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < ActionManager.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < ActionManager.DragMovementThreshold) {
+                    if (Math.abs(this._pickStartingPosition.x - ppi.canvasPointerPos.x) < Scene.DragMovementThreshold && Math.abs(this._pickStartingPosition.y - ppi.canvasPointerPos.y) < Scene.DragMovementThreshold) {
                         prim.actionManager.processTrigger(ActionManager.OnPickTrigger, actionEvent);
                     }
                 }
@@ -1320,7 +1321,7 @@
         private _designUseHorizAxis: boolean;
         public  _primitiveCollisionManager: PrimitiveCollisionManagerBase;
 
-        public _canvasLevelScale: Vector3;
+        public _canvasLevelScale: Vector2;
         public  _renderingSize: Size;
         private _curHWScale;
 
@@ -1342,6 +1343,7 @@
         private _profileInfoText: Text2D;
 
         private static _v = Vector3.Zero(); // Must stay zero
+        private static _cv1 = Vector2.Zero(); // Must stay zero
         private static _m = Matrix.Identity();
         private static _mI = Matrix.Identity(); // Must stay identity
         private static tS = Vector3.Zero();
@@ -1383,8 +1385,8 @@
                 group.levelVisible = proj.z >= 0 && proj.z < 1.0;
 
                 let s = this.scale;
-                group.x = Math.round(proj.x/s);
-                group.y = Math.round((rh - proj.y)/s);
+                group.x = Math.round(proj.x / s);
+                group.y = Math.round((rh - proj.y) / s);
             }
 
             // If it's a WorldSpaceCanvas and it's tracking a node, let's update the WSC transformation data
@@ -1481,7 +1483,7 @@
                 this._setRenderingScale(scale);
             }
         }
-        private static _pCLS = Vector3.Zero();
+        private static _pCLS = Vector2.Zero();
 
         private _updateCanvasState(forceRecompute: boolean) {
             // Check if the update has already been made for this render Frame
@@ -1519,10 +1521,10 @@
                     scale = this._renderingSize.height / (this._designSize.height * hwsl);
                 }
                 this.size = this._designSize.clone();
-                this._canvasLevelScale.copyFromFloats(scale, scale, 1);
+                this._canvasLevelScale.copyFromFloats(scale, scale);
             } else {
                 let ratio = 1 / this._curHWScale;
-                this._canvasLevelScale.copyFromFloats(ratio, ratio, 1);
+                this._canvasLevelScale.copyFromFloats(ratio, ratio);
             }
 
             if (!prevCLS.equals(this._canvasLevelScale)) {
@@ -1627,18 +1629,19 @@
             let isCanvas = parent == null;
             let scale: Vector2;
             if (noResizeScale) {
-                scale = isCanvas ? Canvas2D._unS : group.parent.actualScale;
+                scale = isCanvas ? Canvas2D._unS : group.parent.actualScale.multiply(this._canvasLevelScale);
             } else {
-                scale = group.actualScale;
+                scale = group.actualScale.multiply(this._canvasLevelScale);
             }
 
             // Determine size
             let size = group.actualSize;
-            size = new Size(Math.ceil(size.width * scale.x), Math.ceil(size.height * scale.y));
-            let originalSize = size.clone();
+            let scaledSize = new Size(size.width * scale.x, size.height * scale.y);
+            let roundedScaledSize = new Size(Math.ceil(scaledSize.width), Math.ceil(scaledSize.height));
+            let originalSize = scaledSize.clone();
             if (minSize) {
-                size.width = Math.max(minSize.width, size.width);
-                size.height = Math.max(minSize.height, size.height);
+                roundedScaledSize.width = Math.max(minSize.width, roundedScaledSize.width);
+                roundedScaledSize.height = Math.max(minSize.height, roundedScaledSize.height);
             }
 
             let mapArray = this._groupCacheMaps.getOrAddWithFactory(key, () => new Array<MapTexture>());
@@ -1648,7 +1651,7 @@
             var map: MapTexture;
             for (var _map of mapArray) {
                 map = _map;
-                let node = map.allocateRect(size);
+                let node = map.allocateRect(roundedScaledSize);
                 if (node) {
                     res = { node: node, texture: map }
                     break;
@@ -1660,18 +1663,24 @@
                 let mapSize = new Size(Canvas2D._groupTextureCacheSize, Canvas2D._groupTextureCacheSize);
 
                 // Check if the predefined size would fit, other create a custom size using the nearest bigger power of 2
-                if (size.width > mapSize.width || size.height > mapSize.height) {
-                    mapSize.width = Math.pow(2, Math.ceil(Math.log(size.width) / Math.log(2)));
-                    mapSize.height = Math.pow(2, Math.ceil(Math.log(size.height) / Math.log(2)));
+                if (roundedScaledSize.width > mapSize.width || roundedScaledSize.height > mapSize.height) {
+                    mapSize.width = Math.pow(2, Math.ceil(Math.log(roundedScaledSize.width) / Math.log(2)));
+                    mapSize.height = Math.pow(2, Math.ceil(Math.log(roundedScaledSize.height) / Math.log(2)));
                 }
 
                 let id = `groupsMapChache${this._mapCounter++}forCanvas${this.id}`;
-                map = new MapTexture(id, this._scene, mapSize, useMipMap ? Texture.TRILINEAR_SAMPLINGMODE : Texture.BILINEAR_SAMPLINGMODE, useMipMap);
+                map = new MapTexture(id, this._scene, mapSize, useMipMap ? Texture.TRILINEAR_SAMPLINGMODE : Texture.BILINEAR_SAMPLINGMODE, useMipMap, 2);
                 map.hasAlpha = true;
                 map.anisotropicFilteringLevel = 4;
                 mapArray.splice(0, 0, map);
 
-                let node = map.allocateRect(size);
+                //let debug = false;
+
+                //if (debug) {
+                //    let sprite = new Sprite2D(map, { parent: this, x: 10, y: 10, id: "__cachedSpriteOfGroup__Debug", alignToPixel: true });
+                //}
+
+                let node = map.allocateRect(roundedScaledSize);
                 res = { node: node, texture: map }
             }
 
@@ -1679,6 +1688,8 @@
             // Don't do it in case of the group being a worldspace canvas (because its texture is bound to a WorldSpaceCanvas node)
             if (group !== <any>this || this._isScreenSpace) {
                 let node: PackedRect = res.node;
+                let pos = Canvas2D._cv1;
+                node.getInnerPosToRef(pos);
 
                 // Special case if the canvas is entirely cached: create a group that will have a single sprite it will be rendered specifically at the very end of the rendering process
 
@@ -1688,14 +1699,14 @@
                         this._cachedCanvasGroup.dispose();
                     }
                     this._cachedCanvasGroup = Group2D._createCachedCanvasGroup(this);
-                    sprite = new Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: originalSize, spriteLocation: node.pos });
+                    sprite = new Sprite2D(map, { parent: this._cachedCanvasGroup, id: "__cachedCanvasSprite__", spriteSize: originalSize, size: size, alignToPixel: true, spriteLocation: pos });
                     sprite.zOrder = 1;
                     sprite.origin = Vector2.Zero();
                 }
 
                 // Create a Sprite that will be used to render this cache, the "__cachedSpriteOfGroup__" starting id is a hack to bypass exception throwing in case of the Canvas doesn't normally allows direct primitives
                 else {
-                    sprite = new Sprite2D(map, { parent: parent, id: `__cachedSpriteOfGroup__${group.id}`, x: group.actualPosition.x, y: group.actualPosition.y, spriteSize: originalSize, spriteLocation: node.pos, dontInheritParentScale: true });
+                    sprite = new Sprite2D(map, { parent: parent, id: `__cachedSpriteOfGroup__${group.id}`, x: group.x, y: group.y, spriteSize: originalSize, size: size, spriteLocation: pos, alignToPixel: true, dontInheritParentScale: true });
                     sprite.origin = group.origin.clone();
                     sprite.addExternalData("__cachedGroup__", group);
                     sprite.pointerEventObservable.add((e, s) => {

+ 4 - 4
canvas2D/src/Engine/babylon.ellipse2d.ts

@@ -427,18 +427,18 @@
                 return false;
             }
 
-            let s = Ellipse2D._riv0;
-            this.getActualGlobalScaleToRef(s);
+            //let s = Ellipse2D._riv0;
+            //this.getActualGlobalScaleToRef(s);
 
             if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
                 let d = <Ellipse2DInstanceData>part;
                 let size = this.actualSize;
-                d.properties = new Vector3(size.width * s.x, size.height * s.y, this.subdivisions);
+                d.properties = new Vector3(size.width/* * s.x*/, size.height/* * s.y*/, this.subdivisions);
             }
             else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
                 let d = <Ellipse2DInstanceData>part;
                 let size = this.actualSize;
-                d.properties = new Vector3(size.width * s.x, size.height * s.y, this.subdivisions);
+                d.properties = new Vector3(size.width/* * s.x*/, size.height/* * s.y*/, this.subdivisions);
             }
             return true;
         }

+ 5 - 3
canvas2D/src/Engine/babylon.fontTexture.ts

@@ -415,6 +415,7 @@
             this._signedDistanceField = signedDistanceField;
             this._superSample = false;
             this._isPremultipliedAlpha = !signedDistanceField;
+            this.name = `FontTexture ${font}`;
 
             // SDF will use super sample no matter what, the resolution is otherwise too poor to produce correct result
             if (superSample || signedDistanceField) {
@@ -556,7 +557,7 @@
             var textureSize = this.getSize();
 
             // we reached the end of the current line?
-            let width = Math.ceil(measure.width);
+            let width = Math.ceil(measure.width + 0.5);
             if (this._currentFreePosition.x + width + this._xMargin > textureSize.width) {
                 this._currentFreePosition.x = 0;
                 this._currentFreePosition.y += Math.ceil(this._lineHeightSuper + this._yMargin*2);
@@ -626,9 +627,10 @@
             }
 
             // Fill the CharInfo object
-            info.topLeftUV = new Vector2((curPosXMargin) / textureSize.width, (this._currentFreePosition.y + this._yMargin) / textureSize.height);
-            info.bottomRightUV = new Vector2((curPosXMargin + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + this._yMargin) / textureSize.height));
+            info.topLeftUV = new Vector2((curPosXMargin-0.5) / textureSize.width, (this._currentFreePosition.y-0.5 + this._yMargin) / textureSize.height);
+            info.bottomRightUV = new Vector2((curPosXMargin-0.5 + width) / textureSize.width, info.topLeftUV.y + (this._lineHeightSuper / textureSize.height));
             info.yOffset = info.xOffset = 0;
+            //console.log(`Char: ${char}, Offset: ${curPosX}, ${this._currentFreePosition.y + this._yMargin}, Size: ${width}, ${this._lineHeightSuper}, UV: ${info.topLeftUV}, ${info.bottomRightUV}`);
 
             if (this._signedDistanceField) {
                 let off = 1/textureSize.width;

+ 35 - 15
canvas2D/src/Engine/babylon.group2d.ts

@@ -53,7 +53,7 @@
          * - layoutEngine: either an instance of a layout engine based class (StackPanel.Vertical, StackPanel.Horizontal) or a string ('canvas' for Canvas layout, 'StackPanel' or 'HorizontalStackPanel' for horizontal Stack Panel layout, 'VerticalStackPanel' for vertical Stack Panel layout).
          * - isVisible: true if the group must be visible, false for hidden. Default is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
-         * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
+         * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersect, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
          * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
@@ -126,10 +126,12 @@
 
             let size = (!settings.size && !settings.width && !settings.height) ? null : (settings.size || (new Size(settings.width || 0, settings.height || 0)));
 
-            this._trackedNode = (settings.trackNode == null) ? null : settings.trackNode;
-            this._trackedNodeOffset = (settings.trackNodeOffset == null) ? null : settings.trackNodeOffset;
-            if (this._trackedNode && this.owner) {
-                this.owner._registerTrackedNode(this);
+            if (!(this instanceof WorldSpaceCanvas2D)) {
+                this._trackedNode = (settings.trackNode == null) ? null : settings.trackNode;
+                this._trackedNodeOffset = (settings.trackNodeOffset == null) ? null : settings.trackNodeOffset;
+                if (this._trackedNode && this.owner) {
+                    this.owner._registerTrackedNode(this);
+                }
             }
 
             this._cacheBehavior = (settings.cacheBehavior == null) ? Group2D.GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY : settings.cacheBehavior;
@@ -355,9 +357,12 @@
 
             let s = this.actualSize;
             let a = this.actualScale;
+            let ss = this.owner._canvasLevelScale;
             let hwsl = 1/this.owner.engine.getHardwareScalingLevel();
-            let sw = Math.ceil(s.width * a.x * hwsl);
-            let sh = Math.ceil(s.height * a.y * hwsl);
+            //let sw = Math.ceil(s.width * a.x * ss.x/* * hwsl*/);
+            //let sh = Math.ceil(s.height * a.y *ss.y/* *  hwsl*/);
+            let sw = s.width * a.x * ss.x;
+            let sh = s.height * a.y *ss.y;
 
             // The dimension must be overridden when using the designSize feature, the ratio is maintain to compute a uniform scale, which is mandatory but if the designSize's ratio is different from the rendering surface's ratio, content will be clipped in some cases.
             // So we set the width/height to the rendering's one because that's what we want for the viewport!
@@ -801,6 +806,8 @@
 
         private static _uV = new Vector2(1, 1);
         private static _s = Size.Zero();
+        private static _v1 = Vector2.Zero();
+        private static _s2 = Size.Zero();
         private _bindCacheTarget() {
             let curWidth: number;
             let curHeight: number;
@@ -811,11 +818,11 @@
             let isCanvas = this.parent == null;
             let scale: Vector2;
             if (noResizeScale) {
-                scale = isCanvas ? Group2D._uV: this.parent.actualScale;
+                scale = isCanvas ? Group2D._uV: this.parent.actualScale.multiply(this.owner._canvasLevelScale);
             } else {
-                scale = this.actualScale;
+                scale = this.actualScale.multiply(this.owner._canvasLevelScale);
             }
-
+            let actualSize = this.actualSize;
             if (isCanvas && this.owner.cachingStrategy===Canvas2D.CACHESTRATEGY_CANVAS && this.owner.isScreenSpace) {
                 if(this.owner.designSize || this.owner.fitRenderingDevice){
                     Group2D._s.width = this.owner.engine.getRenderWidth();
@@ -825,14 +832,15 @@
                     Group2D._s.copyFrom(this.owner.size);
                 }
             } else {
-                Group2D._s.width = Math.ceil(this.actualSize.width * scale.x * rs);
-                Group2D._s.height = Math.ceil(this.actualSize.height * scale.y * rs);
+                Group2D._s.width  = Math.ceil(actualSize.width  * scale.x * rs);
+                Group2D._s.height = Math.ceil(actualSize.height * scale.y * rs);
             }
 
             let sizeChanged = !Group2D._s.equals(rd._cacheSize);
 
             if (rd._cacheNode) {
-                let size = rd._cacheNode.contentSize;
+                let size = Group2D._s;
+                rd._cacheNode.getInnerSizeToRef(size);
 
                 // Check if we have to deallocate because the size is too small
                 if ((size.width < Group2D._s.width) || (size.height < Group2D._s.height)) {
@@ -857,6 +865,12 @@
                 }
                 rd._cacheRenderSprite = res.sprite;
                 sizeChanged = true;
+            } else if (sizeChanged) {
+                let sprite = rd._cacheRenderSprite;
+                if (sprite) {
+                    sprite.size = actualSize;
+                    sprite.spriteSize = new Size(actualSize.width * scale.x * rs, actualSize.height * scale.y * rs);
+                }
             }
 
             if (sizeChanged) {
@@ -869,8 +883,9 @@
                 this._setFlags(SmartPropertyPrim.flagWorldCacheChanged);
             }
 
-            let n = rd._cacheNode;
-            rd._cacheTexture.bindTextureForPosSize(n.pos, Group2D._s, true);
+            let pos = Group2D._v1;
+            rd._cacheNode.getInnerPosToRef(pos);
+            rd._cacheTexture.bindTextureForPosSize(pos, Group2D._s, true);
         }
 
         private _unbindCacheTarget() {
@@ -922,6 +937,11 @@
                 case Group2D.actualSizeProperty.id:
                     cachedSprite.size = this.actualSize.clone();
                     break;
+                case Group2D.xProperty.id:
+                    cachedSprite.x = this.x;
+                    break;
+                case Group2D.yProperty.id:
+                    cachedSprite.y = this.y;
             }
         }
 

+ 46 - 15
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -2316,6 +2316,9 @@
          * Uniform scale applied on the primitive. If a non-uniform scale is applied through scaleX/scaleY property the getter of this property will return scaleX.
          */
         public set scale(value: number) {
+            if (value <= 0) {
+                throw new Error("You can't set the scale to less or equal to 0");
+            }
             this._scale.x = this._scale.y = value;
             this._setFlags(SmartPropertyPrim.flagActualScaleDirty);
             this._spreadActualScaleDirty();
@@ -2671,6 +2674,9 @@
          * Scale applied on the X axis of the primitive
          */
         public set scaleX(value: number) {
+            if (value <= 0) {
+                throw new Error("You can't set the scaleX to less or equal to 0");
+            }
             this._scale.x = value;
             this._setFlags(SmartPropertyPrim.flagActualScaleDirty);
             this._spreadActualScaleDirty();
@@ -2686,6 +2692,9 @@
          * Scale applied on the Y axis of the primitive
          */
         public set scaleY(value: number) {
+            if (value <= 0) {
+                throw new Error("You can't set the scaleY to less or equal to 0");
+            }
             this._scale.y = value;
             this._setFlags(SmartPropertyPrim.flagActualScaleDirty);
             this._spreadActualScaleDirty();
@@ -2737,7 +2746,7 @@
          */
         public getActualGlobalScaleToRef(res: Vector2) {
             let as = this.actualScale;
-            let cls = this.owner._canvasLevelScale || Canvas2D._iv3;
+            let cls = this.owner._canvasLevelScale || Prim2DBase._iv2;
             res.x = as.x * cls.x;
             res.y = as.y * cls.y;
         }
@@ -2994,7 +3003,9 @@
                     this._boundingInfo.unionToRef(contentBI, this._boundingInfo);
                 }
 
-                this._clearFlags(SmartPropertyPrim.flagBoundingInfoDirty);
+                if (sizedByContent || !this._isFlagSet(SmartPropertyPrim.flagLevelBoundingInfoDirty)) {
+                    this._clearFlags(SmartPropertyPrim.flagBoundingInfoDirty);
+                }
             } else {
                 C2DLogging.setPostMessage(() => "cache hit");
             }
@@ -3016,7 +3027,9 @@
                 }
 
                 if (this._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
-                    this.owner.addUpdateLayoutCounter(1);
+                    if (this._owner) {
+                        this._owner.addUpdateLayoutCounter(1);
+                    }
                     this._layoutEngine.updateLayout(this);
 
                     this._clearFlags(SmartPropertyPrim.flagLayoutDirty);
@@ -3032,8 +3045,20 @@
                         C2DLogging.setPostMessage(() => "re entrance detected, boundingInfo returned");
                         return this.boundingInfo;
                     }
+
+                    if (this._isFlagSet(SmartPropertyPrim.flagPositioningDirty)) {
+                        C2DLogging.setPostMessage(() => "couldn't compute positioning, boundingInfo returned");
+                        return this.boundingInfo;
+                    }
                 }
 
+                if (!usePositioning) {
+                    let bi = this.boundingInfo;
+                    if (!this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
+                        this._clearFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
+                    }
+                    return bi;
+                }
                 this._clearFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
             } else {
                 C2DLogging.setPostMessage(() => "cache hit");
@@ -3436,6 +3461,9 @@
                 try {
                     Prim2DBase._bypassGroup2DExclusion = true;
                     let ownerGroup = this.getExternalData<Group2D>("__cachedGroup__");
+                    if (!ownerGroup) {
+                        return false;
+                    }
                     return ownerGroup.intersect(intersectInfo);
                 } finally  {
                     Prim2DBase._bypassGroup2DExclusion = false;
@@ -3801,9 +3829,9 @@
         private static _t0: Matrix2D = new Matrix2D();
         private static _t1: Matrix2D = new Matrix2D();
         private static _t2: Matrix2D = new Matrix2D();
-        private static _v0: Vector2 = Vector2.Zero();   // Must stay with the value 0,0
+        private static _v0: Vector2 = Vector2.Zero();    // Must stay with the value 0,0
         private static _v30: Vector3 = Vector3.Zero();   // Must stay with the value 0,0,0
-        private static _iv3: Vector3 = new Vector3(1,1,1); // Must stay identity vector
+        private static _iv2: Vector2 = new Vector2(1,1); // Must stay identity vector
         private static _ts0 = Size.Zero();
 
         private _updateLocalTransform(): boolean {
@@ -3823,12 +3851,12 @@
                 var local: Matrix2D;
                 let pos = this._position ? this.position : (this.layoutAreaPos || Prim2DBase._v0);
                 let postScale = this._postScale;
-                let canvasScale = Prim2DBase._iv3;
-                let hasCanvasScale = false;
-                if (this._parent instanceof Canvas2D) {
-                    hasCanvasScale = true;
-                    canvasScale = (this._parent as Canvas2D)._canvasLevelScale || Prim2DBase._iv3;
-                }
+                let canvasScale = Prim2DBase._iv2;
+                //let hasCanvasScale = false;
+                //if (this._parent instanceof Canvas2D) {
+                //    hasCanvasScale = true;
+                //    canvasScale = (this._parent as Canvas2D)._canvasLevelScale || Prim2DBase._iv2;
+                //}
                 let globalScale = this._scale.multiplyByFloats(/*postScale.x**/canvasScale.x, /*postScale.y**/canvasScale.y);
 
                 if ((this._origin.x === 0 && this._origin.y === 0) || this._hasMargin) {
@@ -3862,10 +3890,10 @@
                     Matrix2D.TranslationToRef(pos.x + this._marginOffset.x, pos.y + this._marginOffset.y, t0);
                     t2.multiplyToRef(t0, this._localTransform);
 
-                    if (hasCanvasScale) {
-                        Matrix2D.ScalingToRef(canvasScale.x, canvasScale.y, Prim2DBase._t1);
-                        this._localTransform.multiplyToRef(Prim2DBase._t1, this._localTransform);
-                    }
+                    //if (hasCanvasScale) {
+                    //    Matrix2D.ScalingToRef(canvasScale.x, canvasScale.y, Prim2DBase._t1);
+                    //    this._localTransform.multiplyToRef(Prim2DBase._t1, this._localTransform);
+                    //}
 
                     this._localLayoutTransform = Matrix2D.Compose(globalScale, rot, pos);
                 }
@@ -4069,6 +4097,9 @@
                 let transformedBSize = Prim2DBase._size3;
                 let bSize = Prim2DBase._size4;
                 let bi = this.boundingInfo;
+                if (this._isFlagSet(SmartPropertyPrim.flagBoundingInfoDirty)) {
+                    success = false;
+                }
                 let tbi = Prim2DBase._tbi;
                 bi.transformToRef(Matrix2D.Rotation(this.rotation), tbi);
                 tbi.sizeToRef(transformedBSize);

+ 4 - 4
canvas2D/src/Engine/babylon.rectangle2d.ts

@@ -593,18 +593,18 @@
                 return false;
             }
 
-            let s = Rectangle2D._riv0;
-            this.getActualGlobalScaleToRef(s);
+            //let s = Rectangle2D._riv0;
+            //this.getActualGlobalScaleToRef(s);
 
             if (part.id === Shape2D.SHAPE2D_BORDERPARTID) {
                 let d = <Rectangle2DInstanceData>part;
                 let size = this.actualSize;
-                d.properties = new Vector3(size.width * s.x, size.height * s.y, this.roundRadius || 0);
+                d.properties = new Vector3(size.width/* * s.x*/, size.height/* * s.y*/, this.roundRadius || 0);
             }
             else if (part.id === Shape2D.SHAPE2D_FILLPARTID) {
                 let d = <Rectangle2DInstanceData>part;
                 let size = this.actualSize;
-                d.properties = new Vector3(size.width * s.x, size.height * s.y, this.roundRadius || 0);
+                d.properties = new Vector3(size.width/* * s.x*/, size.height/* * s.y*/, this.roundRadius || 0);
             }
             return true;
         }

+ 5 - 1
canvas2D/src/Engine/babylon.renderablePrim2d.ts

@@ -951,11 +951,15 @@
             let trn = RenderablePrim2D._t;
             let rot = t.decompose(scl, trn);
             let pas = this.actualScale;
-            let canvasScale = this.owner._canvasLevelScale;
+            //let cachedGroup = (this.getExternalData<Group2D>("__cachedGroup__") !== null);
+            let canvasScale = /*cachedGroup ? RenderablePrim2D._iV2 :  */this.owner._canvasLevelScale;
             scl.x = pas.x * canvasScale.x * this._postScale.x;
             scl.y = pas.y * canvasScale.y * this._postScale.y;
+            trn.multiplyInPlace(canvasScale);
             t = Matrix2D.Compose(this.applyActualScaleOnTransform() ? scl : RenderablePrim2D._iV2, rot, trn);
 
+            //console.log(`Update Instance Data Part: ${this.id}`);
+
             let size = (<Size>this.renderGroup.viewportSize);
             let zBias = this.actualZOffset;
 

+ 3 - 3
canvas2D/src/Engine/babylon.shape2d.ts

@@ -126,9 +126,9 @@
             return cat;
         }
 
-        protected applyActualScaleOnTransform(): boolean {
-            return false;
-        }
+        //protected applyActualScaleOnTransform(): boolean {
+        //    return false;
+        //}
 
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean {
             if (!super.refreshInstanceDataPart(part)) {

+ 10 - 2
canvas2D/src/Engine/babylon.text2d.ts

@@ -368,6 +368,10 @@
             if (!this.owner || !this._text) {
                 return false;
             }
+            let asize = this.actualSize;
+            if (asize.width===0 && asize.height===0) {
+                return false;
+            }
             BoundingInfo2D.CreateFromSizeToRef(this.actualSize, this._levelBoundingInfo);
             return true;
         }
@@ -765,7 +769,7 @@
                     offsetX += (contentAreaWidth - maxLineLen) * .5;
                 }
 
-                offset.x += offsetX;
+                offset.x += Math.floor(offsetX);
 
                 offset.y += contentAreaHeight + textHeight - lh;
                 offset.y += this.padding.bottomPixels;
@@ -796,13 +800,17 @@
                         if(char !== "\t" && !this._isWhiteSpaceCharVert(char)){ 
                             //make sure space char gets processed here or overlapping can occur when text is set
                             let ci = texture.getChar(char);
-                            this.updateInstanceDataPart(d, new Vector2(offset.x + ci.xOffset, offset.y +ci.yOffset));
+                            let partOffset = new Vector2(offset.x + ci.xOffset, offset.y + ci.yOffset);
+                            this.updateInstanceDataPart(d, partOffset);
                             d.topLeftUV = ci.topLeftUV;
                             let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
                             d.sizeUV = suv;
                             d.textureSize = new BABYLON.Vector2(ts.width, ts.height);
                             d.color = this.defaultFontColor;
                             d.superSampleFactor = superSampleFactor;
+
+                            //console.log(`Char: ${char}, Offset: ${partOffset}`);
+
                             ++d.curElement;
                         }
 

+ 10 - 8
canvas2D/src/shaders/sprite2d.vertex.fx

@@ -77,13 +77,15 @@ void main(void) {
 		vUV.y = 1.0 - vUV.y;
 	}
 
+	//vUV.x += 0.5 / textureSize.x;
+
 	vec4 pos;
-	if (alignToPixel == 1.0)
-	{
-		pos.xy = floor(pos2.xy * sizeUV * textureSize);
-	} else {
+	//if (alignToPixel == 1.0)
+	//{
+	//	pos.xy = floor(pos2.xy * sizeUV * textureSize);
+	//} else {
 		pos.xy = pos2.xy * sizeUV * textureSize;
-	}
+	//}
 
 #ifdef Scale9
 	if (invertY == 1.0) {
@@ -111,9 +113,9 @@ void main(void) {
 		float irw = 2.0 / rw;
 		float irh = 2.0 / rh;
 
-		x = floor((x / irw) + 0.5) * irw;
-		y = floor((y / irh) + 0.5) * irh;
+		x = (floor((x / irw)) * irw) + irw / 2.0;
+		y = (floor((y / irh)) * irh) + irh / 2.0;
 	}
 
-	gl_Position = vec4(x, y, zBias.x, 1);
+	gl_Position = vec4(x, y, zBias.x, 1.0);
 }	

+ 6 - 5
canvas2D/src/shaders/text2d.vertex.fx

@@ -53,12 +53,13 @@ void main(void) {
 	}
 
 	// Align texture coordinate to texel to enhance rendering quality
-	vUV = (floor(vUV*textureSize) + vec2(0.0, 0.0)) / textureSize;
+//	vUV = (floor(vUV*textureSize) + vec2(0.5, 0.5)) / textureSize;
+	//vUV.x += 0.5 / textureSize.x;
 
 	vColor = color;
 	vColor.a *= opacity;
 	vec4 pos;
-	pos.xy = floor(pos2.xy * superSampleFactor * sizeUV * textureSize);	// Align on target pixel to avoid bad interpolation
+	pos.xy = pos2.xy * superSampleFactor * sizeUV * textureSize;
 	pos.z = 1.0;
 	pos.w = 1.0;
 
@@ -70,9 +71,9 @@ void main(void) {
 		float irw = 2.0 / rw;
 		float irh = 2.0 / rh;
 
-		x = floor((x / irw) + 0.5) * irw;
-		y = floor((y / irh) + 0.5) * irh;
+		x = (floor((x / irw)) * irw) + irw / 2.0;
+		y = (floor((y / irh)) * irh) + irh / 2.0;
 	}
 
-	gl_Position = vec4(x, y, zBias.x, 1);
+	gl_Position = vec4(x, y, zBias.x, 1.0);
 }

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 28 - 27
dist/preview release/babylon.core.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3270 - 3190
dist/preview release/babylon.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 36 - 36
dist/preview release/babylon.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 696 - 178
dist/preview release/babylon.max.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3270 - 3190
dist/preview release/babylon.module.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 36 - 36
dist/preview release/babylon.noworker.js


+ 6 - 4
dist/preview release/canvas2D/babylon.canvas2d.d.ts

@@ -2496,7 +2496,7 @@ declare module BABYLON {
         private static _t2;
         private static _v0;
         private static _v30;
-        private static _iv3;
+        private static _iv2;
         private static _ts0;
         private _updateLocalTransform();
         private static _transMtx;
@@ -2850,7 +2850,6 @@ declare module BABYLON {
             borderThickness?: number;
         });
         protected getUsedShaderCategories(dataPart: InstanceDataBase): string[];
-        protected applyActualScaleOnTransform(): boolean;
         protected refreshInstanceDataPart(part: InstanceDataBase): boolean;
         private _updateTransparencyStatus();
         protected _mustUpdateInstance(): boolean;
@@ -2917,7 +2916,7 @@ declare module BABYLON {
          * - layoutEngine: either an instance of a layout engine based class (StackPanel.Vertical, StackPanel.Horizontal) or a string ('canvas' for Canvas layout, 'StackPanel' or 'HorizontalStackPanel' for horizontal Stack Panel layout, 'VerticalStackPanel' for vertical Stack Panel layout).
          * - isVisible: true if the group must be visible, false for hidden. Default is true.
          * - isPickable: if true the Primitive can be used with interaction mode and will issue Pointer Event. If false it will be ignored for interaction/intersection test. Default value is true.
-         * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersection, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
+         * - isContainer: if true the Primitive acts as a container for interaction, if the primitive is not pickable or doesn't intersect, no further test will be perform on its children. If set to false, children will always be considered for intersection/interaction. Default value is true.
          * - childrenFlatZOrder: if true all the children (direct and indirect) will share the same Z-Order. Use this when there's a lot of children which don't overlap. The drawing order IS NOT GUARANTED!
          * - levelCollision: this primitive is an actor of the Collision Manager and only this level will be used for collision (i.e. not the children). Use deepCollision if you want collision detection on the primitives and its children.
          * - deepCollision: this primitive is an actor of the Collision Manager, this level AND ALSO its children will be used for collision (note: you don't need to set the children as level/deepCollision).
@@ -3040,6 +3039,8 @@ declare module BABYLON {
         protected _setRenderingScale(scale: number): void;
         private static _uV;
         private static _s;
+        private static _v1;
+        private static _s2;
         private _bindCacheTarget();
         private _unbindCacheTarget();
         protected _spreadActualScaleDirty(): void;
@@ -4502,7 +4503,7 @@ declare module BABYLON {
         private _designSize;
         private _designUseHorizAxis;
         _primitiveCollisionManager: PrimitiveCollisionManagerBase;
-        _canvasLevelScale: Vector3;
+        _canvasLevelScale: Vector2;
         _renderingSize: Size;
         private _curHWScale;
         private _drawCallsOpaqueCounter;
@@ -4521,6 +4522,7 @@ declare module BABYLON {
         private _profilingCanvas;
         private _profileInfoText;
         private static _v;
+        private static _cv1;
         private static _m;
         private static _mI;
         private static tS;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 148 - 76
dist/preview release/canvas2D/babylon.canvas2d.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 12 - 12
dist/preview release/canvas2D/babylon.canvas2d.min.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 6 - 6
dist/preview release/inspector/babylon.inspector.bundle.js


+ 3 - 0
dist/preview release/inspector/babylon.inspector.css

@@ -86,6 +86,8 @@
     height: 100%; }
     .insp-wrapper .tab-panel.searchable {
       height: calc(100% - 30px - 10px); }
+    .insp-wrapper .tab-panel .texture-image {
+      max-height: 400px; }
     .insp-wrapper .tab-panel .scene-actions {
       overflow-y: auto; }
       .insp-wrapper .tab-panel .scene-actions .actions-title {
@@ -243,6 +245,7 @@
     overflow-x: hidden;
     height: calc(50% - 32px - 30px); }
     .insp-wrapper .insp-tree .line {
+      padding: 3px;
       cursor: pointer; }
       .insp-wrapper .insp-tree .line:hover {
         background-color: #2c2c2c; }

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

@@ -18,11 +18,13 @@ declare module INSPECTOR {
         private _popupMode;
         /** The original canvas style, before applying the inspector*/
         private _canvasStyle;
+        private _initialTab;
+        private _parentElement;
         /** The inspector is created with the given engine.
          * If the parameter 'popup' is false, the inspector is created as a right panel on the main window.
          * If the parameter 'popup' is true, the inspector is created in another popup.
          */
-        constructor(scene: BABYLON.Scene, popup?: boolean);
+        constructor(scene: BABYLON.Scene, popup?: boolean, initialTab?: number, parentElement?: HTMLElement);
         /**
          * If the given element has a position 'asbolute' or 'relative',
          * returns the first parent of the given element that has a position 'relative' or 'absolute'.
@@ -90,10 +92,33 @@ declare module INSPECTOR {
             properties: string[];
             format: (tex: BABYLON.Texture) => string;
         };
+        'MapTexture': {
+            type: typeof BABYLON.MapTexture;
+        };
+        'RenderTargetTexture': {
+            type: typeof BABYLON.RenderTargetTexture;
+        };
+        'DynamicTexture': {
+            type: typeof BABYLON.DynamicTexture;
+        };
+        'BaseTexture': {
+            type: typeof BABYLON.BaseTexture;
+        };
+        'FontTexture': {
+            type: typeof BABYLON.FontTexture;
+        };
+        'Sound': {
+            type: typeof BABYLON.Sound;
+            properties: string[];
+        };
         'ArcRotateCamera': {
             type: typeof BABYLON.ArcRotateCamera;
             properties: string[];
         };
+        'FreeCamera': {
+            type: typeof BABYLON.FreeCamera;
+            properties: string[];
+        };
         'Scene': {
             type: typeof BABYLON.Scene;
             properties: string[];
@@ -177,6 +202,10 @@ declare module INSPECTOR {
         correspondsTo(obj: any): boolean;
         /** Returns the adapter unique name */
         readonly name: string;
+        /**
+         * Returns the actual object used for this adapter
+         */
+        readonly object: any;
         /** Returns the list of tools available for this adapter */
         abstract getTools(): Array<AbstractTreeTool>;
         /** Should be overriden in subclasses */
@@ -185,6 +214,47 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    class CameraAdapter extends Adapter implements ICameraPOV {
+        constructor(obj: BABYLON.Camera);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        getTools(): Array<AbstractTreeTool>;
+        setPOV(): void;
+    }
+}
+
+declare module INSPECTOR {
+    class SoundAdapter extends Adapter implements ISoundInteractions {
+        constructor(obj: BABYLON.Sound);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        getTools(): Array<AbstractTreeTool>;
+        setPlaying(callback: Function): void;
+    }
+}
+
+declare module INSPECTOR {
+    class TextureAdapter extends Adapter {
+        constructor(obj: BABYLON.BaseTexture);
+        /** Returns the name displayed in the tree */
+        id(): string;
+        /** Returns the type of this object - displayed in the tree */
+        type(): string;
+        /** Returns the list of properties to be displayed for this adapter */
+        getProperties(): Array<PropertyLine>;
+        getTools(): Array<AbstractTreeTool>;
+    }
+}
+
+declare module INSPECTOR {
     class Canvas2DAdapter extends Adapter implements IToolVisible, IToolDebug {
         constructor(obj: any);
         /** Returns the name displayed in the tree */
@@ -596,6 +666,10 @@ declare module INSPECTOR {
         filter(str: string): void;
         /** Dispose properly this tab */
         abstract dispose(): any;
+        /** Select an item in the tree */
+        select(item: TreeItem): void;
+        /** Highlight the given node, and downplay all others */
+        highlightNode(item?: TreeItem): void;
         /**
          * Returns the total width in pixel of this tab, 0 by default
         */
@@ -639,6 +713,42 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    class CameraTab extends PropertyTab {
+        constructor(tabbar: TabBar, inspector: Inspector);
+        protected _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    class SoundTab extends PropertyTab {
+        constructor(tabbar: TabBar, inspector: Inspector);
+        protected _getTree(): Array<TreeItem>;
+    }
+}
+
+declare module INSPECTOR {
+    class TextureTab extends Tab {
+        private _inspector;
+        /** The panel containing a list of items */
+        protected _treePanel: HTMLElement;
+        protected _treeItems: Array<TreeItem>;
+        private _imagePanel;
+        constructor(tabbar: TabBar, inspector: Inspector);
+        dispose(): void;
+        update(_items?: Array<TreeItem>): void;
+        private _getTree();
+        /** Display the details of the given item */
+        displayDetails(item: TreeItem): void;
+        /** Select an item in the tree */
+        select(item: TreeItem): void;
+        /** Set the given item as active in the tree */
+        activateNode(item: TreeItem): void;
+        /** Highlight the given node, and downplay all others */
+        highlightNode(item?: TreeItem): void;
+    }
+}
+
+declare module INSPECTOR {
     class Canvas2DTab extends PropertyTab {
         constructor(tabbar: TabBar, inspector: Inspector);
         protected _getTree(): Array<TreeItem>;
@@ -769,7 +879,7 @@ declare module INSPECTOR {
         private _invisibleTabs;
         /** The list of tabs visible, displayed in the tab bar */
         private _visibleTabs;
-        constructor(inspector: Inspector);
+        constructor(inspector: Inspector, initialTab?: number);
         update(): void;
         protected _build(): void;
         /**
@@ -909,12 +1019,16 @@ declare module INSPECTOR {
         private _tools;
         children: Array<TreeItem>;
         private _lineContent;
-        constructor(tab: PropertyTab, obj: Adapter);
+        constructor(tab: Tab, obj: Adapter);
         /** Returns the item ID == its adapter ID */
         readonly id: string;
         /** Add the given item as a child of this one */
         add(child: TreeItem): void;
         /**
+         * Returns the original adapter
+         */
+        readonly adapter: Adapter;
+        /**
          * Function used to compare this item to another tree item.
          * Returns the alphabetical sort of the adapter ID
          */
@@ -983,6 +1097,37 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    interface ICameraPOV {
+        setPOV: () => void;
+    }
+    /**
+     *
+     */
+    class CameraPOV extends AbstractTreeTool {
+        private cameraPOV;
+        constructor(camera: ICameraPOV);
+        protected action(): void;
+        private _gotoPOV();
+    }
+}
+
+declare module INSPECTOR {
+    interface ISoundInteractions {
+        setPlaying: (callback: Function) => void;
+    }
+    /**
+     *
+     */
+    class SoundInteractions extends AbstractTreeTool {
+        private playSound;
+        private b;
+        constructor(playSound: ISoundInteractions);
+        protected action(): void;
+        private _playSound();
+    }
+}
+
+declare module INSPECTOR {
     /** Any object implementing this interface should
      * provide methods to toggle its visibility
      */

+ 558 - 50
dist/preview release/inspector/babylon.inspector.js

@@ -5,10 +5,14 @@ var INSPECTOR;
          * If the parameter 'popup' is false, the inspector is created as a right panel on the main window.
          * If the parameter 'popup' is true, the inspector is created in another popup.
          */
-        function Inspector(scene, popup) {
+        function Inspector(scene, popup, initialTab, parentElement) {
             var _this = this;
             /** True if the inspector is built as a popup tab */
             this._popupMode = false;
+            //get Tabbar initialTab
+            this._initialTab = initialTab;
+            //get parentElement of our Inspector
+            this._parentElement = parentElement;
             // get canvas parent only if needed.
             this._scene = scene;
             // Save HTML document and window
@@ -88,22 +92,40 @@ var INSPECTOR;
                 canvas.style.marginTop = "0";
                 canvas.style.marginRight = "0";
                 // Replace canvas with the wrapper...
+                // if (this._parentElement) {
+                //     canvasParent.replaceChild(this._parentElement, canvas);
+                //     this._parentElement.appendChild(canvas);
+                // }
+                // else {
                 canvasParent.replaceChild(this._c2diwrapper, canvas);
                 // ... and add canvas to the wrapper
                 this._c2diwrapper.appendChild(canvas);
-                // add inspector     
-                var inspector = INSPECTOR.Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                // }
+                // add inspector
+                var inspector = void 0;
+                if (this._parentElement) {
+                    this._c2diwrapper.appendChild(this._parentElement);
+                    inspector = INSPECTOR.Helpers.CreateDiv('insp-right-panel', this._parentElement);
+                    inspector.style.width = '100%';
+                    inspector.style.height = '100%';
+                }
+                else {
+                    inspector = INSPECTOR.Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                }
+                console.log(inspector);
                 // Add split bar
-                Split([canvas, inspector], {
-                    direction: 'horizontal',
-                    sizes: [75, 25],
-                    onDrag: function () {
-                        INSPECTOR.Helpers.SEND_EVENT('resize');
-                        if (_this._tabbar) {
-                            _this._tabbar.updateWidth();
+                if (!this._parentElement) {
+                    Split([canvas, inspector], {
+                        direction: 'horizontal',
+                        sizes: [75, 25],
+                        onDrag: function () {
+                            INSPECTOR.Helpers.SEND_EVENT('resize');
+                            if (_this._tabbar) {
+                                _this._tabbar.updateWidth();
+                            }
                         }
-                    }
-                });
+                    });
+                }
                 // Build the inspector
                 this._buildInspector(inspector);
                 // Send resize event to the window
@@ -151,7 +173,7 @@ var INSPECTOR;
         /** Build the inspector panel in the given HTML element */
         Inspector.prototype._buildInspector = function (parent) {
             // tabbar
-            this._tabbar = new INSPECTOR.TabBar(this);
+            this._tabbar = new INSPECTOR.TabBar(this, this._initialTab);
             // Top panel
             this._topPanel = INSPECTOR.Helpers.CreateDiv('top-panel', parent);
             // Add tabbar
@@ -333,9 +355,42 @@ var INSPECTOR;
             ],
             format: function (tex) { return tex.name; }
         },
+        'MapTexture': {
+            type: BABYLON.MapTexture
+        },
+        'RenderTargetTexture': {
+            type: BABYLON.RenderTargetTexture
+        },
+        'DynamicTexture': {
+            type: BABYLON.DynamicTexture
+        },
+        'BaseTexture': {
+            type: BABYLON.BaseTexture
+        },
+        'FontTexture': {
+            type: BABYLON.FontTexture
+        },
+        'Sound': {
+            type: BABYLON.Sound,
+            properties: [
+                'name',
+                'autoplay',
+                'loop',
+                'useCustomAttenuation',
+                'soundTrackId',
+                'spatialSound',
+                'refDistance',
+                'rolloffFactor',
+                'maxDistance',
+                'distanceModel',
+                'isPlaying',
+                'isPaused'
+            ]
+        },
         'ArcRotateCamera': {
             type: BABYLON.ArcRotateCamera,
             properties: [
+                'position',
                 'alpha',
                 'beta',
                 'radius',
@@ -354,6 +409,36 @@ var INSPECTOR;
                 'checkCollisions'
             ]
         },
+        'FreeCamera': {
+            type: BABYLON.FreeCamera,
+            properties: [
+                'position',
+                'rotation',
+                'rotationQuaternion',
+                'cameraDirection',
+                'cameraRotation',
+                'ellipsoid',
+                'applyGravity',
+                'angularSensibility',
+                'keysUp',
+                'keysDown',
+                'keysLeft',
+                'keysRight',
+                'checkCollisions',
+                'speed',
+                'lockedTarget',
+                'noRotationConstraint',
+                'fov',
+                'inertia',
+                'minZ', 'maxZ',
+                'layerMask',
+                'mode',
+                'orthoBottom',
+                'orthoTop',
+                'orthoLeft',
+                'orthoRight'
+            ]
+        },
         'Scene': {
             type: BABYLON.Scene,
             properties: [
@@ -570,6 +655,16 @@ var INSPECTOR;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Adapter.prototype, "object", {
+            /**
+             * Returns the actual object used for this adapter
+             */
+            get: function () {
+                return this._obj;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /** Should be overriden in subclasses */
         Adapter.prototype.highlight = function (b) { };
         ;
@@ -589,6 +684,160 @@ var __extends = (this && this.__extends) || function (d, b) {
 };
 var INSPECTOR;
 (function (INSPECTOR) {
+    var CameraAdapter = (function (_super) {
+        __extends(CameraAdapter, _super);
+        function CameraAdapter(obj) {
+            return _super.call(this, obj) || this;
+        }
+        /** Returns the name displayed in the tree */
+        CameraAdapter.prototype.id = function () {
+            var str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        };
+        /** Returns the type of this object - displayed in the tree */
+        CameraAdapter.prototype.type = function () {
+            return INSPECTOR.Helpers.GET_TYPE(this._obj);
+        };
+        /** Returns the list of properties to be displayed for this adapter */
+        CameraAdapter.prototype.getProperties = function () {
+            var propertiesLines = [];
+            var camToDisplay = [];
+            // The if is there to work with the min version of babylon
+            if (this._obj instanceof BABYLON.ArcRotateCamera) {
+                camToDisplay = INSPECTOR.PROPERTIES['ArcRotateCamera'].properties;
+            }
+            else if (this._obj instanceof BABYLON.FreeCamera) {
+                camToDisplay = INSPECTOR.PROPERTIES['FreeCamera'].properties;
+            }
+            for (var _i = 0, camToDisplay_1 = camToDisplay; _i < camToDisplay_1.length; _i++) {
+                var dirty = camToDisplay_1[_i];
+                var infos = new INSPECTOR.Property(dirty, this._obj);
+                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
+            }
+            return propertiesLines;
+        };
+        CameraAdapter.prototype.getTools = function () {
+            var tools = [];
+            // tools.push(new Checkbox(this));
+            tools.push(new INSPECTOR.CameraPOV(this));
+            return tools;
+        };
+        CameraAdapter.prototype.setPOV = function () {
+            this._obj.getScene().activeCamera = this._obj;
+        };
+        return CameraAdapter;
+    }(INSPECTOR.Adapter));
+    INSPECTOR.CameraAdapter = CameraAdapter;
+})(INSPECTOR || (INSPECTOR = {}));
+
+//# sourceMappingURL=CameraAdapter.js.map
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
+    var SoundAdapter = (function (_super) {
+        __extends(SoundAdapter, _super);
+        function SoundAdapter(obj) {
+            return _super.call(this, obj) || this;
+        }
+        /** Returns the name displayed in the tree */
+        SoundAdapter.prototype.id = function () {
+            var str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        };
+        /** Returns the type of this object - displayed in the tree */
+        SoundAdapter.prototype.type = function () {
+            return INSPECTOR.Helpers.GET_TYPE(this._obj);
+        };
+        /** Returns the list of properties to be displayed for this adapter */
+        SoundAdapter.prototype.getProperties = function () {
+            var propertiesLines = [];
+            var camToDisplay = [];
+            // The if is there to work with the min version of babylon
+            var soundProperties = INSPECTOR.PROPERTIES['Sound'].properties;
+            for (var _i = 0, soundProperties_1 = soundProperties; _i < soundProperties_1.length; _i++) {
+                var dirty = soundProperties_1[_i];
+                var infos = new INSPECTOR.Property(dirty, this._obj);
+                propertiesLines.push(new INSPECTOR.PropertyLine(infos));
+            }
+            return propertiesLines;
+        };
+        SoundAdapter.prototype.getTools = function () {
+            var tools = [];
+            tools.push(new INSPECTOR.SoundInteractions(this));
+            return tools;
+        };
+        SoundAdapter.prototype.setPlaying = function (callback) {
+            this._obj.play();
+            this._obj.onended = function () {
+                callback();
+            };
+        };
+        return SoundAdapter;
+    }(INSPECTOR.Adapter));
+    INSPECTOR.SoundAdapter = SoundAdapter;
+})(INSPECTOR || (INSPECTOR = {}));
+
+//# sourceMappingURL=SoundAdapter.js.map
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
+    var TextureAdapter = (function (_super) {
+        __extends(TextureAdapter, _super);
+        function TextureAdapter(obj) {
+            return _super.call(this, obj) || this;
+        }
+        /** Returns the name displayed in the tree */
+        TextureAdapter.prototype.id = function () {
+            var str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        };
+        /** Returns the type of this object - displayed in the tree */
+        TextureAdapter.prototype.type = function () {
+            return INSPECTOR.Helpers.GET_TYPE(this._obj);
+        };
+        /** Returns the list of properties to be displayed for this adapter */
+        TextureAdapter.prototype.getProperties = function () {
+            // Not used in this tab
+            return [];
+        };
+        TextureAdapter.prototype.getTools = function () {
+            var tools = [];
+            // tools.push(new CameraPOV(this));
+            return tools;
+        };
+        return TextureAdapter;
+    }(INSPECTOR.Adapter));
+    INSPECTOR.TextureAdapter = TextureAdapter;
+})(INSPECTOR || (INSPECTOR = {}));
+
+//# sourceMappingURL=TextureAdapter.js.map
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
     var Canvas2DAdapter = (function (_super) {
         __extends(Canvas2DAdapter, _super);
         function Canvas2DAdapter(obj) {
@@ -1650,8 +1899,6 @@ var INSPECTOR;
     INSPECTOR.HDRCubeTextureElement = HDRCubeTextureElement;
 })(INSPECTOR || (INSPECTOR = {}));
 
-//# sourceMappingURL=HDRCubeTextureElement.js.map
-
 var __extends = (this && this.__extends) || function (d, b) {
     for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
     function __() { this.constructor = d; }
@@ -1694,8 +1941,6 @@ var INSPECTOR;
     INSPECTOR.SearchBar = SearchBar;
 })(INSPECTOR || (INSPECTOR = {}));
 
-//# sourceMappingURL=SearchBar.js.map
-
 var __extends = (this && this.__extends) || function (d, b) {
     for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
     function __() { this.constructor = d; }
@@ -1737,8 +1982,6 @@ var INSPECTOR;
     INSPECTOR.TextureElement = TextureElement;
 })(INSPECTOR || (INSPECTOR = {}));
 
-//# sourceMappingURL=TextureElement.js.map
-
 var INSPECTOR;
 (function (INSPECTOR) {
     /**
@@ -2014,6 +2257,14 @@ var INSPECTOR;
         /** Add this in the propertytab with the searchbar */
         Tab.prototype.filter = function (str) { };
         ;
+        /** Select an item in the tree */
+        Tab.prototype.select = function (item) {
+            // To define in subclasses if needed 
+        };
+        /** Highlight the given node, and downplay all others */
+        Tab.prototype.highlightNode = function (item) {
+            // To define in subclasses if needed
+        };
         /**
          * Returns the total width in pixel of this tab, 0 by default
         */
@@ -2171,6 +2422,192 @@ var __extends = (this && this.__extends) || function (d, b) {
 };
 var INSPECTOR;
 (function (INSPECTOR) {
+    var CameraTab = (function (_super) {
+        __extends(CameraTab, _super);
+        function CameraTab(tabbar, inspector) {
+            return _super.call(this, tabbar, 'Camera', inspector) || this;
+        }
+        /* Overrides super */
+        CameraTab.prototype._getTree = function () {
+            var arr = [];
+            // get all cameras from the first scene
+            var instances = this._inspector.scene;
+            for (var _i = 0, _a = instances.cameras; _i < _a.length; _i++) {
+                var camera = _a[_i];
+                arr.push(new INSPECTOR.TreeItem(this, new INSPECTOR.CameraAdapter(camera)));
+            }
+            return arr;
+        };
+        return CameraTab;
+    }(INSPECTOR.PropertyTab));
+    INSPECTOR.CameraTab = CameraTab;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
+    var SoundTab = (function (_super) {
+        __extends(SoundTab, _super);
+        function SoundTab(tabbar, inspector) {
+            return _super.call(this, tabbar, 'Sound', inspector) || this;
+        }
+        /* Overrides super */
+        SoundTab.prototype._getTree = function () {
+            var _this = this;
+            var arr = [];
+            // get all cameras from the first scene
+            var instances = this._inspector.scene;
+            for (var _i = 0, _a = instances.soundTracks; _i < _a.length; _i++) {
+                var sounds = _a[_i];
+                var sound = sounds.soundCollection;
+                sound.forEach(function (element) {
+                    arr.push(new INSPECTOR.TreeItem(_this, new INSPECTOR.SoundAdapter(element)));
+                });
+            }
+            return arr;
+        };
+        return SoundTab;
+    }(INSPECTOR.PropertyTab));
+    INSPECTOR.SoundTab = SoundTab;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
+    var TextureTab = (function (_super) {
+        __extends(TextureTab, _super);
+        function TextureTab(tabbar, inspector) {
+            var _this = _super.call(this, tabbar, 'Textures') || this;
+            _this._treeItems = [];
+            _this._inspector = inspector;
+            // Build the properties panel : a div that will contains the tree and the detail panel
+            _this._panel = INSPECTOR.Helpers.CreateDiv('tab-panel');
+            // Build the treepanel
+            _this._treePanel = INSPECTOR.Helpers.CreateDiv('insp-tree', _this._panel);
+            _this._imagePanel = INSPECTOR.Helpers.CreateDiv('image-panel', _this._panel);
+            Split([_this._treePanel, _this._imagePanel], {
+                blockDrag: _this._inspector.popupMode,
+                direction: 'vertical'
+            });
+            _this.update();
+            return _this;
+        }
+        TextureTab.prototype.dispose = function () {
+            // Nothing to dispose
+        };
+        TextureTab.prototype.update = function (_items) {
+            var items;
+            if (_items) {
+                items = _items;
+            }
+            else {
+                // Rebuild the tree
+                this._treeItems = this._getTree();
+                items = this._treeItems;
+            }
+            // Clean the tree
+            INSPECTOR.Helpers.CleanDiv(this._treePanel);
+            INSPECTOR.Helpers.CleanDiv(this._imagePanel);
+            // Sort items alphabetically
+            items.sort(function (item1, item2) {
+                return item1.compareTo(item2);
+            });
+            // Display items
+            for (var _i = 0, items_1 = items; _i < items_1.length; _i++) {
+                var item = items_1[_i];
+                this._treePanel.appendChild(item.toHtml());
+            }
+        };
+        /* Overrides super */
+        TextureTab.prototype._getTree = function () {
+            var arr = [];
+            // get all cameras from the first scene
+            var instances = this._inspector.scene;
+            for (var _i = 0, _a = instances.textures; _i < _a.length; _i++) {
+                var tex = _a[_i];
+                arr.push(new INSPECTOR.TreeItem(this, new INSPECTOR.TextureAdapter(tex)));
+            }
+            return arr;
+        };
+        /** Display the details of the given item */
+        TextureTab.prototype.displayDetails = function (item) {
+            // Remove active state on all items
+            this.activateNode(item);
+            INSPECTOR.Helpers.CleanDiv(this._imagePanel);
+            // Get the texture object
+            var texture = item.adapter.object;
+            var img = INSPECTOR.Helpers.CreateElement('img', 'texture-image', this._imagePanel);
+            if (texture instanceof BABYLON.MapTexture) {
+                // instance of Map texture
+                texture.bindTextureForPosSize(new BABYLON.Vector2(0, 0), new BABYLON.Size(texture.getSize().width, texture.getSize().height), false);
+                BABYLON.Tools.DumpFramebuffer(texture.getSize().width, texture.getSize().height, this._inspector.scene.getEngine(), function (data) { return img.src = data; });
+                texture.unbindTexture();
+            }
+            else if (texture instanceof BABYLON.RenderTargetTexture) {
+                // RenderTarget textures
+                BABYLON.Tools.CreateScreenshotUsingRenderTarget(this._inspector.scene.getEngine(), texture.activeCamera, { precision: 1 }, function (data) { return img.src = data; });
+            }
+            else if (texture.url) {
+                // If an url is present, the texture is an image
+                img.src = texture.url;
+            }
+            else if (texture['_canvas']) {
+                // Dynamic texture
+                var base64Image = texture['_canvas'].toDataURL("image/png");
+                img.src = base64Image;
+            }
+        };
+        /** Select an item in the tree */
+        TextureTab.prototype.select = function (item) {
+            // Remove the node highlight
+            this.highlightNode();
+            // Active the node
+            this.activateNode(item);
+            // Display its details
+            this.displayDetails(item);
+        };
+        /** Set the given item as active in the tree */
+        TextureTab.prototype.activateNode = function (item) {
+            if (this._treeItems) {
+                for (var _i = 0, _a = this._treeItems; _i < _a.length; _i++) {
+                    var node = _a[_i];
+                    node.active(false);
+                }
+            }
+            item.active(true);
+        };
+        /** Highlight the given node, and downplay all others */
+        TextureTab.prototype.highlightNode = function (item) {
+            if (this._treeItems) {
+                for (var _i = 0, _a = this._treeItems; _i < _a.length; _i++) {
+                    var node = _a[_i];
+                    node.highlight(false);
+                }
+            }
+            if (item) {
+                item.highlight(true);
+            }
+        };
+        return TextureTab;
+    }(INSPECTOR.Tab));
+    INSPECTOR.TextureTab = TextureTab;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
     var Canvas2DTab = (function (_super) {
         __extends(Canvas2DTab, _super);
         function Canvas2DTab(tabbar, inspector) {
@@ -2633,34 +3070,6 @@ var INSPECTOR;
                 inside = this._beautify(inside, level + 1);
                 return this._beautify(left, level) + '{\n' + inside + '\n' + spaces + '}\n' + this._beautify(right, level);
             }
-            // // Replace bracket with @1 and @2 with correct indentation
-            // let newInside          = "@1\n\t" + inside + "\n@2";
-            // newInside              = newInside.replace(/;\n/g, ";\n\t");
-            // glsl                   = glsl.replace(insideWithBrackets, newInside);
-            // firstBracket       = glsl.indexOf('{');
-            // lastBracket        = glsl.lastIndexOf('}');
-            // }
-            // console.log(glsl);
-            // let regex = /(\{(?:\{??[^\{]*?}))+/gmi;
-            // let tmp = glsl;
-            // let m;
-            // while ((m = regex.exec(tmp)) !== null) {
-            //     // This is necessary to avoid infinite loops with zero-width matches
-            //     if (m.index === regex.lastIndex) {
-            //         regex.lastIndex++;
-            //     }                
-            //     // The result can be accessed through the `m`-variable.
-            //     m.forEach((match, groupIndex) => {
-            //         // Remove the first and the last bracket only
-            //         let matchWithoutBrackets = match.replace(/{/, "").replace(/}/, "");
-            //         // Indent the content inside brackets with tabs
-            //         glsl = glsl.replace(match, `{\n\t${matchWithoutBrackets}\n}\n`);
-            //         // removes the match from tmp
-            //         tmp = tmp.replace(match, "");
-            //         // and continue
-            //     });
-            // }
-            // return 
         };
         return ShaderTab;
     }(INSPECTOR.Tab));
@@ -3085,7 +3494,7 @@ var INSPECTOR;
      */
     var TabBar = (function (_super) {
         __extends(TabBar, _super);
-        function TabBar(inspector) {
+        function TabBar(inspector, initialTab) {
             var _this = _super.call(this) || this;
             // The list of available tabs
             _this._tabs = [];
@@ -3098,6 +3507,7 @@ var INSPECTOR;
             _this._tabs.push(new INSPECTOR.ConsoleTab(_this, _this._inspector));
             _this._tabs.push(new INSPECTOR.StatsTab(_this, _this._inspector));
             _this._meshTab = new INSPECTOR.MeshTab(_this, _this._inspector);
+            _this._tabs.push(new INSPECTOR.TextureTab(_this, _this._inspector));
             _this._tabs.push(_this._meshTab);
             _this._tabs.push(new INSPECTOR.ShaderTab(_this, _this._inspector));
             _this._tabs.push(new INSPECTOR.LightTab(_this, _this._inspector));
@@ -3106,10 +3516,16 @@ var INSPECTOR;
                 _this._tabs.push(new INSPECTOR.Canvas2DTab(_this, _this._inspector));
             }
             _this._tabs.push(new INSPECTOR.MaterialTab(_this, _this._inspector));
+            _this._tabs.push(new INSPECTOR.CameraTab(_this, _this._inspector));
+            _this._tabs.push(new INSPECTOR.SoundTab(_this, _this._inspector));
             _this._toolBar = new INSPECTOR.Toolbar(_this._inspector);
             _this._build();
-            // Active the first tab
-            _this._tabs[0].active(true);
+            //Check initialTab is defined and between tabs bounds
+            if (!initialTab || initialTab < 0 || initialTab >= _this._tabs.length) {
+                initialTab = 0;
+                console.warn('');
+            }
+            _this._tabs[initialTab].active(true);
             // set all tab as visible
             for (var _i = 0, _a = _this._tabs; _i < _a.length; _i++) {
                 var tab = _a[_i];
@@ -3689,6 +4105,16 @@ var INSPECTOR;
             this.children.push(child);
             this.update();
         };
+        Object.defineProperty(TreeItem.prototype, "adapter", {
+            /**
+             * Returns the original adapter
+             */
+            get: function () {
+                return this._adapter;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Function used to compare this item to another tree item.
          * Returns the alphabetical sort of the adapter ID
@@ -3911,6 +4337,88 @@ var __extends = (this && this.__extends) || function (d, b) {
 var INSPECTOR;
 (function (INSPECTOR) {
     /**
+     *
+     */
+    var CameraPOV = (function (_super) {
+        __extends(CameraPOV, _super);
+        function CameraPOV(camera) {
+            var _this = _super.call(this) || this;
+            _this.cameraPOV = camera;
+            _this._elem.classList.add('fa-video-camera');
+            return _this;
+        }
+        CameraPOV.prototype.action = function () {
+            _super.prototype.action.call(this);
+            this._gotoPOV();
+        };
+        CameraPOV.prototype._gotoPOV = function () {
+            var actives = INSPECTOR.Inspector.DOCUMENT.querySelectorAll(".fa-video-camera.active");
+            console.log(actives);
+            for (var i = 0; i < actives.length; i++) {
+                actives[i].classList.remove('active');
+            }
+            //if (this._on) {
+            // set icon camera
+            this._elem.classList.add('active');
+            //}
+            this.cameraPOV.setPOV();
+        };
+        return CameraPOV;
+    }(INSPECTOR.AbstractTreeTool));
+    INSPECTOR.CameraPOV = CameraPOV;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
+    /**
+     *
+     */
+    var SoundInteractions = (function (_super) {
+        __extends(SoundInteractions, _super);
+        function SoundInteractions(playSound) {
+            var _this = _super.call(this) || this;
+            _this.playSound = playSound;
+            _this.b = false;
+            _this._elem.classList.add('fa-play');
+            return _this;
+        }
+        SoundInteractions.prototype.action = function () {
+            _super.prototype.action.call(this);
+            this._playSound();
+        };
+        SoundInteractions.prototype._playSound = function () {
+            var _this = this;
+            if (this._elem.classList.contains('fa-play')) {
+                this._elem.classList.remove('fa-play');
+                this._elem.classList.add('fa-pause');
+            }
+            else {
+                this._elem.classList.remove('fa-pause');
+                this._elem.classList.add('fa-play');
+            }
+            this.playSound.setPlaying(function () {
+                _this._elem.classList.remove('fa-pause');
+                _this._elem.classList.add('fa-play');
+            });
+        };
+        return SoundInteractions;
+    }(INSPECTOR.AbstractTreeTool));
+    INSPECTOR.SoundInteractions = SoundInteractions;
+})(INSPECTOR || (INSPECTOR = {}));
+
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var INSPECTOR;
+(function (INSPECTOR) {
+    /**
      * Checkbox to display/hide the primitive
      */
     var Checkbox = (function (_super) {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -856,7 +856,7 @@ var BABYLON;
                 var translation = node.translation || [0, 0, 0];
                 var rotation = node.rotation || [0, 0, 0, 1];
                 var scale = node.scale || [1, 1, 1];
-                configureNode(lastNode, BABYLON.Vector3.FromArray(translation), BABYLON.Quaternion.RotationAxis(BABYLON.Vector3.FromArray(rotation).normalize(), rotation[3]), BABYLON.Vector3.FromArray(scale));
+                configureNode(lastNode, BABYLON.Vector3.FromArray(translation), BABYLON.Quaternion.FromArray(rotation), BABYLON.Vector3.FromArray(scale));
             }
             lastNode.updateCache(true);
             node.babylonNode = lastNode;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 21 - 0
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.d.ts

@@ -0,0 +1,21 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+declare module BABYLON {
+    class ShadowOnlyMaterial extends Material {
+        private _worldViewProjectionMatrix;
+        private _scaledDiffuse;
+        private _renderId;
+        private _defines;
+        private _cachedDefines;
+        constructor(name: string, scene: Scene);
+        needAlphaBlending(): boolean;
+        needAlphaTesting(): boolean;
+        getAlphaTestTexture(): BaseTexture;
+        private _checkCache(scene, mesh?, useInstances?);
+        isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean;
+        bindOnlyWorldMatrix(world: Matrix): void;
+        bind(world: Matrix, mesh?: Mesh): void;
+        clone(name: string): ShadowOnlyMaterial;
+        serialize(): any;
+        static Parse(source: any, scene: Scene, rootUrl: string): ShadowOnlyMaterial;
+    }
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 209 - 0
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 0
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


+ 0 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.d.ts

@@ -1,5 +1,4 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts" />
-/// <reference path="../simple/babylon.simpleMaterial.d.ts" />
 declare module BABYLON {
     class WaterMaterial extends Material {
         renderTargetSize: Vector2;

+ 0 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.js

@@ -1,5 +1,4 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
-/// <reference path="../simple/babylon.simpleMaterial.ts"/>
 var __extends = (this && this.__extends) || function (d, b) {
     for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
     function __() { this.constructor = d; }

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

@@ -4,12 +4,12 @@
 
 ### Major updates
  - WebGL2 context support. WebGL2 is now used instead of WebGL 1 when available. [More info here](http://doc.babylonjs.com/overviews/webgl2) ([deltakosh](https://github.com/deltakosh))
+ - Complete WebVR 1.1 support including controllers for HTC Vive and Occulus. [More info here](http://doc.babylonjs.com/overviews/webvr_camera) ([raanan](https://github.com/raananw))
  - Support for [Vertex Array Objects](https://www.opengl.org/registry/specs/ARB/vertex_array_object.txt) ([deltakosh](https://github.com/deltakosh))
  - Support for multisample render targets. [Demo](http://www.babylonjs-playground.com/#12MKMN) ([deltakosh](https://github.com/deltakosh))
  - New Unity 5 Editor Toolkit. Complete pipeline integration [Doc](TODO) - ([MackeyK24](https://github.com/MackeyK24))
  - New DebugLayer. [Doc](TODO) - ([temechon](https://github.com/temechon))
  - New `VideoTexture.CreateFromWebCam` to generate video texture using WebRTC. [Demo](https://www.babylonjs-playground.com#1R77YT#2) - (Sebastien Vandenberghe)(https://github.com/sebavanmicrosoft) / ([deltakosh](https://github.com/deltakosh))
- - New `HolographicCamera` to support rendering on Windows Holographic. - ([sebavan](https://github.com/sebavan))
  - New Facet Data feature ([jerome](https://github.com/jbousquie))
  - babylon.fontTexture.ts was moved from babylon.js to canvas2D ([nockawa](https://github.com/nockawa))
  - Multi-platform Compressed Textures for Desktops & Mobile Devices with fall back.  Batch (dos) scripts to convert entire directories of .jpg's & .png's ([jcpalmer](https://github.com/Palmer-JC))
@@ -35,7 +35,11 @@
 - Improved the internal code of `Vector3.RotationFromAxisToRef()` ([jerome](https://github.com/jbousquie), thanks to [abow](https://github.com/abow))  
 - GroundMeshes are now serialized correctly ([deltakosh](https://github.com/deltakosh))
 - Added `mesh.markVerticesDataAsUpdatable()` to allow a specific vertexbuffer to become updatable ([deltakosh](https://github.com/deltakosh)) 
-
+- Added `POINTERTAP` and `POINTERDOUBLETAP` PointerEventTypes to register new Observer mask. (Demo here)[http://www.babylonjs-playground.com/?30] ([yuccai](https://github.com/yuccai))
+- Added OnDoublePickTrigger for ActionManager ([yuccai](https://github.com/yuccai))
+- Added Scene.DoubleClickDelay to set the timing within a double click event like PointerEventTypes.POINTERDOUBLETAP or ActionManager.OnDoublePickTrigger has to be processed ([yuccai](https://github.com/yuccai))
+- New material: ShadowOnlyMaterial to display shadows on transparent surfaces ([deltakosh](https://github.com/deltakosh)) 
+- Added `VertexBuffer.TangentKind` to specify tangents in place of shader-calculated tangents ([dewadswo](https://github.com/dewadswo), [bghgary](https://github.com/bghgary))
  
 ### Bug fixes
 - Fixed a bug with spotlight direction ([deltakosh](https://github.com/deltakosh)) 
@@ -45,7 +49,7 @@
 - Fixed SPS particle access start index when used with `setParticles(start, end)` ([jerome](https://github.com/jbousquie))  
 
 ### API Documentation
-- File `abstractMesh.ts` documented  ([jerome](https://github.com/jbousquie))  
+`- File `abstractMesh.ts` documented  ([jerome](https://github.com/jbousquie))  
 - File `mesh.ts` documented ([jerome](https://github.com/jbousquie))  
 - File `groundMesh.ts` documented ([jerome](https://github.com/jbousquie))  
 - File `instancedMesh.ts` documented ([jerome](https://github.com/jbousquie))  
@@ -61,6 +65,31 @@
 - File `spotLight.ts` documented ([jerome](https://github.com/jbousquie))  
 - File `shadowGenerator.ts` documented ([jerome](https://github.com/jbousquie))  
 
+### Breaking changes
+- WebVRCamera:
+  - `requestVRFullscreen` has been removed. Call `attachControl()` inside a user-interaction callback to start sending frames to the VR display
+  - `setPositionOffset` has been used to change the position offset. it is now done using `camera.position`
+- Ray :
+  - `show` has been removed. Use new `RayHelper.show()` instead
+  - `hide` has been removed. Use new `RayHelper.hide()` instead
+- AbstractMesh:
+  - `onPhysicsCollide` has been removed. Use `mesh.physicsImpostor.registerOnPhysicsCollide()` instead
+  - `setPhysicsState` has been removed. Use `new PhysicsImpostor()` instead
+  - `getPhysicsMass` has been removed. Use `mesh.physicsImpostor.getParam("mass")` instead
+  - `getPhysicsFriction` has been removed. Use `mesh.physicsImpostor.getParam("friction")` instead
+  - `getPhysicsRestitution` has been removed. Use `mesh.physicsImpostor.getParam("restitution")` instead
+  - `updatePhysicsBodyPosition` has been removed. Changes are synchronized automatically now
+- Mesh:
+  - `updateVerticesDataDirectly` has been removed. Use `mesh.updateVerticesData()` instead
+- SsaoRenderingPipeline:
+  - `getBlurHPostProcess` has been removed. Blur post-process is no more required
+  - `getBlurVPostProcess` has been removed. Blur post-process is no more required
+- Scene:
+  - `setGravity` has been removed. Use `scene.getPhysicsEngine().setGravity()` instead
+  - `createCompoundImpostor` has been removed. Use PhysicsImpostor parent/child instead
+- ActionManager:
+  - `LongPressDelay` and `DragMovementThreshold` are now respectively Scene.LongPressDelay and Scene.DragMovementThreshold
+ 
 ## Canvas2D
 
 ### Major Updates

+ 4 - 0
inspector/sass/_tabPanel.scss

@@ -3,6 +3,10 @@
     &.searchable {
         height:calc(100% - #{$searchbar-height} - 10px);   
     }  
+
+    .texture-image {
+        max-height:400px;
+    }
     
     .scene-actions {
         overflow-y: auto;

+ 1 - 0
inspector/sass/_tree.scss

@@ -15,6 +15,7 @@
   
     
     .line {
+        padding:3px;
         cursor:pointer;
         // Hover
         &:hover {

+ 140 - 107
inspector/src/Inspector.ts

@@ -1,119 +1,129 @@
 module INSPECTOR {
     export class Inspector {
 
-        private _c2diwrapper    : HTMLElement;
+        private _c2diwrapper: HTMLElement;
         // private _detailsPanel: DetailPanel;
         /** The panel displayed at the top of the inspector */
-        private _topPanel       : HTMLElement;
+        private _topPanel: HTMLElement;
         /** The div containing the content of the active tab */
-        private _tabPanel       : HTMLElement;
+        private _tabPanel: HTMLElement;
         /** The panel containing the list if items */
         // private _treePanel   : HTMLElement;
         /** The list if tree items displayed in the tree panel. */
-        private _items          : Array<TreeItem>;
-        private _tabbar         : TabBar;
-        private _scene          : BABYLON.Scene;
+        private _items: Array<TreeItem>;
+        private _tabbar: TabBar;
+        private _scene: BABYLON.Scene;
         /** The HTML document relative to this inspector (the window or the popup depending on its mode) */
-        public static DOCUMENT  : HTMLDocument;
+        public static DOCUMENT: HTMLDocument;
         /** The HTML window. In popup mode, it's the popup itself. Otherwise, it's the current tab */
-        public static WINDOW : Window;
+        public static WINDOW: Window;
         /** True if the inspector is built as a popup tab */
-        private _popupMode      : boolean = false;
+        private _popupMode: boolean = false;
         /** The original canvas style, before applying the inspector*/
-        private _canvasStyle :any ;
+        private _canvasStyle: any;
+
+        private _initialTab: number;
+
+        private _parentElement: HTMLElement;
 
         /** The inspector is created with the given engine.
          * If the parameter 'popup' is false, the inspector is created as a right panel on the main window.
          * If the parameter 'popup' is true, the inspector is created in another popup.
          */
-        constructor(scene: BABYLON.Scene, popup?:boolean) {
+        constructor(scene: BABYLON.Scene, popup?: boolean, initialTab?: number, parentElement?: HTMLElement) {
+
+            //get Tabbar initialTab
+            this._initialTab = initialTab;
+
+            //get parentElement of our Inspector
+            this._parentElement = parentElement;
 
             // get canvas parent only if needed.
-            this._scene     = scene;
-            
+            this._scene = scene;
+
             // Save HTML document and window
-            Inspector.DOCUMENT = window.document;   
-            Inspector.WINDOW = window;                       
-            
+            Inspector.DOCUMENT = window.document;
+            Inspector.WINDOW = window;
+
             // Load the Canvas2D library if it's not already done
             if (!BABYLON.Canvas2D) {
-                BABYLON.Tools.LoadScript("http://www.babylonjs.com/babylon.canvas2d.js", () => {});
+                BABYLON.Tools.LoadScript("http://www.babylonjs.com/babylon.canvas2d.js", () => { });
             }
 
             // POPUP MODE
-            if (popup) { 
+            if (popup) {
                 // Build the inspector in the given parent
                 this.openPopup(true);// set to true in order to NOT dispose the inspector (done in openPopup), as it's not existing yet
-            } else {        
+            } else {
                 // Get canvas and its DOM parent
-                let canvas                    = this._scene.getEngine().getRenderingCanvas();            
-                let canvasParent              = canvas.parentElement;            
+                let canvas = this._scene.getEngine().getRenderingCanvas();
+                let canvasParent = canvas.parentElement;
                 let canvasParentComputedStyle = Inspector.WINDOW.getComputedStyle(canvasParent);
 
                 // get canvas style                
-                let canvasComputedStyle  = Inspector.WINDOW.getComputedStyle(canvas);
+                let canvasComputedStyle = Inspector.WINDOW.getComputedStyle(canvas);
 
-                this._canvasStyle = { 
-                    width        : Helpers.Css(canvas, 'width'),
-                    height       : Helpers.Css(canvas, 'height'),
+                this._canvasStyle = {
+                    width: Helpers.Css(canvas, 'width'),
+                    height: Helpers.Css(canvas, 'height'),
 
-                    position     : canvasComputedStyle.position,
-                    top          : canvasComputedStyle.top,
-                    bottom       : canvasComputedStyle.bottom,
-                    left         : canvasComputedStyle.left,
-                    right        : canvasComputedStyle.right,
+                    position: canvasComputedStyle.position,
+                    top: canvasComputedStyle.top,
+                    bottom: canvasComputedStyle.bottom,
+                    left: canvasComputedStyle.left,
+                    right: canvasComputedStyle.right,
 
-                    padding      : canvasComputedStyle.padding,
+                    padding: canvasComputedStyle.padding,
                     paddingBottom: canvasComputedStyle.paddingBottom,
-                    paddingLeft  : canvasComputedStyle.paddingLeft,
-                    paddingTop   : canvasComputedStyle.paddingTop,
-                    paddingRight : canvasComputedStyle.paddingRight,
+                    paddingLeft: canvasComputedStyle.paddingLeft,
+                    paddingTop: canvasComputedStyle.paddingTop,
+                    paddingRight: canvasComputedStyle.paddingRight,
 
-                    margin       : canvasComputedStyle.margin,
-                    marginBottom : canvasComputedStyle.marginBottom,
-                    marginLeft   : canvasComputedStyle.marginLeft,
-                    marginTop    : canvasComputedStyle.marginTop,
-                    marginRight  : canvasComputedStyle.marginRight
+                    margin: canvasComputedStyle.margin,
+                    marginBottom: canvasComputedStyle.marginBottom,
+                    marginLeft: canvasComputedStyle.marginLeft,
+                    marginTop: canvasComputedStyle.marginTop,
+                    marginRight: canvasComputedStyle.marginRight
 
                 };
-                
+
                 // Create c2di wrapper
-                this._c2diwrapper  = Helpers.CreateDiv('insp-wrapper');
-                
+                this._c2diwrapper = Helpers.CreateDiv('insp-wrapper');
+
                 // copy style from canvas to wrapper
                 for (let prop in this._canvasStyle) {
                     this._c2diwrapper.style[prop] = this._canvasStyle[prop];
                 }
-                
+
                 // Convert wrapper size in % (because getComputedStyle returns px only)
-                let widthPx        = parseFloat(canvasComputedStyle.width.substr(0,canvasComputedStyle.width.length-2)) || 0;
-                let heightPx       = parseFloat(canvasComputedStyle.height.substr(0,canvasComputedStyle.height.length-2)) || 0;
+                let widthPx = parseFloat(canvasComputedStyle.width.substr(0, canvasComputedStyle.width.length - 2)) || 0;
+                let heightPx = parseFloat(canvasComputedStyle.height.substr(0, canvasComputedStyle.height.length - 2)) || 0;
 
                 // If the canvas position is absolute, restrain the wrapper width to the window width + left positionning
                 if (canvasComputedStyle.position === "absolute" || canvasComputedStyle.position === "relative") {
                     // compute only left as it takes predominance if right is also specified (and it will be for the wrapper)
-                    let leftPx = parseFloat(canvasComputedStyle.left.substr(0,canvasComputedStyle.left.length-2)) || 0;
+                    let leftPx = parseFloat(canvasComputedStyle.left.substr(0, canvasComputedStyle.left.length - 2)) || 0;
                     if (widthPx + leftPx >= Inspector.WINDOW.innerWidth) {
-                        this._c2diwrapper.style.maxWidth = `${widthPx-leftPx}px`;
+                        this._c2diwrapper.style.maxWidth = `${widthPx - leftPx}px`;
                     }
                 }
-                
+
                 // Check if the parent of the canvas is the body page. If yes, the size ratio is computed
                 let parent = this._getRelativeParent(canvas);
 
-                let parentWidthPx  = parent.clientWidth;
+                let parentWidthPx = parent.clientWidth;
                 let parentHeightPx = parent.clientHeight;
-                
+
                 let pWidth = widthPx / parentWidthPx * 100;
                 let pheight = heightPx / parentHeightPx * 100;
 
-                this._c2diwrapper.style.width = pWidth+"%";
-                this._c2diwrapper.style.height = pheight+"%";            
+                this._c2diwrapper.style.width = pWidth + "%";
+                this._c2diwrapper.style.height = pheight + "%";
 
                 // reset canvas style
                 canvas.style.position = "static";
-                canvas.style.width    = "100%";
-                canvas.style.height   = "100%";
+                canvas.style.width = "100%";
+                canvas.style.height = "100%";
                 canvas.style.paddingBottom = "0";
                 canvas.style.paddingLeft = "0";
                 canvas.style.paddingTop = "0";
@@ -127,25 +137,47 @@ module INSPECTOR {
 
 
                 // Replace canvas with the wrapper...
+                // if (this._parentElement) {
+                //     canvasParent.replaceChild(this._parentElement, canvas);
+                //     this._parentElement.appendChild(canvas);
+                // }
+                // else {
                 canvasParent.replaceChild(this._c2diwrapper, canvas);
                 // ... and add canvas to the wrapper
                 this._c2diwrapper.appendChild(canvas);
-                // add inspector     
-                let inspector      = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                // }
+
+
+
+                // add inspector
+                let inspector;
+                if (this._parentElement) {
+                    this._c2diwrapper.appendChild(this._parentElement);
+                    inspector = Helpers.CreateDiv('insp-right-panel', this._parentElement);
+                    inspector.style.width = '100%';
+                    inspector.style.height = '100%';
+                }
+                else {
+                    inspector = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                }
+                console.log(inspector);
+
                 // Add split bar
-                Split([canvas, inspector], {
-                    direction:'horizontal',
-                    sizes : [75, 25],
-                    onDrag : () => { 
-                        Helpers.SEND_EVENT('resize');
-                        if (this._tabbar) {
-                            this._tabbar.updateWidth()
+                if (!this._parentElement) {
+                    Split([canvas, inspector], {
+                        direction: 'horizontal',
+                        sizes: [75, 25],
+                        onDrag: () => {
+                            Helpers.SEND_EVENT('resize');
+                            if (this._tabbar) {
+                                this._tabbar.updateWidth()
+                            }
                         }
-                    }
-                });
+                    });
+                }
 
                 // Build the inspector
-                this._buildInspector(inspector);   
+                this._buildInspector(inspector);
                 // Send resize event to the window
                 Helpers.SEND_EVENT('resize');
                 this._tabbar.updateWidth();
@@ -156,25 +188,25 @@ module INSPECTOR {
                 this.refresh();
             }
         }
-        
+
         /**
          * If the given element has a position 'asbolute' or 'relative', 
          * returns the first parent of the given element that has a position 'relative' or 'absolute'.
          * If the given element has no position, returns the first parent
          * 
          */
-        private _getRelativeParent(elem:HTMLElement, lookForAbsoluteOrRelative?:boolean) : HTMLElement{
+        private _getRelativeParent(elem: HTMLElement, lookForAbsoluteOrRelative?: boolean): HTMLElement {
             // If the elem has no parent, returns himself
             if (!elem.parentElement) {
                 return elem;
-            } 
+            }
             let computedStyle = Inspector.WINDOW.getComputedStyle(elem);
             // looking for the first element absolute or relative
             if (lookForAbsoluteOrRelative) {
                 // if found, return this one
                 if (computedStyle.position === "relative" || computedStyle.position === "absolute") {
                     return elem;
-                }else {
+                } else {
                     // otherwise keep looking
                     return this._getRelativeParent(elem.parentElement, true);
                 }
@@ -186,30 +218,30 @@ module INSPECTOR {
                 } else {
                     // the elem has a position relative or absolute, look for the closest relative/absolute parent
                     return this._getRelativeParent(elem.parentElement, true);
-                }         
-            }   
+                }
+            }
         }
-        
+
         /** Build the inspector panel in the given HTML element */
-        private _buildInspector(parent:HTMLElement) {            
+        private _buildInspector(parent: HTMLElement) {
             // tabbar
-            this._tabbar = new TabBar(this);
+            this._tabbar = new TabBar(this, this._initialTab);
 
             // Top panel
             this._topPanel = Helpers.CreateDiv('top-panel', parent);
             // Add tabbar
             this._topPanel.appendChild(this._tabbar.toHtml());
             this._tabbar.updateWidth();
-            
+
             // Tab panel
             this._tabPanel = Helpers.CreateDiv('tab-panel-content', this._topPanel);
-            
+
         }
 
-        public get scene() : BABYLON.Scene {
+        public get scene(): BABYLON.Scene {
             return this._scene;
         }
-        public get popupMode() : boolean {
+        public get popupMode(): boolean {
             return this._popupMode;
         }
 
@@ -217,12 +249,12 @@ module INSPECTOR {
          * Filter the list of item present in the tree.
          * All item returned should have the given filter contained in the item id.
         */
-        public filterItem(filter:string){
+        public filterItem(filter: string) {
             this._tabbar.getActiveTab().filter(filter);
         }
-        
+
         /** Display the mesh tab on the given object */
-        public displayObjectDetails(mesh:BABYLON.AbstractMesh) {
+        public displayObjectDetails(mesh: BABYLON.AbstractMesh) {
             this._tabbar.switchMeshTab(mesh);
         }
 
@@ -234,39 +266,40 @@ module INSPECTOR {
             // Get the active tab and its items
             let activeTab = this._tabbar.getActiveTab();
             activeTab.update();
-            this._tabPanel.appendChild(activeTab.getPanel());            
+            this._tabPanel.appendChild(activeTab.getPanel());
             Helpers.SEND_EVENT('resize');
-            
-        }        
-        
+
+        }
+
         /** Remove the inspector panel when it's built as a right panel:
          * remove the right panel and remove the wrapper
          */
         public dispose() {
             if (!this._popupMode) {
                 // Get canvas
-                let canvas         = this._scene.getEngine().getRenderingCanvas(); 
+                let canvas = this._scene.getEngine().getRenderingCanvas();
 
                 // restore canvas style
                 for (let prop in this._canvasStyle) {
                     canvas.style[prop] = this._canvasStyle[prop];
                 }
                 // Get parent of the wrapper 
-                let canvasParent   = canvas.parentElement.parentElement;  
+                let canvasParent = canvas.parentElement.parentElement;
+
                 canvasParent.insertBefore(canvas, this._c2diwrapper);
                 // Remove wrapper
                 Helpers.CleanDiv(this._c2diwrapper);
-                this._c2diwrapper.remove();                   
+                this._c2diwrapper.remove();
                 // Send resize event to the window
-                Helpers.SEND_EVENT('resize');              
+                Helpers.SEND_EVENT('resize');
             }
         }
-        
+
         /** Open the inspector in a new popup
          * Set 'firstTime' to true if there is no inspector created beforehands
          */
-        public openPopup(firstTime?:boolean) {    
-            
+        public openPopup(firstTime?: boolean) {
+
             if (Helpers.IsBrowserEdge()) {
                 console.warn('Inspector - Popup mode is disabled in Edge, as the popup DOM cannot be updated from the main window for security reasons');
             } else {
@@ -275,16 +308,16 @@ module INSPECTOR {
                 popup.document.title = 'Babylon.js INSPECTOR';
                 // Get the inspector style      
                 let styles = Inspector.DOCUMENT.querySelectorAll('style');
-                for (let s = 0; s<styles.length; s++) {
-                    popup.document.body.appendChild(styles[s].cloneNode(true));              
-                } 
+                for (let s = 0; s < styles.length; s++) {
+                    popup.document.body.appendChild(styles[s].cloneNode(true));
+                }
                 let links = document.querySelectorAll('link');
-                for (let l = 0; l<links.length; l++) {
-                    let link  = popup.document.createElement("link");
-                    link.rel  = "stylesheet";
+                for (let l = 0; l < links.length; l++) {
+                    let link = popup.document.createElement("link");
+                    link.rel = "stylesheet";
                     link.href = (links[l] as HTMLLinkElement).href;
-                    popup.document.head.appendChild(link);              
-                } 
+                    popup.document.head.appendChild(link);
+                }
                 // Dispose the right panel if existing
                 if (!firstTime) {
                     this.dispose();
@@ -295,21 +328,21 @@ module INSPECTOR {
                 Inspector.DOCUMENT = popup.document;
                 Inspector.WINDOW = popup;
                 // Build the inspector wrapper
-                this._c2diwrapper  = Helpers.CreateDiv('insp-wrapper', popup.document.body);
+                this._c2diwrapper = Helpers.CreateDiv('insp-wrapper', popup.document.body);
                 // add inspector     
-                let inspector      = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
+                let inspector = Helpers.CreateDiv('insp-right-panel', this._c2diwrapper);
                 inspector.classList.add('popupmode');
                 // and build it in the popup  
-                this._buildInspector(inspector); 
+                this._buildInspector(inspector);
                 // Rebuild it
-                this.refresh(); 
+                this.refresh();
 
-                popup.addEventListener('resize', () => {                    
+                popup.addEventListener('resize', () => {
                     if (this._tabbar) {
                         this._tabbar.updateWidth()
                     }
                 });
-            }             
+            }
         }
     }
 }

+ 25 - 18
inspector/src/adapters/Adapter.ts

@@ -1,36 +1,36 @@
 module INSPECTOR {
-    
+
     export interface IHighlight {
-        highlight : (b:boolean) => void
+        highlight: (b: boolean) => void
     }
 
     export abstract class Adapter implements IHighlight {
-        
-        protected _obj      : any;
+
+        protected _obj: any;
         // a unique name for this adapter, to retrieve its own key in the local storage
         private static _name: string = BABYLON.Geometry.RandomId();
-        
-        constructor(obj:any) {
+
+        constructor(obj: any) {
             this._obj = obj;
         }
-        
+
 
         /** Returns the name displayed in the tree */
-        public abstract id()           : string;
-        
+        public abstract id(): string;
+
         /** Returns the type of this object - displayed in the tree */
-        public abstract type()         : string;
-        
+        public abstract type(): string;
+
         /** Returns the list of properties to be displayed for this adapter */
         public abstract getProperties(): Array<PropertyLine>;
-        
+
         /** Returns the actual object behind this adapter */
-        public get actualObject()      : any {
-            return this._obj; 
+        public get actualObject(): any {
+            return this._obj;
         }
-        
+
         /** Returns true if the given object correspond to this  */
-        public correspondsTo(obj:any) {
+        public correspondsTo(obj: any) {
             return obj === this._obj;
         }
 
@@ -39,10 +39,17 @@ module INSPECTOR {
             return Adapter._name;
         }
 
+        /**
+         * Returns the actual object used for this adapter
+         */
+        public get object(): any {
+            return this._obj;
+        }
+
         /** Returns the list of tools available for this adapter */
-        public abstract getTools() : Array<AbstractTreeTool>;
+        public abstract getTools(): Array<AbstractTreeTool>;
 
         /** Should be overriden in subclasses */
-        public highlight(b:boolean) {};
+        public highlight(b: boolean) { };
     }
 }

+ 55 - 0
inspector/src/adapters/CameraAdapter.ts

@@ -0,0 +1,55 @@
+module INSPECTOR {
+    
+    export class CameraAdapter 
+        extends Adapter 
+         implements ICameraPOV{
+        
+        constructor(obj:BABYLON.Camera) {
+            super(obj);
+        }
+        
+        /** Returns the name displayed in the tree */
+        public id() : string {
+            let str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        }
+        
+        /** Returns the type of this object - displayed in the tree */
+        public type() : string{
+            return Helpers.GET_TYPE(this._obj);
+        }
+        
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties() : Array<PropertyLine> {
+           let propertiesLines : Array<PropertyLine> = [];
+           let camToDisplay = [];
+           // The if is there to work with the min version of babylon
+            if (this._obj instanceof BABYLON.ArcRotateCamera) {
+                camToDisplay =  PROPERTIES['ArcRotateCamera'].properties;
+            } else if (this._obj instanceof BABYLON.FreeCamera) {
+                camToDisplay =  PROPERTIES['FreeCamera'].properties; 
+            }
+                
+            for (let dirty of camToDisplay) {
+                let infos = new Property(dirty, this._obj);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines; 
+        }
+        
+        public getTools() : Array<AbstractTreeTool> {
+            let tools = [];
+            // tools.push(new Checkbox(this));
+            tools.push(new CameraPOV(this));
+            return tools;
+        }
+
+        public setPOV() {
+           (this._obj as BABYLON.Camera).getScene().activeCamera = this._obj;
+        }
+        
+    }
+}

+ 54 - 0
inspector/src/adapters/SoundAdapter.ts

@@ -0,0 +1,54 @@
+module INSPECTOR {
+
+    export class SoundAdapter
+        extends Adapter
+        implements ISoundInteractions {
+
+        constructor(obj: BABYLON.Sound) {
+            super(obj);
+        }
+
+        /** Returns the name displayed in the tree */
+        public id(): string {
+            let str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        }
+
+        /** Returns the type of this object - displayed in the tree */
+        public type(): string {
+            return Helpers.GET_TYPE(this._obj);
+        }
+
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties(): Array<PropertyLine> {
+            let propertiesLines: Array<PropertyLine> = [];
+            let camToDisplay = [];
+            // The if is there to work with the min version of babylon
+
+            let soundProperties = PROPERTIES['Sound'].properties;
+
+
+            for (let dirty of soundProperties) {
+                let infos = new Property(dirty, this._obj);
+                propertiesLines.push(new PropertyLine(infos));
+            }
+            return propertiesLines;
+        }
+
+        public getTools(): Array<AbstractTreeTool> {
+            let tools = [];
+            tools.push(new SoundInteractions(this));
+            return tools;
+        }
+
+        public setPlaying(callback:Function) {
+            (this._obj as BABYLON.Sound).play();
+            (this._obj as BABYLON.Sound).onended = () => {
+                callback();
+            }
+        }
+    }
+}

+ 37 - 0
inspector/src/adapters/TextureAdapter.ts

@@ -0,0 +1,37 @@
+module INSPECTOR {
+
+    export class TextureAdapter
+        extends Adapter {
+
+        constructor(obj: BABYLON.BaseTexture) {
+            super(obj);
+        }
+
+        /** Returns the name displayed in the tree */
+        public id(): string {
+            let str = '';
+            if (this._obj.name) {
+                str = this._obj.name;
+            } // otherwise nothing displayed        
+            return str;
+        }
+
+        /** Returns the type of this object - displayed in the tree */
+        public type(): string {
+            return Helpers.GET_TYPE(this._obj);
+        }
+
+        /** Returns the list of properties to be displayed for this adapter */
+        public getProperties(): Array<PropertyLine> {
+            // Not used in this tab
+            return [];
+        }
+
+        public getTools(): Array<AbstractTreeTool> {
+            let tools = [];
+            // tools.push(new CameraPOV(this));
+            return tools;
+        }
+
+    }
+}

+ 197 - 131
inspector/src/properties.ts

@@ -1,74 +1,108 @@
 module INSPECTOR {
-    
+
     export const PROPERTIES = {
         /** Format the given object : 
          * If a format function exists, returns the result of this function.
          * If this function doesn't exists, return the object type instead */
-        format : (obj:any) => {
-            let type = Helpers.GET_TYPE(obj) ||  'type_not_defined';
+        format: (obj: any) => {
+            let type = Helpers.GET_TYPE(obj) || 'type_not_defined';
             if (PROPERTIES[type] && PROPERTIES[type].format) {
                 return PROPERTIES[type].format(obj);
             } else {
                 return Helpers.GET_TYPE(obj);
             }
         },
-        'type_not_defined' : {
-            properties : [],
+        'type_not_defined': {
+            properties: [],
             format: () => ''
         },
-        
-        'Vector2' : {
+
+        'Vector2': {
             type: BABYLON.Vector2,
             properties: ['x', 'y'],
-            format: (vec : BABYLON.Vector2) => {return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}`;}
+            format: (vec: BABYLON.Vector2) => { return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}`; }
         },
-        'Vector3' : {
+        'Vector3': {
             type: BABYLON.Vector3,
             properties: ['x', 'y', 'z'],
-            format: (vec : BABYLON.Vector3) => {return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}, z:${Helpers.Trunc(vec.z)}`} 
+            format: (vec: BABYLON.Vector3) => { return `x:${Helpers.Trunc(vec.x)}, y:${Helpers.Trunc(vec.y)}, z:${Helpers.Trunc(vec.z)}` }
         },
-        'Color3' : {
+        'Color3': {
             type: BABYLON.Color3,
-            properties : ['r', 'g', 'b'],
-            format: (color: BABYLON.Color3) => { return `R:${color.r}, G:${color.g}, B:${color.b}`}
+            properties: ['r', 'g', 'b'],
+            format: (color: BABYLON.Color3) => { return `R:${color.r}, G:${color.g}, B:${color.b}` }
         },
-        'Quaternion' : {
+        'Quaternion': {
             type: BABYLON.Quaternion,
-            properties : ['x', 'y', 'z', 'w']
+            properties: ['x', 'y', 'z', 'w']
         },
-        'Size' : {
+        'Size': {
             type: BABYLON.Size,
-            properties :['width', 'height'],
-            format: (size:BABYLON.Size) => { return `Size - w:${Helpers.Trunc(size.width)}, h:${Helpers.Trunc(size.height)}`} 
+            properties: ['width', 'height'],
+            format: (size: BABYLON.Size) => { return `Size - w:${Helpers.Trunc(size.width)}, h:${Helpers.Trunc(size.height)}` }
         },
-        'Texture' : {
+        'Texture': {
             type: BABYLON.Texture,
-            properties :[
-                'hasAlpha', 
-                'level', 
-                'name', 
-                'wrapU', 
-                'wrapV', 
-                'uScale', 
-                'vScale', 
-                'uAng', 
-                'vAng', 
-                'wAng', 
-                'uOffset', 
+            properties: [
+                'hasAlpha',
+                'level',
+                'name',
+                'wrapU',
+                'wrapV',
+                'uScale',
+                'vScale',
+                'uAng',
+                'vAng',
+                'wAng',
+                'uOffset',
                 'vOffset'
             ],
-            format: (tex:BABYLON.Texture) => { return tex.name} 
+            format: (tex: BABYLON.Texture) => { return tex.name }
+        },
+        'MapTexture': {
+            type: BABYLON.MapTexture
+        },
+        'RenderTargetTexture': {
+            type: BABYLON.RenderTargetTexture
+        },
+        'DynamicTexture': {
+            type: BABYLON.DynamicTexture
+        },
+        'BaseTexture': {
+            type: BABYLON.BaseTexture
+        },
+        'FontTexture': {
+            type: BABYLON.FontTexture
+        },
+
+        'Sound': {
+            type: BABYLON.Sound,
+            properties: [
+                'name',
+                'autoplay',
+                'loop',
+                'useCustomAttenuation',
+                'soundTrackId',
+                'spatialSound',
+                'refDistance',
+                'rolloffFactor',
+                'maxDistance',
+                'distanceModel',
+                'isPlaying',
+                'isPaused'
+            ]
         },
-        
-        'ArcRotateCamera' : {
+
+        'ArcRotateCamera': {
             type: BABYLON.ArcRotateCamera,
-            properties : [
-                'alpha', 
-                'beta', 
+            properties: [
+                'position',
+                'alpha',
+                'beta',
                 'radius',
                 'angularSensibilityX',
                 'angularSensibilityY',
-                'target', 
+                'target',
 
                 'lowerAlphaLimit',
                 'lowerBetaLimit',
@@ -81,53 +115,85 @@ module INSPECTOR {
                 'wheelPrecision',
                 'allowUpsideDown',
                 'checkCollisions'
-            ]  
+            ]
+        },
+
+        'FreeCamera': {
+            type: BABYLON.FreeCamera,
+            properties: [
+                'position',
+                'rotation',
+                'rotationQuaternion',
+                'cameraDirection',
+                'cameraRotation',
+
+                'ellipsoid',
+                'applyGravity',
+                'angularSensibility',
+                'keysUp',
+                'keysDown',
+                'keysLeft',
+                'keysRight',
+                'checkCollisions',
+                'speed',
+                'lockedTarget',
+                'noRotationConstraint',
+                'fov',
+                'inertia',
+                'minZ', 'maxZ',
+                'layerMask',
+                'mode',
+                'orthoBottom',
+                'orthoTop',
+                'orthoLeft',
+                'orthoRight'
+            ]
         },
-        
-        'Scene' : {
+
+        'Scene': {
             type: BABYLON.Scene,
-            properties:[
-                'actionManager', 
-                'activeCamera', 
-                'ambientColor', 
+            properties: [
+                'actionManager',
+                'activeCamera',
+                'ambientColor',
                 'clearColor',
                 'forceWireframe',
                 'forcePointsCloud',
                 'forceShowBoundingBoxes',
                 'useRightHandedSystem',
                 'hoverCursor',
-                'cameraToUseForPointers', 
-                'fogEnabled', 
-                'fogColor', 
-                'fogDensity', 
-                'fogStart', 
-                'fogEnd', 
-                'shadowsEnabled', 
-                'lightsEnabled', 
+                'cameraToUseForPointers',
+                'fogEnabled',
+                'fogColor',
+                'fogDensity',
+                'fogStart',
+                'fogEnd',
+                'shadowsEnabled',
+                'lightsEnabled',
                 'collisionsEnabled',
                 'gravity',
-                'meshUnderPointer', 
-                'pointerX', 
-                'pointerY', 
+                'meshUnderPointer',
+                'pointerX',
+                'pointerY',
                 'uid'
-            ]  
+            ]
         },
         'Mesh': {
             type: BABYLON.Mesh,
-            properties : [
-                'name', 
-                'position', 
-                'rotation', 
-                'rotationQuaternion', 
-                'absolutePosition', 
+            properties: [
+                'name',
+                'position',
+                'rotation',
+                'rotationQuaternion',
+                'absolutePosition',
                 'material',
-                'actionManager', 
-                'visibility', 
-                'isVisible', 
-                'isPickable', 
+                'actionManager',
+                'visibility',
+                'isVisible',
+                'isPickable',
                 'renderingGroupId',
-                'receiveShadows', 
-                'renderOutline', 
+                'receiveShadows',
+                'renderOutline',
                 'outlineColor',
                 'outlineWidth',
                 'renderOverlay',
@@ -137,102 +203,102 @@ module INSPECTOR {
                 'useVertexColors',
                 'layerMask',
                 'alwaysSelectAsActiveMesh',
-                'ellipsoid', 
-                'ellipsoidOffset', 
-                'edgesWidth', 
-                'edgesColor', 
+                'ellipsoid',
+                'ellipsoidOffset',
+                'edgesWidth',
+                'edgesColor',
                 'checkCollisions',
                 'hasLODLevels'
             ],
-            format : (m:BABYLON.Mesh) : string => {return m.name;}
-        },        
-        'StandardMaterial' : {
+            format: (m: BABYLON.Mesh): string => { return m.name; }
+        },
+        'StandardMaterial': {
             type: BABYLON.StandardMaterial,
-            properties : [
-                'name', 
+            properties: [
+                'name',
                 'alpha',
-                'alphaMode', 
-                'wireframe', 
-                'isFrozen', 
+                'alphaMode',
+                'wireframe',
+                'isFrozen',
                 'zOffset',
-                
-                'ambientColor', 
-                'emissiveColor', 
-                'diffuseColor', 
+
+                'ambientColor',
+                'emissiveColor',
+                'diffuseColor',
                 'specularColor',
-                
-                'specularPower',       
-                'useAlphaFromDiffuseTexture', 
+
+                'specularPower',
+                'useAlphaFromDiffuseTexture',
                 'linkEmissiveWithDiffuse',
                 'useSpecularOverAlpha',
-                
-                'diffuseFresnelParameters', 
-                'opacityFresnelParameters', 
-                'reflectionFresnelParameters', 
-                'refractionFresnelParameters', 
+
+                'diffuseFresnelParameters',
+                'opacityFresnelParameters',
+                'reflectionFresnelParameters',
+                'refractionFresnelParameters',
                 'emissiveFresnelParameters',
-                
-                'diffuseTexture', 
-                'emissiveTexture', 
-                'specularTexture', 
+
+                'diffuseTexture',
+                'emissiveTexture',
+                'specularTexture',
                 'ambientTexture',
                 'bumpTexture',
-                'lightMapTexture', 
-                'opacityTexture', 
+                'lightMapTexture',
+                'opacityTexture',
                 'reflectionTexture',
-                'refractionTexture'                
+                'refractionTexture'
             ],
-            format : (mat:BABYLON.StandardMaterial) : string => {return mat.name;}
+            format: (mat: BABYLON.StandardMaterial): string => { return mat.name; }
         },
-        'PrimitiveAlignment':{
+        'PrimitiveAlignment': {
             type: BABYLON.PrimitiveAlignment,
-            properties:['horizontal', 'vertical']
+            properties: ['horizontal', 'vertical']
         },
-        'PrimitiveThickness':{
+        'PrimitiveThickness': {
             type: BABYLON.PrimitiveThickness,
-            properties:['topPixels', 'leftPixels', 'rightPixels', 'bottomPixels']
+            properties: ['topPixels', 'leftPixels', 'rightPixels', 'bottomPixels']
         },
-        'BoundingInfo2D':{
+        'BoundingInfo2D': {
             type: BABYLON.BoundingInfo2D,
-            properties:['radius','center', 'extent']
+            properties: ['radius', 'center', 'extent']
         },
-        'SolidColorBrush2D':{
+        'SolidColorBrush2D': {
             type: BABYLON.SolidColorBrush2D,
-            properties:['color']
+            properties: ['color']
         },
-        'GradientColorBrush2D':{
+        'GradientColorBrush2D': {
             type: BABYLON.GradientColorBrush2D,
-            properties:['color1', 'color2', 'translation', 'rotation', 'scale']
+            properties: ['color1', 'color2', 'translation', 'rotation', 'scale']
         },
-        'PBRMaterial' : {
+        'PBRMaterial': {
             type: BABYLON.PBRMaterial,
             properties: [
-                'name', 
-                'albedoColor', 
-                'albedoTexture', 
+                'name',
+                'albedoColor',
+                'albedoTexture',
 
-                'opacityTexture', 
-                'reflectionTexture', 
-                'emissiveTexture', 
-                'bumpTexture', 
-                'lightmapTexture', 
+                'opacityTexture',
+                'reflectionTexture',
+                'emissiveTexture',
+                'bumpTexture',
+                'lightmapTexture',
 
-                'opacityFresnelParameters', 
-                'emissiveFresnelParameters', 
+                'opacityFresnelParameters',
+                'emissiveFresnelParameters',
 
-                'linkEmissiveWithAlbedo', 
-                'useLightmapAsShadowmap', 
+                'linkEmissiveWithAlbedo',
+                'useLightmapAsShadowmap',
 
-                'useAlphaFromAlbedoTexture', 
-                'useSpecularOverAlpha', 
-                'useAutoMicroSurfaceFromReflectivityMap', 
-                'useLogarithmicDepth', 
+                'useAlphaFromAlbedoTexture',
+                'useSpecularOverAlpha',
+                'useAutoMicroSurfaceFromReflectivityMap',
+                'useLogarithmicDepth',
 
                 'reflectivityColor',
                 'reflectivityTexture',
                 'reflectionTexture',
                 'reflectionColor',
-                
+
                 'alpha',
                 'linkRefractionWithTransparency',
                 'indexOfRefraction',
@@ -250,7 +316,7 @@ module INSPECTOR {
                 'cameraColorCurves'
             ]
         }
-        
+
     }
-    
+
 }

+ 22 - 0
inspector/src/tabs/CameraTab.ts

@@ -0,0 +1,22 @@
+module INSPECTOR{
+    
+    export class CameraTab extends PropertyTab {
+                
+        constructor(tabbar:TabBar, inspector:Inspector) {
+            super(tabbar, 'Camera', inspector); 
+        }
+    /* Overrides super */
+        protected _getTree() : Array<TreeItem> {
+            let arr = [];
+                        
+            // get all cameras from the first scene
+            let instances = this._inspector.scene;
+            for (let camera of instances.cameras) {
+                arr.push(new TreeItem(this, new CameraAdapter(camera)));
+            }
+            return arr;
+        }
+
+    }
+    
+}

+ 49 - 85
inspector/src/tabs/ShaderTab.ts

@@ -2,80 +2,81 @@ module INSPECTOR {
 
     export class ShaderTab extends Tab {
 
-        private _inspector : Inspector;
-        
-        private _vertexPanel : HTMLElement;
-        private _fragmentPanel : HTMLElement;
+        private _inspector: Inspector;
 
-        constructor(tabbar:TabBar, insp:Inspector) {
-            super(tabbar, 'Shader');            
+        private _vertexPanel: HTMLElement;
+        private _fragmentPanel: HTMLElement;
+
+        constructor(tabbar: TabBar, insp: Inspector) {
+            super(tabbar, 'Shader');
             this._inspector = insp;
 
             // Build the shaders panel : a div that will contains the shaders tree and both shaders panels
-            this._panel         = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+            this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
 
-            let shaderPanel     = Helpers.CreateDiv('shader-tree-panel') as HTMLDivElement;
-            this._vertexPanel   = Helpers.CreateDiv('shader-panel') as HTMLDivElement;
+            let shaderPanel = Helpers.CreateDiv('shader-tree-panel') as HTMLDivElement;
+            this._vertexPanel = Helpers.CreateDiv('shader-panel') as HTMLDivElement;
             this._fragmentPanel = Helpers.CreateDiv('shader-panel') as HTMLDivElement;
 
             this._panel.appendChild(shaderPanel);
             this._panel.appendChild(this._vertexPanel);
             this._panel.appendChild(this._fragmentPanel);
-            
+
             Helpers.LoadScript();
-            
+
             Split([this._vertexPanel, this._fragmentPanel], {
-                blockDrag : this._inspector.popupMode,
-                sizes:[50, 50],
-                direction:'vertical'}
-            );  
-            
+                blockDrag: this._inspector.popupMode,
+                sizes: [50, 50],
+                direction: 'vertical'
+            }
+            );
+
             let comboBox = Helpers.CreateElement('select', '', shaderPanel);
             comboBox.addEventListener('change', this._selectShader.bind(this));
-            
+
             let option = Helpers.CreateElement('option', '', comboBox);
             option.textContent = 'Select a shader';
             option.setAttribute('value', "");
             option.setAttribute('disabled', 'true');
-            option.setAttribute('selected', 'true');            
-            
+            option.setAttribute('selected', 'true');
+
             // Build shaders combobox
             for (let mat of this._inspector.scene.materials) {
                 if (mat instanceof BABYLON.ShaderMaterial) {
                     let option = Helpers.CreateElement('option', '', comboBox);
                     option.setAttribute('value', mat.id);
                     option.textContent = `${mat.name} - ${mat.id}`;
-                    
+
                 }
             }
 
         }
-        
-        private _selectShader(event:Event) {
+
+        private _selectShader(event: Event) {
             let id = (event.target as HTMLSelectElement).value;
-            let mat = this._inspector.scene.getMaterialByID(id); 
-            
+            let mat = this._inspector.scene.getMaterialByID(id);
+
             // Clean shader panel
             Helpers.CleanDiv(this._vertexPanel);
             // add the title - vertex shader
             let title = Helpers.CreateDiv('shader-panel-title', this._vertexPanel);
             title.textContent = 'Vertex shader';
             // add code
-            let code = Helpers.CreateElement('code', 'glsl',  Helpers.CreateElement('pre', '', this._vertexPanel));
+            let code = Helpers.CreateElement('code', 'glsl', Helpers.CreateElement('pre', '', this._vertexPanel));
             code.textContent = this._beautify(mat.getEffect().getVertexShaderSource());
-            
+
             Helpers.CleanDiv(this._fragmentPanel);
             // add the title - fragment shader
             title = Helpers.CreateDiv('shader-panel-title', this._fragmentPanel);
             title.textContent = 'Frgament shader';
             // add code
-            code = Helpers.CreateElement('code', 'glsl',  Helpers.CreateElement('pre', '', this._fragmentPanel));
-            code.textContent = this._beautify(mat.getEffect().getFragmentShaderSource());         
-                                    
+            code = Helpers.CreateElement('code', 'glsl', Helpers.CreateElement('pre', '', this._fragmentPanel));
+            code.textContent = this._beautify(mat.getEffect().getFragmentShaderSource());
+
             // Init the syntax highlighting
             let styleInit = Helpers.CreateElement('script', '', Inspector.DOCUMENT.body);
             styleInit.textContent = 'hljs.initHighlighting();';
-            
+
         }
 
         /** Overrides super.dispose */
@@ -85,9 +86,9 @@ module INSPECTOR {
         /** Returns the position of the first { and the corresponding } */
         private _getBracket(str) {
             let fb = str.indexOf('{');
-            let arr = str.substr(fb+1).split('');
+            let arr = str.substr(fb + 1).split('');
             let counter = 1;
-            let currentPosInString  = fb;
+            let currentPosInString = fb;
             let lastBracketIndex = 0;
             for (let char of arr) {
                 currentPosInString++;
@@ -99,82 +100,45 @@ module INSPECTOR {
                 }
                 if (counter == 0) {
                     lastBracketIndex = currentPosInString;
-                    break;         
+                    break;
                 }
             }
 
-            return {firstBracket : fb, lastBracket:lastBracketIndex};
+            return { firstBracket: fb, lastBracket: lastBracketIndex };
         }
 
         /** 
          * Beautify the given string : correct indentation
          */
-        private _beautify(glsl:string, level: number = 0) {
-            
+        private _beautify(glsl: string, level: number = 0) {
+
             // return condition : no brackets at all
             let brackets = this._getBracket(glsl);
-            let firstBracket       = brackets.firstBracket;
-            let lastBracket        = brackets.lastBracket;
+            let firstBracket = brackets.firstBracket;
+            let lastBracket = brackets.lastBracket;
 
             let spaces = "";
-            for (let i=0 ;i<level; i++) {
+            for (let i = 0; i < level; i++) {
                 spaces += "    "; // 4 spaces
             }
             // If no brackets, return the indented string
             if (firstBracket == -1) {
-                glsl = spaces+glsl; // indent first line
+                glsl = spaces + glsl; // indent first line
                 glsl = glsl
-                    .replace(/;./g, x => '\n'+x.substr(1)) // new line after ;  except the last one
-                    glsl = glsl.replace(/=/g, " = ") // space around =
-                    glsl = glsl.replace(/\n/g, "\n"+spaces); // indentation
+                    .replace(/;./g, x => '\n' + x.substr(1)) // new line after ;  except the last one
+                glsl = glsl.replace(/=/g, " = ") // space around =
+                glsl = glsl.replace(/\n/g, "\n" + spaces); // indentation
                 return glsl;
             } else {
                 // if brackets, beautify the inside                                 
                 // let insideWithBrackets = glsl.substr(firstBracket, lastBracket-firstBracket+1);
-                let left   = glsl.substr(0, firstBracket);
-                let right  = glsl.substr(lastBracket+1, glsl.length); 
-                let inside = glsl.substr(firstBracket+1, lastBracket-firstBracket-1);
-                inside     = this._beautify(inside, level+1);
-                return this._beautify(left, level)+'{\n'+inside+'\n'+spaces+'}\n'+this._beautify(right, level);
+                let left = glsl.substr(0, firstBracket);
+                let right = glsl.substr(lastBracket + 1, glsl.length);
+                let inside = glsl.substr(firstBracket + 1, lastBracket - firstBracket - 1);
+                inside = this._beautify(inside, level + 1);
+                return this._beautify(left, level) + '{\n' + inside + '\n' + spaces + '}\n' + this._beautify(right, level);
 
             }
-
-                
-                // // Replace bracket with @1 and @2 with correct indentation
-                // let newInside          = "@1\n\t" + inside + "\n@2";
-                // newInside              = newInside.replace(/;\n/g, ";\n\t");
-                
-                // glsl                   = glsl.replace(insideWithBrackets, newInside);
-
-                // firstBracket       = glsl.indexOf('{');
-                // lastBracket        = glsl.lastIndexOf('}');
-
-            // }
-
-            // console.log(glsl);
-
-            // let regex = /(\{(?:\{??[^\{]*?}))+/gmi;
-
-            // let tmp = glsl;
-
-            // let m;
-            // while ((m = regex.exec(tmp)) !== null) {
-            //     // This is necessary to avoid infinite loops with zero-width matches
-            //     if (m.index === regex.lastIndex) {
-            //         regex.lastIndex++;
-            //     }                
-            //     // The result can be accessed through the `m`-variable.
-            //     m.forEach((match, groupIndex) => {
-            //         // Remove the first and the last bracket only
-            //         let matchWithoutBrackets = match.replace(/{/, "").replace(/}/, "");
-            //         // Indent the content inside brackets with tabs
-            //         glsl = glsl.replace(match, `{\n\t${matchWithoutBrackets}\n}\n`);
-            //         // removes the match from tmp
-            //         tmp = tmp.replace(match, "");
-            //         // and continue
-            //     });
-            // }
-            // return 
         }
     }
 

+ 26 - 0
inspector/src/tabs/SoundTab.ts

@@ -0,0 +1,26 @@
+module INSPECTOR{
+    
+    export class SoundTab extends PropertyTab {
+                
+        constructor(tabbar:TabBar, inspector:Inspector) {
+            super(tabbar, 'Sound', inspector); 
+        }
+    /* Overrides super */
+        protected _getTree() : Array<TreeItem> {
+            let arr = [];
+                        
+            // get all cameras from the first scene
+            let instances = this._inspector.scene;
+            for (let sounds of instances.soundTracks) {
+                let sound = sounds.soundCollection;
+                sound.forEach(element => {
+                    arr.push(new TreeItem(this, new SoundAdapter(element)));
+                });
+                
+            }
+            return arr;
+        }
+
+    }
+    
+}

+ 42 - 31
inspector/src/tabs/Tab.ts

@@ -1,70 +1,81 @@
-module INSPECTOR{
-    
+module INSPECTOR {
+
     export abstract class Tab extends BasicElement {
-        protected _tabbar  : TabBar;
+        protected _tabbar: TabBar;
         // The tab name displayed in the tabbar
-        public name        : string;        
+        public name: string;
         protected _isActive: boolean = false;
-        
+
         // The whole panel corresponding to this tab. It's what is displayed when the tab is activacted
-        protected _panel : HTMLDivElement;
-        
-        constructor(tabbar:TabBar, name:string) {
-            super();      
-            this._tabbar = tabbar;      
-            this.name   = name;
+        protected _panel: HTMLDivElement;
+
+        constructor(tabbar: TabBar, name: string) {
+            super();
+            this._tabbar = tabbar;
+            this.name = name;
             this._build();
         }
-        
+
         /** True if the tab is active, false otherwise */
-        public isActive() : boolean {
+        public isActive(): boolean {
             return this._isActive;
         }
-        
-        protected _build() {            
-            this._div.className = 'tab';  
-            this._div.textContent = this.name;     
-            
+
+        protected _build() {
+            this._div.className = 'tab';
+            this._div.textContent = this.name;
+
             this._div.addEventListener('click', (evt) => {
                 // Set this tab as active
                 this._tabbar.switchTab(this);
             });
         }
-        
+
         /** Set this tab as active or not, depending on the current state */
-        public active(b:boolean) {
-            if (b) {                
+        public active(b: boolean) {
+            if (b) {
                 this._div.classList.add('active');
             } else {
                 this._div.classList.remove('active');
             }
             this._isActive = b;
         }
-        
+
         public update() {
             // Nothing for the moment
         }
-        
-        /** Creates the tab panel for this tab. */   
-        public getPanel() : HTMLElement {
+
+        /** Creates the tab panel for this tab. */
+        public getPanel(): HTMLElement {
             return this._panel;
         }
-        
+
         /** Add this in the propertytab with the searchbar */
-        public filter(str:string) {};
+        public filter(str: string) { };
 
         /** Dispose properly this tab */
         public abstract dispose();
 
+        /** Select an item in the tree */
+        public select(item: TreeItem) {
+            // To define in subclasses if needed 
+        }
+
+
+        /** Highlight the given node, and downplay all others */
+        public highlightNode(item?: TreeItem) {
+            // To define in subclasses if needed
+        }
+
         /** 
          * Returns the total width in pixel of this tab, 0 by default
         */
-        public getPixelWidth() : number {
+        public getPixelWidth(): number {
             let style = Inspector.WINDOW.getComputedStyle(this._div);
-            let left = parseFloat(style.marginLeft.substr(0,style.marginLeft.length-2)) ||0;
-            let right = parseFloat(style.marginRight.substr(0,style.marginRight.length-2)) ||0;
+            let left = parseFloat(style.marginLeft.substr(0, style.marginLeft.length - 2)) || 0;
+            let right = parseFloat(style.marginRight.substr(0, style.marginRight.length - 2)) || 0;
             return (this._div.clientWidth || 0) + left + right;
         }
     }
-    
+
 }

+ 45 - 35
inspector/src/tabs/TabBar.ts

@@ -4,30 +4,31 @@ module INSPECTOR {
      * The default active tab is the first one of the list.
      */
     export class TabBar extends BasicElement {
-        
+
         // The list of available tabs
-        private _tabs         : Array<Tab> = [];
-        private _inspector    : Inspector;
+        private _tabs: Array<Tab> = [];
+        private _inspector: Inspector;
         /** The tab displaying all meshes */
-        private _meshTab      : MeshTab;
+        private _meshTab: MeshTab;
         /** The toolbar */
-        private _toolBar      : Toolbar;
+        private _toolBar: Toolbar;
         /** The icon displayed at the end of the toolbar displaying a combo box of tabs not displayed */
-        private _moreTabsIcon : HTMLElement;
+        private _moreTabsIcon: HTMLElement;
         /** The panel displayed when the 'more-tab' icon is selected */
         private _moreTabsPanel: HTMLElement;
         /** The list of tab displayed by clicking on the remainingIcon */
         private _invisibleTabs: Array<Tab> = [];
         /** The list of tabs visible, displayed in the tab bar */
-        private _visibleTabs  : Array<Tab> = [];
-                
-        constructor(inspector:Inspector) {
+        private _visibleTabs: Array<Tab> = [];
+
+        constructor(inspector: Inspector, initialTab?: number) {
             super();
             this._inspector = inspector;
             this._tabs.push(new SceneTab(this, this._inspector));
             this._tabs.push(new ConsoleTab(this, this._inspector));
             this._tabs.push(new StatsTab(this, this._inspector));
-            this._meshTab = new MeshTab(this, this._inspector); 
+            this._meshTab = new MeshTab(this, this._inspector);
+            this._tabs.push(new TextureTab(this, this._inspector));
             this._tabs.push(this._meshTab);
             this._tabs.push(new ShaderTab(this, this._inspector));
             this._tabs.push(new LightTab(this, this._inspector));
@@ -37,24 +38,33 @@ module INSPECTOR {
             }
             this._tabs.push(new MaterialTab(this, this._inspector));
 
+            this._tabs.push(new CameraTab(this, this._inspector));
+            this._tabs.push(new SoundTab(this, this._inspector));
+
             this._toolBar = new Toolbar(this._inspector);
 
             this._build();
-            // Active the first tab
-            this._tabs[0].active(true); 
+
+            //Check initialTab is defined and between tabs bounds
+            if (!initialTab || initialTab < 0 || initialTab >= this._tabs.length) {
+                initialTab = 0;
+                console.warn('');
+            }
+
+            this._tabs[initialTab].active(true);
 
             // set all tab as visible
             for (let tab of this._tabs) {
                 this._visibleTabs.push(tab);
             }
         }
-        
+
         // No update
-        public update() {}
-        
-        protected _build() {            
+        public update() { }
+
+        protected _build() {
             this._div.className = 'tabbar';
-            
+
             this._div.appendChild(this._toolBar.toHtml());
             for (let tab of this._tabs) {
                 this._div.appendChild(tab.toHtml());
@@ -62,7 +72,7 @@ module INSPECTOR {
 
 
             this._moreTabsIcon = Helpers.CreateElement('i', 'fa fa-angle-double-right more-tabs');
-         
+
             this._moreTabsPanel = Helpers.CreateDiv('more-tabs-panel');
 
             this._moreTabsIcon.addEventListener('click', () => {
@@ -72,7 +82,7 @@ module INSPECTOR {
                 } else {
                     // Attach more-tabs-panel if not attached yet
                     let topPanel = this._div.parentNode as HTMLElement;
-                    if (! topPanel.contains(this._moreTabsPanel)) {
+                    if (!topPanel.contains(this._moreTabsPanel)) {
                         topPanel.appendChild(this._moreTabsPanel);
                     }
                     // Clean the 'more-tabs-panel'
@@ -80,7 +90,7 @@ module INSPECTOR {
                     // Add each invisible tabs to this panel
                     for (let tab of this._invisibleTabs) {
                         this._addInvisibleTabToPanel(tab);
-                    }        
+                    }
                     // And display it
                     this._moreTabsPanel.style.display = 'flex';
                 }
@@ -91,7 +101,7 @@ module INSPECTOR {
          * Add a tab to the 'more-tabs' panel, displayed by clicking on the 
          * 'more-tabs' icon
          */
-        private _addInvisibleTabToPanel(tab:Tab) {
+        private _addInvisibleTabToPanel(tab: Tab) {
             let div = Helpers.CreateDiv('invisible-tab', this._moreTabsPanel);
             div.textContent = tab.name;
             div.addEventListener('click', () => {
@@ -99,9 +109,9 @@ module INSPECTOR {
                 this.switchTab(tab);
             });
         }
-        
+
         /** Dispose the current tab, set the given tab as active, and refresh the treeview */
-        public switchTab(tab:Tab) {
+        public switchTab(tab: Tab) {
             // Dispose the active tab
             this.getActiveTab().dispose();
 
@@ -111,24 +121,24 @@ module INSPECTOR {
             }
             // activate the given tab
             tab.active(true);
-            
+
             // Refresh the inspector
             this._inspector.refresh();
         }
-        
+
         /** Display the mesh tab.
          * If a parameter is given, the given mesh details are displayed
          */
-        public switchMeshTab(mesh?:BABYLON.AbstractMesh) {
+        public switchMeshTab(mesh?: BABYLON.AbstractMesh) {
             this.switchTab(this._meshTab);
             if (mesh) {
                 let item = this._meshTab.getItemFor(mesh);
                 this._meshTab.select(item);
             }
         }
-        
+
         /** Returns the active tab */
-        public getActiveTab() : Tab {
+        public getActiveTab(): Tab {
             for (let tab of this._tabs) {
                 if (tab.isActive()) {
                     return tab;
@@ -136,7 +146,7 @@ module INSPECTOR {
             }
         }
 
-        public get inspector() : Inspector {
+        public get inspector(): Inspector {
             return this._inspector;
         }
 
@@ -144,7 +154,7 @@ module INSPECTOR {
          * Returns the total width in pixel of the tabbar, 
          * that corresponds to the sum of the width of each visible tab + toolbar width
         */
-        public getPixelWidth() : number {
+        public getPixelWidth(): number {
             let sum = 0;
             for (let tab of this._visibleTabs) {
                 sum += tab.getPixelWidth();
@@ -162,7 +172,7 @@ module INSPECTOR {
         public updateWidth() {
             let parentSize = this._div.parentElement.clientWidth;
             let lastTabWidth = 75;
-            let currentSize = this.getPixelWidth(); 
+            let currentSize = this.getPixelWidth();
 
             // Check if a tab should be removed : if the tab bar width is greater than
             // its parent width
@@ -173,7 +183,7 @@ module INSPECTOR {
                 this._invisibleTabs.push(tab);
                 // and removes it from the DOM
                 this._div.removeChild(tab.toHtml());
-                currentSize = this.getPixelWidth() + lastTabWidth;                
+                currentSize = this.getPixelWidth() + lastTabWidth;
             }
 
             // Check if a tab can be added to the tab bar : if the tab bar width
@@ -184,15 +194,15 @@ module INSPECTOR {
                     this._div.appendChild(lastTab.toHtml());
                     this._visibleTabs.push(lastTab);
                     // Update more-tab icon in last position if needed
-                     if (this._div.contains(this._moreTabsIcon)) {
+                    if (this._div.contains(this._moreTabsIcon)) {
                         this._div.removeChild(this._moreTabsIcon);
-                     }
+                    }
                 }
             }
             if (this._invisibleTabs.length > 0 && !this._div.contains(this._moreTabsIcon)) {
                 this._div.appendChild(this._moreTabsIcon);
             }
         }
-        
+
     }
 }

+ 143 - 0
inspector/src/tabs/TextureTab.ts

@@ -0,0 +1,143 @@
+module INSPECTOR {
+
+    export class TextureTab extends Tab {
+
+        private _inspector: Inspector;
+        /** The panel containing a list of items */
+        protected _treePanel: HTMLElement;
+        protected _treeItems: Array<TreeItem> = [];
+
+        /* Panel containing the texture image */
+        private _imagePanel: HTMLElement;
+
+        constructor(tabbar: TabBar, inspector: Inspector) {
+            super(tabbar, 'Textures');
+            this._inspector = inspector;
+
+            // Build the properties panel : a div that will contains the tree and the detail panel
+            this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
+
+            // Build the treepanel
+            this._treePanel = Helpers.CreateDiv('insp-tree', this._panel);
+
+            this._imagePanel = Helpers.CreateDiv('image-panel', this._panel) as HTMLDivElement;
+
+            Split([this._treePanel, this._imagePanel], {
+                blockDrag: this._inspector.popupMode,
+                direction: 'vertical'
+            });
+
+            this.update();
+        }
+
+        public dispose() {
+            // Nothing to dispose
+        }
+
+        public update(_items?: Array<TreeItem>) {
+
+            let items;
+            if (_items) {
+                items = _items;
+            } else {
+                // Rebuild the tree
+                this._treeItems = this._getTree();
+                items = this._treeItems;
+            }
+            // Clean the tree
+            Helpers.CleanDiv(this._treePanel);
+            Helpers.CleanDiv(this._imagePanel);
+
+            // Sort items alphabetically
+            items.sort((item1, item2) => {
+                return item1.compareTo(item2);
+            });
+
+            // Display items
+            for (let item of items) {
+                this._treePanel.appendChild(item.toHtml());
+            }
+        }
+
+        /* Overrides super */
+        private _getTree(): Array<TreeItem> {
+            let arr = [];
+
+            // get all cameras from the first scene
+            let instances = this._inspector.scene;
+            for (let tex of instances.textures) {
+                arr.push(new TreeItem(this, new TextureAdapter(tex)));
+            }
+            return arr;
+        }
+
+        /** Display the details of the given item */
+        public displayDetails(item: TreeItem) {
+            // Remove active state on all items
+            this.activateNode(item);
+            Helpers.CleanDiv(this._imagePanel);
+            // Get the texture object
+            let texture = item.adapter.object;
+
+            let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
+
+            if (texture instanceof BABYLON.MapTexture) {
+                // instance of Map texture
+                texture.bindTextureForPosSize(new BABYLON.Vector2(0, 0), new BABYLON.Size(texture.getSize().width, texture.getSize().height), false);
+                BABYLON.Tools.DumpFramebuffer(texture.getSize().width, texture.getSize().height, this._inspector.scene.getEngine(), (data) => img.src = data);
+                texture.unbindTexture();
+
+            }
+            else if (texture instanceof BABYLON.RenderTargetTexture) {
+                // RenderTarget textures
+                BABYLON.Tools.CreateScreenshotUsingRenderTarget(this._inspector.scene.getEngine(), texture.activeCamera, { precision: 1 }, (data) => img.src = data);
+
+            } else if (texture.url) {
+                // If an url is present, the texture is an image
+                img.src = texture.url;
+
+            } else if (texture['_canvas']) {
+                // Dynamic texture
+                let base64Image = texture['_canvas'].toDataURL("image/png");
+                img.src = base64Image;
+
+            }
+
+
+        }
+
+        /** Select an item in the tree */
+        public select(item: TreeItem) {
+            // Remove the node highlight
+            this.highlightNode();
+            // Active the node
+            this.activateNode(item);
+            // Display its details
+            this.displayDetails(item);
+        }
+
+        /** Set the given item as active in the tree */
+        public activateNode(item: TreeItem) {
+            if (this._treeItems) {
+                for (let node of this._treeItems) {
+                    node.active(false);
+                }
+            }
+            item.active(true);
+        }
+
+        /** Highlight the given node, and downplay all others */
+        public highlightNode(item?: TreeItem) {
+            if (this._treeItems) {
+                for (let node of this._treeItems) {
+                    node.highlight(false);
+                }
+            }
+            if (item) {
+                item.highlight(true);
+            }
+        }
+
+    }
+
+}

+ 49 - 42
inspector/src/tree/TreeItem.ts

@@ -1,50 +1,57 @@
- module INSPECTOR {
-     
-    export class TreeItem extends BasicElement{
-    
+module INSPECTOR {
+
+    export class TreeItem extends BasicElement {
+
         // Reference to the tab
-        private _tab        : PropertyTab;
+        private _tab: Tab;
         // The object this item is linked to (should be a primitive or a canvas) TODO should be superclass of all primitives
-        private _adapter    : Adapter;
-        private _tools      : Array<AbstractTreeTool>;
-        public children     : Array<TreeItem> = [];
+        private _adapter: Adapter;
+        private _tools: Array<AbstractTreeTool>;
+        public children: Array<TreeItem> = [];
         // Div element that contains all children of this node.
-        private _lineContent: HTMLElement;  
+        private _lineContent: HTMLElement;
 
-        constructor(tab:PropertyTab, obj:Adapter ) {
+        constructor(tab: Tab, obj: Adapter) {
             super();
             this._tab = tab;
             this._adapter = obj;
-            
+
             this._tools = this._adapter.getTools();
-            
+
             this._build();
-            
+
         }
 
         /** Returns the item ID == its adapter ID */
-        public get id() : string {
+        public get id(): string {
             return this._adapter.id();
         }
 
         /** Add the given item as a child of this one */
-        public add(child:TreeItem) {
+        public add(child: TreeItem) {
             this.children.push(child);
             this.update();
         }
 
         /**
+         * Returns the original adapter
+         */
+        public get adapter(): Adapter {
+            return this._adapter;
+        }
+
+        /**
          * Function used to compare this item to another tree item.
          * Returns the alphabetical sort of the adapter ID
          */
-        public compareTo(item:TreeItem) : number {
+        public compareTo(item: TreeItem): number {
             let str1 = this.id;
             let str2 = item.id;
-            return str1.localeCompare(str2, [], {numeric:true});
+            return str1.localeCompare(str2, [], { numeric: true });
         }
-        
+
         /** Returns true if the given obj correspond to the adapter linked to this tree item */
-        public correspondsTo(obj:any) : boolean {
+        public correspondsTo(obj: any): boolean {
             return this._adapter.correspondsTo(obj);
         }
 
@@ -70,30 +77,30 @@
                 this._div.classList.remove('folded');
             }
         }
-        
+
         /** Build the HTML of this item */
         protected _build() {
-            this._div.className = 'line'; 
-            
-            
-            for (let tool of this._tools) { 
+            this._div.className = 'line';
+
+
+            for (let tool of this._tools) {
                 this._div.appendChild(tool.toHtml());
             }
-            
-            
+
+
             // Id
-            let text = Inspector.DOCUMENT.createElement('span');        
+            let text = Inspector.DOCUMENT.createElement('span');
             text.textContent = this._adapter.id();
             this._div.appendChild(text);
-            
+
             // Type
-            let type = Inspector.DOCUMENT.createElement('span'); 
+            let type = Inspector.DOCUMENT.createElement('span');
             type.className = 'property-type';
             if (this._adapter.type() !== 'type_not_defined') {
-                type.textContent = ' - '+this._adapter.type();
-            } 
+                type.textContent = ' - ' + this._adapter.type();
+            }
             this._div.appendChild(type);
-            
+
             this._lineContent = Helpers.CreateDiv('line-content', this._div);
 
             this._addEvent();
@@ -102,10 +109,10 @@
         /**
          * Returns one HTML element (.details) containing all  details of this primitive
          */
-        public getDetails() : Array<PropertyLine> {
+        public getDetails(): Array<PropertyLine> {
             return this._adapter.getProperties();
         }
-        
+
         public update() {
             // Clean division holding all children
             Helpers.CleanDiv(this._lineContent);
@@ -122,7 +129,7 @@
             }
             this.fold();
         }
-        
+
         /**
          * Add an event listener on the item : 
          * - one click display details
@@ -133,10 +140,10 @@
                 this._tab.select(this);
                 // Fold/unfold the tree
                 if (this._isFolded()) {
-                    this.unfold();        
+                    this.unfold();
                 } else {
                     this.fold();
-                }                   
+                }
                 e.stopPropagation();
             });
 
@@ -152,7 +159,7 @@
         }
 
         /** Highlight or downplay this node */
-        public highlight(b:boolean) {
+        public highlight(b: boolean) {
             // Remove highlight for all children 
             if (!b) {
                 for (let child of this.children) {
@@ -162,14 +169,14 @@
             // Highlight this node
             this._adapter.highlight(b);
         }
-        
+
         /** Returns true if the node is folded, false otherwise */
-        private _isFolded() : boolean {
+        private _isFolded(): boolean {
             return !this._div.classList.contains('unfolded');
         }
 
         /** Set this item as active (background lighter) in the tree panel */
-        public active(b:boolean) {
+        public active(b: boolean) {
             this._div.classList.remove('active');
             for (let child of this.children) {
                 child.active(false);
@@ -179,4 +186,4 @@
             }
         }
     }
- }
+}

+ 39 - 0
inspector/src/treetools/CameraPOV.ts

@@ -0,0 +1,39 @@
+module INSPECTOR {
+
+    export interface ICameraPOV {
+        setPOV: () => void
+    }
+
+    /**
+     * 
+     */
+    export class CameraPOV extends AbstractTreeTool {
+        private cameraPOV: ICameraPOV;
+
+        constructor(camera: ICameraPOV) {
+            super();
+            this.cameraPOV = camera;
+            this._elem.classList.add('fa-video-camera');
+        }
+
+        protected action() {
+            super.action();
+            this._gotoPOV();
+        }
+
+        private _gotoPOV() {
+
+            let actives = Inspector.DOCUMENT.querySelectorAll(".fa-video-camera.active");
+            console.log(actives);
+            for (let i = 0; i < actives.length; i++) {
+                actives[i].classList.remove('active');
+            }
+            //if (this._on) {
+                // set icon camera
+                this._elem.classList.add('active');
+            //}
+            this.cameraPOV.setPOV();
+
+        }
+    }
+}

+ 42 - 0
inspector/src/treetools/SoundInteractions.ts

@@ -0,0 +1,42 @@
+module INSPECTOR {
+
+    export interface ISoundInteractions {
+        setPlaying: (callback: Function) => void;
+    }
+
+    /**
+     * 
+     */
+    export class SoundInteractions extends AbstractTreeTool {
+        private playSound: ISoundInteractions;
+        private b: boolean;
+
+        constructor(playSound: ISoundInteractions) {
+            super();
+            this.playSound = playSound;
+            this.b = false;
+            this._elem.classList.add('fa-play');
+        }
+
+        protected action() {
+            super.action();
+            this._playSound();
+        }
+
+        private _playSound() {
+
+            if (this._elem.classList.contains('fa-play')) {
+                this._elem.classList.remove('fa-play');
+                this._elem.classList.add('fa-pause');
+            }
+            else {
+                this._elem.classList.remove('fa-pause');
+                this._elem.classList.add('fa-play');
+            }
+            this.playSound.setPlaying(() => {
+                this._elem.classList.remove('fa-pause');
+                this._elem.classList.add('fa-play');
+            });
+        }
+    }
+}

+ 240 - 91
inspector/test/index.js

@@ -5,8 +5,8 @@ var Test = (function () {
     function Test(canvasId) {
         var _this = this;
         var canvas = document.getElementById(canvasId);
-        this.engine = new BABYLON.Engine(canvas, true);					
-		BABYLONDEVTOOLS.Loader.debugShortcut(this.engine);
+        this.engine = new BABYLON.Engine(canvas, true);
+        BABYLONDEVTOOLS.Loader.debugShortcut(this.engine);
         this.scene = null;
         window.addEventListener("resize", function () {
             _this.engine.resize();
@@ -16,7 +16,7 @@ var Test = (function () {
     Test.prototype._run = function () {
         var _this = this;
         this._initScene();
-        this.scene.debugLayer.show();
+        this.scene.debugLayer.show({ popup: false, initialTab: 4 });
         this.scene.executeWhenReady(function () {
             _this._initGame();
             _this.engine.runRenderLoop(function () {
@@ -26,65 +26,204 @@ var Test = (function () {
     };
     Test.prototype._initScene = function () {
         var scene = new BABYLON.Scene(this.engine);
-        var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 4, Math.PI / 2.5, 200, BABYLON.Vector3.Zero(), scene);
-        camera.attachControl(this.engine.getRenderingCanvas(), true);
-        camera.minZ = 0.1;
-        // Lights
-        var light0 = new BABYLON.PointLight("Omni0", new BABYLON.Vector3(0, 10, 0), scene);
-        var light1 = new BABYLON.PointLight("Omni1", new BABYLON.Vector3(0, -10, 0), scene);
-        var light2 = new BABYLON.PointLight("Omni2", new BABYLON.Vector3(10, 0, 0), scene);
-        var light3 = new BABYLON.DirectionalLight("Dir0", new BABYLON.Vector3(1, -1, 0), scene);
-        var material = new BABYLON.StandardMaterial("kosh", scene);
-        var sphere = BABYLON.Mesh.CreateSphere("Sphere", 16, 3, scene);
-        // Creating light sphere
-        var lightSphere0 = BABYLON.Mesh.CreateSphere("Sphere0", 16, 0.5, scene);
-        var lightSphere1 = BABYLON.Mesh.CreateSphere("Sphere1", 16, 0.5, scene);
-        var lightSphere2 = BABYLON.Mesh.CreateSphere("Sphere2", 16, 0.5, scene);
-        lightSphere0.material = new BABYLON.StandardMaterial("red", scene);
-        lightSphere0.material.diffuseColor = new BABYLON.Color3(0, 0, 0);
-        lightSphere0.material.specularColor = new BABYLON.Color3(0, 0, 0);
-        lightSphere0.material.emissiveColor = new BABYLON.Color3(1, 0, 0);
-        lightSphere1.material = new BABYLON.StandardMaterial("green", scene);
-        lightSphere1.material.diffuseColor = new BABYLON.Color3(0, 0, 0);
-        lightSphere1.material.specularColor = new BABYLON.Color3(0, 0, 0);
-        lightSphere1.material.emissiveColor = new BABYLON.Color3(0, 1, 0);
-        lightSphere2.material = new BABYLON.StandardMaterial("blue", scene);
-        lightSphere2.material.diffuseColor = new BABYLON.Color3(0, 0, 0);
-        lightSphere2.material.specularColor = new BABYLON.Color3(0, 0, 0);
-        lightSphere2.material.emissiveColor = new BABYLON.Color3(0, 0, 1);
-        // Sphere material
-        material.diffuseColor = new BABYLON.Color3(1, 1, 1);
-        sphere.material = material;
-        // Lights colors
-        light0.diffuse = new BABYLON.Color3(1, 0, 0);
-        light0.specular = new BABYLON.Color3(1, 0, 0);
-        light1.diffuse = new BABYLON.Color3(0, 1, 0);
-        light1.specular = new BABYLON.Color3(0, 1, 0);
-        light2.diffuse = new BABYLON.Color3(0, 0, 1);
-        light2.specular = new BABYLON.Color3(0, 0, 1);
-        light3.diffuse = new BABYLON.Color3(1, 1, 1);
-        light3.specular = new BABYLON.Color3(1, 1, 1);
-        BABYLON.Effect.ShadersStore["customVertexShader"] = 'precision highp float;attribute vec3 position;attribute vec2 uv;uniform mat4 worldViewProjection;varying vec2 vUV;varying vec3 vPos;void main(){gl_Position=worldViewProjection*vec4(position,1.),vPos=gl_Position.xyz;if(position.x >2.0) {gl_Position.x = 2.0;} else { gl_Position.y = 1.0;}}';
-        BABYLON.Effect.ShadersStore["customFragmentShader"] = 'precision highp float;varying vec3 vPos;uniform vec3 color;void main(){gl_FragColor=vec4(mix(color,vPos,.05),1.);}';
-        var shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
-            vertex: "custom",
-            fragment: "custom",
-        }, {
-            attributes: ["position", "normal", "uv"],
-            uniforms: ["world", "worldView", "worldViewProjection", "view", "projection"]
+        var canvas = scene.getEngine().getRenderingCanvas();
+        var light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
+
+        var camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, 1.0, 110, new BABYLON.Vector3(0, -20, 0), scene);
+        camera.attachControl(canvas, true);
+
+        var camera1 = new BABYLON.ArcRotateCamera("Camera1", 1.58, 1.2, 110, BABYLON.Vector3.Zero(), scene);
+        var camera2 = new BABYLON.ArcRotateCamera("Camera2", Math.PI / 3, .8, 40, BABYLON.Vector3.Zero(), scene);
+        var camera3 = new BABYLON.ArcRotateCamera("Camera3", -Math.PI / 5, 1.0, 70, BABYLON.Vector3.Zero(), scene);
+        var camera4 = new BABYLON.ArcRotateCamera("Camera4", -Math.PI / 6, 1.3, 110, BABYLON.Vector3.Zero(), scene);
+
+        camera1.layerMask = 2;
+        camera2.layerMask = 2;
+        camera3.layerMask = 2;
+        camera4.layerMask = 2;
+        /*
+            camera1.parent = camera;
+            camera2.parent = camera;
+            camera3.parent = camera;
+            camera4.parent = camera;
+        */
+
+        //Sounds
+        var jump = new BABYLON.Sound("Jump", "/assets/sounds/jump.wav", scene);
+        var explosion = new BABYLON.Sound("Explosion", "/assets/sounds/explosion.wav", scene);
+        jump.setVolume(0.1);
+        window.addEventListener("keydown", function (evt) {
+            if (evt.keyCode === 32) {
+                jump.play();
+            }
         });
-        sphere.material = shaderMaterial;
-        // Animations
-        var alpha = 0;
-        scene.beforeRender = function () {
-            light0.position = new BABYLON.Vector3(10 * Math.sin(alpha), 0, 10 * Math.cos(alpha));
-            light1.position = new BABYLON.Vector3(10 * Math.sin(alpha), 0, -10 * Math.cos(alpha));
-            light2.position = new BABYLON.Vector3(10 * Math.cos(alpha), 0, 10 * Math.sin(alpha));
-            lightSphere0.position = light0.position;
-            lightSphere1.position = light1.position;
-            lightSphere2.position = light2.position;
-            alpha += 0.01;
-        };
+
+        var sphere1 = BABYLON.Mesh.CreateSphere("Sphere1", 10.0, 9.0, scene);
+        var sphere2 = BABYLON.Mesh.CreateSphere("Sphere2", 2.0, 9.0, scene);//Only two segments
+        var sphere3 = BABYLON.Mesh.CreateSphere("Sphere3", 10.0, 9.0, scene);
+        var sphere4 = BABYLON.Mesh.CreateSphere("Sphere4", 10.0, 9.0, scene);
+        var sphere5 = BABYLON.Mesh.CreateSphere("Sphere5", 10.0, 9.0, scene);
+        var sphere6 = BABYLON.Mesh.CreateSphere("Sphere6", 10.0, 9.0, scene);
+
+        sphere1.position.x = 40;
+        sphere2.position.x = 25;
+        sphere3.position.x = 10;
+        sphere4.position.x = -5;
+        sphere5.position.x = -20;
+        sphere6.position.x = -35;
+
+        var rt1 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
+        scene.customRenderTargets.push(rt1);
+        rt1.activeCamera = camera1;
+        rt1.renderList = scene.meshes;
+
+        var rt2 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
+        scene.customRenderTargets.push(rt2);
+        rt2.activeCamera = camera2;
+        rt2.renderList = scene.meshes;
+
+        var rt3 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
+        scene.customRenderTargets.push(rt3);
+        rt3.activeCamera = camera3;
+        rt3.renderList = scene.meshes;
+
+        var rt4 = new BABYLON.RenderTargetTexture("depth", 1024, scene, true, true);
+        scene.customRenderTargets.push(rt4);
+        rt4.activeCamera = camera4;
+        rt4.renderList = scene.meshes;
+
+        var mon1 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
+        mon1.position = new BABYLON.Vector3(-8.8, -7.8, 25)
+        // mon1.showBoundingBox = true;
+        var mon1mat = new BABYLON.StandardMaterial("texturePlane", scene);
+        mon1mat.diffuseTexture = rt1;
+        mon1.material = mon1mat;
+        mon1.parent = camera;
+        mon1.layerMask = 1;
+
+        var mon2 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
+        mon2.position = new BABYLON.Vector3(-2.9, -7.8, 25)
+        // mon2.showBoundingBox = true;
+        var mon2mat = new BABYLON.StandardMaterial("texturePlane", scene);
+        mon2mat.diffuseTexture = rt2;
+        mon2.material = mon2mat;
+        mon2.parent = camera;
+        mon2.layerMask = 1;
+
+        var mon3 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
+        mon3.position = new BABYLON.Vector3(2.9, -7.8, 25)
+        // mon3.showBoundingBox = true;
+        var mon3mat = new BABYLON.StandardMaterial("texturePlane", scene);
+        mon3mat.diffuseTexture = rt3;
+        mon3.material = mon3mat;
+        mon3.parent = camera;
+        mon3.layerMask = 1;
+
+
+        var mon4 = BABYLON.Mesh.CreatePlane("plane", 5, scene);
+        mon4.position = new BABYLON.Vector3(8.8, -7.8, 25)
+        // mon4.showBoundingBox = true;
+        var mon4mat = new BABYLON.StandardMaterial("texturePlane", scene);
+        mon4mat.diffuseTexture = rt4;
+        mon4.material = mon4mat;
+        mon4.parent = camera;
+        mon4.layerMask = 1;
+
+
+        var butback = BABYLON.MeshBuilder.CreatePlane("plane", { width: 25, height: 6 }, scene);
+        butback.position = new BABYLON.Vector3(0, -8.2, 26)
+        // butback.showBoundingBox = true;
+        var butbackmat = new BABYLON.StandardMaterial("texturePlane", scene);
+        butbackmat.diffuseColor = BABYLON.Color3.Black();
+        butback.material = butbackmat;
+        butback.parent = camera;
+        butback.layerMask = 1;
+
+        var plane = BABYLON.Mesh.CreatePlane("plane", 120, scene);
+        plane.position.y = -5;
+        plane.rotation.x = Math.PI / 2;
+
+        var materialSphere1 = new BABYLON.StandardMaterial("texture1", scene);
+        materialSphere1.wireframe = true;
+
+        //Creation of a red material with alpha
+        var materialSphere2 = new BABYLON.StandardMaterial("texture2", scene);
+        materialSphere2.diffuseColor = new BABYLON.Color3(1, 0, 0); //Red
+        materialSphere2.alpha = 0.3;
+
+        //Creation of a material with an image texture
+        var materialSphere3 = new BABYLON.StandardMaterial("texture3", scene);
+        materialSphere3.diffuseTexture = new BABYLON.Texture("/assets/textures/amiga.jpg", scene);
+
+        //Creation of a material with translated texture
+        var materialSphere4 = new BABYLON.StandardMaterial("texture4", scene);
+        materialSphere4.diffuseTexture = new BABYLON.Texture("/assets/textures/floor.png", scene);
+        materialSphere4.diffuseTexture.vOffset = 0.1;//Vertical offset of 10%
+        materialSphere4.diffuseTexture.uOffset = 0.4;//Horizontal offset of 40%
+
+        //Creation of a material with an alpha texture
+        var materialSphere5 = new BABYLON.StandardMaterial("texture5", scene);
+        materialSphere5.diffuseTexture = new BABYLON.Texture("/assets/textures/rock.png", scene);
+        materialSphere5.diffuseTexture.hasAlpha = true;//Has an alpha
+
+        //Creation of a material and show all the faces
+        var materialSphere6 = new BABYLON.StandardMaterial("texture6", scene);
+        materialSphere6.diffuseTexture = new BABYLON.Texture("/assets/textures/grass.png", scene);
+        materialSphere6.diffuseTexture.hasAlpha = true;//Have an alpha
+        materialSphere6.backFaceCulling = false;//Show all the faces of the element
+
+        //Creation of a repeated textured material
+        var materialPlane = new BABYLON.StandardMaterial("texturePlane", scene);
+        materialPlane.diffuseTexture = new BABYLON.Texture("/assets/textures/mixMap.png", scene);
+        materialPlane.diffuseTexture.uScale = 5.0;
+        materialPlane.diffuseTexture.vScale = 5.0;
+        materialPlane.backFaceCulling = false;
+
+        //Apply the materials to meshes
+        sphere1.material = materialSphere1;
+        sphere2.material = materialSphere2;
+
+        sphere3.material = materialSphere3;
+        sphere4.material = materialSphere4;
+
+        sphere5.material = materialSphere5;
+        sphere6.material = materialSphere6;
+
+        plane.material = materialPlane;
+
+
+
+        var d = 50;
+        var cubes = new Array();
+        for (var i = 0; i < 360; i += 20) {
+            var r = BABYLON.Tools.ToRadians(i);
+            var b = BABYLON.Mesh.CreateBox("Box #" + i / 20, 5, scene, false);
+            b.position.x = Math.cos(r) * d;
+            b.position.z = Math.sin(r) * d;
+            cubes.push(b);
+        }
+        var canvas = new BABYLON.ScreenSpaceCanvas2D(scene, {
+            id: "ScreenCanvas",
+            cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS
+
+        });
+        i = 0;
+        for (var _i = 0, cubes_1 = cubes; _i < cubes_1.length; _i++) {
+            var cube = cubes_1[_i];
+            new BABYLON.Group2D({
+                parent: canvas, id: "GroupTag #" + i, width: 80, height: 40, trackNode: cube, origin: BABYLON.Vector2.Zero(),
+                children: [
+                    new BABYLON.Rectangle2D({
+                        id: "firstRect", width: 80, height: 26, x: 0, y: 0, origin: BABYLON.Vector2.Zero(), border: "#FFFFFFFF", fill: "#808080FF", children: [
+                            new BABYLON.Text2D(cube.name, { marginAlignment: "h: center, v:center", fontName: "bold 12px Arial" })
+                        ]
+                    })
+                ]
+            });
+            ++i;
+        }
+
+
         this.scene = scene;
     };
     Test.prototype._initGame = function () {
@@ -95,36 +234,46 @@ var Test = (function () {
      */
     Test.prototype._createCanvas = function () {
         // object hierarchy  g1 -> g2 -> rect
-            
-            // when cachingStrategy is 1 or 2 - everything is rendered
-            // when it is 3 - only direct children of g1 are rendered
-            var canvas = new BABYLON.ScreenSpaceCanvas2D(this.scene, 
-                { id: "ScreenCanvas", 
-                cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_DONTCACHE });           // 1
-                // cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS });      // 2 
-                // cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_ALLGROUPS });           // 3
-            
-            canvas.createCanvasProfileInfoCanvas();
-
-            // parent group            
-            var g1 = new BABYLON.Group2D({parent: canvas, id: "G1",
-                x: 50, y: 50, size: new BABYLON.Size(60, 60)});
-
-            // just to see it    
-            let frame1 = new BABYLON.Rectangle2D({parent: g1, 
-                         x : 0, y: 0,  size: g1.size, border: "#FF0000FF" });
-            
-            // child group
-            let g2 = new BABYLON.Group2D({parent: g1, id: "G2",  
-                        x: 10, y: 10, size: new BABYLON.Size(40, 40)});
-
-            // just to see it
-            let frame2 = new BABYLON.Rectangle2D({parent: g2, x : 0, y: 0, size: g2.size, border: "#0000FFFF" }); 
-            
-            let rect =   new BABYLON.Rectangle2D({parent: g2, x : 10, y: 10, size: new BABYLON.Size(20, 20), 
-                                fill: "#00FF00FF" }) ;              
-                      
-            return canvas;
+
+        // when cachingStrategy is 1 or 2 - everything is rendered
+        // when it is 3 - only direct children of g1 are rendered
+        var canvas = new BABYLON.ScreenSpaceCanvas2D(this.scene,
+            {
+                id: "ScreenCanvas",
+                cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_DONTCACHE
+            });           // 1
+        // cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_TOPLEVELGROUPS });      // 2 
+        // cachingStrategy: BABYLON.Canvas2D.CACHESTRATEGY_ALLGROUPS });           // 3
+
+        canvas.createCanvasProfileInfoCanvas();
+
+        // parent group            
+        var g1 = new BABYLON.Group2D({
+            parent: canvas, id: "G1",
+            x: 50, y: 50, size: new BABYLON.Size(60, 60)
+        });
+
+        // just to see it    
+        let frame1 = new BABYLON.Rectangle2D({
+            parent: g1,
+            x: 0, y: 0, size: g1.size, border: "#FF0000FF"
+        });
+
+        // child group
+        let g2 = new BABYLON.Group2D({
+            parent: g1, id: "G2",
+            x: 10, y: 10, size: new BABYLON.Size(40, 40)
+        });
+
+        // just to see it
+        let frame2 = new BABYLON.Rectangle2D({ parent: g2, x: 0, y: 0, size: g2.size, border: "#0000FFFF" });
+
+        let rect = new BABYLON.Rectangle2D({
+            parent: g2, x: 10, y: 10, size: new BABYLON.Size(20, 20),
+            fill: "#00FF00FF"
+        });
+
+        return canvas;
     };
     return Test;
 }());

+ 1 - 1
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -934,7 +934,7 @@ module BABYLON {
                 var translation = node.translation || [0, 0, 0];
                 var rotation = node.rotation || [0, 0, 0, 1];
                 var scale = node.scale || [1, 1, 1];
-                configureNode(lastNode, Vector3.FromArray(translation), Quaternion.RotationAxis(Vector3.FromArray(rotation).normalize(), rotation[3]), Vector3.FromArray(scale));
+                configureNode(lastNode, Vector3.FromArray(translation), Quaternion.FromArray(rotation), Vector3.FromArray(scale));
             }
 
             lastNode.updateCache(true);

+ 1 - 1
localDev/index.html

@@ -86,4 +86,4 @@
 			});
 	</script>
 </body>
-</html>
+</html>

+ 6 - 2
materialsLibrary/index.html

@@ -168,7 +168,6 @@
 				std.diffuseTexture.uScale = 5;
 				std.diffuseTexture.vScale = 5;
 
-                // Lava
                 var lava = prepareLava();
 
 				var simple = new BABYLON.SimpleMaterial("simple", scene);
@@ -199,19 +198,24 @@
 				var sky = prepareSky();
                 
                 var grid = prepareGrid();
+
+				var shadowOnly = new BABYLON.ShadowOnlyMaterial();
 				
 				// Default to std
 				var currentMaterial = std;
 				sphere.material = std;				
 				sphere.receiveShadows = true;
 
-				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky', 'grid']).onFinishChange(function () {
+				gui.add(options, 'material', ['standard', 'simple', 'water', 'fire', 'lava', 'normal', 'terrain', 'pbr', 'fur', 'triPlanar', 'gradient', 'sky', 'grid', 'shadowOnly']).onFinishChange(function () {
 					water.enableRenderTargets(false);
 					skybox.material = skyboxMaterial;
 					currentMesh.isVisible = true;
 					fur.resetFur();
                     
 					switch (options.material) {
+						case "shadowOnly":
+							currentMaterial = shadowOnly;
+							break;						
 						case "simple":
 							currentMaterial = simple;
 							break;

+ 240 - 0
materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts

@@ -0,0 +1,240 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON {
+    class ShadowOnlyMaterialDefines extends MaterialDefines {
+        public CLIPPLANE = false;
+        public POINTSIZE = false;
+        public FOG = false;
+        public NORMAL = false;
+        public NUM_BONE_INFLUENCERS = 0;
+        public BonesPerMesh = 0;
+        public INSTANCES = false;
+
+        constructor() {
+            super();
+            this.rebuild();
+        }
+    }
+
+    export class ShadowOnlyMaterial extends Material {
+        @serialize()
+
+        private _worldViewProjectionMatrix = Matrix.Zero();
+        private _scaledDiffuse = new Color3();
+        private _renderId: number;
+
+        private _defines = new ShadowOnlyMaterialDefines();
+        private _cachedDefines = new ShadowOnlyMaterialDefines();
+
+        constructor(name: string, scene: Scene) {
+            super(name, scene);
+
+            this._cachedDefines.BonesPerMesh = -1;
+        }
+
+        public needAlphaBlending(): boolean {
+            return true;
+        }
+
+        public needAlphaTesting(): boolean {
+            return false;
+        }
+
+        public getAlphaTestTexture(): BaseTexture {
+            return null;
+        }
+
+        // Methods   
+        private _checkCache(scene: Scene, mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (!mesh) {
+                return true;
+            }
+
+            if (this._defines.INSTANCES !== useInstances) {
+                return false;
+            }
+
+            if (mesh._materialDefines && mesh._materialDefines.isEqual(this._defines)) {
+                return true;
+            }
+
+            return false;
+        }
+
+        public isReady(mesh?: AbstractMesh, useInstances?: boolean): boolean {
+            if (this.checkReadyOnlyOnce) {
+                if (this._wasPreviouslyReady) {
+                    return true;
+                }
+            }
+
+            var scene = this.getScene();
+
+            if (!this.checkReadyOnEveryCall) {
+                if (this._renderId === scene.getRenderId()) {
+                    if (this._checkCache(scene, mesh, useInstances)) {
+                        return true;
+                    }
+                }
+            }
+
+            var engine = scene.getEngine();
+            var needNormals = false;
+
+            this._defines.reset();
+
+            // Effect
+            if (scene.clipPlane) {
+                this._defines.CLIPPLANE = true;
+            }
+
+            // Point size
+            if (this.pointsCloud || scene.forcePointsCloud) {
+                this._defines.POINTSIZE = true;
+            }
+
+            // Fog
+            if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
+                this._defines.FOG = true;
+            }
+
+            if (scene.lightsEnabled) {
+                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, 1);
+            }
+
+            // Attribs
+            if (mesh) {
+                if (needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
+                    this._defines.NORMAL = true;
+                }
+                if (mesh.useBones && mesh.computeBonesUsingShaders) {
+                    this._defines.NUM_BONE_INFLUENCERS = mesh.numBoneInfluencers;
+                    this._defines.BonesPerMesh = (mesh.skeleton.bones.length + 1);
+                }
+
+                // Instances
+                if (useInstances) {
+                    this._defines.INSTANCES = true;
+                }
+            }
+
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+
+                scene.resetCachedMaterial();
+
+                // Fallbacks
+                var fallbacks = new EffectFallbacks();             
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+
+                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, 1);
+                
+                if (this._defines.NUM_BONE_INFLUENCERS > 0) {
+                    fallbacks.addCPUSkinningFallback(0, mesh);
+                }
+
+                //Attributes
+                var attribs = [VertexBuffer.PositionKind];
+
+                if (this._defines.NORMAL) {
+                    attribs.push(VertexBuffer.NormalKind);
+                }
+
+                MaterialHelper.PrepareAttributesForBones(attribs, mesh, this._defines, fallbacks);
+                MaterialHelper.PrepareAttributesForInstances(attribs, this._defines);
+
+                var shaderName = "shadowOnly";
+                var join = this._defines.toString();
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType",
+                                "vFogInfos", "vFogColor", "pointSize",
+                                "mBones",
+                                "vClipPlane", "depthValues"
+                ];
+                var samplers = [];
+                    
+                MaterialHelper.PrepareUniformsAndSamplersList(uniforms, samplers, this._defines, 1);
+                
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs, uniforms, samplers,
+                    join, fallbacks, this.onCompiled, this.onError, {maxSimultaneousLights: 1});
+            }
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            this._renderId = scene.getRenderId();
+            this._wasPreviouslyReady = true;
+
+            if (mesh) {
+                if (!mesh._materialDefines) {
+                    mesh._materialDefines = new ShadowOnlyMaterialDefines();
+                }
+
+                this._defines.cloneTo(mesh._materialDefines);
+            }
+
+            return true;
+        }
+
+        public bindOnlyWorldMatrix(world: Matrix): void {
+            this._effect.setMatrix("world", world);
+        }
+
+        public bind(world: Matrix, mesh?: Mesh): void {
+            var scene = this.getScene();
+
+            // Matrices        
+            this.bindOnlyWorldMatrix(world);
+            this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
+
+            // Bones
+            MaterialHelper.BindBonesParameters(mesh, this._effect);
+
+            if (scene.getCachedMaterial() !== this) {
+                // Clip plane
+                MaterialHelper.BindClipPlane(this._effect, scene);
+
+                // Point size
+                if (this.pointsCloud) {
+                    this._effect.setFloat("pointSize", this.pointSize);
+                }
+
+                this._effect.setVector3("vEyePosition", scene._mirroredCameraPosition ? scene._mirroredCameraPosition : scene.activeCamera.position);                
+            }
+
+            // Lights
+            if (scene.lightsEnabled) {
+                MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, 1);          
+            }
+
+            // View
+            if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE) {
+                this._effect.setMatrix("view", scene.getViewMatrix());
+            }
+
+            // Fog
+            MaterialHelper.BindFogParameters(scene, mesh, this._effect);
+
+            super.bind(world, mesh);
+        }
+
+        public clone(name: string): ShadowOnlyMaterial {
+            return SerializationHelper.Clone<ShadowOnlyMaterial>(() => new ShadowOnlyMaterial(name, this.getScene()), this);
+        }
+        
+        public serialize(): any {
+            var serializationObject = SerializationHelper.Serialize(this);
+            serializationObject.customType = "BABYLON.ShadowOnlyMaterial";
+            return serializationObject;
+        }
+
+        // Statics
+        public static Parse(source: any, scene: Scene, rootUrl: string): ShadowOnlyMaterial {
+            return SerializationHelper.Parse(() => new ShadowOnlyMaterial(source.name, scene), source, scene, rootUrl);
+        }
+    }
+} 
+

+ 50 - 0
materialsLibrary/src/shadowOnly/shadowOnly.fragment.fx

@@ -0,0 +1,50 @@
+precision highp float;
+
+// Constants
+uniform vec3 vEyePosition;
+
+// Input
+varying vec3 vPositionW;
+
+#ifdef NORMAL
+varying vec3 vNormalW;
+#endif
+
+// Lights
+#include<lightFragmentDeclaration>[0..maxSimultaneousLights]
+
+#include<lightsFragmentFunctions>
+#include<shadowsFragmentFunctions>
+
+#include<clipPlaneFragmentDeclaration>
+
+// Fog
+#include<fogFragmentDeclaration>
+
+void main(void) {
+#include<clipPlaneFragment>
+
+	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
+
+	// Normal
+#ifdef NORMAL
+	vec3 normalW = normalize(vNormalW);
+#else
+	vec3 normalW = vec3(1.0, 1.0, 1.0);
+#endif
+
+	// Lighting
+	vec3 diffuseBase = vec3(0., 0., 0.);
+    lightingInfo info;
+	float shadow = 1.;
+    float glossiness = 0.;
+    
+#include<lightFragment>[0..1]
+
+	// Composition
+	vec4 color = vec4(0., 0., 0., 1.0 - clamp(shadow, 0., 1.));
+
+#include<fogFragment>
+
+	gl_FragColor = color;
+}

+ 62 - 0
materialsLibrary/src/shadowOnly/shadowOnly.vertex.fx

@@ -0,0 +1,62 @@
+precision highp float;
+
+// Attributes
+attribute vec3 position;
+#ifdef NORMAL
+attribute vec3 normal;
+#endif
+
+#include<bonesDeclaration>
+
+// Uniforms
+#include<instancesDeclaration>
+
+uniform mat4 view;
+uniform mat4 viewProjection;
+
+#ifdef POINTSIZE
+uniform float pointSize;
+#endif
+
+// Output
+varying vec3 vPositionW;
+#ifdef NORMAL
+varying vec3 vNormalW;
+#endif
+
+#ifdef VERTEXCOLOR
+varying vec4 vColor;
+#endif
+
+
+#include<clipPlaneVertexDeclaration>
+
+#include<fogVertexDeclaration>
+#include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
+
+void main(void) {
+
+#include<instancesVertex>
+#include<bonesVertex>
+
+	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
+
+	vec4 worldPos = finalWorld * vec4(position, 1.0);
+	vPositionW = vec3(worldPos);
+
+#ifdef NORMAL
+	vNormalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
+#endif
+
+	// Clip plane
+#include<clipPlaneVertex>
+
+    // Fog
+#include<fogVertex>
+#include<shadowsVertex>[0..maxSimultaneousLights]
+
+	// Point size
+#ifdef POINTSIZE
+	gl_PointSize = pointSize;
+#endif
+}

+ 0 - 1
materialsLibrary/src/water/babylon.waterMaterial.ts

@@ -1,5 +1,4 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
-/// <reference path="../simple/babylon.simpleMaterial.ts"/>
 
 module BABYLON {
     class WaterMaterialDefines extends MaterialDefines {

+ 78 - 13
src/Actions/babylon.actionManager.ts

@@ -62,16 +62,17 @@
         private static _OnRightPickTrigger = 3;
         private static _OnCenterPickTrigger = 4;
         private static _OnPickDownTrigger = 5;
-        private static _OnPickUpTrigger = 6;
-        private static _OnLongPressTrigger = 7;
-        private static _OnPointerOverTrigger = 8;
-        private static _OnPointerOutTrigger = 9;
-        private static _OnEveryFrameTrigger = 10;
-        private static _OnIntersectionEnterTrigger = 11;
-        private static _OnIntersectionExitTrigger = 12;
-        private static _OnKeyDownTrigger = 13;
-        private static _OnKeyUpTrigger = 14;
-        private static _OnPickOutTrigger = 15;
+        private static _OnDoublePickTrigger = 6;
+        private static _OnPickUpTrigger = 7;
+        private static _OnLongPressTrigger = 8;
+        private static _OnPointerOverTrigger = 9;
+        private static _OnPointerOutTrigger = 10;
+        private static _OnEveryFrameTrigger = 11;
+        private static _OnIntersectionEnterTrigger = 12;
+        private static _OnIntersectionExitTrigger = 13;
+        private static _OnKeyDownTrigger = 14;
+        private static _OnKeyUpTrigger = 15;
+        private static _OnPickOutTrigger = 16;
 
         public static get NothingTrigger(): number {
             return ActionManager._NothingTrigger;
@@ -97,6 +98,10 @@
             return ActionManager._OnPickDownTrigger;
         }
 
+        public static get OnDoublePickTrigger(): number {
+            return ActionManager._OnDoublePickTrigger;
+        }
+
         public static get OnPickUpTrigger(): number {
             return ActionManager._OnPickUpTrigger;
         }
@@ -138,9 +143,8 @@
             return ActionManager._OnKeyUpTrigger;
         }
 
-        public static DragMovementThreshold = 10; // in pixels
-        public static LongPressDelay = 500; // in milliseconds
-        
+        public static Triggers = {};
+
         // Members
         public actions = new Array<Action>();
 
@@ -158,6 +162,14 @@
         public dispose(): void {
             var index = this._scene._actionManagers.indexOf(this);
 
+            for (var i = 0; i < this.actions.length; i++) {
+                var action = this.actions[i];
+                ActionManager.Triggers[action.trigger]--;
+                if (ActionManager.Triggers[action.trigger] === 0) {
+                    delete ActionManager.Triggers[action.trigger]
+                }
+            }
+
             if (index > -1) {
                 this._scene._actionManagers.splice(index, 1);
             }
@@ -234,6 +246,52 @@
         }
 
         /**
+         * Does exist one action manager with at least one trigger 
+         * @return {boolean} whether or not it exists one action manager with one trigger
+        **/
+        public static get HasTriggers(): boolean {
+            for (var t in ActionManager.Triggers) {
+                if (ActionManager.Triggers.hasOwnProperty(t)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Does exist one action manager with at least one pick trigger 
+         * @return {boolean} whether or not it exists one action manager with one pick trigger
+        **/
+        public static get HasPickTriggers(): boolean {
+            for (var t in ActionManager.Triggers) {
+                if (ActionManager.Triggers.hasOwnProperty(t)) {
+                    let t_int = parseInt(t);
+                    if (t_int >= ActionManager._OnPickTrigger && t_int <= ActionManager._OnPickUpTrigger) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Does exist one action manager that handles actions of a given trigger
+         * @param {number} trigger - the trigger to be tested
+         * @return {boolean} whether the trigger is handeled by at least one action manager
+        **/
+        public static HasSpecificTrigger(trigger: number): boolean {
+            for (var t in ActionManager.Triggers) {
+                if (ActionManager.Triggers.hasOwnProperty(t)) {
+                    let t_int = parseInt(t);
+                    if (t_int === trigger) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
          * Registers an action to this action manager
          * @param {BABYLON.Action} action - the action to be registered
          * @return {BABYLON.Action} the action amended (prepared) after registration
@@ -248,6 +306,13 @@
 
             this.actions.push(action);
 
+          if(ActionManager.Triggers[action.trigger]) {
+              ActionManager.Triggers[action.trigger]++;
+            }
+            else{
+              ActionManager.Triggers[action.trigger] = 1;
+            }
+
             action._actionManager = this;
             action._prepare();
 

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

@@ -31,9 +31,10 @@ module BABYLON {
     }
 
     export interface WebVROptions {
-        trackPosition?: boolean; //update the camera's position
+        trackPosition?: boolean; //for the sake of your users - set it to true.
         positionScale?: number;
         displayName?: string; //if there are more than one VRDisplays.
+        controllerMeshes?: boolean; // should the native controller meshes be initialized
     }
 
     export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
@@ -58,9 +59,22 @@ module BABYLON {
         public controllers: Array<WebVRController> = [];
         public onControllersAttached: (controllers: Array<WebVRController>) => void;
 
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = false, private webVROptions: WebVROptions = {}) {
+        constructor(name: string, position: Vector3, scene: Scene, private webVROptions: WebVROptions = {}) {
             super(name, position, scene);
 
+            //legacy support - the compensation boolean was removed.
+            if (arguments.length === 5) {
+                this.webVROptions = arguments[4];
+            }
+
+            // default webVR options
+            if (this.webVROptions.trackPosition == undefined) {
+                this.webVROptions.trackPosition = true;
+            }
+            if (this.webVROptions.controllerMeshes == undefined) {
+                this.webVROptions.controllerMeshes = true;
+            }
+
             this.rotationQuaternion = new Quaternion();
             this.deviceRotationQuaternion = new Quaternion();
 
@@ -208,13 +222,16 @@ module BABYLON {
                 this._webvrViewMatrix.m[12] += parentCamera.position.x;
                 this._webvrViewMatrix.m[13] += parentCamera.position.y;
                 this._webvrViewMatrix.m[14] += parentCamera.position.z;
+
+                // is rotation offset set? 
+                if (!Quaternion.IsIdentity(this.rotationQuaternion)) {
+                    this._webvrViewMatrix.decompose(Tmp.Vector3[0], Tmp.Quaternion[0], Tmp.Vector3[1]);
+                    this.rotationQuaternion.multiplyToRef(Tmp.Quaternion[0], Tmp.Quaternion[0]);
+                    Matrix.ComposeToRef(Tmp.Vector3[0], Tmp.Quaternion[0], Tmp.Vector3[1], this._webvrViewMatrix);
+                }
+
                 this._webvrViewMatrix.invert();
             }
-            // is rotation offset set? 
-            if (!Quaternion.IsIdentity(this.rotationQuaternion)) {
-                this.rotationQuaternion.toRotationMatrix(this._tempMatrix);
-                this._tempMatrix.multiplyToRef(this._webvrViewMatrix, this._webvrViewMatrix);
-            }
 
             this._updateCameraRotationMatrix();
             Vector3.TransformCoordinatesToRef(this._referencePoint, this._cameraRotationMatrix, this._transformedReferencePoint);
@@ -227,7 +244,7 @@ module BABYLON {
             return this._webvrViewMatrix;
         }
 
-        protected _updateCameraRotationMatrix() {
+        public _updateWebVRCameraRotationMatrix() {
             this._webvrViewMatrix.getRotationMatrixToRef(this._cameraRotationMatrix);
         }
 
@@ -252,6 +269,9 @@ module BABYLON {
             new BABYLON.Gamepads((gp) => {
                 if (gp.type === BABYLON.Gamepad.POSE_ENABLED) {
                     let webVrController: WebVRController = <WebVRController>gp;
+                    if (this.webVROptions.controllerMeshes) {
+                        webVrController.initControllerMesh(this.getScene());
+                    }
                     webVrController.attachToPoseControlledCamera(this);
 
                     // since this is async - sanity check. Is the controller already stored?

+ 16 - 10
src/Cameras/babylon.camera.ts

@@ -599,38 +599,39 @@
                     this._rigCameras[1]._cameraRigParams.vrPreViewMatrix = metrics.rightPreViewMatrix;
                     this._rigCameras[1].getProjectionMatrix = this._rigCameras[1]._getVRProjectionMatrix;
 
-   
-                   if (metrics.compensateDistortion) {
+
+                    if (metrics.compensateDistortion) {
                         this._rigCameras[0]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Left", this._rigCameras[0], false, metrics);
                         this._rigCameras[1]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Right", this._rigCameras[1], true, metrics);
                     }
                     break;
                 case Camera.RIG_MODE_WEBVR:
                     if (rigParams.vrDisplay) {
-                        //var leftEye = rigParams.vrDisplay.getEyeParameters('left');
-                        //var rightEye = rigParams.vrDisplay.getEyeParameters('right');
+                        var leftEye = rigParams.vrDisplay.getEyeParameters('left');
+                        var rightEye = rigParams.vrDisplay.getEyeParameters('right');
 
                         //Left eye
                         this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
                         this._rigCameras[0].setCameraRigParameter("left", true);
+                        this._rigCameras[0].setCameraRigParameter("eyeParameters", leftEye);
                         this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
                         this._rigCameras[0].setCameraRigParameter("parentCamera", rigParams.parentCamera);
-                        //this._rigCameras[0].setCameraRigParameter('eyeParameters', leftEye);
                         this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
                         this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
                         this._rigCameras[0]._isSynchronizedViewMatrix = this._isSynchronizedViewMatrix;
-                        this._rigCameras[0]._updateCameraRotationMatrix = this._updateCameraRotationMatrix;
+                        this._rigCameras[0]._updateCameraRotationMatrix = this._updateWebVRCameraRotationMatrix;
+
                         //Right eye
                         this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
-                        //this._rigCameras[1].setCameraRigParameter('eyeParameters', rightEye);
+                        this._rigCameras[1].setCameraRigParameter('eyeParameters', rightEye);
                         this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
                         this._rigCameras[1].setCameraRigParameter("parentCamera", rigParams.parentCamera);
                         this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
                         this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
                         this._rigCameras[1]._isSynchronizedViewMatrix = this._isSynchronizedViewMatrix;
-                        this._rigCameras[1]._updateCameraRotationMatrix = this._updateCameraRotationMatrix;
+                        this._rigCameras[1]._updateCameraRotationMatrix = this._updateWebVRCameraRotationMatrix;
                     }
                     break;
 
@@ -646,10 +647,15 @@
             this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams.vrHMatrix, this._projectionMatrix);
             return this._projectionMatrix;
         }
-        
+
         protected _updateCameraRotationMatrix() {
-           // only here for webvr
+            //Here for WebVR
         }
+
+        protected _updateWebVRCameraRotationMatrix() {
+            //Here for WebVR
+        }
+
         /**
          * This function MUST be overwritten by the different WebVR cameras available.
          * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.

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

@@ -177,6 +177,14 @@ module BABYLON {
                 this.rotation.x += this.cameraRotation.x;
                 this.rotation.y += this.cameraRotation.y;
 
+                //rotate, if quaternion is set and rotation was used
+                if (this.rotationQuaternion) {
+                    var len = this.rotation.length();
+                    if (len) {
+                        Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, this.rotationQuaternion);
+                    }
+                }
+
 
                 if (!this.noRotationConstraint) {
                     var limit = (Math.PI / 2) * 0.95;

+ 12 - 9
src/Debug/babylon.debugLayer.ts

@@ -1,25 +1,28 @@
 module BABYLON {
 
     // declare INSPECTOR namespace for compilation issue
-    declare var INSPECTOR : any;
+    declare var INSPECTOR: any;
 
     export class DebugLayer {
         private _scene: Scene;
         public static InspectorURL = 'http://www.babylonjs.com/babylon.inspector.bundle.js';
         // The inspector instance
-        private _inspector : any;
+        private _inspector: any;
 
         constructor(scene: Scene) {
             this._scene = scene;
         }
 
         /** Creates the inspector window. */
-        private _createInspector(popup?:boolean) {
+        private _createInspector(config: { popup?: boolean, initialTab?: number, parentElement?: HTMLElement } = {}) {
+            let popup = config.popup || false;
+            let initialTab = config.initialTab || 0;
+            let parentElement = config.parentElement || null;
             if (!this._inspector) {
-                this._inspector = new INSPECTOR.Inspector(this._scene, popup);
+                this._inspector = new INSPECTOR.Inspector(this._scene, popup, initialTab, parentElement);
             } // else nothing to do,; instance is already existing
         }
-        
+
         public isVisible(): boolean {
             if (!this._inspector) {
                 return false;
@@ -33,14 +36,14 @@ module BABYLON {
                 this._inspector = null;
             }
         }
-        
-        public show(popup?:boolean) {
+
+        public show(config: { popup?: boolean, initialTab?: number, parentElement?: HTMLElement } = {}) {
             if (typeof INSPECTOR == 'undefined') {
                 // Load inspector and add it to the DOM
-                Tools.LoadScript(DebugLayer.InspectorURL, this._createInspector.bind(this, popup));
+                Tools.LoadScript(DebugLayer.InspectorURL, this._createInspector.bind(this, config));
             } else {
                 // Otherwise creates the inspector
-                this._createInspector(popup);
+                this._createInspector(config);
             }
         }
 

+ 8 - 0
src/Culling/babylon.rayHelper.ts

@@ -13,6 +13,14 @@ module BABYLON {
         private _meshSpaceDirection: Vector3;
         private _meshSpaceOrigin: Vector3;
 
+        public static CreateAndShow(ray: Ray, scene: Scene, color:Color3): RayHelper {
+            var helper = new RayHelper(ray);
+
+            helper.show(scene, color);
+
+            return helper;
+        }
+
         constructor(ray:Ray) {
             this.ray = ray;
         }

+ 5 - 1
src/Loading/babylon.sceneLoader.ts

@@ -209,7 +209,11 @@
 
                 Tools.LoadFile(rootUrl + sceneFilename, data => {
                     importMeshFromData(data);
-                }, progressCallBack, database, useArrayBuffer);
+                }, progressCallBack, database, useArrayBuffer, () => {
+                    if (onerror) {
+                        onerror(scene, 'Unable to load file ' + rootUrl + sceneFilename)
+                    }
+                });
             };
 
             if (scene.getEngine().enableOfflineSupport && !directLoad) {

+ 2 - 2
src/Materials/Textures/babylon.mapTexture.ts

@@ -6,7 +6,7 @@
 
         private _replacedViewport: Viewport;
 
-        constructor(name: string, scene: Scene, size: ISize, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, useMipMap: boolean=false) {
+        constructor(name: string, scene: Scene, size: ISize, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE, useMipMap: boolean=false, margin=0) {
             super(null, scene, !useMipMap, false, samplingMode);
 
             this.name = name;
@@ -15,7 +15,7 @@
             this.wrapV = Texture.CLAMP_ADDRESSMODE;
 
             // Create the rectPackMap that will allocate portion of the texture
-            this._rectPackingMap = new RectPackingMap(new Size(size.width, size.height));
+            this._rectPackingMap = new RectPackingMap(new Size(size.width, size.height), margin);
 
             // Create the texture that will store the content
             this._texture = scene.getEngine().createRenderTargetTexture(size, { generateMipMaps: !this.noMipmap, type: Engine.TEXTURETYPE_UNSIGNED_INT });

+ 10 - 2
src/Materials/Textures/babylon.videoTexture.ts

@@ -85,17 +85,25 @@
                 minWidth: number, 
                 maxWidth: number, 
                 minHeight: number, 
-                maxHeight: number
+                maxHeight: number,
+                deviceId: string
             }): void {
             var video = document.createElement("video");
+            var constraintsDeviceId;
+            if (constraints && constraints.deviceId){
+                constraintsDeviceId = { 
+                    exact: constraints.deviceId                     
+                }
+            }
 
 		    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
 		    window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
 
 
 		    if (navigator.getUserMedia) {
-			    navigator.getUserMedia({ 
+			    navigator.getUserMedia({                     
                     video: {
+                        deviceId: constraintsDeviceId,
                         width: {
                             min: (constraints && constraints.minWidth) || 256,
                             max: (constraints && constraints.maxWidth) || 640

+ 10 - 0
src/Materials/babylon.pbrMaterial.ts

@@ -21,6 +21,7 @@
         public EMISSIVEFRESNEL = false;
         public FRESNEL = false;
         public NORMAL = false;
+        public TANGENT = false;
         public UV1 = false;
         public UV2 = false;
         public VERTEXCOLOR = false;
@@ -946,6 +947,9 @@
             if (mesh) {
                 if (needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                     this._defines.NORMAL = true;
+                    if (mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
+                        this._defines.TANGENT = true;
+                    }
                 }
                 if (needUVs) {
                     if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
@@ -1050,6 +1054,10 @@
                     attribs.push(VertexBuffer.NormalKind);
                 }
 
+                if (this._defines.TANGENT) {
+                    attribs.push(VertexBuffer.TangentKind);
+                }
+
                 if (this._defines.UV1) {
                     attribs.push(VertexBuffer.UVKind);
                 }
@@ -1479,6 +1487,8 @@
                 }
             }
 
+            this._renderTargets.dispose();
+
             super.dispose(forceDisposeEffect, forceDisposeTextures);
         }
 

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

@@ -24,6 +24,7 @@
         public EMISSIVEFRESNEL = false;
         public FRESNEL = false;
         public NORMAL = false;
+        public TANGENT = false;
         public UV1 = false;
         public UV2 = false;
         public VERTEXCOLOR = false;
@@ -547,6 +548,9 @@
             if (mesh) {
                 if (needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
                     this._defines.NORMAL = true;
+                    if (mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
+                        this._defines.TANGENT = true;
+                    }
                 }
                 if (needUVs) {
                     if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
@@ -651,6 +655,10 @@
                     attribs.push(VertexBuffer.NormalKind);
                 }
 
+                if (this._defines.TANGENT) {
+                    attribs.push(VertexBuffer.TangentKind);
+                }
+
                 if (this._defines.UV1) {
                     attribs.push(VertexBuffer.UVKind);
                 }
@@ -987,6 +995,8 @@
                 }
             }
 
+            this._renderTargets.dispose();
+
             super.dispose(forceDisposeEffect, forceDisposeTextures);
         }
 

+ 37 - 3
src/Math/babylon.math.ts

@@ -2144,7 +2144,7 @@
         /**
          * Returns a new Vector4 set from the starting index of the passed array.  
          */
-        public static FromArray(array: number[], offset?: number): Vector4 {
+        public static FromArray(array: number[]  | Float32Array, offset?: number): Vector4 {
             if (!offset) {
                 offset = 0;
             }
@@ -2153,7 +2153,7 @@
         /**
          * Updates the passed vector "result" from the starting index of the passed array.  
          */
-        public static FromArrayToRef(array: number[], offset: number, result: Vector4): void {
+        public static FromArrayToRef(array: number[] | Float32Array, offset: number, result: Vector4): void {
             result.x = array[offset];
             result.y = array[offset + 1];
             result.z = array[offset + 2];
@@ -2219,7 +2219,6 @@
         /**
          * Returns the squared distance (float) between the vectors "value1" and "value2".  
          */
-        publi
         public static DistanceSquared(value1: Vector4, value2: Vector4): number {
             var x = value1.x - value2.x;
             var y = value1.y - value2.y;
@@ -2236,6 +2235,41 @@
             center.scaleInPlace(0.5);
             return center;
         }
+
+        /**
+         * Returns a new Vector4 set with the result of the normal transformation by the passed matrix of the passed vector.  
+         * This methods computes transformed normalized direction vectors only.  
+         */
+        public static TransformNormal(vector: Vector4, transformation: Matrix): Vector4 {
+            var result = Vector4.Zero();
+            Vector4.TransformNormalToRef(vector, transformation, result);
+            return result;
+        }
+
+        /**
+         * Sets the passed vector "result" with the result of the normal transformation by the passed matrix of the passed vector.  
+         * This methods computes transformed normalized direction vectors only. 
+         */
+        public static TransformNormalToRef(vector: Vector4, transformation: Matrix, result: Vector4): void {
+            var x = (vector.x * transformation.m[0]) + (vector.y * transformation.m[4]) + (vector.z * transformation.m[8]);
+            var y = (vector.x * transformation.m[1]) + (vector.y * transformation.m[5]) + (vector.z * transformation.m[9]);
+            var z = (vector.x * transformation.m[2]) + (vector.y * transformation.m[6]) + (vector.z * transformation.m[10]);
+            result.x = x;
+            result.y = y;
+            result.z = z;
+            result.w = vector.w;
+        }
+
+        /**
+         * Sets the passed vector "result" with the result of the normal transformation by the passed matrix of the passed floats (x, y, z, w).  
+         * This methods computes transformed normalized direction vectors only. 
+         */
+        public static TransformNormalFromFloatsToRef(x: number, y: number, z: number, w: number, transformation: Matrix, result: Vector4): void {
+            result.x = (x * transformation.m[0]) + (y * transformation.m[4]) + (z * transformation.m[8]);
+            result.y = (x * transformation.m[1]) + (y * transformation.m[5]) + (z * transformation.m[9]);
+            result.z = (x * transformation.m[2]) + (y * transformation.m[6]) + (z * transformation.m[10]);
+            result.w = w;
+        }
     }
 
     export interface ISize {

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

@@ -865,13 +865,7 @@
             // Composing transformations
             this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
             Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
-
-            // Mesh referal
-            var completeMeshReferalMatrix = Tmp.Matrix[6];
-            if (this._meshToBoneReferal && this.parent && this.parent.getWorldMatrix) {
-                this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), completeMeshReferalMatrix);
-            }
-
+            
             // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
             if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && this.getScene().activeCamera) {
                 if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
@@ -880,7 +874,8 @@
 
                     if (this.parent && this.parent.getWorldMatrix) {
                         if (this._meshToBoneReferal) {
-                            Vector3.TransformCoordinatesToRef(this.position, completeMeshReferalMatrix, currentPosition);
+                            this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
+                            Vector3.TransformCoordinatesToRef(this.position, Tmp.Matrix[6], currentPosition);
                         } else {
                             Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), currentPosition);
                         }
@@ -927,7 +922,8 @@
 
                 if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE) {
                     if (this._meshToBoneReferal) {
-                        Tmp.Matrix[5].copyFrom(completeMeshReferalMatrix);
+                        this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
+                        Tmp.Matrix[5].copyFrom(Tmp.Matrix[6]);
                     } else {
                         Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
                     }
@@ -939,7 +935,8 @@
                     
                 } else {
                     if (this._meshToBoneReferal) {
-                        this._localWorld.multiplyToRef(completeMeshReferalMatrix, this._worldMatrix);
+                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
+                        Tmp.Matrix[6].multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), this._worldMatrix);
                     } else {
                         this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
                     }

+ 51 - 8
src/Mesh/babylon.mesh.vertexData.ts

@@ -14,6 +14,7 @@
     export class VertexData {
         public positions: number[] | Float32Array;
         public normals: number[] | Float32Array;
+        public tangents: number[] | Float32Array;
         public uvs: number[] | Float32Array;
         public uvs2: number[] | Float32Array;
         public uvs3: number[] | Float32Array;
@@ -35,6 +36,9 @@
                 case VertexBuffer.NormalKind:
                     this.normals = data;
                     break;
+                case VertexBuffer.TangentKind:
+                    this.tangents = data;
+                    break;
                 case VertexBuffer.UVKind:
                     this.uvs = data;
                     break;
@@ -118,6 +122,10 @@
                 meshOrGeometry.setVerticesData(VertexBuffer.NormalKind, this.normals, updatable);
             }
 
+            if (this.tangents) {
+                meshOrGeometry.setVerticesData(VertexBuffer.TangentKind, this.tangents, updatable);
+            }
+
             if (this.uvs) {
                 meshOrGeometry.setVerticesData(VertexBuffer.UVKind, this.uvs, updatable);
             }
@@ -177,6 +185,10 @@
                 meshOrGeometry.updateVerticesData(VertexBuffer.NormalKind, this.normals, updateExtends, makeItUnique);
             }
 
+            if (this.tangents) {
+                meshOrGeometry.updateVerticesData(VertexBuffer.TangentKind, this.tangents, updateExtends, makeItUnique);
+            }
+
             if (this.uvs) {
                 meshOrGeometry.updateVerticesData(VertexBuffer.UVKind, this.uvs, updateExtends, makeItUnique);
             }
@@ -259,6 +271,22 @@
                     this.normals[index + 2] = transformed.z;
                 }
             }
+
+            if (this.tangents) {
+                var tangent = Vector4.Zero();
+                var tangentTransformed = Vector4.Zero();
+
+                for (index = 0; index < this.tangents.length; index += 4) {
+                    Vector4.FromArrayToRef(this.tangents, index, tangent);
+
+                    Vector4.TransformNormalToRef(tangent, matrix, tangentTransformed);
+                    this.tangents[index] = tangentTransformed.x;
+                    this.tangents[index + 1] = tangentTransformed.y;
+                    this.tangents[index + 2] = tangentTransformed.z;
+                    this.tangents[index + 3] = tangentTransformed.w;
+                }
+            }
+
             return this;
         }
 
@@ -281,6 +309,7 @@
 
             this.positions = this._mergeElement(this.positions, other.positions);
             this.normals = this._mergeElement(this.normals, other.normals);
+            this.tangents = this._mergeElement(this.tangents, other.tangents);
             this.uvs = this._mergeElement(this.uvs, other.uvs);
             this.uvs2 = this._mergeElement(this.uvs2, other.uvs2);
             this.uvs3 = this._mergeElement(this.uvs3, other.uvs3);
@@ -339,6 +368,10 @@
                 serializationObject.normals = this.normals;
             }
 
+            if (this.tangents) {
+                serializationObject.tangents = this.tangents;
+            }
+
             if (this.uvs) {
                 serializationObject.uvs = this.uvs;
             }
@@ -416,6 +449,10 @@
                 result.normals = meshOrGeometry.getVerticesData(VertexBuffer.NormalKind, copyWhenShared);
             }
 
+            if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.TangentKind)) {
+                result.tangents = meshOrGeometry.getVerticesData(VertexBuffer.TangentKind, copyWhenShared);
+            }
+
             if (meshOrGeometry.isVerticesDataPresent(VertexBuffer.UVKind)) {
                 result.uvs = meshOrGeometry.getVerticesData(VertexBuffer.UVKind, copyWhenShared);
             }
@@ -2085,7 +2122,7 @@
          * bbSize : optional bounding box size data, required for facetPartitioning computation
          * bInfo : optional bounding info, required for facetPartitioning computation
          */
-        public static ComputeNormals(positions: any, indices: any, normals: any, 
+        public static ComputeNormals(positions: any, indices: any, normals: any,
             options?: { facetNormals?: any, facetPositions?: any, facetPartitioning?: any, ratio?: number, bInfo?: any, bbSize?: Vector3, subDiv?: any}): void {
 
             // temporary scalar variables
@@ -2119,7 +2156,7 @@
             }
 
             // facetPartitioning reinit if needed
-            if (computeFacetPartitioning) {  
+            if (computeFacetPartitioning) {
                 var ox = 0;                 // X partitioning index for facet position
                 var oy = 0;                 // Y partinioning index for facet position
                 var oz = 0;                 // Z partinioning index for facet position
@@ -2145,7 +2182,7 @@
                 var subSq = options.subDiv.max * options.subDiv.max;
                 options.facetPartitioning.length = 0;
             }
-        
+
             // reset the normals
             for (index = 0; index < positions.length; index++) {
                 normals[index] = 0.0;
@@ -2164,7 +2201,7 @@
                 v2z = v2x + 2;
                 v3x = indices[index * 3 + 2] * 3;
                 v3y = v3x + 1;
-                v3z = v3x + 2;        
+                v3z = v3x + 2;
 
                 p1p2x = positions[v1x] - positions[v2x];          // compute two vectors per facet : p1p2 and p3p2
                 p1p2y = positions[v1y] - positions[v2y];
@@ -2175,7 +2212,7 @@
                 p3p2z = positions[v3z] - positions[v2z];
 
                 // compute the face normal with the cross product
-                faceNormalx = p1p2y * p3p2z - p1p2z * p3p2y;            
+                faceNormalx = p1p2y * p3p2z - p1p2z * p3p2y;
                 faceNormaly = p1p2z * p3p2x - p1p2x * p3p2z;
                 faceNormalz = p1p2x * p3p2y - p1p2y * p3p2x;
                 // normalize this normal and store it in the array facetData
@@ -2186,7 +2223,7 @@
                 faceNormalz /= length;
 
                 if (computeFacetNormals) {
-                    options.facetNormals[index].x = faceNormalx;                                  
+                    options.facetNormals[index].x = faceNormalx;
                     options.facetNormals[index].y = faceNormaly;
                     options.facetNormals[index].z = faceNormalz;
                 }
@@ -2213,7 +2250,7 @@
                     b3x = Math.floor((positions[v3x] - options.bInfo.minimum.x * options.ratio) * xSubRatio);
                     b3y = Math.floor((positions[v3y] - options.bInfo.minimum.y * options.ratio) * ySubRatio);
                     b3z = Math.floor((positions[v3z] - options.bInfo.minimum.z * options.ratio) * zSubRatio);
-                    
+
                     block_idx_v1 = b1x + options.subDiv.max * b1y + subSq * b1z;
                     block_idx_v2 = b2x + options.subDiv.max * b2y + subSq * b2z;
                     block_idx_v3 = b3x + options.subDiv.max * b3y + subSq * b3z;
@@ -2233,7 +2270,7 @@
                         options.facetPartitioning[block_idx_v3].push(index);
                     }
                     if (!(block_idx_o == block_idx_v1 || block_idx_o == block_idx_v2 || block_idx_o == block_idx_v3)) {
-                        options.facetPartitioning[block_idx_o].push(index); 
+                        options.facetPartitioning[block_idx_o].push(index);
                     }
                 }
 
@@ -2338,6 +2375,12 @@
                 vertexData.set(normals, VertexBuffer.NormalKind);
             }
 
+            // tangents
+            var tangents = parsedVertexData.tangents;
+            if (tangents) {
+                vertexData.set(tangents, VertexBuffer.TangentKind);
+            }
+
             // uvs
             var uvs = parsedVertexData.uvs;
             if (uvs) {

+ 6 - 0
src/Mesh/babylon.vertexBuffer.ts

@@ -25,6 +25,7 @@
                     case VertexBuffer.UV6Kind:
                         stride = 2;
                         break;
+                    case VertexBuffer.TangentKind:
                     case VertexBuffer.ColorKind:
                         stride = 4;
                         break;
@@ -154,6 +155,7 @@
         // Enums
         private static _PositionKind = "position";
         private static _NormalKind = "normal";
+        private static _TangentKind = "tangent";
         private static _UVKind = "uv";
         private static _UV2Kind = "uv2";
         private static _UV3Kind = "uv3";
@@ -174,6 +176,10 @@
             return VertexBuffer._NormalKind;
         }
 
+        public static get TangentKind(): string {
+            return VertexBuffer._TangentKind;
+        }
+
         public static get UVKind(): string {
             return VertexBuffer._UVKind;
         }

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

@@ -251,14 +251,14 @@
         }
 
         public dispose(camera?: Camera): void {
-            camera = camera || this._camera;
+            camera = camera || this._camera;            
 
             if (this._textures.length > 0) {
                 for (var i = 0; i < this._textures.length; i++) {
                     this._engine._releaseTexture(this._textures.data[i]);
                 }
-                this._textures.reset();
             }
+            this._textures.dispose();
 
             if (!camera) {
                 return;

+ 2 - 0
src/Rendering/babylon.boundingBoxRenderer.ts

@@ -94,6 +94,8 @@
                 return;
             }
 
+            this.renderList.dispose();
+
             this._colorShader.dispose();
 
             var buffer = this._vertexBuffers[VertexBuffer.PositionKind];

+ 8 - 0
src/Rendering/babylon.renderingGroup.ts

@@ -256,6 +256,14 @@
             this._spriteManagers.reset();
         }
 
+        public dispose(): void {
+            this._opaqueSubMeshes.dispose();
+            this._transparentSubMeshes.dispose();
+            this._alphaTestSubMeshes.dispose();
+            this._particleSystems.dispose();
+            this._spriteManagers.dispose();
+        }
+
         /**
          * Inserts the submesh in its correct queue depending on its material.
          * @param subMesh The submesh to dispatch

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

@@ -127,6 +127,17 @@
             }
         }
 
+        public dispose(): void {
+            for (let index = RenderingManager.MIN_RENDERINGGROUPS; index < RenderingManager.MAX_RENDERINGGROUPS; index++) {
+                var renderingGroup = this._renderingGroups[index];
+                if (renderingGroup) {
+                    renderingGroup.dispose();
+                }
+            }
+
+            this._renderingGroups.length = 0;
+        }
+
         private _prepareRenderingGroup(renderingGroupId: number): void {
             if (!this._renderingGroups[renderingGroupId]) {
                 this._renderingGroups[renderingGroupId] = new RenderingGroup(renderingGroupId, this._scene,

+ 5 - 1
src/Shaders/ShadersInclude/bumpFragment.fx

@@ -1,7 +1,11 @@
 vec2 uvOffset = vec2(0.0, 0.0);
 
 #if defined(BUMP) || defined(PARALLAX)
-	mat3 TBN = cotangent_frame(normalW * vBumpInfos.y, -viewDirectionW, vBumpUV);
+	#if defined(TANGENT) && defined(NORMAL)
+		mat3 TBN = vTBN;
+	#else
+		mat3 TBN = cotangent_frame(normalW * vBumpInfos.y, -viewDirectionW, vBumpUV);
+	#endif
 #endif
 
 #ifdef PARALLAX

+ 3 - 0
src/Shaders/ShadersInclude/bumpFragmentFunctions.fx

@@ -2,6 +2,9 @@
 	varying vec2 vBumpUV;
 	uniform vec3 vBumpInfos;
 	uniform sampler2D bumpSampler;
+#if defined(TANGENT) && defined(NORMAL) 
+	varying mat3 vTBN;
+#endif
 
 	// Thanks to http://www.thetenthplanet.de/archives/1180
 	mat3 cotangent_frame(vec3 normal, vec3 p, vec2 uv)

+ 8 - 0
src/Shaders/ShadersInclude/bumpVertex.fx

@@ -0,0 +1,8 @@
+#if defined(BUMP) || defined(PARALLAX)
+	#if defined(TANGENT) && defined(NORMAL)
+		vec3 normalW = normalize(vec3(finalWorld * vec4(normal, 0.0)));
+		vec3 tangentW = normalize(vec3(finalWorld * vec4(tangent.xyz, 0.0)));
+		vec3 bitangentW = cross(normalW, tangentW) * tangent.w;
+		vTBN = mat3(tangentW, bitangentW, normalW);
+	#endif
+#endif

+ 5 - 0
src/Shaders/ShadersInclude/bumpVertexDeclaration.fx

@@ -0,0 +1,5 @@
+#if defined(BUMP) || defined(PARALLAX)
+	#if defined(TANGENT) && defined(NORMAL) 
+		varying mat3 vTBN;
+	#endif
+#endif

+ 6 - 0
src/Shaders/default.vertex.fx

@@ -3,6 +3,9 @@ attribute vec3 position;
 #ifdef NORMAL
 attribute vec3 normal;
 #endif
+#ifdef TANGENT
+attribute vec4 tangent;
+#endif
 #ifdef UV1
 attribute vec2 uv;
 #endif
@@ -75,6 +78,8 @@ varying vec3 vNormalW;
 varying vec4 vColor;
 #endif
 
+#include<bumpVertexDeclaration>
+
 #include<clipPlaneVertexDeclaration>
 
 #include<fogVertexDeclaration>
@@ -196,6 +201,7 @@ void main(void) {
 	}
 #endif
 
+#include<bumpVertex>
 #include<clipPlaneVertex>
 #include<fogVertex>
 #include<shadowsVertex>[0..maxSimultaneousLights]

+ 7 - 0
src/Shaders/pbr.vertex.fx

@@ -5,6 +5,9 @@ attribute vec3 position;
 #ifdef NORMAL
 attribute vec3 normal;
 #endif
+#ifdef TANGENT
+attribute vec4 tangent;
+#endif
 #ifdef UV1
 attribute vec2 uv;
 #endif
@@ -80,6 +83,7 @@ varying vec4 vColor;
 #endif
 
 
+#include<bumpVertexDeclaration>
 #include<clipPlaneVertexDeclaration>
 #include<fogVertexDeclaration>
 #include<shadowsVertexDeclaration>[0..maxSimultaneousLights]
@@ -200,6 +204,9 @@ void main(void) {
     }
 #endif
 
+    // TBN
+#include<bumpVertex>
+
     // Clip plane
 #include<clipPlaneVertex>
 

+ 91 - 19
src/Tools/babylon.extendedGamepad.ts

@@ -38,8 +38,10 @@ module BABYLON {
 
         public rawPose: DevicePose; //GamepadPose;
 
-        private _mesh: AbstractMesh; // a node that will be attached to this Gamepad
-        private _poseControlledCamera: PoseControlled;
+        public _mesh: AbstractMesh; // a node that will be attached to this Gamepad
+        private _poseControlledCamera: TargetCamera;
+
+        private _leftHandSystemQuaternion: Quaternion = new Quaternion();
 
         constructor(public vrGamepad) {
             super(vrGamepad.id, vrGamepad.index, vrGamepad);
@@ -52,16 +54,17 @@ module BABYLON {
 
             this._calculatedPosition = Vector3.Zero();
             this._calculatedRotation = new Quaternion();
+            Quaternion.RotationYawPitchRollToRef(Math.PI, 0, 0, this._leftHandSystemQuaternion);
         }
 
         public update() {
             super.update();
             // update this device's offset position from the attached camera, if provided
-            if (this._poseControlledCamera && this._poseControlledCamera.deviceScaleFactor) {
-                //this.position.copyFrom(this._poseControlledCamera.position);
-                //this.rotationQuaternion.copyFrom(this._poseControlledCamera.rotationQuaternion);
-                this.deviceScaleFactor = this._poseControlledCamera.deviceScaleFactor;
-            }
+            //if (this._poseControlledCamera && this._poseControlledCamera.deviceScaleFactor) {
+            //this.position.copyFrom(this._poseControlledCamera.position);
+            //this.rotationQuaternion.copyFrom(this._poseControlledCamera.rotationQuaternion);
+            //this.deviceScaleFactor = this._poseControlledCamera.deviceScaleFactor;
+            //}
             var pose: GamepadPose = this.vrGamepad.pose;
             this.updateFromDevice(pose);
 
@@ -84,38 +87,65 @@ module BABYLON {
                     this._calculatedPosition.addInPlace(this.position);
 
                     // scale the position using the scale factor, add the device's position
-                    if (this._poseControlledCamera) {
+                    /*if (this._poseControlledCamera) {
                         // this allows total positioning freedom - the device, the camera and the mesh can be individually controlled.
                         this._calculatedPosition.addInPlace(this._poseControlledCamera.position);
-                    }
+                    }*/
                 }
                 if (poseData.orientation) {
                     this.deviceRotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);
-                    if (this._mesh && this._mesh.getScene().useRightHandedSystem) {
-                        this.deviceRotationQuaternion.z *= -1;
-                        this.deviceRotationQuaternion.w *= -1;
+                    if (this._mesh) {
+                        if (this._mesh.getScene().useRightHandedSystem) {
+                            this.deviceRotationQuaternion.z *= -1;
+                            this.deviceRotationQuaternion.w *= -1;
+                        } else {
+                            this.deviceRotationQuaternion.multiplyToRef(this._leftHandSystemQuaternion, this.deviceRotationQuaternion);
+                        }
                     }
 
                     // if the camera is set, rotate to the camera's rotation
-                    this.rotationQuaternion.multiplyToRef(this.deviceRotationQuaternion, this._calculatedRotation);
-                    if (this._poseControlledCamera) {
-                        this._calculatedRotation.multiplyToRef(this._poseControlledCamera.rotationQuaternion, this._calculatedRotation);
-                    }
+                    this.deviceRotationQuaternion.multiplyToRef(this.rotationQuaternion, this._calculatedRotation);
+
+                    /*if (this._poseControlledCamera) {
+                        Matrix.ScalingToRef(1, 1, 1, Tmp.Matrix[1]);
+                        this._calculatedRotation.toRotationMatrix(Tmp.Matrix[0]);
+                        Matrix.TranslationToRef(this._calculatedPosition.x, this._calculatedPosition.y, this._calculatedPosition.z, Tmp.Matrix[2]);
+
+                        //Matrix.Identity().multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
+                        Tmp.Matrix[1].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
+
+                        this._poseControlledCamera.getWorldMatrix().getTranslationToRef(Tmp.Vector3[0])
+
+                        Matrix.ComposeToRef(new Vector3(this.deviceScaleFactor, this.deviceScaleFactor, this.deviceScaleFactor), this._poseControlledCamera.rotationQuaternion, Tmp.Vector3[0], Tmp.Matrix[4]);
+                        Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], Tmp.Matrix[1]);
+
+                        Tmp.Matrix[1].multiplyToRef(Tmp.Matrix[4], Tmp.Matrix[2]);
+                        Tmp.Matrix[2].decompose(Tmp.Vector3[0], this._calculatedRotation, this._calculatedPosition);
+
+                    }*/
                 }
             }
         }
 
 
         public attachToMesh(mesh: AbstractMesh) {
+            if (this._mesh) {
+                this._mesh.parent = undefined;
+            }
             this._mesh = mesh;
+            if (this._poseControlledCamera) {
+                this._mesh.parent = this._poseControlledCamera;
+            }
             if (!this._mesh.rotationQuaternion) {
                 this._mesh.rotationQuaternion = new Quaternion();
             }
         }
 
-        public attachToPoseControlledCamera(camera: PoseControlled) {
+        public attachToPoseControlledCamera(camera: TargetCamera) {
             this._poseControlledCamera = camera;
-            this.deviceScaleFactor = camera.deviceScaleFactor;
+            if (this._mesh) {
+                this._mesh.parent = this._poseControlledCamera;
+            }
         }
 
         public detachMesh() {
@@ -136,7 +166,6 @@ module BABYLON {
 
         public onTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
 
-
         public onMainButtonStateChangedObservable = new Observable<ExtendedGamepadButton>();
 
         public onSecondaryButtonStateChangedObservable = new Observable<ExtendedGamepadButton>();
@@ -176,6 +205,8 @@ module BABYLON {
 
         protected abstract handleButtonChange(buttonIdx: number, value: ExtendedGamepadButton, changes: GamepadButtonChanges);
 
+        public abstract initControllerMesh(scene: Scene)
+
         private _setButtonValue(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton, buttonIndex: number) {
             if (!currentState) {
                 this._buttons[buttonIndex] = {
@@ -225,6 +256,27 @@ module BABYLON {
             this.controllerType = PoseEnabledControllerType.OCULUS;
         }
 
+        public initControllerMesh(scene: Scene) {
+
+            let meshName = this.hand === 'right' ? 'RightTouch.babylon' : 'LeftTouch.babylon';
+            SceneLoader.ImportMesh("", "http://cdn.babylonjs.com/models/", meshName, scene, (newMeshes) => {
+                /*
+                Parent Mesh name: oculus_touch_left
+                - body
+                - trigger
+                - thumbstick
+                - grip
+                - button_y 
+                - button_x
+                - button_enter
+                */
+
+                var mesh = newMeshes[7];
+                this.attachToMesh(mesh);
+            });
+        }
+
+
         // helper getters for left and right hand.
         public get onAButtonStateChangedObservable() {
             if (this.hand === 'right') {
@@ -300,6 +352,26 @@ module BABYLON {
             this.controllerType = PoseEnabledControllerType.VIVE;
         }
 
+        public initControllerMesh(scene: Scene) {
+            SceneLoader.ImportMesh("", "http://cdn.babylonjs.com/models/", "ViveWand.babylon", scene, (newMeshes) => {
+                /*
+                Parent Mesh name: ViveWand
+                - body
+                - r_gripper
+                - l_gripper
+                - menu_button
+                - system_button
+                - trackpad
+                - trigger
+                - LED
+                */
+
+                var mesh = newMeshes[1];
+                this.attachToMesh(mesh);
+            });
+        }
+
+
         public get onLeftButtonStateChangedObservable() {
             return this.onMainButtonStateChangedObservable;
         }

+ 14 - 0
src/Tools/babylon.observable.ts

@@ -154,6 +154,20 @@
 
             return result;
         }
+
+        /**
+         * Does this observable handles observer registered with a given mask
+         * @param {number} trigger - the mask to be tested
+         * @return {boolean} whether or not one observer registered with the given mask is handeled 
+        **/
+        public hasSpecificMask(mask: number = -1): boolean {
+            for (var obs of this._observers) {
+                if (obs.mask & mask && obs.mask === mask) {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 
 }

+ 65 - 24
src/Tools/babylon.rectPackingMap.ts

@@ -1,11 +1,11 @@
 module BABYLON {
 
     /**
-  * This class describe a rectangle that were added to the map.
-  * You have access to its coordinates either in pixel or normalized (UV)
-  */
+     * This class describe a rectangle that were added to the map.
+     * You have access to its coordinates either in pixel or normalized (UV)
+     */
     export class PackedRect {
-        constructor(root: PackedRect, parent: PackedRect, pos: Vector2, size: Size) {
+        constructor(root: RectPackingMap, parent: PackedRect, pos: Vector2, size: Size) {
             this._pos         = pos;
             this._size        = size;
             this._root        = root;
@@ -20,7 +20,7 @@
         /**
          * @returns the position of this node into the map
          */
-        public get pos() {
+        public get pos(): Vector2 {
             return this._pos;
         }
 
@@ -32,25 +32,49 @@
         }
 
         /**
+         * Retrieve the inner position (considering the margin) and stores it into the res object
+         * @param res must be a valid Vector2 that will contain the inner position after this call
+         */
+        public getInnerPosToRef(res: Vector2) {
+            let m = this._root._margin;
+            res.x = this._pos.x + m;
+            res.y = this._pos.y + m;
+        }
+
+        /**
+         * Retrieve the inner size (considering the margin) and stores it into the res object
+         * @param res must be a valid Size that will contain the inner size after this call
+         */
+        public getInnerSizeToRef(res: Size) {
+            let m = this._root._margin;
+            res.width = this._contentSize.width - (m*2);
+            res.height = this._contentSize.height - (m*2);
+        }
+
+        /**
          * Compute the UV of the top/left, top/right, bottom/right, bottom/left points of the rectangle this node handles into the map
          * @returns And array of 4 Vector2, containing UV coordinates for the four corners of the Rectangle into the map
          */
         public get UVs(): Vector2[] {
-            return this.getUVsForCustomSize(this._root._size);
+            if (!this._contentSize) {
+                throw new Error("Can't compute UVs for this object because it's nor allocated");
+            }
+            return this.getUVsForCustomSize(this._contentSize);
         }
 
-
         /**
-         * You may have allocated the PackedRect using over-provisioning (you allocated more than you need in order to prevent frequent deallocations/reallocations) and then using only a part of the PackRect.
+         * You may have allocated the PackedRect using over-provisioning (you allocated more than you need in order to prevent frequent deallocations/reallocations) 
+         * and then using only a part of the PackRect.
          * This method will return the UVs for this part by given the custom size of what you really use
          * @param customSize must be less/equal to the allocated size, UV will be compute from this 
          */
         public getUVsForCustomSize(customSize: Size): Vector2[] {
             var mainWidth = this._root._size.width;
             var mainHeight = this._root._size.height;
+            let margin = this._root._margin;
 
-            var topLeft = new Vector2(this._pos.x / mainWidth, this._pos.y / mainHeight);
-            var rightBottom = new Vector2((this._pos.x + customSize.width - 1) / mainWidth, (this._pos.y + customSize.height - 1) / mainHeight);
+            var topLeft = new Vector2((this._pos.x+margin) / mainWidth, (this._pos.y+margin) / mainHeight);
+            var rightBottom = new Vector2((this._pos.x + customSize.width + margin - 1) / mainWidth, (this._pos.y + customSize.height + margin - 1) / mainHeight);
             var uvs = new Array<Vector2>();
             uvs.push(topLeft);
             uvs.push(new Vector2(rightBottom.x, topLeft.y));
@@ -94,6 +118,7 @@
 
         private findNode(size: Size): PackedRect {
             var resNode: PackedRect = null;
+            let margin = this._root._margin * 2;
 
             // If this node is used, recurse to each of his subNodes to find an available one in its branch
             if (this.isUsed) {
@@ -110,7 +135,7 @@
 
             // The node is free, but was previously allocated (_initialSize is set), rely on initialSize to make the test as it's the space we have
             else if (this._initialSize) {
-                if ((size.width <= this._initialSize.width) && (size.height <= this._initialSize.height))
+                if (((size.width+margin) <= this._initialSize.width) && ((size.height+margin) <= this._initialSize.height))
                 {
                     resNode = this;
                 } else {
@@ -119,28 +144,35 @@
             }
 
             // The node is free and empty, rely on its size for the test
-            else if ((size.width <= this._size.width) && (size.height <= this._size.height)) {
+            else if (((size.width+margin) <= this._size.width) && ((size.height+margin) <= this._size.height)) {
                 resNode = this;
             }
             return resNode;
         }
 
+        private static  TpsSize = Size.Zero();
         private splitNode(contentSize: Size): PackedRect {
+            let cs = PackedRect.TpsSize;
+            let margin = this._root._margin*2;
+            cs.copyFrom(contentSize);
+            cs.width += margin;
+            cs.height += margin;
+
             // If there's no contentSize but an initialSize it means this node were previously allocated, but freed, we need to create a _leftNode as subNode and use to allocate the space we need (and this node will have a right/bottom subNode for the space left as this._initialSize may be greater than contentSize)
             if (!this._contentSize && this._initialSize) {
-                this._contentSize = contentSize.clone();
+                this._contentSize = cs.clone();
                 this._leftNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y), new Size(this._initialSize.width, this._initialSize.height));
                 return this._leftNode.splitNode(contentSize);
             } else {
-                this._contentSize = contentSize.clone();
-                this._initialSize = contentSize.clone();
+                this._contentSize = cs.clone();
+                this._initialSize = cs.clone();
 
-                if (contentSize.width !== this._size.width) {
-                    this._rightNode = new PackedRect(this._root, this, new Vector2(this._pos.x + contentSize.width, this._pos.y), new Size(this._size.width - contentSize.width, contentSize.height));
+                if (cs.width !== this._size.width) {
+                    this._rightNode = new PackedRect(this._root, this, new Vector2(this._pos.x + cs.width, this._pos.y), new Size(this._size.width - cs.width, cs.height));
                 }
 
-                if (contentSize.height !== this._size.height) {
-                    this._bottomNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y + contentSize.height), new Size(this._size.width, this._size.height - contentSize.height));
+                if (cs.height !== this._size.height) {
+                    this._bottomNode = new PackedRect(this._root, this, new Vector2(this._pos.x, this._pos.y + cs.height), new Size(this._size.width, this._size.height - cs.height));
                 }
                 return this;
             }
@@ -170,10 +202,13 @@
             var levelSize = 0;
 
             if (!this.isUsed) {
-                if (this._initialSize) {
-                    levelSize = this._initialSize.surface;
+                let margin = this._root._margin;
+                let is = this._initialSize;
+                if (is) {
+                    levelSize = is.surface - (is.width*margin) - (is.height*margin);
                 } else {
-                    levelSize = this._size.surface;
+                    let size = this._size;
+                    levelSize = size.surface - (size.width*margin) - (size.height*margin);
                 }
             }
 
@@ -188,7 +223,7 @@
             return levelSize + size;
         }
 
-        protected _root: PackedRect;
+        protected _root: RectPackingMap;
         protected _parent: PackedRect;
         private _contentSize: Size;
         private _initialSize: Size;
@@ -204,15 +239,19 @@
      * The purpose of this class is to pack several Rectangles into a big map, while trying to fit everything as optimally as possible.
      * This class is typically used to build lightmaps, sprite map or to pack several little textures into a big one.
      * Note that this class allows allocated Rectangles to be freed: that is the map is dynamically maintained so you can add/remove rectangle based on their life-cycle.
+     * In case you need a margin around the allocated rect, specify the amount in the margin argument during construction.
+     * In such case you will have to rely on innerPositionToRef and innerSizeToRef calls to get the proper size
      */
     export class RectPackingMap extends PackedRect {
         /**
          * Create an instance of the object with a dimension using the given size
          * @param size The dimension of the rectangle that will contain all the sub ones.
+         * @param margin The margin (empty space) created (in pixels) around the allocated Rectangles
          */
-        constructor(size: Size) {
+        constructor(size: Size, margin=0) {
             super(null, null, Vector2.Zero(), size);
 
+            this._margin = margin;
             this._root = this;
         }
 
@@ -236,5 +275,7 @@
 
             return freeSize / (this._size.width * this._size.height);
         }
+
+        public _margin: number;
     }
 }

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

@@ -48,6 +48,11 @@
             this._duplicateId++;
         }
 
+        public dispose(): void {
+            this.reset();
+            this.data.length = 0;
+        }
+
         public concat(array: any): void {
             if (array.length === 0) {
                 return;

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

@@ -1184,6 +1184,8 @@
      * For count you first have to call fetchNewFrame() to notify the start of a new frame to monitor, then call addCount() how many time required to increment the count value you monitor.
      */
     export class PerfCounter {
+        public static Enabled = true;
+
         /**
          * Returns the smallest value ever
          */
@@ -1253,6 +1255,9 @@
          * @param fetchResult true when it's the last time in the frame you add to the counter and you wish to update the statistics properties (min/max/average), false if you only want to update statistics.
          */
         public addCount(newCount: number, fetchResult: boolean) {
+            if (!PerfCounter.Enabled) {
+                return;
+            }
             this._current += newCount;
             if (fetchResult) {
                 this._fetchResult();
@@ -1263,6 +1268,9 @@
          * Start monitoring this performance counter
          */
         public beginMonitoring() {
+            if (!PerfCounter.Enabled) {
+                return;
+            }
             this._startMonitoringTime = Tools.Now;
         }
 
@@ -1271,6 +1279,10 @@
          * @param newFrame true by default to fetch the result and monitor a new frame, if false the time monitored will be added to the current frame counter
          */
         public endMonitoring(newFrame: boolean = true) {
+            if (!PerfCounter.Enabled) {
+                return;
+            }
+                        
             if (newFrame) {
                 this.fetchNewFrame();
             }

+ 16 - 4
src/babylon.engine.ts

@@ -190,6 +190,7 @@
         limitDeviceRatio?: number;
         autoEnableWebVR?: boolean;
         disableWebGL2Support?: boolean;
+        audioEngine?: boolean;
     }
 
     /**
@@ -421,6 +422,7 @@
         public isPointerLock = false;
         public cullBackFaces = true;
         public renderEvenInBackground = true;
+        public preventCacheWipeBetweenFrames = false;
         // To enable/disable IDB support and avoid XHR on .manifest
         public enableOfflineSupport = true;
         public scenes = new Array<Scene>();
@@ -550,6 +552,10 @@
                 options.preserveDrawingBuffer = false;
             }
 
+            if (options.audioEngine === undefined) {
+                options.audioEngine = true;
+            }
+
             // GL
             if (!options.disableWebGL2Support) {
                 try {
@@ -751,7 +757,7 @@
             document.addEventListener("mozpointerlockchange", this._onPointerLockChange, false);
             document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
 
-            if (AudioEngine && !Engine.audioEngine) {
+            if (options.audioEngine && AudioEngine && !Engine.audioEngine) {
                 Engine.audioEngine = new AudioEngine();
             }
 
@@ -1126,10 +1132,13 @@
          *   });
          */
         public resize(): void {
-            var width = navigator.isCocoonJS ? window.innerWidth : this._renderingCanvas.clientWidth;
-            var height = navigator.isCocoonJS ? window.innerHeight : this._renderingCanvas.clientHeight;
+            // We're not resizing the size of the canvas while in VR mode & presenting
+            if (!(this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting)) {
+                var width = navigator.isCocoonJS ? window.innerWidth : this._renderingCanvas.clientWidth;
+                var height = navigator.isCocoonJS ? window.innerHeight : this._renderingCanvas.clientHeight;
 
-            this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel);
+                this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel);
+            }
         }
 
         /**
@@ -2057,6 +2066,9 @@
 
         // Textures
         public wipeCaches(): void {
+            if (this.preventCacheWipeBetweenFrames) {
+                return;
+            }
             this.resetTextureCache();
             this._currentEffect = null;
 

+ 350 - 77
src/babylon.scene.ts

@@ -3,12 +3,47 @@
         dispose(): void;
     }
 
+    class ClickInfo {
+        private _singleClick = false;
+        private _doubleClick = false;
+        private _hasSwiped = false;
+        private _ignore = false;
+
+        public get singleClick(): boolean {
+            return this._singleClick;
+        }
+        public get doubleClick(): boolean {
+            return this._doubleClick;
+        }
+        public get hasSwiped(): boolean{
+            return this._hasSwiped;
+        }
+        public get ignore(): boolean{
+            return this._ignore;
+        }
+
+        public set singleClick(b: boolean) {
+            this._singleClick = b;
+        }
+        public set doubleClick(b: boolean) {
+            this._doubleClick = b;
+        }
+        public set hasSwiped(b: boolean) {
+            this._hasSwiped = b;
+        }
+        public set ignore(b: boolean) {
+            this._ignore = b;
+        }
+    }
+
     export class PointerEventTypes {
         static _POINTERDOWN = 0x01;
         static _POINTERUP = 0x02;
         static _POINTERMOVE = 0x04;
         static _POINTERWHEEL = 0x08;
         static _POINTERPICK = 0x10;
+        static _POINTERTAP = 0x20;
+        static _POINTERDOUBLETAP = 0x40;
 
         public static get POINTERDOWN(): number {
             return PointerEventTypes._POINTERDOWN;
@@ -29,6 +64,14 @@
         public static get POINTERPICK(): number {
             return PointerEventTypes._POINTERPICK;
         }
+        
+        public static get POINTERTAP(): number {
+            return PointerEventTypes._POINTERTAP;
+        }
+
+        public static get POINTERDOUBLETAP(): number {
+            return PointerEventTypes._POINTERDOUBLETAP;
+        }
     }
 
     export class PointerInfoBase {
@@ -324,13 +367,34 @@
             return new Vector2(this._unTranslatedPointerX, this._unTranslatedPointerY);
         }
 
+        public static DragMovementThreshold = 10; // in pixels
+        public static LongPressDelay = 500; // in milliseconds
+        public static DoubleClickDelay = 300; // in milliseconds
+        public static ExclusiveDoubleClickMode = false; // If you need to check double click without raising a single click at first click, enable this flag
+
+        private _initClickEvent: (obs1: Observable<PointerInfoPre>, obs2: Observable<PointerInfo>, evt: PointerEvent, cb: (clickInfo: ClickInfo, pickResult: PointerInfo) => void) => void;
+        private _initActionManager: (act: ActionManager, clickInfo: ClickInfo) => ActionManager;
+        private _delayedSimpleClick: (btn: number, clickInfo: ClickInfo, cb: (clickInfo: ClickInfo, pickResult: PointerInfo) => void) => void;
+        private _delayedSimpleClickTimeout;
+        private _previousDelayedSimpleClickTimeout;
+        private _meshPickProceed = false;
+
+        private _previousButtonPressed;
+        private _previousHasSwiped = false;
+        private _currentPickResult = null;
+        private _previousPickResult = null;
+        private _isButtonPressed = false;
+        private _doubleClickOccured = false;
+
         public cameraToUseForPointers: Camera = null; // Define this parameter if you are using multiple cameras and you want to specify which one should be used for pointer position
         private _pointerX: number;
         private _pointerY: number;
         private _unTranslatedPointerX: number;
         private _unTranslatedPointerY: number;
         private _startingPointerPosition = new Vector2(0, 0);
+        private _previousStartingPointerPosition = new Vector2(0, 0);
         private _startingPointerTime = 0;
+        private _previousStartingPointerTime = 0;
         // Mirror
         public _mirroredCameraPosition: Vector3;
 
@@ -549,6 +613,7 @@
         private _uniqueIdCounter = 0;
 
         private _pickedDownMesh: AbstractMesh;
+        private _pickedUpMesh: AbstractMesh;
         private _pickedDownSprite: Sprite;
         private _externalData: StringDictionary<Object>;
         private _uid: string;
@@ -779,6 +844,136 @@
         * @param attachMove defines if you want to attach events to pointermove
         */
         public attachControl(attachUp = true, attachDown = true, attachMove = true) {
+            this._initActionManager = (act: ActionManager, clickInfo: ClickInfo): ActionManager => {
+                if (!this._meshPickProceed) {
+                    let pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY, this.pointerDownPredicate, false, this.cameraToUseForPointers);
+                    this._currentPickResult = pickResult;
+                    act = (pickResult.hit && pickResult.pickedMesh) ? pickResult.pickedMesh.actionManager : null;
+                    this._meshPickProceed = true;
+                }
+                return act;
+            };
+
+            this._delayedSimpleClick = (btn: number, clickInfo: ClickInfo, cb: (clickInfo: ClickInfo, pickResult: PointerInfo) => void) => {
+                // double click delay is over and that no double click has been raised since, or the 2 consecutive keys pressed are different
+                if ((new Date().getTime() - this._previousStartingPointerTime > Scene.DoubleClickDelay && !this._doubleClickOccured) ||
+                btn !== this._previousButtonPressed ) {
+                    this._doubleClickOccured = false;
+                    clickInfo.singleClick = true;
+                    clickInfo.ignore = false;
+                    cb(clickInfo, this._currentPickResult);
+                }
+            }
+
+            this._initClickEvent = (obs1: Observable<PointerInfoPre>, obs2: Observable<PointerInfo>, evt: PointerEvent, cb: (clickInfo: ClickInfo, pickResult: PointerInfo) => void): void => {
+                    let clickInfo = new ClickInfo();
+                    this._currentPickResult = null;
+                    let act;
+
+                    let checkPicking = obs1.hasSpecificMask(PointerEventTypes.POINTERPICK) || obs2.hasSpecificMask(PointerEventTypes.POINTERPICK)
+                                    || obs1.hasSpecificMask(PointerEventTypes.POINTERTAP) || obs2.hasSpecificMask(PointerEventTypes.POINTERTAP)
+                                    || obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) || obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                    if (!checkPicking && ActionManager.HasPickTriggers) {
+                        act = this._initActionManager(act, clickInfo);
+                        if (act)
+                            checkPicking = act.hasPickTriggers;
+                    }
+                    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;
+
+                        if (!clickInfo.hasSwiped) {
+                            let checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
+
+                            if (!checkSingleClickImmediately) {
+                                checkSingleClickImmediately = !obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) &&
+                                                              !obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+
+                                if (checkSingleClickImmediately && !ActionManager.HasSpecificTrigger(ActionManager.OnDoublePickTrigger)) {
+                                    act = this._initActionManager(act, clickInfo);
+                                    if (act)
+                                        checkSingleClickImmediately = !act.hasSpecificTrigger(ActionManager.OnDoublePickTrigger);
+                                }
+                            }
+
+                            if (checkSingleClickImmediately) {
+                                // single click detected if double click delay is over or two different successive keys pressed without exclusive double click or no double click required
+                                if (new Date().getTime() - this._previousStartingPointerTime > Scene.DoubleClickDelay ||
+                                    btn !== this._previousButtonPressed ) {
+                                        clickInfo.singleClick = true;
+                                        cb(clickInfo, this._currentPickResult);
+                                }
+                            }
+                            // at least one double click is required to be check and exclusive double click is enabled
+                            else {
+                                // wait that no double click has been raised during the double click delay
+                                this._previousDelayedSimpleClickTimeout = this._delayedSimpleClickTimeout;
+                                this._delayedSimpleClickTimeout = window.setTimeout(this._delayedSimpleClick.bind(this, btn, clickInfo, cb), Scene.DoubleClickDelay);
+                            }
+
+                            let checkDoubleClick = obs1.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP) ||
+                                                   obs2.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP);
+                            if (!checkDoubleClick && ActionManager.HasSpecificTrigger(ActionManager.OnDoublePickTrigger)){
+                                act = this._initActionManager(act, clickInfo);
+                                if (act)
+                                    checkDoubleClick = act.hasSpecificTrigger(ActionManager.OnDoublePickTrigger);
+                            }
+                            if (checkDoubleClick) {
+                                // two successive keys pressed are equal, double click delay is not over and double click has not just occurred
+                                if (btn === this._previousButtonPressed &&
+                                    new Date().getTime() - this._previousStartingPointerTime < Scene.DoubleClickDelay &&
+                                    !this._doubleClickOccured
+                                ) {
+                                    // 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._previousStartingPointerTime = 0;
+                                        this._doubleClickOccured = true;
+                                        clickInfo.doubleClick = true;
+                                        clickInfo.ignore = false;
+                                        if (Scene.ExclusiveDoubleClickMode && this._previousDelayedSimpleClickTimeout && this._previousDelayedSimpleClickTimeout.clearTimeout)
+                                            this._previousDelayedSimpleClickTimeout.clearTimeout();
+                                        this._previousDelayedSimpleClickTimeout = this._delayedSimpleClickTimeout;
+                                        cb(clickInfo, this._currentPickResult);
+                                    }
+                                    // if the two successive clicks are too far, it's just two simple clicks
+                                    else {
+                                        this._doubleClickOccured = false;
+                                        this._previousStartingPointerTime = this._startingPointerTime;
+                                        this._previousStartingPointerPosition.x = this._startingPointerPosition.x;
+                                        this._previousStartingPointerPosition.y = this._startingPointerPosition.y;
+                                        this._previousButtonPressed = btn;
+                                        this._previousHasSwiped = clickInfo.hasSwiped;
+                                        if (Scene.ExclusiveDoubleClickMode){
+                                            if (this._previousDelayedSimpleClickTimeout && this._previousDelayedSimpleClickTimeout.clearTimeout) {
+                                                this._previousDelayedSimpleClickTimeout.clearTimeout();
+                                            }
+                                            this._previousDelayedSimpleClickTimeout = this._delayedSimpleClickTimeout;
+                                            cb(clickInfo, this._previousPickResult);
+                                        }
+                                        else {
+                                            cb(clickInfo, this._currentPickResult);
+                                        }
+                                    }
+                                }
+                                // just the first click of the double has been raised
+                                else {
+                                    this._doubleClickOccured = false;
+                                    this._previousStartingPointerTime = this._startingPointerTime;
+                                    this._previousStartingPointerPosition.x = this._startingPointerPosition.x;
+                                    this._previousStartingPointerPosition.y = this._startingPointerPosition.y;
+                                    this._previousButtonPressed = btn;
+                                    this._previousHasSwiped = clickInfo.hasSwiped;
+                                }
+                            }
+                        }
+                    }
+                    clickInfo.ignore = true;
+                    cb(clickInfo, this._currentPickResult);
+            };
+
             var spritePredicate = (sprite: Sprite): boolean => {
                 return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
             };
@@ -855,6 +1050,10 @@
             };
 
             this._onPointerDown = (evt: PointerEvent) => {
+                this._isButtonPressed = true;
+                this._pickedDownMesh = null;
+                this._meshPickProceed = false;
+
                 this._updatePointerPosition(evt);
 
                 // PreObservable support
@@ -887,40 +1086,39 @@
 
                 if (pickResult.hit && pickResult.pickedMesh) {
                     this._pickedDownMesh = pickResult.pickedMesh;
-                    if (pickResult.pickedMesh.actionManager) {
-                        if (pickResult.pickedMesh.actionManager.hasPickTriggers) {
+                    var actionManager = pickResult.pickedMesh.actionManager;
+                    if (actionManager) {
+                        if (actionManager.hasPickTriggers) {
+                            actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                             switch (evt.button) {
                                 case 0:
-                                    pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                                    actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                     break;
                                 case 1:
-                                    pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                                    actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                     break;
                                 case 2:
-                                    pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                                    actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                     break;
                             }
-                            if (pickResult.pickedMesh.actionManager) {
-                                pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
-                            }
                         }
 
-                        if (pickResult.pickedMesh.actionManager && pickResult.pickedMesh.actionManager.hasSpecificTrigger(ActionManager.OnLongPressTrigger)) {
-                            var that = this;
-                            window.setTimeout(function () {
-                                var pickResult = that.pick(that._unTranslatedPointerX, that._unTranslatedPointerY,
-                                    (mesh: AbstractMesh): boolean => mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasSpecificTrigger(ActionManager.OnLongPressTrigger),
-                                    false, that.cameraToUseForPointers);
+                        if (actionManager.hasSpecificTrigger(ActionManager.OnLongPressTrigger)) {
+                            window.setTimeout((function () {
+                                var pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY,
+                                    (mesh: AbstractMesh): boolean => mesh.isPickable && mesh.isVisible && mesh.isReady() && mesh.actionManager && mesh.actionManager.hasSpecificTrigger(ActionManager.OnLongPressTrigger) && mesh == this._pickedDownMesh,
+                                    false, this.cameraToUseForPointers);
 
                                 if (pickResult.hit && pickResult.pickedMesh) {
-                                    if (pickResult.pickedMesh.actionManager) {
-                                        if (that._startingPointerTime !== 0 && ((new Date().getTime() - that._startingPointerTime) > ActionManager.LongPressDelay) && (Math.abs(that._startingPointerPosition.x - that._pointerX) < ActionManager.DragMovementThreshold && Math.abs(that._startingPointerPosition.y - that._pointerY) < ActionManager.DragMovementThreshold)) {
-                                            that._startingPointerTime = 0;
-                                            pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
-                                        }
+                                    if (this._isButtonPressed &&
+                                        ((new Date().getTime() - this._startingPointerTime) > Scene.LongPressDelay) &&
+                                        (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold &&
+                                         Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold)) {
+                                            this._startingPointerTime = 0;
+                                            actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                     }
                                 }
-                            }, ActionManager.LongPressDelay);
+                            }).bind(this), Scene.LongPressDelay);
                         }
                     }
                 }
@@ -963,85 +1161,140 @@
             };
 
             this._onPointerUp = (evt: PointerEvent) => {
+                this._isButtonPressed = false;
+                this._pickedUpMesh = null;
+                this._meshPickProceed = false;
+
                 this._updatePointerPosition(evt);
 
-                // PreObservable support
-                if (this.onPrePointerObservable.hasObservers()) {
-                    let type = PointerEventTypes.POINTERUP;
-                    let pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
-                    this.onPrePointerObservable.notifyObservers(pi, type);
-                    if (pi.skipOnPointerObservable) {
-                        return;
+                this._initClickEvent(this.onPrePointerObservable, this.onPointerObservable,  evt, (function(clickInfo, pickResult){
+                    // PreObservable support
+                    if (this.onPrePointerObservable.hasObservers()) {
+                        if (!clickInfo.ignore) {
+                            if (!clickInfo.hasSwiped) {
+                                if (clickInfo.singleClick && this.onPrePointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
+                                    let type = PointerEventTypes.POINTERTAP;
+                                    let pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                                    this.onPrePointerObservable.notifyObservers(pi, type);
+                                    if (pi.skipOnPointerObservable) {
+                                        return;
+                                    }
+                                }
+                                if (clickInfo.doubleClick && this.onPrePointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
+                                    let type = PointerEventTypes.POINTERDOUBLETAP;
+                                    let pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                                    this.onPrePointerObservable.notifyObservers(pi, type);
+                                    if (pi.skipOnPointerObservable) {
+                                        return;
+                                    }
+                                }
+                            }
+                        }
+                        else {
+                            let type = PointerEventTypes.POINTERUP;
+                            let pi = new PointerInfoPre(type, evt, this._unTranslatedPointerX, this._unTranslatedPointerY);
+                            this.onPrePointerObservable.notifyObservers(pi, type);
+                            if (pi.skipOnPointerObservable) {
+                                return;
+                            }
+                        }
                     }
-                }
 
-                if (!this.cameraToUseForPointers && !this.activeCamera) {
-                    return;
-                }
+                    if (!this.cameraToUseForPointers && !this.activeCamera) {
+                        return;
+                    }
 
-                if (!this.pointerUpPredicate) {
-                    this.pointerUpPredicate = (mesh: AbstractMesh): boolean => {
-                        return mesh.isPickable && mesh.isVisible && mesh.isReady();
-                    };
-                }
+                    if (!this.pointerUpPredicate) {
+                        this.pointerUpPredicate = (mesh: AbstractMesh): boolean => {
+                            return mesh.isPickable && mesh.isVisible && mesh.isReady();
+                        };
+                    }
 
-                // Meshes
-                var pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY, this.pointerUpPredicate, false, this.cameraToUseForPointers);
+                    // Meshes
+                    if (!this._meshPickProceed && ActionManager.HasTriggers) {
+                        this._initActionManager(null, clickInfo);
+                    }
+                    if (!pickResult) {
+                        pickResult = this._currentPickResult;
+                    }
 
-                if (pickResult.hit && pickResult.pickedMesh) {
-                    if (this._pickedDownMesh != null && pickResult.pickedMesh == this._pickedDownMesh) {
-                        if (this.onPointerPick) {
-                            this.onPointerPick(evt, pickResult);
-                        }
-                        if (this.onPointerObservable.hasObservers()) {
-                            let type = PointerEventTypes.POINTERPICK;
-                            let pi = new PointerInfo(type, evt, pickResult);
-                            this.onPointerObservable.notifyObservers(pi, type);
+                    if (pickResult && pickResult.pickedMesh) {
+                        this._pickedUpMesh = pickResult.pickedMesh;
+                        if (this._pickedDownMesh === this._pickedUpMesh) {
+                            if (this.onPointerPick) {
+                                this.onPointerPick(evt, pickResult);
+                            }
+                            if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
+                                let type = PointerEventTypes.POINTERPICK;
+                                let pi = new PointerInfo(type, evt, pickResult);
+                                this.onPointerObservable.notifyObservers(pi, type);
+                            }
                         }
-                    }
-                    if (pickResult.pickedMesh.actionManager) {
-                        pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                         if (pickResult.pickedMesh.actionManager) {
-                            if (Math.abs(this._startingPointerPosition.x - this._pointerX) < ActionManager.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < ActionManager.DragMovementThreshold) {
+                            if (clickInfo.ignore) {
+                                pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                            }
+                            if (!clickInfo.hasSwiped && !clickInfo.ignore && clickInfo.singleClick) {
                                 pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                             }
+                            if (clickInfo.doubleClick && !clickInfo.ignore && pickResult.pickedMesh.actionManager.hasSpecificTrigger(ActionManager.OnDoublePickTrigger)) {
+                                pickResult.pickedMesh.actionManager.processTrigger(ActionManager.OnDoublePickTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
+                            }
                         }
                     }
-                }
-                if (this._pickedDownMesh && this._pickedDownMesh.actionManager && this._pickedDownMesh !== pickResult.pickedMesh) {
-                    this._pickedDownMesh.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNew(this._pickedDownMesh, evt));
-                }
-
-                if (this.onPointerUp) {
-                    this.onPointerUp(evt, pickResult);
-                }
+                    if (this._pickedDownMesh &&
+                        this._pickedDownMesh.actionManager && 
+                        this._pickedDownMesh.actionManager.hasSpecificTrigger(ActionManager.OnPickOutTrigger) &&
+                        this._pickedDownMesh !== this._pickedUpMesh) {
+                        this._pickedDownMesh.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNew(this._pickedDownMesh, evt));
+                    }
 
-                if (this.onPointerObservable.hasObservers()) {
-                    let type = PointerEventTypes.POINTERUP;
-                    let pi = new PointerInfo(type, evt, pickResult);
-                    this.onPointerObservable.notifyObservers(pi, type);
-                }
+                    if (this.onPointerUp) {
+                        this.onPointerUp(evt, pickResult);
+                    }
 
-                this._startingPointerTime = 0;
+                    if (this.onPointerObservable.hasObservers()) {
+                        if (!clickInfo.ignore) {
+                            if (!clickInfo.hasSwiped) {
+                                if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERTAP)) {
+                                    let type = PointerEventTypes.POINTERTAP;
+                                    let pi = new PointerInfo(type, evt, pickResult);
+                                    this.onPointerObservable.notifyObservers(pi, type);
+                                }
+                                if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(PointerEventTypes.POINTERDOUBLETAP)) {
+                                    let type = PointerEventTypes.POINTERDOUBLETAP;
+                                    let pi = new PointerInfo(type, evt, pickResult);
+                                    this.onPointerObservable.notifyObservers(pi, type);
+                                }
+                            }
+                        }
+                        else {
+                            let type = PointerEventTypes.POINTERUP;
+                            let pi = new PointerInfo(type, evt, pickResult);
+                            this.onPointerObservable.notifyObservers(pi, type);
+                        }
+                    }
 
-                // Sprites
-                if (this.spriteManagers.length > 0) {
-                    pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, spritePredicate, false, this.cameraToUseForPointers);
+                    // Sprites
+                    if (this.spriteManagers.length > 0) {
+                        pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, spritePredicate, false, this.cameraToUseForPointers);
 
-                    if (pickResult.hit && pickResult.pickedSprite) {
-                        if (pickResult.pickedSprite.actionManager) {
-                            pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
+                        if (pickResult.hit && pickResult.pickedSprite) {
                             if (pickResult.pickedSprite.actionManager) {
-                                if (Math.abs(this._startingPointerPosition.x - this._pointerX) < ActionManager.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < ActionManager.DragMovementThreshold) {
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
+                                if (pickResult.pickedSprite.actionManager) {
+                                    if (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold) {
+                                        pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
+                                    }
                                 }
                             }
                         }
+                        if (this._pickedDownSprite && this._pickedDownSprite.actionManager && this._pickedDownSprite !== pickResult.pickedSprite) {
+                            this._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(this._pickedDownSprite, this, evt));
+                        }
                     }
-                    if (this._pickedDownSprite && this._pickedDownSprite.actionManager && this._pickedDownSprite !== pickResult.pickedSprite) {
-                        this._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(this._pickedDownSprite, this, evt));
-                    }
-                }
+                    this._previousPickResult = this._currentPickResult;
+                }).bind(this));
             };
 
             this._onKeyDown = (evt: Event) => {
@@ -1113,6 +1366,10 @@
             for (index = 0; index < this.meshes.length; index++) {
                 var mesh = this.meshes[index];
 
+                if (!mesh.isEnabled()) {
+                    continue;
+                }
+
                 if (!mesh.isReady()) {
                     return false;
                 }
@@ -2682,6 +2939,22 @@
                 this._depthRenderer.dispose();
             }
 
+            // Smart arrays            
+            if (this.activeCamera) {
+                this.activeCamera._activeMeshes.dispose();
+                this.activeCamera = null;
+            }
+            this._activeMeshes.dispose();
+            this._renderingManager.dispose();
+            this._processedMaterials.dispose();
+            this._activeParticleSystems.dispose();
+            this._activeSkeletons.dispose();
+            this._softwareSkinnedMeshes.dispose();
+            this._boundingBoxRenderer.dispose();
+            this._edgesRenderers.dispose();
+            this._meshesForIntersections.dispose();
+            this._toBeDisposed.dispose();
+
             // Debug layer
             if (this._debugLayer) {
                 this._debugLayer.hide();

+ 1 - 0
what's new.md

@@ -89,3 +89,4 @@
   - `WorldSpaceCanvas2D`:
 	- WorldSpaceRenderScale is no longer supported (deprecated because of adaptive feature added).
 
+