소스 검색

Update from upstream

nockawa 8 년 전
부모
커밋
423fd4547c
74개의 변경된 파일6953개의 추가작업 그리고 3766개의 파일을 삭제
  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. 15 0
      Tools/Gulp/config.json
  7. 7 5
      canvas2D/src/Engine/babylon.group2d.ts
  8. 180 59
      canvas2D/src/Engine/babylon.prim2dBase.ts
  9. 4 0
      canvas2D/src/Engine/babylon.text2d.ts
  10. 28 27
      dist/preview release/babylon.core.js
  11. 1361 1274
      dist/preview release/babylon.d.ts
  12. 36 36
      dist/preview release/babylon.js
  13. 707 191
      dist/preview release/babylon.max.js
  14. 1361 1274
      dist/preview release/babylon.module.d.ts
  15. 36 36
      dist/preview release/babylon.noworker.js
  16. 24 2
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  17. 209 85
      dist/preview release/canvas2D/babylon.canvas2d.js
  18. 12 12
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  19. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  20. 3 0
      dist/preview release/inspector/babylon.inspector.css
  21. 107 3
      dist/preview release/inspector/babylon.inspector.d.ts
  22. 414 48
      dist/preview release/inspector/babylon.inspector.js
  23. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  24. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js
  25. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  26. 21 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.d.ts
  27. 209 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js
  28. 1 0
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  29. 0 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.d.ts
  30. 0 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.js
  31. 31 3
      dist/preview release/what's new.md
  32. 4 0
      inspector/sass/_tabPanel.scss
  33. 1 0
      inspector/sass/_tree.scss
  34. 140 107
      inspector/src/Inspector.ts
  35. 25 18
      inspector/src/adapters/Adapter.ts
  36. 55 0
      inspector/src/adapters/CameraAdapter.ts
  37. 37 0
      inspector/src/adapters/TextureAdapter.ts
  38. 179 131
      inspector/src/properties.ts
  39. 22 0
      inspector/src/tabs/CameraTab.ts
  40. 49 85
      inspector/src/tabs/ShaderTab.ts
  41. 42 31
      inspector/src/tabs/Tab.ts
  42. 44 35
      inspector/src/tabs/TabBar.ts
  43. 143 0
      inspector/src/tabs/TextureTab.ts
  44. 49 42
      inspector/src/tree/TreeItem.ts
  45. 39 0
      inspector/src/treetools/CameraPOV.ts
  46. 228 91
      inspector/test/index.js
  47. 1 1
      loaders/src/glTF/babylon.glTFFileLoader.ts
  48. 1 1
      localDev/index.html
  49. 6 2
      materialsLibrary/index.html
  50. 240 0
      materialsLibrary/src/shadowOnly/babylon.shadowOnlyMaterial.ts
  51. 50 0
      materialsLibrary/src/shadowOnly/shadowOnly.fragment.fx
  52. 62 0
      materialsLibrary/src/shadowOnly/shadowOnly.vertex.fx
  53. 0 1
      materialsLibrary/src/water/babylon.waterMaterial.ts
  54. 78 13
      src/Actions/babylon.actionManager.ts
  55. 28 8
      src/Cameras/VR/babylon.webVRCamera.ts
  56. 16 10
      src/Cameras/babylon.camera.ts
  57. 8 0
      src/Cameras/babylon.targetCamera.ts
  58. 12 9
      src/Debug/babylon.debugLayer.ts
  59. 5 1
      src/Loading/babylon.sceneLoader.ts
  60. 2 0
      src/Materials/babylon.pbrMaterial.ts
  61. 2 0
      src/Materials/babylon.standardMaterial.ts
  62. 7 10
      src/Mesh/babylon.abstractMesh.ts
  63. 2 2
      src/PostProcess/babylon.postProcess.ts
  64. 2 0
      src/Rendering/babylon.boundingBoxRenderer.ts
  65. 8 0
      src/Rendering/babylon.renderingGroup.ts
  66. 11 0
      src/Rendering/babylon.renderingManager.ts
  67. 91 19
      src/Tools/babylon.extendedGamepad.ts
  68. 14 0
      src/Tools/babylon.observable.ts
  69. 5 0
      src/Tools/babylon.smartArray.ts
  70. 12 0
      src/Tools/babylon.tools.ts
  71. 16 4
      src/babylon.engine.ts
  72. 7 0
      src/babylon.node.ts
  73. 346 77
      src/babylon.scene.ts
  74. 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

+ 15 - 0
Tools/Gulp/config.json

@@ -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,8 @@
                     "../../inspector/src/properties.ts",
                     "../../inspector/src/gui/BasicElement.ts",
                     "../../inspector/src/adapters/Adapter.ts",
+                    "../../inspector/src/adapters/CameraAdapter.ts",
+                    "../../inspector/src/adapters/TextureAdapter.ts",
                     "../../inspector/src/adapters/Canvas2DAdapter.ts",
                     "../../inspector/src/adapters/LightAdapter.ts",
                     "../../inspector/src/adapters/MaterialAdapter.ts",
@@ -594,6 +606,8 @@
                     "../../inspector/src/scheduler/Scheduler.ts",
                     "../../inspector/src/tabs/Tab.ts",
                     "../../inspector/src/tabs/PropertyTab.ts",
+                    "../../inspector/src/tabs/CameraTab.ts",
+                    "../../inspector/src/tabs/TextureTab.ts",
                     "../../inspector/src/tabs/Canvas2DTab.ts",
                     "../../inspector/src/tabs/LightTab.ts",
                     "../../inspector/src/tabs/MaterialTab.ts",
@@ -614,6 +628,7 @@
                     "../../inspector/src/tree/TreeItem.ts",
                     "../../inspector/src/treetools/AbstractTreeTool.ts",
                     "../../inspector/src/treetools/BoundingBox.ts",
+                    "../../inspector/src/treetools/CameraPOV.ts",
                     "../../inspector/src/treetools/Checkbox.ts",
                     "../../inspector/src/treetools/DebugArea.ts",
                     "../../inspector/src/treetools/Info.ts",

+ 7 - 5
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;

+ 180 - 59
canvas2D/src/Engine/babylon.prim2dBase.ts

@@ -1178,7 +1178,7 @@
                         } else {
                             dstArea.width = (sourceArea.width * isx) - (dstOffset.x + rightPixels) * isx;
                         }
-                        
+
                         dstOffset.z = this.rightPixels;
                         break;
                     }
@@ -1208,7 +1208,7 @@
                         break;
                     }
                 }
-                
+
             }
 
             if (computeAxis & PrimitiveThickness.ComputeV) {
@@ -1308,7 +1308,7 @@
 
                         break;
                     }
-                }                
+                }
             }
         }
 
@@ -1466,7 +1466,7 @@
             dontInheritParentScale  ?: boolean,
             alignToPixel            ?: boolean,
             opacity                 ?: number,
-            zOrder                  ?: number, 
+            zOrder                  ?: number,
             origin                  ?: Vector2,
             layoutEngine            ?: LayoutEngineBase | string,
             isVisible               ?: boolean,
@@ -1757,7 +1757,7 @@
         /**
          * Return the ObservableStringDictionary containing all the primitives intersecting with this one.
          * The key is the primitive uid, the value is the ActorInfo object
-         * @returns {} 
+         * @returns {}
          */
         public get intersectWith(): ObservableStringDictionary<ActorInfoBase> {
             if (!this._actorInfo) {
@@ -1994,6 +1994,9 @@
             return this.actualPosition.x;
         }
 
+        /**
+         * DO NOT INVOKE for internal purpose only
+         */
         public set actualX(val: number) {
             this._actualPosition.x = val;
             this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualPosition);
@@ -2007,6 +2010,9 @@
             return this.actualPosition.y;
         }
 
+        /**
+        * DO NOT INVOKE for internal purpose only
+        */
         public set actualY(val: number) {
             this._actualPosition.y = val;
             this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, this._actualPosition);
@@ -2027,20 +2033,44 @@
         }
 
         public set position(value: Vector2) {
-            if (!this._checkPositionChange()) {
+            //if (!this._checkPositionChange()) {
+            //    return;
+            //}
+            if (this._checkUseMargin()) {
+                switch (this._marginAlignment.horizontal) {
+                    case PrimitiveAlignment.AlignLeft:
+                    case PrimitiveAlignment.AlignStretch:
+                    case PrimitiveAlignment.AlignCenter:
+                        this.margin.leftPixels = value.x;
+                        break;
+                    case PrimitiveAlignment.AlignRight:
+                        this.margin.rightPixels = value.x;
+                        break;
+                    }
+                switch (this._marginAlignment.vertical) {
+                    case PrimitiveAlignment.AlignBottom:
+                    case PrimitiveAlignment.AlignStretch:
+                    case PrimitiveAlignment.AlignCenter:
+                        this.margin.bottomPixels = value.y;
+                        break;
+                    case PrimitiveAlignment.AlignTop:
+                        this.margin.topPixels = value.y;
+                        break;
+                }
                 return;
-            }
-            if (!value) {
-                this._position = null;
             } else {
-                if (!this._position) {
-                    this._position = value.clone();
+                if (!value) {
+                    this._position = null;
                 } else {
-                    this._position.copyFrom(value);
+                    if (!this._position) {
+                        this._position = value.clone();
+                    } else {
+                        this._position.copyFrom(value);
+                    }
                 }
+                this._actualPosition = null;
+                this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
             }
-            this._actualPosition = null;
-            this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
         }
 
         /**
@@ -2056,24 +2086,38 @@
         }
 
         public set x(value: number) {
-            if (!this._checkPositionChange()) {
-                return;
-            }
+            //if (!this._checkPositionChange()) {
+            //    return;
+            //}
             if (value == null) {
                 throw new Error(`Can't set a null x in primitive ${this.id}, only the position can be turned to null`);
             }
-            if (!this._position) {
-                this._position = Vector2.Zero();
-            }
-
-            if (this._position.x === value) {
+            if (this._checkUseMargin()) {
+                switch (this._marginAlignment.horizontal) {
+                    case PrimitiveAlignment.AlignLeft:
+                    case PrimitiveAlignment.AlignStretch:
+                    case PrimitiveAlignment.AlignCenter:
+                        this.margin.leftPixels = value;
+                        break;
+                    case PrimitiveAlignment.AlignRight:
+                        this.margin.rightPixels = value;
+                        break;
+                    }
                 return;
-            }
+            } else {
+                if (!this._position) {
+                    this._position = Vector2.Zero();
+                }
 
-            this._position.x = value;
-            this._actualPosition = null;
-            this._triggerPropertyChanged(Prim2DBase.positionProperty, value);
-            this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
+                if (this._position.x === value) {
+                    return;
+                }
+
+                this._position.x = value;
+                this._actualPosition = null;
+                this._triggerPropertyChanged(Prim2DBase.positionProperty, value);
+                this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
+            }
         }
 
         /**
@@ -2089,24 +2133,38 @@
         }
 
         public set y(value: number) {
-            if (!this._checkPositionChange()) {
-                return;
-            }
+            //if (!this._checkPositionChange()) {
+            //    return;
+            //}
             if (value == null) {
                 throw new Error(`Can't set a null y in primitive ${this.id}, only the position can be turned to null`);
             }
-            if (!this._position) {
-                this._position = Vector2.Zero();
-            }
-
-            if (this._position.y === value) {
+            if (this._checkUseMargin()) {
+                switch (this._marginAlignment.vertical) {
+                    case PrimitiveAlignment.AlignBottom:
+                    case PrimitiveAlignment.AlignStretch:
+                    case PrimitiveAlignment.AlignCenter:
+                        this.margin.bottomPixels = value;
+                        break;
+                    case PrimitiveAlignment.AlignTop:
+                        this.margin.topPixels = value;
+                        break;
+                }
                 return;
-            }
+            } else {
+                if (!this._position) {
+                    this._position = Vector2.Zero();
+                }
 
-            this._position.y = value;
-            this._actualPosition = null;
-            this._triggerPropertyChanged(Prim2DBase.positionProperty, value);
-            this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
+                if (this._position.y === value) {
+                    return;
+                }
+
+                this._position.y = value;
+                this._actualPosition = null;
+                this._triggerPropertyChanged(Prim2DBase.positionProperty, value);
+                this._triggerPropertyChanged(Prim2DBase.actualPositionProperty, value);
+            }
         }
 
         private static boundinbBoxReentrency: number = -1;
@@ -2153,7 +2211,7 @@
 
                 return this._internalSize || Prim2DBase._nullSize;
             } else {
-                C2DLogging.setPostMessage(() => "user set size");                
+                C2DLogging.setPostMessage(() => "user set size");
             }
             return this._size || Prim2DBase._nullSize;
         }
@@ -2258,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();
@@ -2484,6 +2545,16 @@
         }
 
         /**
+         * Set the margin from a string value
+         * @param value is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
+         * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
+         */
+        public setMargin(value: string) {
+            this.margin.fromString(value);
+            this._updatePositioningState();
+        }
+
+        /**
          * Check for both margin and marginAlignment, return true if at least one of them is specified with a non default value
          */
         public get _hasMargin(): boolean {
@@ -2517,6 +2588,15 @@
             this._updatePositioningState();
         }
 
+        /**
+         * Set the padding from a string value
+         * @param value is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
+         * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.         */
+        public setPadding(value: string) {
+            this.padding.fromString(value);
+            this._updatePositioningState();
+        }
+
         private get _hasPadding(): boolean {
             return this._padding !== null && !this._padding.isDefault;
         }
@@ -2543,6 +2623,15 @@
         }
 
         /**
+         * Set the margin's horizontal and or vertical alignments from a string value.
+         * @param value can be: [<h:|horizontal:><left|right|center|stretch>], [<v:|vertical:><top|bottom|center|stretch>]
+         */
+        public setMarginalignment(value: string) {
+            this.marginAlignment.fromString(value);
+            this._updatePositioningState();
+        }
+
+        /**
          * Check if there a marginAlignment specified (non null and not default)
          */
         public get _hasMarginAlignment(): boolean {
@@ -2585,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();
@@ -2600,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();
@@ -2908,9 +3003,11 @@
                     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");                
+                C2DLogging.setPostMessage(() => "cache hit");
             }
             return this._boundingInfo;
         }
@@ -2948,11 +3045,23 @@
                         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");                
+                C2DLogging.setPostMessage(() => "cache hit");
             }
             return usePositioning ? this._layoutBoundingInfo : this.boundingInfo;
         }
@@ -3130,7 +3239,7 @@
             prims[3][0].levelVisible = true;
 
             // Current offset
-            let curOffset = Vector2.Zero(); 
+            let curOffset = Vector2.Zero();
 
             // Store the area info of the layout area
             let curAreaIndex = 0;
@@ -3358,7 +3467,7 @@
                     return ownerGroup.intersect(intersectInfo);
                 } finally  {
                     Prim2DBase._bypassGroup2DExclusion = false;
-                } 
+                }
             }
 
             // If we're testing a cachedGroup, we must reject pointer outside its levelBoundingInfo because children primitives could be partially clipped outside so we must not accept them as intersected when it's the case (because they're not visually visible).
@@ -3672,18 +3781,27 @@
 
         }
 
-        private _checkPositionChange(): boolean {
-            if (this.parent && this.parent.layoutEngine.isChildPositionAllowed === false) {
-                console.log(`Can't manually set the position of ${this.id}, the Layout Engine of its parent doesn't allow it`);
+        //private _checkPositionChange(): boolean {
+        //    if (this.parent && this.parent.layoutEngine.isChildPositionAllowed === false) {
+        //        console.log(`Can't manually set the position of ${this.id}, the Layout Engine of its parent doesn't allow it`);
+        //        return false;
+        //    }
+        //    if (this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
+        //        if (<any>this instanceof Group2D && (<Group2D><any>this).trackedNode == null) {
+        //            console.log(`You can't set the position/x/y of ${this.id} properties while positioning engine is used (margin, margin alignment and/or padding are set`);
+        //            return false;
+        //        }
+        //    }
+        //    return true;
+        //}
+
+        private _checkUseMargin(): boolean {
+            // Special cae: tracked node
+            if (<any>this instanceof Group2D && (<Group2D><any>this).trackedNode != null) {
                 return false;
             }
-            if (this._isFlagSet(SmartPropertyPrim.flagUsePositioning)) {
-                if (<any>this instanceof Group2D && (<Group2D><any>this).trackedNode == null) {
-                    console.log(`You can't set the position/x/y of ${this.id} properties while positioning engine is used (margin, margin alignment and/or padding are set`);
-                    return false;
-                }
-            }
-            return true;
+
+            return this._isFlagSet(SmartPropertyPrim.flagUsePositioning);
         }
 
         @logMethod("", true)
@@ -3804,7 +3922,7 @@
             this._updateCachesProcessStep = ownerProcessStep;
 
             this.owner.addUpdateCachedStateCounter(1);
-            
+
             // Check if the parent is synced
             if (this._parent && ((this._parent._globalTransformProcessStep !== this.owner._globalTransformProcessStep) || this._parent._areSomeFlagsSet(SmartPropertyPrim.flagLayoutDirty | SmartPropertyPrim.flagPositioningDirty | SmartPropertyPrim.flagZOrderDirty))) {
                 this._parent.updateCachedStates(false);
@@ -3979,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);
@@ -4202,7 +4323,7 @@
         }
 
         protected onSetOwner() {
-            
+
         }
 
         private static _zOrderChangedNotifList = new Array<Prim2DBase>();
@@ -4459,7 +4580,7 @@
         // If a child prim has an older _parentTransformStep it means the child's transform should be updated
         protected _globalTransformStep: number;
 
-        // Stores the previous 
+        // Stores the previous
         protected _globalTransformProcessStep: number;
         protected _prepareProcessStep: number;
         protected _updateCachesProcessStep: number;

+ 4 - 0
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;
         }

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 28 - 27
dist/preview release/babylon.core.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1361 - 1274
dist/preview release/babylon.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 36 - 36
dist/preview release/babylon.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 707 - 191
dist/preview release/babylon.max.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1361 - 1274
dist/preview release/babylon.module.d.ts


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 36 - 36
dist/preview release/babylon.noworker.js


+ 24 - 2
dist/preview release/canvas2D/babylon.canvas2d.d.ts

@@ -2191,10 +2191,16 @@ declare module BABYLON {
         /**
          * Shortcut to actualPosition.x
          */
+        /**
+         * DO NOT INVOKE for internal purpose only
+         */
         actualX: number;
         /**
          * Shortcut to actualPosition.y
          */
+        /**
+        * DO NOT INVOKE for internal purpose only
+        */
         actualY: number;
         /**
          * Position of the primitive, relative to its parent.
@@ -2254,13 +2260,29 @@ declare module BABYLON {
         readonly isManualZOrder: boolean;
         margin: PrimitiveThickness;
         /**
+         * Set the margin from a string value
+         * @param value is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
+         * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.
+         */
+        setMargin(value: string): void;
+        /**
          * Check for both margin and marginAlignment, return true if at least one of them is specified with a non default value
          */
         readonly _hasMargin: boolean;
         padding: PrimitiveThickness;
+        /**
+         * Set the padding from a string value
+         * @param value is "top: <value>, left:<value>, right:<value>, bottom:<value>" or "<value>" (same for all edges) each are optional, auto will be set if it's omitted.
+         * Values are: 'auto', 'inherit', 'XX%' for percentage, 'XXpx' or 'XX' for pixels.         */
+        setPadding(value: string): void;
         private readonly _hasPadding;
         marginAlignment: PrimitiveAlignment;
         /**
+         * Set the margin's horizontal and or vertical alignments from a string value.
+         * @param value can be: [<h:|horizontal:><left|right|center|stretch>], [<v:|vertical:><top|bottom|center|stretch>]
+         */
+        setMarginalignment(value: string): void;
+        /**
          * Check if there a marginAlignment specified (non null and not default)
          */
         readonly _hasMarginAlignment: boolean;
@@ -2465,7 +2487,7 @@ declare module BABYLON {
         protected updateCachedStatesOf(list: Prim2DBase[], recurse: boolean): void;
         private _parentLayoutDirty();
         protected _setLayoutDirty(): void;
-        private _checkPositionChange();
+        private _checkUseMargin();
         protected _positioningDirty(): void;
         protected _spreadActualOpacityChanged(): void;
         private _changeLayoutEngine(engine);
@@ -2895,7 +2917,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).

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 209 - 85
dist/preview release/canvas2D/babylon.canvas2d.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 12 - 12
dist/preview release/canvas2D/babylon.canvas2d.min.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 4 - 4
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; }

+ 107 - 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,29 @@ 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;
+        };
         'ArcRotateCamera': {
             type: typeof BABYLON.ArcRotateCamera;
             properties: string[];
         };
+        'FreeCamera': {
+            type: typeof BABYLON.FreeCamera;
+            properties: string[];
+        };
         'Scene': {
             type: typeof BABYLON.Scene;
             properties: string[];
@@ -177,6 +198,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 +210,33 @@ 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 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 +648,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 +695,35 @@ declare module INSPECTOR {
 }
 
 declare module INSPECTOR {
+    class CameraTab 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 +854,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 +994,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 +1072,21 @@ 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 {
     /** Any object implementing this interface should
      * provide methods to toggle its visibility
      */

+ 414 - 48
dist/preview release/inspector/babylon.inspector.js

@@ -5,10 +5,16 @@ 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;
+            console.log(initialTab);
+            //get parentElement of our Inspector
+            this._parentElement = parentElement;
+            console.log(this._parentElement);
             // get canvas parent only if needed.
             this._scene = scene;
             // Save HTML document and window
@@ -88,22 +94,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 +175,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 +357,25 @@ 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
+        },
         'ArcRotateCamera': {
             type: BABYLON.ArcRotateCamera,
             properties: [
+                'position',
                 'alpha',
                 'beta',
                 'radius',
@@ -354,6 +394,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 +640,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 +669,105 @@ 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 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) {
@@ -1694,8 +1873,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 +1914,6 @@ var INSPECTOR;
     INSPECTOR.TextureElement = TextureElement;
 })(INSPECTOR || (INSPECTOR = {}));
 
-//# sourceMappingURL=TextureElement.js.map
-
 var INSPECTOR;
 (function (INSPECTOR) {
     /**
@@ -2014,6 +2189,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 +2354,160 @@ 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 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 +2970,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 +3394,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 = [];
@@ -3106,10 +3415,15 @@ 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._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];
@@ -3123,9 +3437,12 @@ var INSPECTOR;
             var _this = this;
             this._div.className = 'tabbar';
             this._div.appendChild(this._toolBar.toHtml());
+            var i = 1;
             for (var _i = 0, _a = this._tabs; _i < _a.length; _i++) {
                 var tab = _a[_i];
                 this._div.appendChild(tab.toHtml());
+                tab.toHtml().id = 'tab' + i;
+                i++;
             }
             this._moreTabsIcon = INSPECTOR.Helpers.CreateElement('i', 'fa fa-angle-double-right more-tabs');
             this._moreTabsPanel = INSPECTOR.Helpers.CreateDiv('more-tabs-panel');
@@ -3689,6 +4006,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 +4238,45 @@ 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) {
+    /**
      * Checkbox to display/hide the primitive
      */
     var Checkbox = (function (_super) {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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;
+    }
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 209 - 0
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 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; }

+ 31 - 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,10 @@
 - 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)) 
  
 ### Bug fixes
 - Fixed a bug with spotlight direction ([deltakosh](https://github.com/deltakosh)) 
@@ -45,7 +48,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 +64,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;
+        }
+        
+    }
+}

+ 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;
+        }
+
+    }
+}

+ 179 - 131
inspector/src/properties.ts

@@ -1,74 +1,90 @@
 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
         },
-        
-        'ArcRotateCamera' : {
+        'DynamicTexture': {
+            type: BABYLON.DynamicTexture
+        },
+        'BaseTexture': {
+            type: BABYLON.BaseTexture
+        },
+        'FontTexture': {
+            type: BABYLON.FontTexture
+        },
+
+        'ArcRotateCamera': {
             type: BABYLON.ArcRotateCamera,
-            properties : [
-                'alpha', 
-                'beta', 
+            properties: [
+                'position',
+                'alpha',
+                'beta',
                 'radius',
                 'angularSensibilityX',
                 'angularSensibilityY',
-                'target', 
+                'target',
 
                 'lowerAlphaLimit',
                 'lowerBetaLimit',
@@ -81,53 +97,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 +185,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 +298,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 
         }
     }
 

+ 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;
         }
     }
-    
+
 }

+ 44 - 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,32 @@ module INSPECTOR {
             }
             this._tabs.push(new MaterialTab(this, this._inspector));
 
+            this._tabs.push(new CameraTab(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 +71,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 +81,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 +89,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 +100,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 +108,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 +120,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 +145,7 @@ module INSPECTOR {
             }
         }
 
-        public get inspector() : Inspector {
+        public get inspector(): Inspector {
             return this._inspector;
         }
 
@@ -144,7 +153,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 +171,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 +182,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 +193,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();
+
+        }
+    }
+}

+ 228 - 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,192 @@ 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;
+        */
+
+        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
+
         });
-        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;
-        };
+        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 +222,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);
             }
         }
 

+ 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 - 0
src/Materials/babylon.pbrMaterial.ts

@@ -1479,6 +1479,8 @@
                 }
             }
 
+            this._renderTargets.dispose();
+
             super.dispose(forceDisposeEffect, forceDisposeTextures);
         }
 

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

@@ -987,6 +987,8 @@
                 }
             }
 
+            this._renderTargets.dispose();
+
             super.dispose(forceDisposeEffect, forceDisposeTextures);
         }
 

+ 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);
                     }

+ 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,

+ 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;
+        }
     }
 
 }

+ 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;
 

+ 7 - 0
src/babylon.node.ts

@@ -277,6 +277,13 @@ module BABYLON {
             return results;
         }
 
+        /**
+         * Get all direct children of this node.
+        */
+        public getChildren(predicate?: (node: Node) => boolean): Node[] {
+            return this.getDescendants(true, predicate);
+        }
+
         public _setReady(state: boolean): void {
             if (state === this._isReady) {
                 return;

+ 346 - 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) => {
@@ -2682,6 +2935,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).
 
+