Browse Source

resolved conflicts

ssaket 6 years ago
parent
commit
0d6b035a1e
66 changed files with 28602 additions and 27604 deletions
  1. 13498 13449
      Playground/babylon.d.txt
  2. 12575 12545
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 90 21
      dist/preview release/babylon.max.js
  5. 90 21
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 90 21
      dist/preview release/es6.js
  8. 106 76
      dist/preview release/gui/babylon.gui.d.ts
  9. 1 1
      dist/preview release/gui/babylon.gui.js
  10. 1 1
      dist/preview release/gui/babylon.gui.min.js
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  12. 228 171
      dist/preview release/gui/babylon.gui.module.d.ts
  13. 8 8
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  15. 1 1
      dist/preview release/inspector/babylon.inspector.d.ts
  16. 2 2
      dist/preview release/inspector/babylon.inspector.module.d.ts
  17. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  18. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  19. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  20. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  21. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  22. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  23. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  24. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  25. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  26. 21 7
      dist/preview release/viewer/babylon.viewer.d.ts
  27. 3 3
      dist/preview release/viewer/babylon.viewer.js
  28. 7 7
      dist/preview release/viewer/babylon.viewer.max.js
  29. 24 7
      dist/preview release/viewer/babylon.viewer.module.d.ts
  30. 5 2
      dist/preview release/what's new.md
  31. 7 11
      gui/src/2D/advancedDynamicTexture.ts
  32. 6 1
      gui/src/2D/controls/button.ts
  33. 32 34
      gui/src/2D/controls/checkbox.ts
  34. 89 41
      gui/src/2D/controls/colorpicker.ts
  35. 87 53
      gui/src/2D/controls/container.ts
  36. 147 72
      gui/src/2D/controls/control.ts
  37. 2 3
      gui/src/2D/controls/displayGrid.ts
  38. 37 5
      gui/src/2D/controls/grid.ts
  39. 51 35
      gui/src/2D/controls/image.ts
  40. 4 4
      gui/src/2D/controls/index.ts
  41. 135 123
      gui/src/2D/controls/inputText.ts
  42. 9 11
      gui/src/2D/controls/line.ts
  43. 19 21
      gui/src/2D/controls/multiLine.ts
  44. 31 35
      gui/src/2D/controls/radioButton.ts
  45. 0 435
      gui/src/2D/controls/scrollViewer.ts
  46. 375 0
      gui/src/2D/controls/scrollViewers/scrollViewer.ts
  47. 74 0
      gui/src/2D/controls/scrollViewers/scrollViewerWindow.ts
  48. 1 1
      gui/src/2D/controls/selector.ts
  49. 0 227
      gui/src/2D/controls/slider.ts
  50. 4 3
      gui/src/2D/controls/baseSlider.ts
  51. 46 44
      gui/src/2D/controls/imageBasedSlider.ts
  52. 155 0
      gui/src/2D/controls/sliders/scrollBar.ts
  53. 240 0
      gui/src/2D/controls/sliders/slider.ts
  54. 41 22
      gui/src/2D/controls/stackPanel.ts
  55. 49 25
      gui/src/2D/controls/textBlock.ts
  56. 13 0
      gui/src/2D/valueAndUnit.ts
  57. 9 1
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  58. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx
  59. 42 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/scrollViewerPropertyGridComponent.tsx
  60. 1 0
      inspector/src/components/actionTabs/tabs/toolsTabComponent.tsx
  61. 5 0
      inspector/src/components/sceneExplorer/entities/gui/advancedDynamicTextureTreeItemComponent.tsx
  62. 23 18
      inspector/src/inspector.ts
  63. 24 18
      src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts
  64. 22 0
      src/Cameras/babylon.arcRotateCamera.ts
  65. 20 0
      src/Math/babylon.math.ts
  66. 38 4
      src/Physics/Plugins/babylon.ammoJSPlugin.ts

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


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


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


+ 90 - 21
dist/preview release/babylon.max.js

@@ -5688,6 +5688,16 @@ var BABYLON;
         Vector3.Up = function () {
             return new Vector3(0.0, 1.0, 0.0);
         };
+        Object.defineProperty(Vector3, "UpReadOnly", {
+            /**
+             * Gets a up Vector3 that must not be updated
+             */
+            get: function () {
+                return Vector3._UpReadOnly;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Returns a new Vector3 set to (0.0, -1.0, 0.0)
          * @returns a new down Vector3
@@ -6165,6 +6175,7 @@ var BABYLON;
             Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             quat.toEulerAnglesToRef(ref);
         };
+        Vector3._UpReadOnly = Vector3.Up();
         return Vector3;
     }());
     BABYLON.Vector3 = Vector3;
@@ -6823,6 +6834,16 @@ var BABYLON;
             result.z = (x * m[2]) + (y * m[6]) + (z * m[10]);
             result.w = w;
         };
+        /**
+         * Creates a new Vector4 from a Vector3
+         * @param source defines the source data
+         * @param w defines the 4th component (default is 0)
+         * @returns a new Vector4
+         */
+        Vector4.FromVector3 = function (source, w) {
+            if (w === void 0) { w = 0; }
+            return new Vector4(source.x, source.y, source.z, w);
+        };
         return Vector4;
     }());
     BABYLON.Vector4 = Vector4;
@@ -52632,6 +52653,19 @@ var BABYLON;
             }
             var target = this._getTargetPosition();
             this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
+            // Rotate according to up vector
+            if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
+                if (!this._tempAxisVector) {
+                    this._tempAxisVector = new BABYLON.Vector3();
+                    this._tempAxisRotationMatrix = new BABYLON.Matrix();
+                }
+                BABYLON.Vector3.CrossToRef(BABYLON.Vector3.Up(), this.upVector, this._tempAxisVector);
+                this._tempAxisVector.normalize();
+                var angle = Math.acos(BABYLON.Vector3.Dot(BABYLON.Vector3.UpReadOnly, this.upVector));
+                BABYLON.Matrix.RotationAxisToRef(this._tempAxisVector, angle, this._tempAxisRotationMatrix);
+                this._tempAxisVector.copyFrom(this._computationVector);
+                BABYLON.Vector3.TransformCoordinatesToRef(this._tempAxisVector, this._tempAxisRotationMatrix, this._computationVector);
+            }
             target.addToRef(this._computationVector, this._newPosition);
             if (this.getScene().collisionsEnabled && this.checkCollisions) {
                 if (!this._collider) {
@@ -99909,6 +99943,7 @@ var BABYLON;
              */
             this.name = "AmmoJSPlugin";
             this._timeStep = 1 / 60;
+            this._fixedTimeStep = 1 / 60;
             this._maxSteps = 5;
             this._tmpQuaternion = new BABYLON.Quaternion();
             this._tmpContactCallbackResult = false;
@@ -99952,6 +99987,20 @@ var BABYLON;
             this._timeStep = timeStep;
         };
         /**
+         * Increment to step forward in the physics engine (If timeStep is set to 1/60 and fixedTimeStep is set to 1/120 the physics engine should run 2 steps per frame) (Default: 1/60)
+         * @param fixedTimeStep fixedTimeStep to use in seconds
+         */
+        AmmoJSPlugin.prototype.setFixedTimeStep = function (fixedTimeStep) {
+            this._fixedTimeStep = fixedTimeStep;
+        };
+        /**
+         * Sets the maximum number of steps by the physics engine per frame (Default: 5)
+         * @param maxSteps the maximum number of steps by the physics engine per frame
+         */
+        AmmoJSPlugin.prototype.setMaxSteps = function (maxSteps) {
+            this._maxSteps = maxSteps;
+        };
+        /**
          * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
          * @returns the current timestep in seconds
          */
@@ -100012,7 +100061,7 @@ var BABYLON;
                 // Update physics world objects to match babylon world
                 impostor.beforeStep();
             }
-            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps, this._fixedTimeStep);
             for (var _a = 0, impostors_2 = impostors; _a < impostors_2.length; _a++) {
                 var mainImpostor = impostors_2[_a];
                 // After physics update make babylon world objects match physics world objects
@@ -100098,6 +100147,10 @@ var BABYLON;
                     body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
                     body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
                 }
+                // Disable collision if NoImpostor, but keep collision if shape is btCompoundShape
+                if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
+                }
                 body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
@@ -100152,7 +100205,9 @@ var BABYLON;
          * @param impostorJoint the imposter joint to remove the joint from
          */
         AmmoJSPlugin.prototype.removeJoint = function (impostorJoint) {
-            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            if (this.world) {
+                this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            }
         };
         // adds all verticies (including child verticies) to the triangle mesh
         AmmoJSPlugin.prototype._addMeshVerts = function (btTriangleMesh, topLevelObject, object) {
@@ -100212,7 +100267,11 @@ var BABYLON;
                         var childImpostor = childMesh.getPhysicsImpostor();
                         if (childImpostor) {
                             var shape = _this._createShape(childImpostor);
-                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+                            // Position needs to be scaled based on parent's scaling
+                            var parentMat = childMesh.parent.getWorldMatrix().clone();
+                            var s = new BABYLON.Vector3();
+                            parentMat.decompose(s);
+                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x * s.x, childMesh.position.y * s.y, childMesh.position.z * s.z);
                             _this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion.x, childMesh.rotationQuaternion.y, childMesh.rotationQuaternion.z, childMesh.rotationQuaternion.w);
                             _this._tmpAmmoTransform.setRotation(_this._tmpAmmoQuaternion);
                             returnValue.addChildShape(_this._tmpAmmoTransform, shape);
@@ -100223,7 +100282,6 @@ var BABYLON;
                     var shape = this._createShape(impostor, true);
                     if (shape) {
                         this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
-                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
                         this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
                         this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
                         returnValue.addChildShape(this._tmpAmmoTransform, shape);
@@ -100255,6 +100313,10 @@ var BABYLON;
                         returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
                     }
                     break;
+                case BABYLON.PhysicsImpostor.NoImpostor:
+                    // Fill with sphere but collision is disabled on the rigid body in generatePhysicsBody, using an empty shape caused unexpected movement with joints
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
             }
             return returnValue;
         };
@@ -100505,6 +100567,7 @@ var BABYLON;
             Ammo.destroy(this._tmpAmmoConcreteContactResultCallback);
             this.world = null;
         };
+        AmmoJSPlugin.DISABLE_COLLISION_FLAG = 4;
         AmmoJSPlugin.KINEMATIC_FLAG = 2;
         AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG = 4;
         return AmmoJSPlugin;
@@ -103645,6 +103708,10 @@ var BABYLON;
              */
             this.zDragFactor = 3;
             /**
+             * If the object should rotate to face the drag origin
+             */
+            this.rotateDraggedObject = true;
+            /**
              * If the behavior is currently in a dragging state
              */
             this.dragging = false;
@@ -103711,7 +103778,7 @@ var BABYLON;
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!_this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
                         if (_this._scene.activeCamera && _this._scene.activeCamera.cameraRigMode == BABYLON.Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.globalPosition);
                         }
                         pickedMesh = _this._ownerNode;
                         BABYLON.BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
@@ -103767,7 +103834,7 @@ var BABYLON;
                     if (_this.currentDraggingPointerID == pointerInfo.event.pointerId && _this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh) {
                         var zDragFactor = _this.zDragFactor;
                         if (_this._scene.activeCamera && _this._scene.activeCamera.cameraRigMode == BABYLON.Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.globalPosition);
                             zDragFactor = 0;
                         }
                         // Calculate controller drag distance in controller space
@@ -103803,21 +103870,23 @@ var BABYLON;
                     BABYLON.BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
                     // Slowly move mesh to avoid jitter
                     pickedMesh.position.addInPlace(_this._targetPosition.subtract(pickedMesh.position).scale(_this.dragDeltaRatio));
-                    // Get change in rotation
-                    tmpQuaternion.copyFrom(_this._startingOrientation);
-                    tmpQuaternion.x = -tmpQuaternion.x;
-                    tmpQuaternion.y = -tmpQuaternion.y;
-                    tmpQuaternion.z = -tmpQuaternion.z;
-                    _this._virtualDragMesh.rotationQuaternion.multiplyToRef(tmpQuaternion, tmpQuaternion);
-                    // Convert change in rotation to only y axis rotation
-                    BABYLON.Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
-                    tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
-                    // Slowly move mesh to avoid jitter
-                    var oldParent = pickedMesh.parent;
-                    pickedMesh.setParent(null);
-                    BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
-                    pickedMesh.setParent(oldParent);
-                    BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    if (_this.rotateDraggedObject) {
+                        // Get change in rotation
+                        tmpQuaternion.copyFrom(_this._startingOrientation);
+                        tmpQuaternion.x = -tmpQuaternion.x;
+                        tmpQuaternion.y = -tmpQuaternion.y;
+                        tmpQuaternion.z = -tmpQuaternion.z;
+                        _this._virtualDragMesh.rotationQuaternion.multiplyToRef(tmpQuaternion, tmpQuaternion);
+                        // Convert change in rotation to only y axis rotation
+                        BABYLON.Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
+                        tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
+                        // Slowly move mesh to avoid jitter
+                        var oldParent = pickedMesh.parent;
+                        pickedMesh.setParent(null);
+                        BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
+                        pickedMesh.setParent(oldParent);
+                        BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    }
                 }
             });
         };

+ 90 - 21
dist/preview release/babylon.no-module.max.js

@@ -5655,6 +5655,16 @@ var BABYLON;
         Vector3.Up = function () {
             return new Vector3(0.0, 1.0, 0.0);
         };
+        Object.defineProperty(Vector3, "UpReadOnly", {
+            /**
+             * Gets a up Vector3 that must not be updated
+             */
+            get: function () {
+                return Vector3._UpReadOnly;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Returns a new Vector3 set to (0.0, -1.0, 0.0)
          * @returns a new down Vector3
@@ -6132,6 +6142,7 @@ var BABYLON;
             Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             quat.toEulerAnglesToRef(ref);
         };
+        Vector3._UpReadOnly = Vector3.Up();
         return Vector3;
     }());
     BABYLON.Vector3 = Vector3;
@@ -6790,6 +6801,16 @@ var BABYLON;
             result.z = (x * m[2]) + (y * m[6]) + (z * m[10]);
             result.w = w;
         };
+        /**
+         * Creates a new Vector4 from a Vector3
+         * @param source defines the source data
+         * @param w defines the 4th component (default is 0)
+         * @returns a new Vector4
+         */
+        Vector4.FromVector3 = function (source, w) {
+            if (w === void 0) { w = 0; }
+            return new Vector4(source.x, source.y, source.z, w);
+        };
         return Vector4;
     }());
     BABYLON.Vector4 = Vector4;
@@ -52599,6 +52620,19 @@ var BABYLON;
             }
             var target = this._getTargetPosition();
             this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
+            // Rotate according to up vector
+            if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
+                if (!this._tempAxisVector) {
+                    this._tempAxisVector = new BABYLON.Vector3();
+                    this._tempAxisRotationMatrix = new BABYLON.Matrix();
+                }
+                BABYLON.Vector3.CrossToRef(BABYLON.Vector3.Up(), this.upVector, this._tempAxisVector);
+                this._tempAxisVector.normalize();
+                var angle = Math.acos(BABYLON.Vector3.Dot(BABYLON.Vector3.UpReadOnly, this.upVector));
+                BABYLON.Matrix.RotationAxisToRef(this._tempAxisVector, angle, this._tempAxisRotationMatrix);
+                this._tempAxisVector.copyFrom(this._computationVector);
+                BABYLON.Vector3.TransformCoordinatesToRef(this._tempAxisVector, this._tempAxisRotationMatrix, this._computationVector);
+            }
             target.addToRef(this._computationVector, this._newPosition);
             if (this.getScene().collisionsEnabled && this.checkCollisions) {
                 if (!this._collider) {
@@ -99876,6 +99910,7 @@ var BABYLON;
              */
             this.name = "AmmoJSPlugin";
             this._timeStep = 1 / 60;
+            this._fixedTimeStep = 1 / 60;
             this._maxSteps = 5;
             this._tmpQuaternion = new BABYLON.Quaternion();
             this._tmpContactCallbackResult = false;
@@ -99919,6 +99954,20 @@ var BABYLON;
             this._timeStep = timeStep;
         };
         /**
+         * Increment to step forward in the physics engine (If timeStep is set to 1/60 and fixedTimeStep is set to 1/120 the physics engine should run 2 steps per frame) (Default: 1/60)
+         * @param fixedTimeStep fixedTimeStep to use in seconds
+         */
+        AmmoJSPlugin.prototype.setFixedTimeStep = function (fixedTimeStep) {
+            this._fixedTimeStep = fixedTimeStep;
+        };
+        /**
+         * Sets the maximum number of steps by the physics engine per frame (Default: 5)
+         * @param maxSteps the maximum number of steps by the physics engine per frame
+         */
+        AmmoJSPlugin.prototype.setMaxSteps = function (maxSteps) {
+            this._maxSteps = maxSteps;
+        };
+        /**
          * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
          * @returns the current timestep in seconds
          */
@@ -99979,7 +100028,7 @@ var BABYLON;
                 // Update physics world objects to match babylon world
                 impostor.beforeStep();
             }
-            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps, this._fixedTimeStep);
             for (var _a = 0, impostors_2 = impostors; _a < impostors_2.length; _a++) {
                 var mainImpostor = impostors_2[_a];
                 // After physics update make babylon world objects match physics world objects
@@ -100065,6 +100114,10 @@ var BABYLON;
                     body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
                     body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
                 }
+                // Disable collision if NoImpostor, but keep collision if shape is btCompoundShape
+                if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
+                }
                 body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
@@ -100119,7 +100172,9 @@ var BABYLON;
          * @param impostorJoint the imposter joint to remove the joint from
          */
         AmmoJSPlugin.prototype.removeJoint = function (impostorJoint) {
-            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            if (this.world) {
+                this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            }
         };
         // adds all verticies (including child verticies) to the triangle mesh
         AmmoJSPlugin.prototype._addMeshVerts = function (btTriangleMesh, topLevelObject, object) {
@@ -100179,7 +100234,11 @@ var BABYLON;
                         var childImpostor = childMesh.getPhysicsImpostor();
                         if (childImpostor) {
                             var shape = _this._createShape(childImpostor);
-                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+                            // Position needs to be scaled based on parent's scaling
+                            var parentMat = childMesh.parent.getWorldMatrix().clone();
+                            var s = new BABYLON.Vector3();
+                            parentMat.decompose(s);
+                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x * s.x, childMesh.position.y * s.y, childMesh.position.z * s.z);
                             _this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion.x, childMesh.rotationQuaternion.y, childMesh.rotationQuaternion.z, childMesh.rotationQuaternion.w);
                             _this._tmpAmmoTransform.setRotation(_this._tmpAmmoQuaternion);
                             returnValue.addChildShape(_this._tmpAmmoTransform, shape);
@@ -100190,7 +100249,6 @@ var BABYLON;
                     var shape = this._createShape(impostor, true);
                     if (shape) {
                         this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
-                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
                         this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
                         this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
                         returnValue.addChildShape(this._tmpAmmoTransform, shape);
@@ -100222,6 +100280,10 @@ var BABYLON;
                         returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
                     }
                     break;
+                case BABYLON.PhysicsImpostor.NoImpostor:
+                    // Fill with sphere but collision is disabled on the rigid body in generatePhysicsBody, using an empty shape caused unexpected movement with joints
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
             }
             return returnValue;
         };
@@ -100472,6 +100534,7 @@ var BABYLON;
             Ammo.destroy(this._tmpAmmoConcreteContactResultCallback);
             this.world = null;
         };
+        AmmoJSPlugin.DISABLE_COLLISION_FLAG = 4;
         AmmoJSPlugin.KINEMATIC_FLAG = 2;
         AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG = 4;
         return AmmoJSPlugin;
@@ -103612,6 +103675,10 @@ var BABYLON;
              */
             this.zDragFactor = 3;
             /**
+             * If the object should rotate to face the drag origin
+             */
+            this.rotateDraggedObject = true;
+            /**
              * If the behavior is currently in a dragging state
              */
             this.dragging = false;
@@ -103678,7 +103745,7 @@ var BABYLON;
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!_this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
                         if (_this._scene.activeCamera && _this._scene.activeCamera.cameraRigMode == BABYLON.Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.globalPosition);
                         }
                         pickedMesh = _this._ownerNode;
                         BABYLON.BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
@@ -103734,7 +103801,7 @@ var BABYLON;
                     if (_this.currentDraggingPointerID == pointerInfo.event.pointerId && _this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh) {
                         var zDragFactor = _this.zDragFactor;
                         if (_this._scene.activeCamera && _this._scene.activeCamera.cameraRigMode == BABYLON.Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.globalPosition);
                             zDragFactor = 0;
                         }
                         // Calculate controller drag distance in controller space
@@ -103770,21 +103837,23 @@ var BABYLON;
                     BABYLON.BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
                     // Slowly move mesh to avoid jitter
                     pickedMesh.position.addInPlace(_this._targetPosition.subtract(pickedMesh.position).scale(_this.dragDeltaRatio));
-                    // Get change in rotation
-                    tmpQuaternion.copyFrom(_this._startingOrientation);
-                    tmpQuaternion.x = -tmpQuaternion.x;
-                    tmpQuaternion.y = -tmpQuaternion.y;
-                    tmpQuaternion.z = -tmpQuaternion.z;
-                    _this._virtualDragMesh.rotationQuaternion.multiplyToRef(tmpQuaternion, tmpQuaternion);
-                    // Convert change in rotation to only y axis rotation
-                    BABYLON.Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
-                    tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
-                    // Slowly move mesh to avoid jitter
-                    var oldParent = pickedMesh.parent;
-                    pickedMesh.setParent(null);
-                    BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
-                    pickedMesh.setParent(oldParent);
-                    BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    if (_this.rotateDraggedObject) {
+                        // Get change in rotation
+                        tmpQuaternion.copyFrom(_this._startingOrientation);
+                        tmpQuaternion.x = -tmpQuaternion.x;
+                        tmpQuaternion.y = -tmpQuaternion.y;
+                        tmpQuaternion.z = -tmpQuaternion.z;
+                        _this._virtualDragMesh.rotationQuaternion.multiplyToRef(tmpQuaternion, tmpQuaternion);
+                        // Convert change in rotation to only y axis rotation
+                        BABYLON.Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
+                        tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
+                        // Slowly move mesh to avoid jitter
+                        var oldParent = pickedMesh.parent;
+                        pickedMesh.setParent(null);
+                        BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
+                        pickedMesh.setParent(oldParent);
+                        BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    }
                 }
             });
         };

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


+ 90 - 21
dist/preview release/es6.js

@@ -5655,6 +5655,16 @@ var BABYLON;
         Vector3.Up = function () {
             return new Vector3(0.0, 1.0, 0.0);
         };
+        Object.defineProperty(Vector3, "UpReadOnly", {
+            /**
+             * Gets a up Vector3 that must not be updated
+             */
+            get: function () {
+                return Vector3._UpReadOnly;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Returns a new Vector3 set to (0.0, -1.0, 0.0)
          * @returns a new down Vector3
@@ -6132,6 +6142,7 @@ var BABYLON;
             Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             quat.toEulerAnglesToRef(ref);
         };
+        Vector3._UpReadOnly = Vector3.Up();
         return Vector3;
     }());
     BABYLON.Vector3 = Vector3;
@@ -6790,6 +6801,16 @@ var BABYLON;
             result.z = (x * m[2]) + (y * m[6]) + (z * m[10]);
             result.w = w;
         };
+        /**
+         * Creates a new Vector4 from a Vector3
+         * @param source defines the source data
+         * @param w defines the 4th component (default is 0)
+         * @returns a new Vector4
+         */
+        Vector4.FromVector3 = function (source, w) {
+            if (w === void 0) { w = 0; }
+            return new Vector4(source.x, source.y, source.z, w);
+        };
         return Vector4;
     }());
     BABYLON.Vector4 = Vector4;
@@ -52599,6 +52620,19 @@ var BABYLON;
             }
             var target = this._getTargetPosition();
             this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
+            // Rotate according to up vector
+            if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
+                if (!this._tempAxisVector) {
+                    this._tempAxisVector = new BABYLON.Vector3();
+                    this._tempAxisRotationMatrix = new BABYLON.Matrix();
+                }
+                BABYLON.Vector3.CrossToRef(BABYLON.Vector3.Up(), this.upVector, this._tempAxisVector);
+                this._tempAxisVector.normalize();
+                var angle = Math.acos(BABYLON.Vector3.Dot(BABYLON.Vector3.UpReadOnly, this.upVector));
+                BABYLON.Matrix.RotationAxisToRef(this._tempAxisVector, angle, this._tempAxisRotationMatrix);
+                this._tempAxisVector.copyFrom(this._computationVector);
+                BABYLON.Vector3.TransformCoordinatesToRef(this._tempAxisVector, this._tempAxisRotationMatrix, this._computationVector);
+            }
             target.addToRef(this._computationVector, this._newPosition);
             if (this.getScene().collisionsEnabled && this.checkCollisions) {
                 if (!this._collider) {
@@ -99876,6 +99910,7 @@ var BABYLON;
              */
             this.name = "AmmoJSPlugin";
             this._timeStep = 1 / 60;
+            this._fixedTimeStep = 1 / 60;
             this._maxSteps = 5;
             this._tmpQuaternion = new BABYLON.Quaternion();
             this._tmpContactCallbackResult = false;
@@ -99919,6 +99954,20 @@ var BABYLON;
             this._timeStep = timeStep;
         };
         /**
+         * Increment to step forward in the physics engine (If timeStep is set to 1/60 and fixedTimeStep is set to 1/120 the physics engine should run 2 steps per frame) (Default: 1/60)
+         * @param fixedTimeStep fixedTimeStep to use in seconds
+         */
+        AmmoJSPlugin.prototype.setFixedTimeStep = function (fixedTimeStep) {
+            this._fixedTimeStep = fixedTimeStep;
+        };
+        /**
+         * Sets the maximum number of steps by the physics engine per frame (Default: 5)
+         * @param maxSteps the maximum number of steps by the physics engine per frame
+         */
+        AmmoJSPlugin.prototype.setMaxSteps = function (maxSteps) {
+            this._maxSteps = maxSteps;
+        };
+        /**
          * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
          * @returns the current timestep in seconds
          */
@@ -99979,7 +100028,7 @@ var BABYLON;
                 // Update physics world objects to match babylon world
                 impostor.beforeStep();
             }
-            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps, this._fixedTimeStep);
             for (var _a = 0, impostors_2 = impostors; _a < impostors_2.length; _a++) {
                 var mainImpostor = impostors_2[_a];
                 // After physics update make babylon world objects match physics world objects
@@ -100065,6 +100114,10 @@ var BABYLON;
                     body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.KINEMATIC_FLAG);
                     body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
                 }
+                // Disable collision if NoImpostor, but keep collision if shape is btCompoundShape
+                if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
+                }
                 body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
@@ -100119,7 +100172,9 @@ var BABYLON;
          * @param impostorJoint the imposter joint to remove the joint from
          */
         AmmoJSPlugin.prototype.removeJoint = function (impostorJoint) {
-            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            if (this.world) {
+                this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            }
         };
         // adds all verticies (including child verticies) to the triangle mesh
         AmmoJSPlugin.prototype._addMeshVerts = function (btTriangleMesh, topLevelObject, object) {
@@ -100179,7 +100234,11 @@ var BABYLON;
                         var childImpostor = childMesh.getPhysicsImpostor();
                         if (childImpostor) {
                             var shape = _this._createShape(childImpostor);
-                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+                            // Position needs to be scaled based on parent's scaling
+                            var parentMat = childMesh.parent.getWorldMatrix().clone();
+                            var s = new BABYLON.Vector3();
+                            parentMat.decompose(s);
+                            _this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x * s.x, childMesh.position.y * s.y, childMesh.position.z * s.z);
                             _this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion.x, childMesh.rotationQuaternion.y, childMesh.rotationQuaternion.z, childMesh.rotationQuaternion.w);
                             _this._tmpAmmoTransform.setRotation(_this._tmpAmmoQuaternion);
                             returnValue.addChildShape(_this._tmpAmmoTransform, shape);
@@ -100190,7 +100249,6 @@ var BABYLON;
                     var shape = this._createShape(impostor, true);
                     if (shape) {
                         this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
-                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
                         this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
                         this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
                         returnValue.addChildShape(this._tmpAmmoTransform, shape);
@@ -100222,6 +100280,10 @@ var BABYLON;
                         returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
                     }
                     break;
+                case BABYLON.PhysicsImpostor.NoImpostor:
+                    // Fill with sphere but collision is disabled on the rigid body in generatePhysicsBody, using an empty shape caused unexpected movement with joints
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
             }
             return returnValue;
         };
@@ -100472,6 +100534,7 @@ var BABYLON;
             Ammo.destroy(this._tmpAmmoConcreteContactResultCallback);
             this.world = null;
         };
+        AmmoJSPlugin.DISABLE_COLLISION_FLAG = 4;
         AmmoJSPlugin.KINEMATIC_FLAG = 2;
         AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG = 4;
         return AmmoJSPlugin;
@@ -103612,6 +103675,10 @@ var BABYLON;
              */
             this.zDragFactor = 3;
             /**
+             * If the object should rotate to face the drag origin
+             */
+            this.rotateDraggedObject = true;
+            /**
              * If the behavior is currently in a dragging state
              */
             this.dragging = false;
@@ -103678,7 +103745,7 @@ var BABYLON;
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!_this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
                         if (_this._scene.activeCamera && _this._scene.activeCamera.cameraRigMode == BABYLON.Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.globalPosition);
                         }
                         pickedMesh = _this._ownerNode;
                         BABYLON.BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
@@ -103734,7 +103801,7 @@ var BABYLON;
                     if (_this.currentDraggingPointerID == pointerInfo.event.pointerId && _this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh) {
                         var zDragFactor = _this.zDragFactor;
                         if (_this._scene.activeCamera && _this._scene.activeCamera.cameraRigMode == BABYLON.Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(_this._scene.activeCamera.globalPosition);
                             zDragFactor = 0;
                         }
                         // Calculate controller drag distance in controller space
@@ -103770,21 +103837,23 @@ var BABYLON;
                     BABYLON.BoundingBoxGizmo._RemoveAndStorePivotPoint(pickedMesh);
                     // Slowly move mesh to avoid jitter
                     pickedMesh.position.addInPlace(_this._targetPosition.subtract(pickedMesh.position).scale(_this.dragDeltaRatio));
-                    // Get change in rotation
-                    tmpQuaternion.copyFrom(_this._startingOrientation);
-                    tmpQuaternion.x = -tmpQuaternion.x;
-                    tmpQuaternion.y = -tmpQuaternion.y;
-                    tmpQuaternion.z = -tmpQuaternion.z;
-                    _this._virtualDragMesh.rotationQuaternion.multiplyToRef(tmpQuaternion, tmpQuaternion);
-                    // Convert change in rotation to only y axis rotation
-                    BABYLON.Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
-                    tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
-                    // Slowly move mesh to avoid jitter
-                    var oldParent = pickedMesh.parent;
-                    pickedMesh.setParent(null);
-                    BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
-                    pickedMesh.setParent(oldParent);
-                    BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    if (_this.rotateDraggedObject) {
+                        // Get change in rotation
+                        tmpQuaternion.copyFrom(_this._startingOrientation);
+                        tmpQuaternion.x = -tmpQuaternion.x;
+                        tmpQuaternion.y = -tmpQuaternion.y;
+                        tmpQuaternion.z = -tmpQuaternion.z;
+                        _this._virtualDragMesh.rotationQuaternion.multiplyToRef(tmpQuaternion, tmpQuaternion);
+                        // Convert change in rotation to only y axis rotation
+                        BABYLON.Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
+                        tmpQuaternion.multiplyToRef(_this._startingOrientation, tmpQuaternion);
+                        // Slowly move mesh to avoid jitter
+                        var oldParent = pickedMesh.parent;
+                        pickedMesh.setParent(null);
+                        BABYLON.Quaternion.SlerpToRef(pickedMesh.rotationQuaternion, tmpQuaternion, _this.dragDeltaRatio, pickedMesh.rotationQuaternion);
+                        pickedMesh.setParent(oldParent);
+                        BABYLON.BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    }
                 }
             });
         };

+ 106 - 76
dist/preview release/gui/babylon.gui.d.ts

@@ -1,6 +1,7 @@
 /*Babylon.js GUI*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../Tools/gulp/babylonjs
+//   ../../../../Tools/gulp/2D
 declare module BABYLON.GUI {
 }
 declare module BABYLON.GUI {
@@ -60,8 +61,6 @@ declare module BABYLON.GUI {
             _layerToDispose: BABYLON.Nullable<BABYLON.Layer>;
             /** @hidden */
             _linkedControls: Control[];
-            /** @hidden */
-            _needRedraw: boolean;
             /**
                 * BABYLON.Observable event triggered each time an clipboard event is received from the rendering canvas
                 */
@@ -516,6 +515,13 @@ declare module BABYLON.GUI {
                 */
             getValueInPixel(host: AdvancedDynamicTexture, refValue: number): number;
             /**
+                * Update the current value and unit. This should be done cautiously as the GUi won't be marked as dirty with this function.
+                * @param value defines the value to store
+                * @param unit defines the unit to store
+                * @returns the current ValueAndUnit
+                */
+            updateInPlace(value: number, unit?: number): ValueAndUnit;
+            /**
                 * Gets the value accordingly to its unit
                 * @param host  defines the root host
                 * @returns the value
@@ -724,7 +730,7 @@ declare module BABYLON.GUI {
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             /**
@@ -746,7 +752,10 @@ declare module BABYLON.GUI {
             onValueChangedObservable: BABYLON.Observable<BABYLON.Color3>;
             /** Gets or sets the color of the color picker */
             value: BABYLON.Color3;
-            /** Gets or sets control width */
+            /**
+                * Gets or sets control width
+                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                */
             width: string | number;
             /** Gets or sets control height */
             height: string | number;
@@ -759,7 +768,9 @@ declare module BABYLON.GUI {
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
             _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
@@ -782,6 +793,8 @@ declare module BABYLON.GUI {
             protected _adaptWidthToChildren: boolean;
             /** @hidden */
             protected _adaptHeightToChildren: boolean;
+            /** @hidden */
+            protected _rebuildLayout: boolean;
             /** Gets or sets a boolean indicating if the container should try to adapt to its children height */
             adaptHeightToChildren: boolean;
             /** Gets or sets a boolean indicating if the container should try to adapt to its children width */
@@ -836,20 +849,27 @@ declare module BABYLON.GUI {
             /** @hidden */
             _reOrderControl(control: Control): void;
             /** @hidden */
+            _offsetLeft(offset: number): void;
+            /** @hidden */
+            _offsetTop(offset: number): void;
+            /** @hidden */
             _markAllAsDirty(): void;
             /** @hidden */
             protected _localDraw(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _link(root: BABYLON.Nullable<Container>, host: AdvancedDynamicTexture): void;
+            _link(host: AdvancedDynamicTexture): void;
+            /** @hidden */
+            protected _beforeLayout(): void;
+            /** @hidden */
+            _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
+            protected _postMeasure(): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _getDescendants(results: Control[], directDescendantsOnly?: boolean, predicate?: (control: Control) => boolean): void;
             /** @hidden */
             _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
             /** @hidden */
-            protected _clipForChildren(context: CanvasRenderingContext2D): void;
-            /** @hidden */
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
@@ -868,8 +888,6 @@ declare module BABYLON.GUI {
                 */
             static AllowAlphaInheritance: boolean;
             /** @hidden */
-            _root: BABYLON.Nullable<Container>;
-            /** @hidden */
             _host: AdvancedDynamicTexture;
             /** Gets or sets the control parent */
             parent: BABYLON.Nullable<Container>;
@@ -890,6 +908,8 @@ declare module BABYLON.GUI {
             /** @hidden */
             protected _verticalAlignment: number;
             /** @hidden */
+            protected _isDirty: boolean;
+            /** @hidden */
             _tempParentMeasure: Measure;
             /** @hidden */
             protected _cachedParentMeasure: Measure;
@@ -906,6 +926,8 @@ declare module BABYLON.GUI {
             protected _isEnabled: boolean;
             protected _disabledColor: string;
             /** @hidden */
+            _isClipped: boolean;
+            /** @hidden */
             _tag: any;
             /**
                 * Gets or sets the unique id of the node. Please note that this number will be updated when the control is added to a container
@@ -1170,6 +1192,12 @@ declare module BABYLON.GUI {
             name?: string | undefined);
             /** @hidden */
             protected _getTypeName(): string;
+            /**
+                * Gets the first ascendant in the hierarchy of the given type
+                * @param className defines the required type
+                * @returns the ascendant or null if not found
+                */
+            getAscendantOfClass(className: string): BABYLON.Nullable<Control>;
             /** @hidden */
             _resetFontCache(): void;
             /**
@@ -1221,6 +1249,10 @@ declare module BABYLON.GUI {
             /** @hidden */
             _moveToProjectedPosition(projectedPosition: BABYLON.Vector3): void;
             /** @hidden */
+            _offsetLeft(offset: number): void;
+            /** @hidden */
+            _offsetTop(offset: number): void;
+            /** @hidden */
             _markMatrixAsDirty(): void;
             /** @hidden */
             _flagDescendantsAsMatrixDirty(): void;
@@ -1229,19 +1261,19 @@ declare module BABYLON.GUI {
             /** @hidden */
             _markAllAsDirty(): void;
             /** @hidden */
-            _link(root: BABYLON.Nullable<Container>, host: AdvancedDynamicTexture): void;
+            _link(host: AdvancedDynamicTexture): void;
             /** @hidden */
             protected _transform(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _renderHighlight(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** @hidden */
             protected _applyStates(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
+            _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
             /** @hidden */
-            protected _clip(context: CanvasRenderingContext2D): void;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
             _measure(): void;
             /** @hidden */
@@ -1251,7 +1283,11 @@ declare module BABYLON.GUI {
             /** @hidden */
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _clipForChildren(context: CanvasRenderingContext2D): void;
+            /** @hidden */
+            _render(context: CanvasRenderingContext2D): boolean;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             /**
                 * Tests if a given coordinates belong to the current control
                 * @param x defines x coordinate to test
@@ -1347,6 +1383,18 @@ declare module BABYLON.GUI {
             /** Gets the list of children */
             readonly children: Control[];
             /**
+                * Gets the definition of a specific row
+                * @param index defines the index of the row
+                * @returns the row definition
+                */
+            getRowDefinition(index: number): BABYLON.Nullable<ValueAndUnit>;
+            /**
+                * Gets the definition of a specific column
+                * @param index defines the index of the column
+                * @returns the column definition
+                */
+            getColumnDefinition(index: number): BABYLON.Nullable<ValueAndUnit>;
+            /**
                 * Adds a new row to the grid
                 * @param height defines the height of the row (either in pixel or a value between 0 and 1)
                 * @param isPixel defines if the height is expressed in pixel (or in percentage)
@@ -1418,7 +1466,7 @@ declare module BABYLON.GUI {
             protected _getGridDefinitions(definitionCallback: (lefts: number[], tops: number[], widths: number[], heights: number[]) => void): void;
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _flagDescendantsAsMatrixDirty(): void;
-            protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
     }
@@ -1492,7 +1540,8 @@ declare module BABYLON.GUI {
             protected _getTypeName(): string;
             /** Force the control to synchronize with its content */
             synchronizeSizeWithContent(): void;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             dispose(): void;
             /** STRETCH_NONE */
             static readonly STRETCH_NONE: number;
@@ -1591,7 +1640,7 @@ declare module BABYLON.GUI {
                 * @param evt Defines the KeyboardEvent
                 */
             processKeyboard(evt: KeyboardEvent): void;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
             protected _beforeRenderText(text: string): string;
@@ -1634,7 +1683,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _measure(): void;
             protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /**
@@ -1709,7 +1758,7 @@ declare module BABYLON.GUI {
             horizontalAlignment: number;
             verticalAlignment: number;
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _measure(): void;
             protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
@@ -1740,7 +1789,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             /**
                 * Utility function to easily create a radio button with a header
@@ -1777,7 +1826,10 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
+            /** @hidden */
             protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _postMeasure(): void;
     }
 }
 declare module BABYLON.GUI {
@@ -1959,60 +2011,31 @@ declare module BABYLON.GUI {
         * Class used to hold a viewer window and sliders in a grid
      */
     export class ScrollViewer extends Rectangle {
-            /** name of ScrollViewer */
-            name?: string | undefined;
-            /**
-                * Adds windowContents to the grid view window
-                * @param windowContents the contents to add the grid view window
-                */
-            addToWindow(windowContents: Control): void;
-            /**
-                * Gets or sets a value indicating the padding to use on the left of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingLeft: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the left of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingLeftInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the right of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingRight: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the right of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingRightInPixels: number;
             /**
-                * Gets or sets a value indicating the padding to use on the top of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingTop: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the top of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingTopInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the bottom of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                * Adds a new control to the current container
+                * @param control defines the control to add
+                * @returns the current container
                 */
-            paddingBottom: string | number;
+            addControl(control: BABYLON.Nullable<Control>): Container;
             /**
-                * Gets a value indicating the padding in pixels to use on the bottom of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                * Removes a control from the current container
+                * @param control defines the control to remove
+                * @returns the current container
                 */
-            readonly paddingBottomInPixels: number;
+            removeControl(control: Control): Container;
+            /** Gets the list of children */
+            readonly children: Control[];
+            _flagDescendantsAsMatrixDirty(): void;
             /**
              * Creates a new ScrollViewer
              * @param name of ScrollViewer
              */
-            constructor(
-            /** name of ScrollViewer */
-            name?: string | undefined);
+            constructor(name?: string);
+            /** Reset the scroll viewer window to initial size */
+            resetWindow(): void;
+            protected _getTypeName(): string;
+            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _postMeasure(): void;
             /**
                 * Gets or sets the mouse wheel precision
                 * from 0 to 1 with a default value of 0.05
@@ -2020,12 +2043,14 @@ declare module BABYLON.GUI {
             wheelPrecision: number;
             /** Gets or sets the bar color */
             barColor: string;
+            /** Gets or sets the size of the bar */
+            barSize: number;
             /** Gets or sets the bar color */
             barBorderColor: string;
             /** Gets or sets the bar background */
             barBackground: string;
-            /** @hidden */
-            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _link(host: AdvancedDynamicTexture): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
     }
@@ -2111,10 +2136,10 @@ declare module BABYLON.GUI {
                 */
             name?: string | undefined, text?: string);
             protected _getTypeName(): string;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _applyStates(context: CanvasRenderingContext2D): void;
-            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             protected _breakLines(refWidth: number, context: CanvasRenderingContext2D): object[];
             protected _parseLine(line: string | undefined, context: CanvasRenderingContext2D): object;
             protected _parseLineEllipsis(line: string | undefined, width: number, context: CanvasRenderingContext2D): object;
@@ -2265,7 +2290,7 @@ declare module BABYLON.GUI {
                 * @param name defines the control name
                 */
             constructor(name?: string | undefined);
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _getTypeName(): string;
     }
 }
@@ -2317,6 +2342,8 @@ declare module BABYLON.GUI {
             protected _getThumbPosition(): number;
             protected _getThumbThickness(type: string): number;
             protected _prepareRenderingData(type: string): void;
+            /** @hidden */
+            protected _updateValueFromPointer(x: number, y: number): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
             _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
@@ -2328,6 +2355,9 @@ declare module BABYLON.GUI {
         */
     export class Slider extends BaseSlider {
             name?: string | undefined;
+            protected _displayValueBar: boolean;
+            /** Gets or sets a boolean indicating if the value bar must be rendered */
+            displayValueBar: boolean;
             /** Gets or sets border color */
             borderColor: string;
             /** Gets or sets background color */
@@ -2340,7 +2370,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
     }
 }
 declare module BABYLON.GUI {
@@ -2368,7 +2398,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
     }
 }
 declare module BABYLON.GUI {

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


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


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


+ 228 - 171
dist/preview release/gui/babylon.gui.module.d.ts

@@ -1,6 +1,7 @@
 /*Babylon.js GUI*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../Tools/gulp/babylonjs
+//   ../../../../Tools/gulp/2D
 
 declare module 'babylonjs-gui' {
     export * from "babylonjs-gui/2D";
@@ -40,14 +41,14 @@ declare module 'babylonjs-gui/2D/controls' {
     export * from "babylonjs-gui/2D/controls/radioButton";
     export * from "babylonjs-gui/2D/controls/stackPanel";
     export * from "babylonjs-gui/2D/controls/selector";
-    export * from "babylonjs-gui/2D/controls/scrollViewer";
+    export * from "babylonjs-gui/2D/controls/scrollViewers/scrollViewer";
     export * from "babylonjs-gui/2D/controls/textBlock";
     export * from "babylonjs-gui/2D/controls/virtualKeyboard";
     export * from "babylonjs-gui/2D/controls/rectangle";
     export * from "babylonjs-gui/2D/controls/displayGrid";
-    export * from "babylonjs-gui/2D/controls/baseSlider";
-    export * from "babylonjs-gui/2D/controls/slider";
-    export * from "babylonjs-gui/2D/controls/imageBasedSlider";
+    export * from "babylonjs-gui/2D/controls/sliders/baseSlider";
+    export * from "babylonjs-gui/2D/controls/sliders/slider";
+    export * from "babylonjs-gui/2D/controls/sliders/imageBasedSlider";
     export * from "babylonjs-gui/2D/controls/statics";
 }
 
@@ -106,8 +107,6 @@ declare module 'babylonjs-gui/2D/advancedDynamicTexture' {
             _layerToDispose: Nullable<Layer>;
             /** @hidden */
             _linkedControls: Control[];
-            /** @hidden */
-            _needRedraw: boolean;
             /**
                 * Observable event triggered each time an clipboard event is received from the rendering canvas
                 */
@@ -575,6 +574,13 @@ declare module 'babylonjs-gui/2D/valueAndUnit' {
                 */
             getValueInPixel(host: AdvancedDynamicTexture, refValue: number): number;
             /**
+                * Update the current value and unit. This should be done cautiously as the GUi won't be marked as dirty with this function.
+                * @param value defines the value to store
+                * @param unit defines the unit to store
+                * @returns the current ValueAndUnit
+                */
+            updateInPlace(value: number, unit?: number): ValueAndUnit;
+            /**
                 * Gets the value accordingly to its unit
                 * @param host  defines the root host
                 * @returns the value
@@ -788,7 +794,6 @@ declare module 'babylonjs-gui/2D/controls/button' {
 
 declare module 'babylonjs-gui/2D/controls/checkbox' {
     import { Control } from "babylonjs-gui/2D/controls/control";
-    import { Measure } from "babylonjs-gui/2D/measure";
     import { Observable, Vector2 } from "babylonjs";
     import { StackPanel } from "babylonjs-gui/2D/controls/stackPanel";
     /**
@@ -815,7 +820,7 @@ declare module 'babylonjs-gui/2D/controls/checkbox' {
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
             /**
@@ -831,7 +836,7 @@ declare module 'babylonjs-gui/2D/controls/checkbox' {
 declare module 'babylonjs-gui/2D/controls/colorpicker' {
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { Color3, Observable, Vector2 } from "babylonjs";
-    import { Measure } from "babylonjs-gui/2D/measure";
+    import { Measure } from "2D";
     /** Class used to create color pickers */
     export class ColorPicker extends Control {
             name?: string | undefined;
@@ -841,7 +846,10 @@ declare module 'babylonjs-gui/2D/controls/colorpicker' {
             onValueChangedObservable: Observable<Color3>;
             /** Gets or sets the color of the color picker */
             value: Color3;
-            /** Gets or sets control width */
+            /**
+                * Gets or sets control width
+                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                */
             width: string | number;
             /** Gets or sets control height */
             height: string | number;
@@ -854,7 +862,9 @@ declare module 'babylonjs-gui/2D/controls/colorpicker' {
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerMove(target: Control, coordinates: Vector2): void;
             _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
@@ -882,6 +892,8 @@ declare module 'babylonjs-gui/2D/controls/container' {
             protected _adaptWidthToChildren: boolean;
             /** @hidden */
             protected _adaptHeightToChildren: boolean;
+            /** @hidden */
+            protected _rebuildLayout: boolean;
             /** Gets or sets a boolean indicating if the container should try to adapt to its children height */
             adaptHeightToChildren: boolean;
             /** Gets or sets a boolean indicating if the container should try to adapt to its children width */
@@ -936,20 +948,27 @@ declare module 'babylonjs-gui/2D/controls/container' {
             /** @hidden */
             _reOrderControl(control: Control): void;
             /** @hidden */
+            _offsetLeft(offset: number): void;
+            /** @hidden */
+            _offsetTop(offset: number): void;
+            /** @hidden */
             _markAllAsDirty(): void;
             /** @hidden */
             protected _localDraw(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void;
+            _link(host: AdvancedDynamicTexture): void;
+            /** @hidden */
+            protected _beforeLayout(): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
+            protected _postMeasure(): void;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _getDescendants(results: Control[], directDescendantsOnly?: boolean, predicate?: (control: Control) => boolean): void;
             /** @hidden */
             _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
             /** @hidden */
-            protected _clipForChildren(context: CanvasRenderingContext2D): void;
-            /** @hidden */
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
@@ -976,8 +995,6 @@ declare module 'babylonjs-gui/2D/controls/control' {
                 */
             static AllowAlphaInheritance: boolean;
             /** @hidden */
-            _root: Nullable<Container>;
-            /** @hidden */
             _host: AdvancedDynamicTexture;
             /** Gets or sets the control parent */
             parent: Nullable<Container>;
@@ -998,6 +1015,8 @@ declare module 'babylonjs-gui/2D/controls/control' {
             /** @hidden */
             protected _verticalAlignment: number;
             /** @hidden */
+            protected _isDirty: boolean;
+            /** @hidden */
             _tempParentMeasure: Measure;
             /** @hidden */
             protected _cachedParentMeasure: Measure;
@@ -1014,6 +1033,8 @@ declare module 'babylonjs-gui/2D/controls/control' {
             protected _isEnabled: boolean;
             protected _disabledColor: string;
             /** @hidden */
+            _isClipped: boolean;
+            /** @hidden */
             _tag: any;
             /**
                 * Gets or sets the unique id of the node. Please note that this number will be updated when the control is added to a container
@@ -1278,6 +1299,12 @@ declare module 'babylonjs-gui/2D/controls/control' {
             name?: string | undefined);
             /** @hidden */
             protected _getTypeName(): string;
+            /**
+                * Gets the first ascendant in the hierarchy of the given type
+                * @param className defines the required type
+                * @returns the ascendant or null if not found
+                */
+            getAscendantOfClass(className: string): Nullable<Control>;
             /** @hidden */
             _resetFontCache(): void;
             /**
@@ -1329,6 +1356,10 @@ declare module 'babylonjs-gui/2D/controls/control' {
             /** @hidden */
             _moveToProjectedPosition(projectedPosition: Vector3): void;
             /** @hidden */
+            _offsetLeft(offset: number): void;
+            /** @hidden */
+            _offsetTop(offset: number): void;
+            /** @hidden */
             _markMatrixAsDirty(): void;
             /** @hidden */
             _flagDescendantsAsMatrixDirty(): void;
@@ -1337,19 +1368,19 @@ declare module 'babylonjs-gui/2D/controls/control' {
             /** @hidden */
             _markAllAsDirty(): void;
             /** @hidden */
-            _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void;
+            _link(host: AdvancedDynamicTexture): void;
             /** @hidden */
             protected _transform(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _renderHighlight(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** @hidden */
             protected _applyStates(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
+            _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
             /** @hidden */
-            protected _clip(context: CanvasRenderingContext2D): void;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
             _measure(): void;
             /** @hidden */
@@ -1359,7 +1390,11 @@ declare module 'babylonjs-gui/2D/controls/control' {
             /** @hidden */
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _clipForChildren(context: CanvasRenderingContext2D): void;
+            /** @hidden */
+            _render(context: CanvasRenderingContext2D): boolean;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             /**
                 * Tests if a given coordinates belong to the current control
                 * @param x defines x coordinate to test
@@ -1444,6 +1479,7 @@ declare module 'babylonjs-gui/2D/controls/ellipse' {
 
 declare module 'babylonjs-gui/2D/controls/grid' {
     import { Container } from "babylonjs-gui/2D/controls/container";
+    import { ValueAndUnit } from "babylonjs-gui/2D/valueAndUnit";
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { Measure } from "babylonjs-gui/2D/measure";
     import { Nullable } from "babylonjs";
@@ -1463,6 +1499,18 @@ declare module 'babylonjs-gui/2D/controls/grid' {
             /** Gets the list of children */
             readonly children: Control[];
             /**
+                * Gets the definition of a specific row
+                * @param index defines the index of the row
+                * @returns the row definition
+                */
+            getRowDefinition(index: number): Nullable<ValueAndUnit>;
+            /**
+                * Gets the definition of a specific column
+                * @param index defines the index of the column
+                * @returns the column definition
+                */
+            getColumnDefinition(index: number): Nullable<ValueAndUnit>;
+            /**
                 * Adds a new row to the grid
                 * @param height defines the height of the row (either in pixel or a value between 0 and 1)
                 * @param isPixel defines if the height is expressed in pixel (or in percentage)
@@ -1534,7 +1582,7 @@ declare module 'babylonjs-gui/2D/controls/grid' {
             protected _getGridDefinitions(definitionCallback: (lefts: number[], tops: number[], widths: number[], heights: number[]) => void): void;
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _flagDescendantsAsMatrixDirty(): void;
-            protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
     }
@@ -1543,7 +1591,7 @@ declare module 'babylonjs-gui/2D/controls/grid' {
 declare module 'babylonjs-gui/2D/controls/image' {
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { Nullable, Observable } from "babylonjs";
-    import { Measure } from "babylonjs-gui/2D/measure";
+    import { Measure } from "2D";
     /**
         * Class used to create 2D images
         */
@@ -1612,7 +1660,8 @@ declare module 'babylonjs-gui/2D/controls/image' {
             protected _getTypeName(): string;
             /** Force the control to synchronize with its content */
             synchronizeSizeWithContent(): void;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             dispose(): void;
             /** STRETCH_NONE */
             static readonly STRETCH_NONE: number;
@@ -1629,7 +1678,6 @@ declare module 'babylonjs-gui/2D/controls/inputText' {
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { IFocusableControl } from "babylonjs-gui/2D/advancedDynamicTexture";
     import { Nullable, Observable, Vector2 } from 'babylonjs';
-    import { Measure } from "babylonjs-gui/2D/measure";
     import { VirtualKeyboard } from "babylonjs-gui/2D/controls/virtualKeyboard";
     /**
         * Class used to create input text control
@@ -1717,7 +1765,7 @@ declare module 'babylonjs-gui/2D/controls/inputText' {
                 * @param evt Defines the KeyboardEvent
                 */
             processKeyboard(evt: KeyboardEvent): void;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
             protected _beforeRenderText(text: string): string;
@@ -1766,7 +1814,7 @@ declare module 'babylonjs-gui/2D/controls/line' {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _measure(): void;
             protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /**
@@ -1846,7 +1894,7 @@ declare module 'babylonjs-gui/2D/controls/multiLine' {
             horizontalAlignment: number;
             verticalAlignment: number;
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _measure(): void;
             protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
@@ -1857,7 +1905,6 @@ declare module 'babylonjs-gui/2D/controls/multiLine' {
 declare module 'babylonjs-gui/2D/controls/radioButton' {
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { Observable, Vector2 } from "babylonjs";
-    import { Measure } from "babylonjs-gui/2D/measure";
     import { StackPanel } from "babylonjs-gui/2D/controls";
     /**
         * Class used to create radio button controls
@@ -1882,7 +1929,7 @@ declare module 'babylonjs-gui/2D/controls/radioButton' {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
             /**
                 * Utility function to easily create a radio button with a header
@@ -1922,7 +1969,10 @@ declare module 'babylonjs-gui/2D/controls/stackPanel' {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
+            /** @hidden */
             protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _postMeasure(): void;
     }
 }
 
@@ -2103,68 +2153,41 @@ declare module 'babylonjs-gui/2D/controls/selector' {
     }
 }
 
-declare module 'babylonjs-gui/2D/controls/scrollViewer' {
-    import { Measure } from "babylonjs-gui/2D/measure";
+declare module 'babylonjs-gui/2D/controls/scrollViewers/scrollViewer' {
     import { Rectangle } from "babylonjs-gui/2D/controls/rectangle";
     import { Control } from "babylonjs-gui/2D/controls/control";
+    import { Container } from "babylonjs-gui/2D/controls/container";
+    import { Nullable } from "babylonjs";
+    import { AdvancedDynamicTexture, Measure } from "2D";
     /**
         * Class used to hold a viewer window and sliders in a grid
      */
     export class ScrollViewer extends Rectangle {
-            /** name of ScrollViewer */
-            name?: string | undefined;
-            /**
-                * Adds windowContents to the grid view window
-                * @param windowContents the contents to add the grid view window
-                */
-            addToWindow(windowContents: Control): void;
-            /**
-                * Gets or sets a value indicating the padding to use on the left of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingLeft: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the left of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingLeftInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the right of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingRight: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the right of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingRightInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the top of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingTop: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the top of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingTopInPixels: number;
             /**
-                * Gets or sets a value indicating the padding to use on the bottom of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                * Adds a new control to the current container
+                * @param control defines the control to add
+                * @returns the current container
                 */
-            paddingBottom: string | number;
+            addControl(control: Nullable<Control>): Container;
             /**
-                * Gets a value indicating the padding in pixels to use on the bottom of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                * Removes a control from the current container
+                * @param control defines the control to remove
+                * @returns the current container
                 */
-            readonly paddingBottomInPixels: number;
+            removeControl(control: Control): Container;
+            /** Gets the list of children */
+            readonly children: Control[];
+            _flagDescendantsAsMatrixDirty(): void;
             /**
              * Creates a new ScrollViewer
              * @param name of ScrollViewer
              */
-            constructor(
-            /** name of ScrollViewer */
-            name?: string | undefined);
+            constructor(name?: string);
+            /** Reset the scroll viewer window to initial size */
+            resetWindow(): void;
+            protected _getTypeName(): string;
+            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _postMeasure(): void;
             /**
                 * Gets or sets the mouse wheel precision
                 * from 0 to 1 with a default value of 0.05
@@ -2172,12 +2195,14 @@ declare module 'babylonjs-gui/2D/controls/scrollViewer' {
             wheelPrecision: number;
             /** Gets or sets the bar color */
             barColor: string;
+            /** Gets or sets the size of the bar */
+            barSize: number;
             /** Gets or sets the bar color */
             barBorderColor: string;
             /** Gets or sets the bar background */
             barBackground: string;
-            /** @hidden */
-            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _link(host: AdvancedDynamicTexture): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
     }
@@ -2267,10 +2292,10 @@ declare module 'babylonjs-gui/2D/controls/textBlock' {
                 */
             name?: string | undefined, text?: string);
             protected _getTypeName(): string;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _applyStates(context: CanvasRenderingContext2D): void;
-            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             protected _breakLines(refWidth: number, context: CanvasRenderingContext2D): object[];
             protected _parseLine(line: string | undefined, context: CanvasRenderingContext2D): object;
             protected _parseLineEllipsis(line: string | undefined, width: number, context: CanvasRenderingContext2D): object;
@@ -2402,7 +2427,6 @@ declare module 'babylonjs-gui/2D/controls/rectangle' {
 
 declare module 'babylonjs-gui/2D/controls/displayGrid' {
     import { Control } from "babylonjs-gui/2D/controls";
-    import { Measure } from "babylonjs-gui/2D";
     /** Class used to render a grid  */
     export class DisplayGrid extends Control {
             name?: string | undefined;
@@ -2431,12 +2455,12 @@ declare module 'babylonjs-gui/2D/controls/displayGrid' {
                 * @param name defines the control name
                 */
             constructor(name?: string | undefined);
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _getTypeName(): string;
     }
 }
 
-declare module 'babylonjs-gui/2D/controls/baseSlider' {
+declare module 'babylonjs-gui/2D/controls/sliders/baseSlider' {
     import { Control } from "babylonjs-gui/2D/controls/control";
     import { ValueAndUnit } from "babylonjs-gui/2D/valueAndUnit";
     import { Observable, Vector2 } from "babylonjs";
@@ -2487,20 +2511,24 @@ declare module 'babylonjs-gui/2D/controls/baseSlider' {
             protected _getThumbPosition(): number;
             protected _getThumbThickness(type: string): number;
             protected _prepareRenderingData(type: string): void;
+            /** @hidden */
+            protected _updateValueFromPointer(x: number, y: number): void;
             _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerMove(target: Control, coordinates: Vector2): void;
             _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
     }
 }
 
-declare module 'babylonjs-gui/2D/controls/slider' {
-    import { Measure } from "babylonjs-gui/2D/measure";
-    import { BaseSlider } from "babylonjs-gui/2D/controls/baseSlider";
+declare module 'babylonjs-gui/2D/controls/sliders/slider' {
+    import { BaseSlider } from "babylonjs-gui/2D/controls/sliders/baseSlider";
     /**
         * Class used to create slider controls
         */
     export class Slider extends BaseSlider {
             name?: string | undefined;
+            protected _displayValueBar: boolean;
+            /** Gets or sets a boolean indicating if the value bar must be rendered */
+            displayValueBar: boolean;
             /** Gets or sets border color */
             borderColor: string;
             /** Gets or sets background color */
@@ -2513,13 +2541,12 @@ declare module 'babylonjs-gui/2D/controls/slider' {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
     }
 }
 
-declare module 'babylonjs-gui/2D/controls/imageBasedSlider' {
-    import { BaseSlider } from "babylonjs-gui/2D/controls/baseSlider";
-    import { Measure } from "babylonjs-gui/2D/measure";
+declare module 'babylonjs-gui/2D/controls/sliders/imageBasedSlider' {
+    import { BaseSlider } from "babylonjs-gui/2D/controls/sliders/baseSlider";
     import { Image } from "babylonjs-gui/2D/controls/image";
     /**
         * Class used to create slider controls based on images
@@ -2545,7 +2572,7 @@ declare module 'babylonjs-gui/2D/controls/imageBasedSlider' {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
     }
 }
 
@@ -3134,7 +3161,8 @@ declare module 'babylonjs-gui/3D/materials/fluentMaterial' {
 
 /*Babylon.js GUI*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../Tools/gulp/babylonjs
+//   ../../../../Tools/gulp/2D
 declare module BABYLON.GUI {
 }
 declare module BABYLON.GUI {
@@ -3194,8 +3222,6 @@ declare module BABYLON.GUI {
             _layerToDispose: BABYLON.Nullable<BABYLON.Layer>;
             /** @hidden */
             _linkedControls: Control[];
-            /** @hidden */
-            _needRedraw: boolean;
             /**
                 * BABYLON.Observable event triggered each time an clipboard event is received from the rendering canvas
                 */
@@ -3650,6 +3676,13 @@ declare module BABYLON.GUI {
                 */
             getValueInPixel(host: AdvancedDynamicTexture, refValue: number): number;
             /**
+                * Update the current value and unit. This should be done cautiously as the GUi won't be marked as dirty with this function.
+                * @param value defines the value to store
+                * @param unit defines the unit to store
+                * @returns the current ValueAndUnit
+                */
+            updateInPlace(value: number, unit?: number): ValueAndUnit;
+            /**
                 * Gets the value accordingly to its unit
                 * @param host  defines the root host
                 * @returns the value
@@ -3858,7 +3891,7 @@ declare module BABYLON.GUI {
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             /**
@@ -3880,7 +3913,10 @@ declare module BABYLON.GUI {
             onValueChangedObservable: BABYLON.Observable<BABYLON.Color3>;
             /** Gets or sets the color of the color picker */
             value: BABYLON.Color3;
-            /** Gets or sets control width */
+            /**
+                * Gets or sets control width
+                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                */
             width: string | number;
             /** Gets or sets control height */
             height: string | number;
@@ -3893,7 +3929,9 @@ declare module BABYLON.GUI {
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
             _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
@@ -3916,6 +3954,8 @@ declare module BABYLON.GUI {
             protected _adaptWidthToChildren: boolean;
             /** @hidden */
             protected _adaptHeightToChildren: boolean;
+            /** @hidden */
+            protected _rebuildLayout: boolean;
             /** Gets or sets a boolean indicating if the container should try to adapt to its children height */
             adaptHeightToChildren: boolean;
             /** Gets or sets a boolean indicating if the container should try to adapt to its children width */
@@ -3970,20 +4010,27 @@ declare module BABYLON.GUI {
             /** @hidden */
             _reOrderControl(control: Control): void;
             /** @hidden */
+            _offsetLeft(offset: number): void;
+            /** @hidden */
+            _offsetTop(offset: number): void;
+            /** @hidden */
             _markAllAsDirty(): void;
             /** @hidden */
             protected _localDraw(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _link(root: BABYLON.Nullable<Container>, host: AdvancedDynamicTexture): void;
+            _link(host: AdvancedDynamicTexture): void;
+            /** @hidden */
+            protected _beforeLayout(): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
+            protected _postMeasure(): void;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _getDescendants(results: Control[], directDescendantsOnly?: boolean, predicate?: (control: Control) => boolean): void;
             /** @hidden */
             _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
             /** @hidden */
-            protected _clipForChildren(context: CanvasRenderingContext2D): void;
-            /** @hidden */
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
@@ -4002,8 +4049,6 @@ declare module BABYLON.GUI {
                 */
             static AllowAlphaInheritance: boolean;
             /** @hidden */
-            _root: BABYLON.Nullable<Container>;
-            /** @hidden */
             _host: AdvancedDynamicTexture;
             /** Gets or sets the control parent */
             parent: BABYLON.Nullable<Container>;
@@ -4024,6 +4069,8 @@ declare module BABYLON.GUI {
             /** @hidden */
             protected _verticalAlignment: number;
             /** @hidden */
+            protected _isDirty: boolean;
+            /** @hidden */
             _tempParentMeasure: Measure;
             /** @hidden */
             protected _cachedParentMeasure: Measure;
@@ -4040,6 +4087,8 @@ declare module BABYLON.GUI {
             protected _isEnabled: boolean;
             protected _disabledColor: string;
             /** @hidden */
+            _isClipped: boolean;
+            /** @hidden */
             _tag: any;
             /**
                 * Gets or sets the unique id of the node. Please note that this number will be updated when the control is added to a container
@@ -4304,6 +4353,12 @@ declare module BABYLON.GUI {
             name?: string | undefined);
             /** @hidden */
             protected _getTypeName(): string;
+            /**
+                * Gets the first ascendant in the hierarchy of the given type
+                * @param className defines the required type
+                * @returns the ascendant or null if not found
+                */
+            getAscendantOfClass(className: string): BABYLON.Nullable<Control>;
             /** @hidden */
             _resetFontCache(): void;
             /**
@@ -4355,6 +4410,10 @@ declare module BABYLON.GUI {
             /** @hidden */
             _moveToProjectedPosition(projectedPosition: BABYLON.Vector3): void;
             /** @hidden */
+            _offsetLeft(offset: number): void;
+            /** @hidden */
+            _offsetTop(offset: number): void;
+            /** @hidden */
             _markMatrixAsDirty(): void;
             /** @hidden */
             _flagDescendantsAsMatrixDirty(): void;
@@ -4363,19 +4422,19 @@ declare module BABYLON.GUI {
             /** @hidden */
             _markAllAsDirty(): void;
             /** @hidden */
-            _link(root: BABYLON.Nullable<Container>, host: AdvancedDynamicTexture): void;
+            _link(host: AdvancedDynamicTexture): void;
             /** @hidden */
             protected _transform(context: CanvasRenderingContext2D): void;
             /** @hidden */
             _renderHighlight(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** @hidden */
             protected _applyStates(context: CanvasRenderingContext2D): void;
             /** @hidden */
-            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
+            _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean;
             /** @hidden */
-            protected _clip(context: CanvasRenderingContext2D): void;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
             _measure(): void;
             /** @hidden */
@@ -4385,7 +4444,11 @@ declare module BABYLON.GUI {
             /** @hidden */
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _clipForChildren(context: CanvasRenderingContext2D): void;
+            /** @hidden */
+            _render(context: CanvasRenderingContext2D): boolean;
+            /** @hidden */
+            _draw(context: CanvasRenderingContext2D): void;
             /**
                 * Tests if a given coordinates belong to the current control
                 * @param x defines x coordinate to test
@@ -4481,6 +4544,18 @@ declare module BABYLON.GUI {
             /** Gets the list of children */
             readonly children: Control[];
             /**
+                * Gets the definition of a specific row
+                * @param index defines the index of the row
+                * @returns the row definition
+                */
+            getRowDefinition(index: number): BABYLON.Nullable<ValueAndUnit>;
+            /**
+                * Gets the definition of a specific column
+                * @param index defines the index of the column
+                * @returns the column definition
+                */
+            getColumnDefinition(index: number): BABYLON.Nullable<ValueAndUnit>;
+            /**
                 * Adds a new row to the grid
                 * @param height defines the height of the row (either in pixel or a value between 0 and 1)
                 * @param isPixel defines if the height is expressed in pixel (or in percentage)
@@ -4552,7 +4627,7 @@ declare module BABYLON.GUI {
             protected _getGridDefinitions(definitionCallback: (lefts: number[], tops: number[], widths: number[], heights: number[]) => void): void;
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _flagDescendantsAsMatrixDirty(): void;
-            protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
     }
@@ -4626,7 +4701,8 @@ declare module BABYLON.GUI {
             protected _getTypeName(): string;
             /** Force the control to synchronize with its content */
             synchronizeSizeWithContent(): void;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             dispose(): void;
             /** STRETCH_NONE */
             static readonly STRETCH_NONE: number;
@@ -4725,7 +4801,7 @@ declare module BABYLON.GUI {
                 * @param evt Defines the KeyboardEvent
                 */
             processKeyboard(evt: KeyboardEvent): void;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
             protected _beforeRenderText(text: string): string;
@@ -4768,7 +4844,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _measure(): void;
             protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /**
@@ -4843,7 +4919,7 @@ declare module BABYLON.GUI {
             horizontalAlignment: number;
             verticalAlignment: number;
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             _measure(): void;
             protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
@@ -4874,7 +4950,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             /**
                 * Utility function to easily create a radio button with a header
@@ -4911,7 +4987,10 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
+            /** @hidden */
             protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _postMeasure(): void;
     }
 }
 declare module BABYLON.GUI {
@@ -5093,60 +5172,31 @@ declare module BABYLON.GUI {
         * Class used to hold a viewer window and sliders in a grid
      */
     export class ScrollViewer extends Rectangle {
-            /** name of ScrollViewer */
-            name?: string | undefined;
             /**
-                * Adds windowContents to the grid view window
-                * @param windowContents the contents to add the grid view window
-                */
-            addToWindow(windowContents: Control): void;
-            /**
-                * Gets or sets a value indicating the padding to use on the left of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingLeft: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the left of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingLeftInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the right of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingRight: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the right of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingRightInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the top of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            paddingTop: string | number;
-            /**
-                * Gets a value indicating the padding in pixels to use on the top of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-                */
-            readonly paddingTopInPixels: number;
-            /**
-                * Gets or sets a value indicating the padding to use on the bottom of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                * Adds a new control to the current container
+                * @param control defines the control to add
+                * @returns the current container
                 */
-            paddingBottom: string | number;
+            addControl(control: BABYLON.Nullable<Control>): Container;
             /**
-                * Gets a value indicating the padding in pixels to use on the bottom of the viewer window
-                * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+                * Removes a control from the current container
+                * @param control defines the control to remove
+                * @returns the current container
                 */
-            readonly paddingBottomInPixels: number;
+            removeControl(control: Control): Container;
+            /** Gets the list of children */
+            readonly children: Control[];
+            _flagDescendantsAsMatrixDirty(): void;
             /**
              * Creates a new ScrollViewer
              * @param name of ScrollViewer
              */
-            constructor(
-            /** name of ScrollViewer */
-            name?: string | undefined);
+            constructor(name?: string);
+            /** Reset the scroll viewer window to initial size */
+            resetWindow(): void;
+            protected _getTypeName(): string;
+            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            protected _postMeasure(): void;
             /**
                 * Gets or sets the mouse wheel precision
                 * from 0 to 1 with a default value of 0.05
@@ -5154,12 +5204,14 @@ declare module BABYLON.GUI {
             wheelPrecision: number;
             /** Gets or sets the bar color */
             barColor: string;
+            /** Gets or sets the size of the bar */
+            barSize: number;
             /** Gets or sets the bar color */
             barBorderColor: string;
             /** Gets or sets the bar background */
             barBackground: string;
-            /** @hidden */
-            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _link(host: AdvancedDynamicTexture): void;
+            _renderHighlightSpecific(context: CanvasRenderingContext2D): void;
             /** Releases associated resources */
             dispose(): void;
     }
@@ -5245,10 +5297,10 @@ declare module BABYLON.GUI {
                 */
             name?: string | undefined, text?: string);
             protected _getTypeName(): string;
+            protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             /** @hidden */
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _applyStates(context: CanvasRenderingContext2D): void;
-            protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
             protected _breakLines(refWidth: number, context: CanvasRenderingContext2D): object[];
             protected _parseLine(line: string | undefined, context: CanvasRenderingContext2D): object;
             protected _parseLineEllipsis(line: string | undefined, width: number, context: CanvasRenderingContext2D): object;
@@ -5399,7 +5451,7 @@ declare module BABYLON.GUI {
                 * @param name defines the control name
                 */
             constructor(name?: string | undefined);
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
             protected _getTypeName(): string;
     }
 }
@@ -5451,6 +5503,8 @@ declare module BABYLON.GUI {
             protected _getThumbPosition(): number;
             protected _getThumbThickness(type: string): number;
             protected _prepareRenderingData(type: string): void;
+            /** @hidden */
+            protected _updateValueFromPointer(x: number, y: number): void;
             _onPointerDown(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number): boolean;
             _onPointerMove(target: Control, coordinates: BABYLON.Vector2): void;
             _onPointerUp(target: Control, coordinates: BABYLON.Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void;
@@ -5462,6 +5516,9 @@ declare module BABYLON.GUI {
         */
     export class Slider extends BaseSlider {
             name?: string | undefined;
+            protected _displayValueBar: boolean;
+            /** Gets or sets a boolean indicating if the value bar must be rendered */
+            displayValueBar: boolean;
             /** Gets or sets border color */
             borderColor: string;
             /** Gets or sets background color */
@@ -5474,7 +5531,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
     }
 }
 declare module BABYLON.GUI {
@@ -5502,7 +5559,7 @@ declare module BABYLON.GUI {
                 */
             constructor(name?: string | undefined);
             protected _getTypeName(): string;
-            _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+            _draw(context: CanvasRenderingContext2D): void;
     }
 }
 declare module BABYLON.GUI {

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


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


+ 1 - 1
dist/preview release/inspector/babylon.inspector.d.ts

@@ -1,6 +1,6 @@
 /*Babylon.js Inspector*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../Tools/gulp/babylonjs
 declare module INSPECTOR {
 }
 declare module INSPECTOR {

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

@@ -1,6 +1,6 @@
 /*Babylon.js Inspector*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../Tools/gulp/babylonjs
 
 declare module 'babylonjs-inspector' {
     export * from "babylonjs-inspector/inspector";
@@ -31,7 +31,7 @@ declare module 'babylonjs-inspector/components/propertyChangedEvent' {
 
 /*Babylon.js Inspector*/
 // Dependencies for this module:
-//   ../../../../Tools/Gulp/babylonjs
+//   ../../../../Tools/gulp/babylonjs
 declare module INSPECTOR {
 }
 declare module INSPECTOR {

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


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


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


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


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


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


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


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


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


+ 21 - 7
dist/preview release/viewer/babylon.viewer.d.ts

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

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


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


+ 24 - 7
dist/preview release/viewer/babylon.viewer.module.d.ts

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

+ 5 - 2
dist/preview release/what's new.md

@@ -19,6 +19,7 @@
   - Added new [ImageBasedSlider](http://doc.babylonjs.com/how_to/gui#imagebasedslider) to let users customize sliders using images ([Deltakosh](https://github.com/deltakosh))
   - Added support for clipboard events to let users perform `cut`, `copy` and `paste` events ([Saket Saurabh](https://github.com/ssaket))
   - Added new [ScrollViewer](https://doc.babylonjs.com/how_to/scrollviewer) with mouse wheel scrolling for larger containers to be viewed using Sliders ([JohnK](https://github.com/BabylonJSGuide/))
+  - Moved to a measure / draw mechanism ([Deltakosh](https://github.com/deltakosh))
 
 ## Updates
 
@@ -72,7 +73,7 @@
 - Added support for overriding the mesh used for the world matrix for a mesh with a skeleton ([bghgary](https://github.com/bghgary))
 - Added support for linking a bone to a transform node ([bghgary](https://github.com/bghgary))
 - Factored out `setDirection` function from `lookAt` for transform node ([bghgary](https://github.com/bghgary))
-- Added support for AmmoJS physics plugin ([TrevorDev](https://github.com/TrevorDev))
+- Added support for AmmoJS as a physics plugin ([TrevorDev](https://github.com/TrevorDev))
 - Add support for setting renderingGroupId and creating instances to `AxesViewer` ([bghgary](https://github.com/bghgary))
 - Invert vScale of compressed ktx textures as they are inverted in the file and UNPACK_FLIP_Y_WEBGL is not supported by ktx ([TrevorDev](https://github.com/TrevorDev))
 
@@ -93,6 +94,7 @@
 ### Materials Library
 
 ## Bug fixes
+- Fixed ArcRotateCamera control when upVector was modified ([Deltakosh](https://github.com/deltakosh))
 - Fixed anaglyph mode for Free and Universal cameras ([Deltakosh](https://github.com/deltakosh))
 - Fixed FileLoader's loading of a skybox, & added a parsed value for whether to create with PBR or STDMaterial ([Palmer-JC](https://github.com/Palmer-JC))
 - Removed bones from rootNodes where they should never have been ([Deltakosh](https://github.com/deltakosh))
@@ -109,7 +111,8 @@
 - MakeNotPickableAndWrapInBoundingBox had unexpected behavior when input had scaling of 0 on an axis ([TrevorDev](https://github.com/TrevorDev))
 - Fixed an issue with loading base64 encoded images in the glTF loader ([bghgary](https://github.com/bghgary))
 - In multi-camera scenes the inspector would cause the camera's interaction events to get detached ([TrevorDev](https://github.com/TrevorDev))
-- Delete highlighted text on keyboard input, beat delay after double click event in InputText ([Saket Saurabh](https://github.com/ssaket))
+- Fix delete highlighted text after keyboard input, beat delay after double click event in InputText ([Saket Saurabh](https://github.com/ssaket))
+- SixDofDragBehavior will support when the camera is parented ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))

+ 7 - 11
gui/src/2D/advancedDynamicTexture.ts

@@ -69,9 +69,6 @@ export class AdvancedDynamicTexture extends DynamicTexture {
     private _renderScale = 1;
     private _rootCanvas: Nullable<HTMLCanvasElement>;
 
-    /** @hidden */
-    public _needRedraw = false;
-
     /**
      * Define type to string to ensure compatibility across browsers
      * Safari doesn't support DataTransfer constructor
@@ -317,7 +314,7 @@ export class AdvancedDynamicTexture extends DynamicTexture {
             info.skipOnPointerObservable = true;
         });
 
-        this._rootContainer._link(null, this);
+        this._rootContainer._link(this);
 
         this.hasAlpha = true;
 
@@ -576,12 +573,10 @@ export class AdvancedDynamicTexture extends DynamicTexture {
         context.font = "18px Arial";
         context.strokeStyle = "white";
         var measure = new Measure(0, 0, renderWidth, renderHeight);
-        this._rootContainer._draw(measure, context);
+        this._rootContainer._layout(measure, context);
+        this._isDirty = false; // Restoring the dirty state that could have been set by controls during layout processing
 
-        if (this._needRedraw) { // We need to redraw as some elements dynamically adapt to their content
-            this._needRedraw = false;
-            this._render();
-        }
+        this._rootContainer._render(context);
     }
 
     /** @hidden */
@@ -833,16 +828,17 @@ export class AdvancedDynamicTexture extends DynamicTexture {
     }
 
     private _attachToOnPointerOut(scene: Scene): void {
+
         this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add((pointerEvent) => {
             if (this._lastControlOver[pointerEvent.pointerId]) {
                 this._lastControlOver[pointerEvent.pointerId]._onPointerOut(this._lastControlOver[pointerEvent.pointerId]);
             }
             delete this._lastControlOver[pointerEvent.pointerId];
 
-            if (this._lastControlDown[pointerEvent.pointerId]) {
+            if (this._lastControlDown[pointerEvent.pointerId] && this._lastControlDown[pointerEvent.pointerId] !== this._capturingControl[pointerEvent.pointerId]) {
                 this._lastControlDown[pointerEvent.pointerId]._forcePointerUp();
+                delete this._lastControlDown[pointerEvent.pointerId];
             }
-            delete this._lastControlDown[pointerEvent.pointerId];
         });
     }
 

+ 6 - 1
gui/src/2D/controls/button.ts

@@ -51,12 +51,17 @@ export class Button extends Rectangle {
         this.thickness = 1;
         this.isPointerBlocker = true;
 
+        let alphaStore: Nullable<number> = null;
+
         this.pointerEnterAnimation = () => {
+            alphaStore = this.alpha;
             this.alpha -= 0.1;
         };
 
         this.pointerOutAnimation = () => {
-            this.alpha += 0.1;
+            if (alphaStore !== null) {
+                this.alpha = alphaStore;
+            }
         };
 
         this.pointerDownAnimation = () => {

+ 32 - 34
gui/src/2D/controls/checkbox.ts

@@ -1,5 +1,4 @@
 import { Control } from "./control";
-import { Measure } from "../measure";
 import { Observable, Vector2 } from "babylonjs";
 import { StackPanel } from "./stackPanel";
 import { TextBlock } from "./textBlock";
@@ -92,43 +91,42 @@ export class Checkbox extends Control {
     }
 
     /** @hidden */
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-            let actualWidth = this._currentMeasure.width - this._thickness;
-            let actualHeight = this._currentMeasure.height - this._thickness;
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowColor = this.shadowColor;
-                context.shadowBlur = this.shadowBlur;
-                context.shadowOffsetX = this.shadowOffsetX;
-                context.shadowOffsetY = this.shadowOffsetY;
-            }
-
-            context.fillStyle = this._isEnabled ? this._background : this._disabledColor;
-            context.fillRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, actualWidth, actualHeight);
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowBlur = 0;
-                context.shadowOffsetX = 0;
-                context.shadowOffsetY = 0;
-            }
-
-            if (this._isChecked) {
-                context.fillStyle = this._isEnabled ? this.color : this._disabledColor;
-                let offsetWidth = actualWidth * this._checkSizeRatio;
-                let offseHeight = actualHeight * this._checkSizeRatio;
-
-                context.fillRect(this._currentMeasure.left + this._thickness / 2 + (actualWidth - offsetWidth) / 2, this._currentMeasure.top + this._thickness / 2 + (actualHeight - offseHeight) / 2, offsetWidth, offseHeight);
-            }
-
-            context.strokeStyle = this.color;
-            context.lineWidth = this._thickness;
-
-            context.strokeRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, actualWidth, actualHeight);
+        let actualWidth = this._currentMeasure.width - this._thickness;
+        let actualHeight = this._currentMeasure.height - this._thickness;
+
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowColor = this.shadowColor;
+            context.shadowBlur = this.shadowBlur;
+            context.shadowOffsetX = this.shadowOffsetX;
+            context.shadowOffsetY = this.shadowOffsetY;
         }
+
+        context.fillStyle = this._isEnabled ? this._background : this._disabledColor;
+        context.fillRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, actualWidth, actualHeight);
+
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowBlur = 0;
+            context.shadowOffsetX = 0;
+            context.shadowOffsetY = 0;
+        }
+
+        if (this._isChecked) {
+            context.fillStyle = this._isEnabled ? this.color : this._disabledColor;
+            let offsetWidth = actualWidth * this._checkSizeRatio;
+            let offseHeight = actualHeight * this._checkSizeRatio;
+
+            context.fillRect(this._currentMeasure.left + this._thickness / 2 + (actualWidth - offsetWidth) / 2, this._currentMeasure.top + this._thickness / 2 + (actualHeight - offseHeight) / 2, offsetWidth, offseHeight);
+        }
+
+        context.strokeStyle = this.color;
+        context.lineWidth = this._thickness;
+
+        context.strokeRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, actualWidth, actualHeight);
+
         context.restore();
     }
 

+ 89 - 41
gui/src/2D/controls/colorpicker.ts

@@ -1,9 +1,10 @@
 import { Control } from "./control";
 import { Color3, Observable, Vector2 } from "babylonjs";
-import { Measure } from "../measure";
+import { Measure } from "2D";
 
 /** Class used to create color pickers */
 export class ColorPicker extends Control {
+    private static _Epsilon = 0.000001;
     private _colorWheelCanvas: HTMLCanvasElement;
 
     private _value: Color3 = Color3.Red();
@@ -45,10 +46,41 @@ export class ColorPicker extends Control {
 
         this._markAsDirty();
 
+        if (this._value.r <= ColorPicker._Epsilon) {
+            this._value.r = 0;
+        }
+
+        if (this._value.g <= ColorPicker._Epsilon) {
+            this._value.g = 0;
+        }
+
+        if (this._value.b <= ColorPicker._Epsilon) {
+            this._value.b = 0;
+        }
+
+        if (this._value.r >= 1.0 - ColorPicker._Epsilon) {
+            this._value.r = 1.0;
+        }
+
+        if (this._value.g >= 1.0 - ColorPicker._Epsilon) {
+            this._value.g = 1.0;
+        }
+
+        if (this._value.b >= 1.0 - ColorPicker._Epsilon) {
+            this._value.b = 1.0;
+        }
+
         this.onValueChangedObservable.notifyObservers(this._value);
     }
 
-    /** Gets or sets control width */
+    /**
+     * Gets or sets control width
+     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+     */
+    public get width(): string | number {
+        return this._width.toString(this._host);
+    }
+
     public set width(value: string | number) {
         if (this._width.toString(this._host) === value) {
             return;
@@ -60,6 +92,14 @@ export class ColorPicker extends Control {
         }
     }
 
+    /**
+     * Gets or sets control height
+     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
+     */
+    public get height(): string | number {
+        return this._height.toString(this._host);
+    }
+
     /** Gets or sets control height */
     public set height(value: string | number) {
         if (this._height.toString(this._host) === value) {
@@ -96,6 +136,16 @@ export class ColorPicker extends Control {
         return "ColorPicker";
     }
 
+    /** @hidden */
+    protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+
+        if (parentMeasure.width < parentMeasure.height) {
+            this._currentMeasure.height = parentMeasure.width;
+        } else {
+            this._currentMeasure.width = parentMeasure.height;
+        }
+    }
+
     private _updateSquareProps(): void {
         var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
         var wheelThickness = radius * .2;
@@ -273,58 +323,56 @@ export class ColorPicker extends Control {
     }
 
     /** @hidden */
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
 
-            var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
-            var wheelThickness = radius * .2;
-            var left = this._currentMeasure.left;
-            var top = this._currentMeasure.top;
+        var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height) * .5;
+        var wheelThickness = radius * .2;
+        var left = this._currentMeasure.left;
+        var top = this._currentMeasure.top;
 
-            if (!this._colorWheelCanvas || this._colorWheelCanvas.width != radius * 2) {
-                this._colorWheelCanvas = this._createColorWheelCanvas(radius, wheelThickness);
-            }
+        if (!this._colorWheelCanvas || this._colorWheelCanvas.width != radius * 2) {
+            this._colorWheelCanvas = this._createColorWheelCanvas(radius, wheelThickness);
+        }
 
-            this._updateSquareProps();
+        this._updateSquareProps();
 
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowColor = this.shadowColor;
-                context.shadowBlur = this.shadowBlur;
-                context.shadowOffsetX = this.shadowOffsetX;
-                context.shadowOffsetY = this.shadowOffsetY;
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowColor = this.shadowColor;
+            context.shadowBlur = this.shadowBlur;
+            context.shadowOffsetX = this.shadowOffsetX;
+            context.shadowOffsetY = this.shadowOffsetY;
 
-                context.fillRect(this._squareLeft, this._squareTop, this._squareSize, this._squareSize);
-            }
+            context.fillRect(this._squareLeft, this._squareTop, this._squareSize, this._squareSize);
+        }
 
-            context.drawImage(this._colorWheelCanvas, left, top);
+        context.drawImage(this._colorWheelCanvas, left, top);
 
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowBlur = 0;
-                context.shadowOffsetX = 0;
-                context.shadowOffsetY = 0;
-            }
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowBlur = 0;
+            context.shadowOffsetX = 0;
+            context.shadowOffsetY = 0;
+        }
 
-            this._drawGradientSquare(this._h,
-                this._squareLeft,
-                this._squareTop,
-                this._squareSize,
-                this._squareSize,
-                context);
+        this._drawGradientSquare(this._h,
+            this._squareLeft,
+            this._squareTop,
+            this._squareSize,
+            this._squareSize,
+            context);
 
-            var cx = this._squareLeft + this._squareSize * this._s;
-            var cy = this._squareTop + this._squareSize * (1 - this._v);
+        var cx = this._squareLeft + this._squareSize * this._s;
+        var cy = this._squareTop + this._squareSize * (1 - this._v);
 
-            this._drawCircle(cx, cy, radius * .04, context);
+        this._drawCircle(cx, cy, radius * .04, context);
 
-            var dist = radius - wheelThickness * .5;
-            cx = left + radius + Math.cos((this._h - 180) * Math.PI / 180) * dist;
-            cy = top + radius + Math.sin((this._h - 180) * Math.PI / 180) * dist;
-            this._drawCircle(cx, cy, wheelThickness * .35, context);
+        var dist = radius - wheelThickness * .5;
+        cx = left + radius + Math.cos((this._h - 180) * Math.PI / 180) * dist;
+        cy = top + radius + Math.sin((this._h - 180) * Math.PI / 180) * dist;
+        this._drawCircle(cx, cy, wheelThickness * .35, context);
 
-        }
         context.restore();
     }
 
@@ -343,9 +391,9 @@ export class ColorPicker extends Control {
             this._s = (x - this._squareLeft) / this._squareSize;
             this._v = 1 - (y - this._squareTop) / this._squareSize;
             this._s = Math.min(this._s, 1);
-            this._s = Math.max(this._s, 0.00001);
+            this._s = Math.max(this._s, ColorPicker._Epsilon);
             this._v = Math.min(this._v, 1);
-            this._v = Math.max(this._v, 0.00001);
+            this._v = Math.max(this._v, ColorPicker._Epsilon);
         }
 
         this._HSVtoRGB(this._h, this._s, this._v, this._tmpColor);

+ 87 - 53
gui/src/2D/controls/container.ts

@@ -18,6 +18,8 @@ export class Container extends Control {
     protected _adaptWidthToChildren = false;
     /** @hidden */
     protected _adaptHeightToChildren = false;
+    /** @hidden */
+    protected _rebuildLayout = false;
 
     /** Gets or sets a boolean indicating if the container should try to adapt to its children height */
     public get adaptHeightToChildren(): boolean {
@@ -149,7 +151,7 @@ export class Container extends Control {
         if (index !== -1) {
             return this;
         }
-        control._link(this, this._host);
+        control._link(this._host);
 
         control._markAllAsDirty();
 
@@ -216,6 +218,24 @@ export class Container extends Control {
     }
 
     /** @hidden */
+    public _offsetLeft(offset: number) {
+        super._offsetLeft(offset);
+
+        for (var child of this._children) {
+            child._offsetLeft(offset);
+        }
+    }
+
+    /** @hidden */
+    public _offsetTop(offset: number) {
+        super._offsetTop(offset);
+
+        for (var child of this._children) {
+            child._offsetTop(offset);
+        }
+    }
+
+    /** @hidden */
     public _markAllAsDirty(): void {
         super._markAllAsDirty();
 
@@ -227,6 +247,7 @@ export class Container extends Control {
     /** @hidden */
     protected _localDraw(context: CanvasRenderingContext2D): void {
         if (this._background) {
+            context.save();
             if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                 context.shadowColor = this.shadowColor;
                 context.shadowBlur = this.shadowBlur;
@@ -236,86 +257,104 @@ export class Container extends Control {
 
             context.fillStyle = this._background;
             context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowBlur = 0;
-                context.shadowOffsetX = 0;
-                context.shadowOffsetY = 0;
-            }
+            context.restore();
         }
     }
 
     /** @hidden */
-    public _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void {
-        super._link(root, host);
+    public _link(host: AdvancedDynamicTexture): void {
+        super._link(host);
 
         for (var child of this._children) {
-            child._link(this, host);
+            child._link(host);
         }
     }
 
     /** @hidden */
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    protected _beforeLayout() {
+        // Do nothing
+    }
+
+    /** @hidden */
+    public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
         if (!this.isVisible || this.notRenderable) {
-            return;
+            return false;
         }
-        context.save();
-
-        this._applyStates(context);
 
-        if (this._processMeasures(parentMeasure, context)) {
+        let rebuildCount = 0;
 
-            if (this.onBeforeDrawObservable.hasObservers()) {
-                this.onBeforeDrawObservable.notifyObservers(this);
-            }
+        context.save();
 
-            this._localDraw(context);
-            this._renderHighlight(context);
+        this._applyStates(context);
 
-            if (this.clipChildren) {
-                this._clipForChildren(context);
-            }
+        this._beforeLayout();
 
+        do {
             let computedWidth = -1;
             let computedHeight = -1;
+            this._rebuildLayout = false;
+            this._processMeasures(parentMeasure, context);
 
-            for (var child of this._children) {
-                if (child.isVisible && !child.notRenderable) {
+            if (!this._isClipped) {
+                for (var child of this._children) {
                     child._tempParentMeasure.copyFrom(this._measureForChildren);
 
-                    child._draw(this._measureForChildren, context);
-                    child._renderHighlight(context);
+                    if (child._layout(this._measureForChildren, context)) {
 
-                    if (child.onAfterDrawObservable.hasObservers()) {
-                        child.onAfterDrawObservable.notifyObservers(child);
+                        if (this.adaptWidthToChildren && child._width.isPixel) {
+                            computedWidth = Math.max(computedWidth, child._currentMeasure.width);
+                        }
+                        if (this.adaptHeightToChildren && child._height.isPixel) {
+                            computedHeight = Math.max(computedHeight, child._currentMeasure.height);
+                        }
                     }
+                }
 
-                    if (this.adaptWidthToChildren && child._width.isPixel) {
-                        computedWidth = Math.max(computedWidth, child._currentMeasure.width);
+                if (this.adaptWidthToChildren && computedWidth >= 0) {
+                    if (this.width !== computedWidth + "px") {
+                        this.width = computedWidth + "px";
+                        this._rebuildLayout = true;
                     }
-                    if (this.adaptHeightToChildren && child._height.isPixel) {
-                        computedHeight = Math.max(computedHeight, child._currentMeasure.height);
+                }
+                if (this.adaptHeightToChildren && computedHeight >= 0) {
+                    if (this.height !== computedHeight + "px") {
+                        this.height = computedHeight + "px";
+                        this._rebuildLayout = true;
                     }
                 }
-            }
 
-            if (this.adaptWidthToChildren && computedWidth >= 0) {
-                if (this.width !== computedWidth + "px") {
-                    this.width = computedWidth + "px";
-                    this._host._needRedraw = true;
-                }
-            }
-            if (this.adaptHeightToChildren && computedHeight >= 0) {
-                if (this.height !== computedHeight + "px") {
-                    this.height = computedHeight + "px";
-                    this._host._needRedraw = true;
-                }
+                this._postMeasure();
             }
+            rebuildCount++;
+        }
+        while (this._rebuildLayout && rebuildCount < 3);
+
+        if (rebuildCount >= 3) {
+            BABYLON.Tools.Error(`Layout cycle detected in GUI (Container uniqueId=${this.uniqueId})`);
         }
+
         context.restore();
 
-        if (this.onAfterDrawObservable.hasObservers()) {
-            this.onAfterDrawObservable.notifyObservers(this);
+        this._isDirty = false;
+
+        return true;
+    }
+
+    protected _postMeasure() {
+        // Do nothing by default
+    }
+
+    /** @hidden */
+    public _draw(context: CanvasRenderingContext2D): void {
+
+        this._localDraw(context);
+
+        if (this.clipChildren) {
+            this._clipForChildren(context);
+        }
+
+        for (var child of this._children) {
+            child._render(context);
         }
     }
 
@@ -367,11 +406,6 @@ export class Container extends Control {
     }
 
     /** @hidden */
-    protected _clipForChildren(context: CanvasRenderingContext2D): void {
-        // DO nothing
-    }
-
-    /** @hidden */
     protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
         super._additionalProcessing(parentMeasure, context);
 

+ 147 - 72
gui/src/2D/controls/control.ts

@@ -20,8 +20,6 @@ export class Control {
     private _alphaSet = false;
     private _zIndex = 0;
     /** @hidden */
-    public _root: Nullable<Container>;
-    /** @hidden */
     public _host: AdvancedDynamicTexture;
     /** Gets or sets the control parent */
     public parent: Nullable<Container>;
@@ -45,7 +43,8 @@ export class Control {
     protected _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
     /** @hidden */
     protected _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
-    private _isDirty = true;
+    /** @hidden */
+    protected _isDirty = true;
     /** @hidden */
     public _tempParentMeasure = Measure.Empty();
     /** @hidden */
@@ -68,7 +67,6 @@ export class Control {
     protected _invertTransformMatrix = Matrix2D.Identity();
     /** @hidden */
     protected _transformedPosition = Vector2.Zero();
-    private _onlyMeasureMode = false;
     private _isMatrixDirty = true;
     private _cachedOffsetX: number;
     private _cachedOffsetY: number;
@@ -84,6 +82,10 @@ export class Control {
     private _downPointerIds: { [id: number]: boolean } = {};
     protected _isEnabled = true;
     protected _disabledColor = "#9a9a9a";
+
+    /** @hidden */
+    public _isClipped = false;
+
     /** @hidden */
     public _tag: any;
 
@@ -575,8 +577,8 @@ export class Control {
 
         this._zIndex = value;
 
-        if (this._root) {
-            this._root._reOrderControl(this);
+        if (this.parent) {
+            this.parent._reOrderControl(this);
         }
     }
 
@@ -848,6 +850,23 @@ export class Control {
         return "Control";
     }
 
+    /**
+     * Gets the first ascendant in the hierarchy of the given type
+     * @param className defines the required type
+     * @returns the ascendant or null if not found
+     */
+    public getAscendantOfClass(className: string): Nullable<Control> {
+        if (!this.parent) {
+            return null;
+        }
+
+        if (this.parent.getClassName() === className) {
+            return this.parent;
+        }
+
+        return this.parent.getAscendantOfClass(className);
+    }
+
     /** @hidden */
     public _resetFontCache(): void {
         this._fontSet = true;
@@ -916,7 +935,7 @@ export class Control {
      * @param scene defines the hosting scene
      */
     public moveToVector3(position: Vector3, scene: Scene): void {
-        if (!this._host || this._root !== this._host._rootContainer) {
+        if (!this._host || this.parent !== this._host._rootContainer) {
             Tools.Error("Cannot move a control to a vector3 if the control is not at root level");
             return;
         }
@@ -961,7 +980,7 @@ export class Control {
      * @see http://doc.babylonjs.com/how_to/gui#tracking-positions
      */
     public linkWithMesh(mesh: Nullable<AbstractMesh>): void {
-        if (!this._host || this._root && this._root !== this._host._rootContainer) {
+        if (!this._host || this.parent && this.parent !== this._host._rootContainer) {
             if (mesh) {
                 Tools.Error("Cannot link a control to a mesh if the control is not at root level");
             }
@@ -982,7 +1001,6 @@ export class Control {
         this.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         this.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
         this._linkedMesh = mesh;
-        this._onlyMeasureMode = this._currentMeasure.width === 0 || this._currentMeasure.height === 0;
         this._host._linkedControls.push(this);
     }
 
@@ -1012,6 +1030,18 @@ export class Control {
     }
 
     /** @hidden */
+    public _offsetLeft(offset: number) {
+        this._isDirty = true;
+        this._currentMeasure.left += offset;
+    }
+
+    /** @hidden */
+    public _offsetTop(offset: number) {
+        this._isDirty = true;
+        this._currentMeasure.top += offset;
+    }
+
+    /** @hidden */
     public _markMatrixAsDirty(): void {
         this._isMatrixDirty = true;
         this._flagDescendantsAsMatrixDirty();
@@ -1046,8 +1076,7 @@ export class Control {
     }
 
     /** @hidden */
-    public _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void {
-        this._root = root;
+    public _link(host: AdvancedDynamicTexture): void {
         this._host = host;
         if (this._host) {
             this.uniqueId = this._host.getScene()!.getUniqueId();
@@ -1081,7 +1110,7 @@ export class Control {
             this._isMatrixDirty = false;
             this._flagDescendantsAsMatrixDirty();
 
-            Matrix2D.ComposeToRef(-offsetX, -offsetY, this._rotation, this._scaleX, this._scaleY, this._root ? this._root._transformMatrix : null, this._transformMatrix);
+            Matrix2D.ComposeToRef(-offsetX, -offsetY, this._rotation, this._scaleX, this._scaleY, this.parent ? this.parent._transformMatrix : null, this._transformMatrix);
 
             this._transformMatrix.invertToRef(this._invertTransformMatrix);
         }
@@ -1102,7 +1131,7 @@ export class Control {
     }
 
     /** @hidden */
-    protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void {
+    public _renderHighlightSpecific(context: CanvasRenderingContext2D): void {
         context.strokeRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
     }
 
@@ -1133,9 +1162,26 @@ export class Control {
     }
 
     /** @hidden */
-    protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
+    public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
+        if (!this.isVisible || this.notRenderable) {
+            return false;
+        }
+        context.save();
+
+        this._applyStates(context);
+
+        this._processMeasures(parentMeasure, context);
+
+        context.restore();
+
+        this._isDirty = false;
+
+        return true;
+    }
+
+    /** @hidden */
+    protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
         if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
-            this._isDirty = false;
             this._currentMeasure.copyFrom(parentMeasure);
 
             // Let children take some pre-measurement actions
@@ -1160,64 +1206,30 @@ export class Control {
             }
         }
 
-        if (this._currentMeasure.left > parentMeasure.left + parentMeasure.width) {
-            return false;
-        }
-
-        if (this._currentMeasure.left + this._currentMeasure.width < parentMeasure.left) {
-            return false;
-        }
-
-        if (this._currentMeasure.top > parentMeasure.top + parentMeasure.height) {
-            return false;
-        }
-
-        if (this._currentMeasure.top + this._currentMeasure.height < parentMeasure.top) {
-            return false;
-        }
-
-        // Transform
-        this._transform(context);
+        if (this.parent && this.parent.clipChildren) {
+            // Early clip
+            if (this._currentMeasure.left > parentMeasure.left + parentMeasure.width) {
+                this._isClipped = true;
+                return;
+            }
 
-        if (this._onlyMeasureMode) {
-            this._onlyMeasureMode = false;
-            return false; // We do not want rendering for this frame as they are measure dependant information that need to be gathered
-        }
+            if (this._currentMeasure.left + this._currentMeasure.width < parentMeasure.left) {
+                this._isClipped = true;
+                return;
+            }
 
-        // Clip
-        if (this.clipChildren) {
-            this._clip(context);
-            context.clip();
-        }
+            if (this._currentMeasure.top > parentMeasure.top + parentMeasure.height) {
+                this._isClipped = true;
+                return;
+            }
 
-        if (this.onBeforeDrawObservable.hasObservers()) {
-            this.onBeforeDrawObservable.notifyObservers(this);
+            if (this._currentMeasure.top + this._currentMeasure.height < parentMeasure.top) {
+                this._isClipped = true;
+                return;
+            }
         }
 
-        return true;
-    }
-
-    /** @hidden */
-    protected _clip(context: CanvasRenderingContext2D) {
-        context.beginPath();
-
-        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-            var shadowOffsetX = this.shadowOffsetX;
-            var shadowOffsetY = this.shadowOffsetY;
-            var shadowBlur = this.shadowBlur;
-
-            var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur * 2, 0);
-            var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur * 2, 0);
-            var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur * 2, 0);
-            var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur * 2, 0);
-
-            context.rect(this._currentMeasure.left + leftShadowOffset,
-                this._currentMeasure.top + topShadowOffset,
-                this._currentMeasure.width + rightShadowOffset - leftShadowOffset,
-                this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
-        } else {
-            context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-        }
+        this._isClipped = false;
     }
 
     /** @hidden */
@@ -1327,7 +1339,70 @@ export class Control {
     }
 
     /** @hidden */
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    protected _clipForChildren(context: CanvasRenderingContext2D): void {
+        // DO nothing
+    }
+
+    private _clip(context: CanvasRenderingContext2D) {
+        context.beginPath();
+
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            var shadowOffsetX = this.shadowOffsetX;
+            var shadowOffsetY = this.shadowOffsetY;
+            var shadowBlur = this.shadowBlur;
+
+            var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur * 2, 0);
+            var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur * 2, 0);
+            var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur * 2, 0);
+            var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur * 2, 0);
+
+            context.rect(this._currentMeasure.left + leftShadowOffset,
+                this._currentMeasure.top + topShadowOffset,
+                this._currentMeasure.width + rightShadowOffset - leftShadowOffset,
+                this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
+        } else {
+            context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+        }
+
+        context.clip();
+    }
+
+    /** @hidden */
+    public _render(context: CanvasRenderingContext2D): boolean {
+        if (!this.isVisible || this.notRenderable || this._isClipped) {
+            this._isDirty = false;
+            return false;
+        }
+        context.save();
+
+        this._applyStates(context);
+
+        // Transform
+        this._transform(context);
+
+        // Clip
+        if (this.clipChildren) {
+            this._clip(context);
+        }
+
+        if (this.onBeforeDrawObservable.hasObservers()) {
+            this.onBeforeDrawObservable.notifyObservers(this);
+        }
+
+        this._draw(context);
+        this._renderHighlight(context);
+
+        if (this.onAfterDrawObservable.hasObservers()) {
+            this.onAfterDrawObservable.notifyObservers(this);
+        }
+
+        context.restore();
+
+        return true;
+    }
+
+    /** @hidden */
+    public _draw(context: CanvasRenderingContext2D): void {
         // Do nothing
     }
 
@@ -1550,9 +1625,9 @@ export class Control {
             this._styleObserver = null;
         }
 
-        if (this._root) {
-            this._root.removeControl(this);
-            this._root = null;
+        if (this.parent) {
+            this.parent.removeControl(this);
+            this.parent = null;
         }
 
         if (this._host) {

+ 2 - 3
gui/src/2D/controls/displayGrid.ts

@@ -1,6 +1,5 @@
 
 import { Control } from ".";
-import { Measure } from "..";
 
 /** Class used to render a grid  */
 export class DisplayGrid extends Control {
@@ -147,12 +146,12 @@ export class DisplayGrid extends Control {
         super(name);
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
 
-        if (this._isEnabled && this._processMeasures(parentMeasure, context)) {
+        if (this._isEnabled) {
 
             if (this._background) {
                 context.fillStyle = this._background;

+ 37 - 5
gui/src/2D/controls/grid.ts

@@ -33,6 +33,32 @@ export class Grid extends Container {
     }
 
     /**
+     * Gets the definition of a specific row
+     * @param index defines the index of the row
+     * @returns the row definition
+     */
+    public getRowDefinition(index: number): Nullable<ValueAndUnit> {
+        if (index < 0 || index >= this._rowDefinitions.length) {
+            return null;
+        }
+
+        return this._rowDefinitions[index];
+    }
+
+    /**
+     * Gets the definition of a specific column
+     * @param index defines the index of the column
+     * @returns the column definition
+     */
+    public getColumnDefinition(index: number): Nullable<ValueAndUnit> {
+        if (index < 0 || index >= this._columnDefinitions.length) {
+            return null;
+        }
+
+        return this._columnDefinitions[index];
+    }
+
+    /**
      * Adds a new row to the grid
      * @param height defines the height of the row (either in pixel or a value between 0 and 1)
      * @param isPixel defines if the height is expressed in pixel (or in percentage)
@@ -72,6 +98,11 @@ export class Grid extends Container {
             return this;
         }
 
+        let current = this._rowDefinitions[index];
+        if (current && current.isPixel === isPixel && current.internalValue === height) {
+            return this;
+        }
+
         this._rowDefinitions[index] = new ValueAndUnit(height, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE);
 
         this._markAsDirty();
@@ -91,6 +122,11 @@ export class Grid extends Container {
             return this;
         }
 
+        let current = this._columnDefinitions[index];
+        if (current && current.isPixel === isPixel && current.internalValue === width) {
+            return this;
+        }
+
         this._columnDefinitions[index] = new ValueAndUnit(width, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE);
 
         this._markAsDirty();
@@ -388,11 +424,7 @@ export class Grid extends Container {
         }
     }
 
-    protected _renderHighlightSpecific(context: CanvasRenderingContext2D): void {
-        if (!this.isHighlighted) {
-            return;
-        }
-
+    public _renderHighlightSpecific(context: CanvasRenderingContext2D): void {
         super._renderHighlightSpecific(context);
 
         this._getGridDefinitions((lefts: number[], tops: number[], widths: number[], heights: number[]) => {

+ 51 - 35
gui/src/2D/controls/image.ts

@@ -1,6 +1,6 @@
 import { Control } from "./control";
 import { Nullable, Tools, Observable } from "babylonjs";
-import { Measure } from "../measure";
+import { Measure } from "2D";
 
 /**
  * Class used to create 2D images
@@ -267,7 +267,31 @@ export class Image extends Control {
         this.height = this._domImage.height + "px";
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        if (this._loaded) {
+            switch (this._stretch) {
+                case Image.STRETCH_NONE:
+                    break;
+                case Image.STRETCH_FILL:
+                    break;
+                case Image.STRETCH_UNIFORM:
+                    break;
+                case Image.STRETCH_EXTEND:
+                    if (this._autoScale) {
+                        this.synchronizeSizeWithContent();
+                    }
+                    if (this.parent && this.parent.parent) { // Will update root size if root is not the top root
+                        this.parent.adaptWidthToChildren = true;
+                        this.parent.adaptHeightToChildren = true;
+                    }
+                    break;
+            }
+        }
+
+        super._processMeasures(parentMeasure, context);
+    }
+
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -297,41 +321,33 @@ export class Image extends Control {
         }
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-            if (this._loaded) {
-                switch (this._stretch) {
-                    case Image.STRETCH_NONE:
-                        context.drawImage(this._domImage, x, y, width, height,
-                            this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-                        break;
-                    case Image.STRETCH_FILL:
-                        context.drawImage(this._domImage, x, y, width, height,
-                            this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-                        break;
-                    case Image.STRETCH_UNIFORM:
-                        var hRatio = this._currentMeasure.width / width;
-                        var vRatio = this._currentMeasure.height / height;
-                        var ratio = Math.min(hRatio, vRatio);
-                        var centerX = (this._currentMeasure.width - width * ratio) / 2;
-                        var centerY = (this._currentMeasure.height - height * ratio) / 2;
-
-                        context.drawImage(this._domImage, x, y, width, height,
-                            this._currentMeasure.left + centerX, this._currentMeasure.top + centerY, width * ratio, height * ratio);
-                        break;
-                    case Image.STRETCH_EXTEND:
-                        context.drawImage(this._domImage, x, y, width, height,
-                            this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-                        if (this._autoScale) {
-                            this.synchronizeSizeWithContent();
-                        }
-                        if (this._root && this._root.parent) { // Will update root size if root is not the top root
-                            this._root.width = this.width;
-                            this._root.height = this.height;
-                        }
-                        break;
-                }
+        if (this._loaded) {
+            switch (this._stretch) {
+                case Image.STRETCH_NONE:
+                    context.drawImage(this._domImage, x, y, width, height,
+                        this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                    break;
+                case Image.STRETCH_FILL:
+                    context.drawImage(this._domImage, x, y, width, height,
+                        this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                    break;
+                case Image.STRETCH_UNIFORM:
+                    var hRatio = this._currentMeasure.width / width;
+                    var vRatio = this._currentMeasure.height / height;
+                    var ratio = Math.min(hRatio, vRatio);
+                    var centerX = (this._currentMeasure.width - width * ratio) / 2;
+                    var centerY = (this._currentMeasure.height - height * ratio) / 2;
+
+                    context.drawImage(this._domImage, x, y, width, height,
+                        this._currentMeasure.left + centerX, this._currentMeasure.top + centerY, width * ratio, height * ratio);
+                    break;
+                case Image.STRETCH_EXTEND:
+                    context.drawImage(this._domImage, x, y, width, height,
+                        this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                    break;
             }
         }
+
         context.restore();
     }
 

+ 4 - 4
gui/src/2D/controls/index.ts

@@ -13,13 +13,13 @@ export * from "./multiLine";
 export * from "./radioButton";
 export * from "./stackPanel";
 export * from "./selector";
-export * from "./scrollViewer";
+export * from "./scrollViewers/scrollViewer";
 export * from "./textBlock";
 export * from "./virtualKeyboard";
 export * from "./rectangle";
 export * from "./displayGrid";
-export * from "./baseSlider";
-export * from "./slider";
-export * from "./imageBasedSlider";
+export * from "./sliders/baseSlider";
+export * from "./sliders/slider";
+export * from "./sliders/imageBasedSlider";
 
 export * from "./statics";

+ 135 - 123
gui/src/2D/controls/inputText.ts

@@ -2,7 +2,6 @@ import { Control } from "./control";
 import { IFocusableControl } from "../advancedDynamicTexture";
 import { ValueAndUnit } from "../valueAndUnit";
 import { Nullable, Observable, Observer, Vector2, ClipboardEventTypes, ClipboardInfo, PointerInfo } from 'babylonjs';
-import { Measure } from "../measure";
 import { VirtualKeyboard } from "./virtualKeyboard";
 
 /**
@@ -509,7 +508,14 @@ export class InputText extends Control implements IFocusableControl {
                     this._blinkIsEven = false;
                     // shift + ctrl/cmd + <-
                     if (evt.ctrlKey || evt.metaKey) {
-                        this._endHighlightIndex = this._isTextHighlightOn ? this._endHighlightIndex : this._text.length - this._cursorOffset + 1;
+                        if (!this._isTextHighlightOn) {
+                            if (this._text.length === this._cursorOffset) {
+                                return;
+                            }
+                            else {
+                                this._endHighlightIndex = this._text.length - this._cursorOffset + 1;
+                            }
+                        }
                         this._startHighlightIndex = 0;
                         this._cursorIndex = this._text.length - this._endHighlightIndex;
                         this._cursorOffset = this._text.length;
@@ -565,8 +571,15 @@ export class InputText extends Control implements IFocusableControl {
                     this._blinkIsEven = false;
                     //shift + ctrl/cmd + ->
                     if (evt.ctrlKey || evt.metaKey) {
+                        if (!this._isTextHighlightOn) {
+                            if (this._cursorOffset === 0) {
+                                return;
+                            }
+                            else {
+                                this._startHighlightIndex = this._text.length - this._cursorOffset - 1;
+                            }
+                        }
                         this._endHighlightIndex = this._text.length;
-                        this._startHighlightIndex = (this._isTextHighlightOn) ? this._startHighlightIndex : this._text.length - this._cursorOffset - 1;
                         this._isTextHighlightOn = true;
                         this._cursorIndex = this._text.length - this._startHighlightIndex;
                         this._cursorOffset = 0;
@@ -756,156 +769,155 @@ export class InputText extends Control implements IFocusableControl {
         this.text = this._text.slice(0, insertPosition) + data + this._text.slice(insertPosition);
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowColor = this.shadowColor;
-                context.shadowBlur = this.shadowBlur;
-                context.shadowOffsetX = this.shadowOffsetX;
-                context.shadowOffsetY = this.shadowOffsetY;
-            }
-
-            // Background
-            if (this._isFocused) {
-                if (this._focusedBackground) {
-                    context.fillStyle = this._isEnabled ? this._focusedBackground : this._disabledColor;
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowColor = this.shadowColor;
+            context.shadowBlur = this.shadowBlur;
+            context.shadowOffsetX = this.shadowOffsetX;
+            context.shadowOffsetY = this.shadowOffsetY;
+        }
 
-                    context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
-                }
-            } else if (this._background) {
-                context.fillStyle = this._isEnabled ? this._background : this._disabledColor;
+        // Background
+        if (this._isFocused) {
+            if (this._focusedBackground) {
+                context.fillStyle = this._isEnabled ? this._focusedBackground : this._disabledColor;
 
                 context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
             }
+        } else if (this._background) {
+            context.fillStyle = this._isEnabled ? this._background : this._disabledColor;
 
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowBlur = 0;
-                context.shadowOffsetX = 0;
-                context.shadowOffsetY = 0;
-            }
+            context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+        }
 
-            if (!this._fontOffset) {
-                this._fontOffset = Control._GetFontOffset(context.font);
-            }
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowBlur = 0;
+            context.shadowOffsetX = 0;
+            context.shadowOffsetY = 0;
+        }
 
-            // Text
-            let clipTextLeft = this._currentMeasure.left + this._margin.getValueInPixel(this._host, parentMeasure.width);
-            if (this.color) {
-                context.fillStyle = this.color;
-            }
+        if (!this._fontOffset) {
+            this._fontOffset = Control._GetFontOffset(context.font);
+        }
 
-            let text = this._beforeRenderText(this._text);
+        // Text
+        let clipTextLeft = this._currentMeasure.left + this._margin.getValueInPixel(this._host, this._tempParentMeasure.width);
+        if (this.color) {
+            context.fillStyle = this.color;
+        }
 
-            if (!this._isFocused && !this._text && this._placeholderText) {
-                text = this._placeholderText;
+        let text = this._beforeRenderText(this._text);
 
-                if (this._placeholderColor) {
-                    context.fillStyle = this._placeholderColor;
-                }
-            }
+        if (!this._isFocused && !this._text && this._placeholderText) {
+            text = this._placeholderText;
 
-            this._textWidth = context.measureText(text).width;
-            let marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
-            if (this._autoStretchWidth) {
-                this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), this._textWidth + marginWidth) + "px";
+            if (this._placeholderColor) {
+                context.fillStyle = this._placeholderColor;
             }
+        }
 
-            let rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
-            let availableWidth = this._width.getValueInPixel(this._host, parentMeasure.width) - marginWidth;
-            context.save();
-            context.beginPath();
-            context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
-            context.clip();
-
-            if (this._isFocused && this._textWidth > availableWidth) {
-                let textLeft = clipTextLeft - this._textWidth + availableWidth;
-                if (!this._scrollLeft) {
-                    this._scrollLeft = textLeft;
-                }
-            } else {
-                this._scrollLeft = clipTextLeft;
-            }
+        this._textWidth = context.measureText(text).width;
+        let marginWidth = this._margin.getValueInPixel(this._host, this._tempParentMeasure.width) * 2;
+        if (this._autoStretchWidth) {
+            this.width = Math.min(this._maxWidth.getValueInPixel(this._host, this._tempParentMeasure.width), this._textWidth + marginWidth) + "px";
+        }
 
-            context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
+        let rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
+        let availableWidth = this._width.getValueInPixel(this._host, this._tempParentMeasure.width) - marginWidth;
+        context.save();
+        context.beginPath();
+        context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
+        context.clip();
+
+        if (this._isFocused && this._textWidth > availableWidth) {
+            let textLeft = clipTextLeft - this._textWidth + availableWidth;
+            if (!this._scrollLeft) {
+                this._scrollLeft = textLeft;
+            }
+        } else {
+            this._scrollLeft = clipTextLeft;
+        }
 
-            // Cursor
-            if (this._isFocused) {
+        context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
 
-                // Need to move cursor
-                if (this._clickedCoordinate) {
-                    var rightPosition = this._scrollLeft + this._textWidth;
-                    var absoluteCursorPosition = rightPosition - this._clickedCoordinate;
-                    var currentSize = 0;
-                    this._cursorOffset = 0;
-                    var previousDist = 0;
-                    do {
-                        if (this._cursorOffset) {
-                            previousDist = Math.abs(absoluteCursorPosition - currentSize);
-                        }
-                        this._cursorOffset++;
-                        currentSize = context.measureText(text.substr(text.length - this._cursorOffset, this._cursorOffset)).width;
+        // Cursor
+        if (this._isFocused) {
 
-                    } while (currentSize < absoluteCursorPosition && (text.length >= this._cursorOffset));
-
-                    // Find closest move
-                    if (Math.abs(absoluteCursorPosition - currentSize) > previousDist) {
-                        this._cursorOffset--;
+            // Need to move cursor
+            if (this._clickedCoordinate) {
+                var rightPosition = this._scrollLeft + this._textWidth;
+                var absoluteCursorPosition = rightPosition - this._clickedCoordinate;
+                var currentSize = 0;
+                this._cursorOffset = 0;
+                var previousDist = 0;
+                do {
+                    if (this._cursorOffset) {
+                        previousDist = Math.abs(absoluteCursorPosition - currentSize);
                     }
+                    this._cursorOffset++;
+                    currentSize = context.measureText(text.substr(text.length - this._cursorOffset, this._cursorOffset)).width;
 
-                    this._blinkIsEven = false;
-                    this._clickedCoordinate = null;
+                } while (currentSize < absoluteCursorPosition && (text.length >= this._cursorOffset));
+
+                // Find closest move
+                if (Math.abs(absoluteCursorPosition - currentSize) > previousDist) {
+                    this._cursorOffset--;
                 }
 
-                // Render cursor
-                if (!this._blinkIsEven) {
-                    let cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
-                    let cursorOffsetWidth = context.measureText(cursorOffsetText).width;
-                    let cursorLeft = this._scrollLeft + this._textWidth - cursorOffsetWidth;
+                this._blinkIsEven = false;
+                this._clickedCoordinate = null;
+            }
 
-                    if (cursorLeft < clipTextLeft) {
-                        this._scrollLeft += (clipTextLeft - cursorLeft);
-                        cursorLeft = clipTextLeft;
-                        this._markAsDirty();
-                    } else if (cursorLeft > clipTextLeft + availableWidth) {
-                        this._scrollLeft += (clipTextLeft + availableWidth - cursorLeft);
-                        cursorLeft = clipTextLeft + availableWidth;
-                        this._markAsDirty();
-                    }
-                    if (!this._isTextHighlightOn) {
-                        context.fillRect(cursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, 2, this._fontOffset.height);
-                    }
-                }
+            // Render cursor
+            if (!this._blinkIsEven) {
+                let cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
+                let cursorOffsetWidth = context.measureText(cursorOffsetText).width;
+                let cursorLeft = this._scrollLeft + this._textWidth - cursorOffsetWidth;
 
-                clearTimeout(this._blinkTimeout);
-                this._blinkTimeout = <any>setTimeout(() => {
-                    this._blinkIsEven = !this._blinkIsEven;
+                if (cursorLeft < clipTextLeft) {
+                    this._scrollLeft += (clipTextLeft - cursorLeft);
+                    cursorLeft = clipTextLeft;
+                    this._markAsDirty();
+                } else if (cursorLeft > clipTextLeft + availableWidth) {
+                    this._scrollLeft += (clipTextLeft + availableWidth - cursorLeft);
+                    cursorLeft = clipTextLeft + availableWidth;
                     this._markAsDirty();
-                }, 500);
+                }
+                if (!this._isTextHighlightOn) {
+                    context.fillRect(cursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, 2, this._fontOffset.height);
+                }
+            }
 
-                //show the highlighted text
-                if (this._isTextHighlightOn) {
-                    clearTimeout(this._blinkTimeout);
-                    let highlightCursorOffsetWidth = context.measureText(this.text.substring(this._startHighlightIndex)).width;
-                    let highlightCursorLeft = this._scrollLeft + this._textWidth - highlightCursorOffsetWidth;
-                    this._highlightedText = this.text.substring(this._startHighlightIndex, this._endHighlightIndex);
-                    let width = context.measureText(this.text.substring(this._startHighlightIndex, this._endHighlightIndex)).width;
-                    if (highlightCursorLeft < clipTextLeft) {
-                        width =  width - (clipTextLeft - highlightCursorLeft);
-                        if (!width) {
-                            width = 2; //when using left arrow on text > availableWidth;
-                        }
-                        highlightCursorLeft = clipTextLeft;
+            clearTimeout(this._blinkTimeout);
+            this._blinkTimeout = <any>setTimeout(() => {
+                this._blinkIsEven = !this._blinkIsEven;
+                this._markAsDirty();
+            }, 500);
+
+            //show the highlighted text
+            if (this._isTextHighlightOn) {
+                clearTimeout(this._blinkTimeout);
+                let highlightCursorOffsetWidth = context.measureText(this.text.substring(this._startHighlightIndex)).width;
+                let highlightCursorLeft = this._scrollLeft + this._textWidth - highlightCursorOffsetWidth;
+                this._highlightedText = this.text.substring(this._startHighlightIndex, this._endHighlightIndex);
+                let width = context.measureText(this.text.substring(this._startHighlightIndex, this._endHighlightIndex)).width;
+                if (highlightCursorLeft < clipTextLeft) {
+                    width =  width - (clipTextLeft - highlightCursorLeft);
+                    if (!width) {
+                        // when using left arrow on text.length > availableWidth;
+                        // assigns the width of the first letter after clipTextLeft
+                        width = context.measureText(this.text.charAt(this.text.length - this._cursorOffset)).width;
                     }
-                    //for transparancy
-                    context.globalAlpha = this._highligherOpacity;
-                    context.fillStyle = this._textHighlightColor;
-                    context.fillRect(highlightCursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, width, this._fontOffset.height);
-                    context.globalAlpha = 1.0;
+                    highlightCursorLeft = clipTextLeft;
                 }
+                //for transparancy
+                context.globalAlpha = this._highligherOpacity;
+                context.fillStyle = this._textHighlightColor;
+                context.fillRect(highlightCursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, width, this._fontOffset.height);
+                context.globalAlpha = 1.0;
             }
 
             context.restore();

+ 9 - 11
gui/src/2D/controls/line.ts

@@ -159,7 +159,7 @@ export class Line extends Control {
         return "Line";
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -170,18 +170,16 @@ export class Line extends Control {
         }
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-            context.strokeStyle = this.color;
-            context.lineWidth = this._lineWidth;
-            context.setLineDash(this._dash);
+        context.strokeStyle = this.color;
+        context.lineWidth = this._lineWidth;
+        context.setLineDash(this._dash);
 
-            context.beginPath();
-            context.moveTo(this._x1.getValue(this._host), this._y1.getValue(this._host));
+        context.beginPath();
+        context.moveTo(this._x1.getValue(this._host), this._y1.getValue(this._host));
 
-            context.lineTo(this._effectiveX2, this._effectiveY2);
+        context.lineTo(this._effectiveX2, this._effectiveY2);
 
-            context.stroke();
-        }
+        context.stroke();
 
         context.restore();
     }
@@ -204,7 +202,7 @@ export class Line extends Control {
      * @param end (opt) Set to true to assign x2 and y2 coordinates of the line. Default assign to x1 and y1.
      */
     public moveToVector3(position: Vector3, scene: Scene, end: boolean = false): void {
-        if (!this._host || this._root !== this._host._rootContainer) {
+        if (!this._host || this.parent !== this._host._rootContainer) {
             Tools.Error("Cannot move a control to a vector3 if the control is not at root level");
             return;
         }

+ 19 - 21
gui/src/2D/controls/multiLine.ts

@@ -170,7 +170,7 @@ export class MultiLine extends Control {
         return "MultiLine";
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -182,32 +182,30 @@ export class MultiLine extends Control {
 
         this._applyStates(context);
 
-        if (this._processMeasures(parentMeasure, context)) {
-            context.strokeStyle = this.color;
-            context.lineWidth = this._lineWidth;
-            context.setLineDash(this._dash);
+        context.strokeStyle = this.color;
+        context.lineWidth = this._lineWidth;
+        context.setLineDash(this._dash);
 
-            context.beginPath();
+        context.beginPath();
 
-            var first: boolean = true; //first index is not necessarily 0
+        var first: boolean = true; //first index is not necessarily 0
 
-            this._points.forEach((point) => {
-                if (!point) {
-                    return;
-                }
+        this._points.forEach((point) => {
+            if (!point) {
+                return;
+            }
 
-                if (first) {
-                    context.moveTo(point._point.x, point._point.y);
+            if (first) {
+                context.moveTo(point._point.x, point._point.y);
 
-                    first = false;
-                }
-                else {
-                    context.lineTo(point._point.x, point._point.y);
-                }
-            });
+                first = false;
+            }
+            else {
+                context.lineTo(point._point.x, point._point.y);
+            }
+        });
 
-            context.stroke();
-        }
+        context.stroke();
 
         context.restore();
     }

+ 31 - 35
gui/src/2D/controls/radioButton.ts

@@ -1,6 +1,5 @@
 import { Control } from "./control";
 import { Observable, Vector2 } from "babylonjs";
-import { Measure } from "../measure";
 import { StackPanel, TextBlock } from ".";
 
 /**
@@ -109,51 +108,48 @@ export class RadioButton extends Control {
         return "RadioButton";
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-            let actualWidth = this._currentMeasure.width - this._thickness;
-            let actualHeight = this._currentMeasure.height - this._thickness;
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowColor = this.shadowColor;
-                context.shadowBlur = this.shadowBlur;
-                context.shadowOffsetX = this.shadowOffsetX;
-                context.shadowOffsetY = this.shadowOffsetY;
-            }
-
-            // Outer
-            Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2,
-                this._currentMeasure.width / 2 - this._thickness / 2, this._currentMeasure.height / 2 - this._thickness / 2, context);
+        let actualWidth = this._currentMeasure.width - this._thickness;
+        let actualHeight = this._currentMeasure.height - this._thickness;
+
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowColor = this.shadowColor;
+            context.shadowBlur = this.shadowBlur;
+            context.shadowOffsetX = this.shadowOffsetX;
+            context.shadowOffsetY = this.shadowOffsetY;
+        }
 
-            context.fillStyle = this._isEnabled ? this._background : this._disabledColor;
-            context.fill();
+        // Outer
+        Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2,
+            this._currentMeasure.width / 2 - this._thickness / 2, this._currentMeasure.height / 2 - this._thickness / 2, context);
 
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowBlur = 0;
-                context.shadowOffsetX = 0;
-                context.shadowOffsetY = 0;
-            }
+        context.fillStyle = this._isEnabled ? this._background : this._disabledColor;
+        context.fill();
 
-            context.strokeStyle = this.color;
-            context.lineWidth = this._thickness;
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowBlur = 0;
+            context.shadowOffsetX = 0;
+            context.shadowOffsetY = 0;
+        }
 
-            context.stroke();
+        context.strokeStyle = this.color;
+        context.lineWidth = this._thickness;
 
-            // Inner
-            if (this._isChecked) {
-                context.fillStyle = this._isEnabled ? this.color : this._disabledColor;
-                let offsetWidth = actualWidth * this._checkSizeRatio;
-                let offseHeight = actualHeight * this._checkSizeRatio;
+        context.stroke();
 
-                Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2,
-                    offsetWidth / 2 - this._thickness / 2, offseHeight / 2 - this._thickness / 2, context);
+        // Inner
+        if (this._isChecked) {
+            context.fillStyle = this._isEnabled ? this.color : this._disabledColor;
+            let offsetWidth = actualWidth * this._checkSizeRatio;
+            let offseHeight = actualHeight * this._checkSizeRatio;
 
-                context.fill();
-            }
+            Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2,
+                offsetWidth / 2 - this._thickness / 2, offseHeight / 2 - this._thickness / 2, context);
 
+            context.fill();
         }
         context.restore();
     }

+ 0 - 435
gui/src/2D/controls/scrollViewer.ts

@@ -1,435 +0,0 @@
-import { Measure } from "../measure";
-import { Rectangle } from "./rectangle";
-import { Grid } from "./grid";
-import { Control } from "./control";
-import { Slider } from "./slider";
-import { ValueAndUnit } from "../valueAndUnit";
-import { Container } from "./container";
-import { TextBlock } from "./textBlock";
-import { PointerInfo, Observer, Nullable } from "babylonjs";
-
-/**
- * Class used to hold a viewer window and sliders in a grid
-*/
-export class ScrollViewer extends Rectangle {
-    private _grid: Grid;
-    private _horizontalBarSpace: Rectangle;
-    private _verticalBarSpace: Rectangle;
-    private _dragSpace: Rectangle;
-    private _horizontalBar: Slider;
-    private _verticalBar: Slider;
-    private _barColor: string = "grey";
-    private _barBorderColor: string = "#444444";
-    private _barBackground: string = "white";
-    private _scrollGridWidth: number = 30;
-    private _scrollGridHeight: number = 30;
-    private _widthScale: number;
-    private _heightScale: number;
-    private _endLeft: number;
-    private _endTop: number;
-    private _window: Container;
-    private _windowContents: Control;
-    private _pointerIsOver: Boolean = false;
-    private _wheelPrecision: number = 0.05;
-    private _onPointerObserver: Nullable<Observer<PointerInfo>>;
-
-    /**
-     * Adds windowContents to the grid view window
-     * @param windowContents the contents to add the grid view window
-     */
-    public addToWindow(windowContents: Control): void {
-        this._window.removeControl(this._windowContents);
-        this._windowContents.dispose();
-        this._windowContents = windowContents;
-        if (windowContents.typeName === "TextBlock") {
-            this._updateTextBlock(windowContents);
-        }
-        else {
-            this._updateScroller(windowContents);
-        }
-        this._window.addControl(windowContents);
-    }
-
-    /**
-     * Gets or sets a value indicating the padding to use on the left of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingLeft(): string | number {
-        return this._windowContents.paddingLeft;
-    }
-
-    /**
-     * Gets a value indicating the padding in pixels to use on the left of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingLeftInPixels(): number {
-        return this._windowContents.paddingLeftInPixels;
-    }
-
-    public set paddingLeft(value: string | number) {
-        this._windowContents.paddingLeft = value;
-    }
-
-    /**
-     * Gets or sets a value indicating the padding to use on the right of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingRight(): string | number {
-        return this._windowContents.paddingRight;
-    }
-
-    /**
-     * Gets a value indicating the padding in pixels to use on the right of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingRightInPixels(): number {
-        return this._windowContents.paddingRightInPixels;
-    }
-
-    public set paddingRight(value: string | number) {
-        this._windowContents.paddingRight = value;
-    }
-
-    /**
-     * Gets or sets a value indicating the padding to use on the top of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingTop(): string | number {
-        return this._windowContents.paddingTop;
-    }
-
-    /**
-     * Gets a value indicating the padding in pixels to use on the top of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingTopInPixels(): number {
-        return this._windowContents.paddingTopInPixels;
-    }
-
-    public set paddingTop(value: string | number) {
-        this._windowContents.paddingTop = value;
-    }
-
-    /**
-     * Gets or sets a value indicating the padding to use on the bottom of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingBottom(): string | number {
-        return this._windowContents.paddingBottom;
-    }
-
-    /**
-     * Gets a value indicating the padding in pixels to use on the bottom of the viewer window
-     * @see http://doc.babylonjs.com/how_to/gui#position-and-size
-     */
-    public get paddingBottomInPixels(): number {
-        return this._windowContents.paddingBottomInPixels;
-    }
-
-    public set paddingBottom(value: string | number) {
-        this._windowContents.paddingBottom = value;
-    }
-
-    /**
-    * Creates a new ScrollViewer
-    * @param name of ScrollViewer
-    */
-    constructor(
-        /** name of ScrollViewer */
-        public name?: string) {
-        super(name);
-
-        this.onDirtyObservable.add(() => {
-            this._horizontalBarSpace.color = this.color;
-            this._verticalBarSpace.color = this.color;
-            this._dragSpace.color = this.color;
-            this._updateScroller(this._windowContents);
-            if (this._windowContents.typeName === "TextBlock") {
-                this._updateTextBlock(this._windowContents);
-            }
-        });
-
-        this.onPointerEnterObservable.add(() => {
-            this._pointerIsOver = true;
-        });
-
-        this.onPointerOutObservable.add(() => {
-            this._pointerIsOver = false;
-        });
-
-        this._grid = new Grid();
-        this._horizontalBar = new Slider();
-        this._verticalBar = new Slider();
-
-        this._window = new Container();
-        this._window.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
-        this._window.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
-
-        this._windowContents = new Control();
-        this._window.addControl(this._windowContents);
-
-        this._width = new ValueAndUnit(0.25, ValueAndUnit.UNITMODE_PERCENTAGE, false);
-        this._height = new ValueAndUnit(0.25, ValueAndUnit.UNITMODE_PERCENTAGE, false);
-        this._background = "black";
-
-        this.fontSize = "16px";
-
-        this._grid.addColumnDefinition(1, true);
-        this._grid.addColumnDefinition(this._scrollGridWidth, true);
-        this._grid.addRowDefinition(1, true);
-        this._grid.addRowDefinition(this._scrollGridHeight, true);
-
-        this.addControl(this._grid);
-        this._grid.addControl(this._window, 0, 0);
-
-        this._verticalBar.paddingLeft = 0;
-        this._verticalBar.width = "25px";
-        this._verticalBar.value = 0;
-        this._verticalBar.maximum = 100;
-        this._verticalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
-        this._verticalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
-        this._verticalBar.left = 0.05;
-        this._verticalBar.isThumbClamped = true;
-        this._verticalBar.color = "grey";
-        this._verticalBar.borderColor = "#444444";
-        this._verticalBar.background = "white";
-        this._verticalBar.isVertical = true;
-        this._verticalBar.rotation = Math.PI;
-
-        this._verticalBarSpace = new Rectangle();
-        this._verticalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
-        this._verticalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
-        this._verticalBarSpace.color = this.color;
-        this._verticalBarSpace.thickness = 1;
-        this._grid.addControl(this._verticalBarSpace, 0, 1);
-        this._verticalBarSpace.addControl(this._verticalBar);
-
-        this._verticalBar.onValueChangedObservable.add((value) => {
-            this._window.top = value * this._endTop / 100 + "px";
-        });
-
-        this._horizontalBar.paddingLeft = 0;
-        this._horizontalBar.height = "25px";
-        this._horizontalBar.value = 0;
-        this._horizontalBar.maximum = 100;
-        this._horizontalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
-        this._horizontalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
-        this._horizontalBar.left = 0.05;
-        this._horizontalBar.isThumbClamped = true;
-        this._horizontalBar.color = "grey";
-        this._horizontalBar.borderColor = "#444444";
-        this._horizontalBar.background = "white";
-
-        this._horizontalBarSpace = new Rectangle();
-        this._horizontalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
-        this._horizontalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
-        this._horizontalBarSpace.color = this.color;
-        this._horizontalBarSpace.thickness = 1;
-        this._grid.addControl(this._horizontalBarSpace, 1, 0);
-        this._horizontalBarSpace.addControl(this._horizontalBar);
-
-        this._horizontalBar.onValueChangedObservable.add((value) => {
-            this._window.left = value * this._endLeft / 100 + "px";
-        });
-
-        this._dragSpace = new Rectangle();
-        this._dragSpace.color = this.color;
-        this._dragSpace.thickness = 2;
-        this._dragSpace.background = this._barColor;
-        this._grid.addControl(this._dragSpace, 1, 1);
-    }
-
-    /**
-     * Gets or sets the mouse wheel precision
-     * from 0 to 1 with a default value of 0.05
-     * */
-    public get wheelPrecision(): number {
-        return this._wheelPrecision;
-    }
-
-    public set wheelPrecision(value: number) {
-        if (this._wheelPrecision === value) {
-            return;
-        }
-
-        if (value < 0) {
-            value = 0;
-        }
-
-        if (value > 1) {
-            value = 1;
-        }
-
-        this._wheelPrecision = value;
-    }
-
-    /** Gets or sets the bar color */
-    public get barColor(): string {
-        return this._barColor;
-    }
-
-    public set barColor(color: string) {
-        if (this._barColor === color) {
-            return;
-        }
-
-        this._barColor = color;
-        this._horizontalBar.color = color;
-        this._verticalBar.color = color;
-        this._dragSpace.background = color;
-    }
-
-    /** Gets or sets the bar color */
-    public get barBorderColor(): string {
-        return this._barBorderColor;
-    }
-
-    public set barBorderColor(color: string) {
-        if (this._barBorderColor === color) {
-            return;
-        }
-
-        this._barBorderColor = color;
-        this._horizontalBar.borderColor = color;
-        this._verticalBar.borderColor = color;
-    }
-
-    /** Gets or sets the bar background */
-    public get barBackground(): string {
-        return this._barBackground;
-    }
-
-    public set barBackground(color: string) {
-        if (this._barBackground === color) {
-            return;
-        }
-
-        this._barBackground = color;
-        this._horizontalBar.background = color;
-        this._verticalBar.background = color;
-    }
-
-    /** @hidden */
-    protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
-        super._additionalProcessing(parentMeasure, context);
-
-        let viewerWidth = this._width.getValueInPixel(this._host, parentMeasure.width);
-        let viewerHeight = this._height.getValueInPixel(this._host, parentMeasure.height);
-
-        let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
-        let innerHeight = viewerHeight  - this._scrollGridHeight - 2 * this.thickness;
-        this._horizontalBar.width = (innerWidth * 0.8) + "px";
-        this._verticalBar.height = (innerHeight * 0.8) + "px";
-
-        this._grid.setColumnDefinition(0, innerWidth, true);
-        this._grid.setRowDefinition(0, innerHeight, true);
-
-        this._attachWheel();
-    }
-
-    /** @hidden */
-    private _updateScroller(windowContents: Control): void {
-
-        let windowContentsWidth: number  = parseFloat(windowContents.width.toString());
-        if (windowContents._width.unit === 0) {
-            this._widthScale = windowContentsWidth / 100;
-            windowContentsWidth = this._host.getSize().width * this._widthScale;
-            windowContents.width = windowContentsWidth + "px";
-        }
-
-        let windowContentsHeight: number  = parseFloat(windowContents.height.toString());
-        if (windowContents._height.unit === 0) {
-            this._heightScale = windowContentsHeight / 100;
-            windowContentsHeight = this._host.getSize().height * this._heightScale;
-            windowContents.height = this._host.getSize().height * this._heightScale + "px";
-        }
-
-        this._window.width = windowContents.width;
-        this._window.height = windowContents.height;
-        this._windowContents.width = windowContents.width;
-        this._windowContents.height = windowContents.height;
-
-        let viewerWidth = this._width.getValueInPixel(this._host, this._host.getSize().width);
-        let viewerHeight = this._height.getValueInPixel(this._host, this._host.getSize().height);
-
-        let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
-        let innerHeight = viewerHeight  - this._scrollGridHeight - 2 * this.thickness;
-
-        if (windowContentsWidth <= innerWidth) {
-            this._grid.setRowDefinition(0, viewerHeight - 2 * this.thickness , true);
-            this._grid.setRowDefinition(1, 0, true);
-            this._horizontalBar.isVisible = false;
-        }
-        else {
-            this._grid.setRowDefinition(0, innerHeight, true);
-            this._grid.setRowDefinition(1, this._scrollGridHeight, true);
-            this._horizontalBar.isVisible = true;
-        }
-
-        if (windowContentsHeight < innerHeight) {
-            this._grid.setColumnDefinition(0, viewerWidth - 2 * this.thickness, true);
-            this._grid.setColumnDefinition(1, 0, true);
-            this._verticalBar.isVisible = false;
-        }
-        else {
-            this._grid.setColumnDefinition(0, innerWidth, true);
-            this._grid.setColumnDefinition(1, this._scrollGridWidth, true);
-            this._verticalBar.isVisible = true;
-        }
-
-        this._endLeft = innerWidth - windowContentsWidth;
-        this._endTop = innerHeight - windowContentsHeight;
-    }
-
-    /** @hidden */
-    private _updateTextBlock(windowContents: Control): void {
-        let viewerWidth = this._width.getValueInPixel(this._host, this._host.getSize().width);
-        let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
-
-        windowContents.width = innerWidth + "px";
-
-        this._window.width = windowContents.width;
-        this._windowContents.width = windowContents.width;
-
-        (<TextBlock>windowContents).onLinesReadyObservable.add(() => {
-            let windowContentsHeight = (this.fontOffset.height) * (<TextBlock>windowContents).lines.length + windowContents.paddingTopInPixels + windowContents.paddingBottomInPixels;
-            windowContents.height = windowContentsHeight + "px";
-            this._window.height = windowContents.height;
-            this._windowContents.height = windowContents.height;
-            this._updateScroller(windowContents);
-        });
-    }
-
-    /** @hidden */
-    private _attachWheel() {
-        let scene = this._host.getScene();
-        this._onPointerObserver = scene!.onPointerObservable.add((pi, state) => {
-            if (!this._pointerIsOver || pi.type !== BABYLON.PointerEventTypes.POINTERWHEEL) {
-                return;
-            }
-            if (this._verticalBar.isVisible == true) {
-                if ((<MouseWheelEvent>pi.event).deltaY < 0 && this._verticalBar.value > 0) {
-                    this._verticalBar.value -= this._wheelPrecision * 100;
-                } else if ((<MouseWheelEvent>pi.event).deltaY > 0 && this._verticalBar.value < this._verticalBar.maximum) {
-                    this._verticalBar.value += this._wheelPrecision * 100;
-                }
-            }
-            if (this._horizontalBar.isVisible == true) {
-                if ((<MouseWheelEvent>pi.event).deltaX < 0 && this._horizontalBar.value < this._horizontalBar.maximum) {
-                    this._horizontalBar.value += this._wheelPrecision * 100;
-                } else if ((<MouseWheelEvent>pi.event).deltaX > 0 && this._horizontalBar.value > 0) {
-                    this._horizontalBar.value -= this._wheelPrecision * 100;
-                }
-            }
-        });
-    }
-
-    /** Releases associated resources */
-    public dispose() {
-        let scene = this._host.getScene();
-        if (scene && this._onPointerObserver) {
-            scene.onPointerObservable.remove(this._onPointerObserver);
-        }
-        super.dispose();
-    }
-}

+ 375 - 0
gui/src/2D/controls/scrollViewers/scrollViewer.ts

@@ -0,0 +1,375 @@
+import { Rectangle } from "../rectangle";
+import { Grid } from "../grid";
+import { Control } from "../control";
+import { Container } from "../container";
+import { PointerInfo, Observer, Nullable } from "babylonjs";
+import { AdvancedDynamicTexture, Measure } from "2D";
+import { _ScrollViewerWindow } from "./scrollViewerWindow";
+import { ScrollBar } from "../sliders/scrollBar";
+
+/**
+ * Class used to hold a viewer window and sliders in a grid
+*/
+export class ScrollViewer extends Rectangle {
+    private _grid: Grid;
+    private _horizontalBarSpace: Rectangle;
+    private _verticalBarSpace: Rectangle;
+    private _dragSpace: Rectangle;
+    private _horizontalBar: ScrollBar;
+    private _verticalBar: ScrollBar;
+    private _barColor: string;
+    private _barBorderColor: string;
+    private _barBackground: string ;
+    private _barSize: number = 20;
+    private _endLeft: number;
+    private _endTop: number;
+    private _window: _ScrollViewerWindow;
+    private _pointerIsOver: Boolean = false;
+    private _wheelPrecision: number = 0.05;
+    private _onPointerObserver: Nullable<Observer<PointerInfo>>;
+    private _clientWidth: number;
+    private _clientHeight: number;
+
+    /**
+     * Adds a new control to the current container
+     * @param control defines the control to add
+     * @returns the current container
+     */
+    public addControl(control: Nullable<Control>): Container {
+        if (!control) {
+            return this;
+        }
+
+        this._window.addControl(control);
+
+        return this;
+    }
+
+    /**
+     * Removes a control from the current container
+     * @param control defines the control to remove
+     * @returns the current container
+     */
+    public removeControl(control: Control): Container {
+        this._window.removeControl(control);
+        return this;
+    }
+
+    /** Gets the list of children */
+    public get children(): Control[] {
+        return this._window.children;
+    }
+
+    public _flagDescendantsAsMatrixDirty(): void {
+        for (var child of this._children) {
+            child._markMatrixAsDirty();
+        }
+    }
+
+    /**
+    * Creates a new ScrollViewer
+    * @param name of ScrollViewer
+    */
+    constructor(name?: string) {
+        super(name);
+
+        this.onDirtyObservable.add(() => {
+            this._horizontalBarSpace.color = this.color;
+            this._verticalBarSpace.color = this.color;
+            this._dragSpace.color = this.color;
+        });
+
+        this.onPointerEnterObservable.add(() => {
+            this._pointerIsOver = true;
+        });
+
+        this.onPointerOutObservable.add(() => {
+            this._pointerIsOver = false;
+        });
+
+        this._grid = new Grid();
+        this._horizontalBar = new ScrollBar();
+        this._verticalBar = new ScrollBar();
+
+        this._window = new _ScrollViewerWindow();
+        this._window.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
+        this._window.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
+
+        this._grid.addColumnDefinition(1);
+        this._grid.addColumnDefinition(0, true);
+        this._grid.addRowDefinition(1);
+        this._grid.addRowDefinition(0, true);
+
+        super.addControl(this._grid);
+        this._grid.addControl(this._window, 0, 0);
+
+        this._verticalBar.paddingLeft = 0;
+        this._verticalBar.width = "100%";
+        this._verticalBar.height = "100%";
+        this._verticalBar.barOffset = 0;
+        this._verticalBar.value = 0;
+        this._verticalBar.maximum = 1;
+        this._verticalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
+        this._verticalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
+        this._verticalBar.isVertical = true;
+        this._verticalBar.rotation = Math.PI;
+        this._verticalBar.isVisible = false;
+
+        this._verticalBarSpace = new Rectangle();
+        this._verticalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
+        this._verticalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
+        this._verticalBarSpace.thickness = 1;
+        this._grid.addControl(this._verticalBarSpace, 0, 1);
+        this._verticalBarSpace.addControl(this._verticalBar);
+
+        this._verticalBar.onValueChangedObservable.add((value) => {
+            this._window.top = value * this._endTop + "px";
+        });
+
+        this._horizontalBar.paddingLeft = 0;
+        this._horizontalBar.width = "100%";
+        this._horizontalBar.height = "100%";
+        this._horizontalBar.barOffset = 0;
+        this._horizontalBar.value = 0;
+        this._horizontalBar.maximum = 1;
+        this._horizontalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
+        this._horizontalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
+        this._horizontalBar.isVisible = false;
+
+        this._horizontalBarSpace = new Rectangle();
+        this._horizontalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
+        this._horizontalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
+        this._horizontalBarSpace.thickness = 1;
+        this._grid.addControl(this._horizontalBarSpace, 1, 0);
+        this._horizontalBarSpace.addControl(this._horizontalBar);
+
+        this._horizontalBar.onValueChangedObservable.add((value) => {
+            this._window.left = value * this._endLeft + "px";
+        });
+
+        this._dragSpace = new Rectangle();
+        this._dragSpace.thickness = 1;
+        this._grid.addControl(this._dragSpace, 1, 1);
+
+        // Colors
+        this.barColor = "grey";
+        this.barBackground = "transparent";
+    }
+
+    /** Reset the scroll viewer window to initial size */
+    public resetWindow() {
+        this._window.width = "100%";
+        this._window.height = "100%";
+    }
+
+    protected _getTypeName(): string {
+        return "ScrollViewer";
+    }
+
+    private _buildClientSizes() {
+        this._window.parentClientWidth = this._currentMeasure.width - (this._verticalBar.isVisible ? this._barSize : 0) - 2 * this.thickness;
+        this._window.parentClientHeight = this._currentMeasure.height - (this._horizontalBar.isVisible ? this._barSize : 0) - 2 * this.thickness;
+
+        this._clientWidth = this._window.parentClientWidth;
+        this._clientHeight = this._window.parentClientHeight;
+    }
+
+    protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        super._additionalProcessing(parentMeasure, context);
+
+        this._buildClientSizes();
+    }
+
+    protected _postMeasure(): void {
+        super._postMeasure();
+
+        this._updateScroller();
+    }
+
+    /**
+     * Gets or sets the mouse wheel precision
+     * from 0 to 1 with a default value of 0.05
+     * */
+    public get wheelPrecision(): number {
+        return this._wheelPrecision;
+    }
+
+    public set wheelPrecision(value: number) {
+        if (this._wheelPrecision === value) {
+            return;
+        }
+
+        if (value < 0) {
+            value = 0;
+        }
+
+        if (value > 1) {
+            value = 1;
+        }
+
+        this._wheelPrecision = value;
+    }
+
+    /** Gets or sets the bar color */
+    public get barColor(): string {
+        return this._barColor;
+    }
+
+    public set barColor(color: string) {
+        if (this._barColor === color) {
+            return;
+        }
+
+        this._barColor = color;
+        this._horizontalBar.color = color;
+        this._verticalBar.color = color;
+    }
+
+    /** Gets or sets the size of the bar */
+    public get barSize(): number {
+        return this._barSize;
+    }
+
+    public set barSize(value: number) {
+        if (this._barSize === value) {
+            return;
+        }
+
+        this._barSize = value;
+        this._markAsDirty();
+
+        if (this._horizontalBar.isVisible) {
+            this._grid.setRowDefinition(1, this._barSize, true);
+        }
+        if (this._verticalBar.isVisible) {
+            this._grid.setColumnDefinition(1, this._barSize, true);
+        }
+    }
+
+    /** Gets or sets the bar color */
+    public get barBorderColor(): string {
+        return this._barBorderColor;
+    }
+
+    public set barBorderColor(color: string) {
+        if (this._barBorderColor === color) {
+            return;
+        }
+
+        this._barBorderColor = color;
+        this._horizontalBar.borderColor = color;
+        this._verticalBar.borderColor = color;
+    }
+
+    /** Gets or sets the bar background */
+    public get barBackground(): string {
+        return this._barBackground;
+    }
+
+    public set barBackground(color: string) {
+        if (this._barBackground === color) {
+            return;
+        }
+
+        this._barBackground = color;
+        this._horizontalBar.background = color;
+        this._verticalBar.background = color;
+        this._dragSpace.background = color;
+    }
+
+    /** @hidden */
+    private _updateScroller(): void {
+        let windowContentsWidth = this._window._currentMeasure.width;
+        let windowContentsHeight = this._window._currentMeasure.height;
+
+        if (this._horizontalBar.isVisible && windowContentsWidth <= this._clientWidth) {
+            this._grid.setRowDefinition(1, 0, true);
+            this._horizontalBar.isVisible = false;
+            this._horizontalBar.value = 0;
+            this._rebuildLayout = true;
+        }
+        else if (!this._horizontalBar.isVisible && windowContentsWidth > this._clientWidth) {
+            this._grid.setRowDefinition(1, this._barSize, true);
+            this._horizontalBar.isVisible = true;
+            this._rebuildLayout = true;
+        }
+
+        if (this._verticalBar.isVisible && windowContentsHeight <= this._clientHeight) {
+            this._grid.setColumnDefinition(1, 0, true);
+            this._verticalBar.isVisible = false;
+            this._verticalBar.value = 0;
+            this._rebuildLayout = true;
+        }
+        else if (!this._verticalBar.isVisible && windowContentsHeight > this._clientHeight) {
+            this._grid.setColumnDefinition(1, this._barSize, true);
+            this._verticalBar.isVisible = true;
+            this._rebuildLayout = true;
+        }
+
+        this._buildClientSizes();
+        this._endLeft = this._clientWidth - windowContentsWidth;
+        this._endTop = this._clientHeight - windowContentsHeight;
+
+        let horizontalMultiplicator = this._clientWidth / windowContentsWidth;
+        let verticalMultiplicator = this._clientHeight / windowContentsHeight;
+
+        this._horizontalBar.thumbWidth = (this._clientWidth * horizontalMultiplicator) + "px";
+        this._verticalBar.thumbWidth = (this._clientHeight * verticalMultiplicator) + "px";
+    }
+
+    public _link(host: AdvancedDynamicTexture): void {
+        super._link(host);
+
+        this._attachWheel();
+    }
+
+    /** @hidden */
+    private _attachWheel() {
+        if (this._onPointerObserver) {
+            return;
+        }
+
+        let scene = this._host.getScene();
+        this._onPointerObserver = scene!.onPointerObservable.add((pi, state) => {
+            if (!this._pointerIsOver || pi.type !== BABYLON.PointerEventTypes.POINTERWHEEL) {
+                return;
+            }
+            if (this._verticalBar.isVisible == true) {
+                if ((<MouseWheelEvent>pi.event).deltaY < 0 && this._verticalBar.value > 0) {
+                    this._verticalBar.value -= this._wheelPrecision;
+                } else if ((<MouseWheelEvent>pi.event).deltaY > 0 && this._verticalBar.value < this._verticalBar.maximum) {
+                    this._verticalBar.value += this._wheelPrecision;
+                }
+            }
+            if (this._horizontalBar.isVisible == true) {
+                if ((<MouseWheelEvent>pi.event).deltaX < 0 && this._horizontalBar.value < this._horizontalBar.maximum) {
+                    this._horizontalBar.value += this._wheelPrecision;
+                } else if ((<MouseWheelEvent>pi.event).deltaX > 0 && this._horizontalBar.value > 0) {
+                    this._horizontalBar.value -= this._wheelPrecision;
+                }
+            }
+        });
+    }
+
+    public _renderHighlightSpecific(context: CanvasRenderingContext2D): void {
+        if (!this.isHighlighted) {
+            return;
+        }
+
+        super._renderHighlightSpecific(context);
+
+        this._grid._renderHighlightSpecific(context);
+
+        context.restore();
+    }
+
+    /** Releases associated resources */
+    public dispose() {
+        let scene = this._host.getScene();
+        if (scene && this._onPointerObserver) {
+            scene.onPointerObservable.remove(this._onPointerObserver);
+            this._onPointerObserver  = null;
+        }
+        super.dispose();
+    }
+}

+ 74 - 0
gui/src/2D/controls/scrollViewers/scrollViewerWindow.ts

@@ -0,0 +1,74 @@
+import { Measure } from "../../measure";
+import { Container } from "../container";
+import { ValueAndUnit } from "../../valueAndUnit";
+import { Control } from "../control";
+
+/**
+ * Class used to hold a the container for ScrollViewer
+ * @hidden
+*/
+export class _ScrollViewerWindow extends Container {
+    public parentClientWidth: number;
+    public parentClientHeight: number;
+
+    /**
+    * Creates a new ScrollViewerWindow
+    * @param name of ScrollViewerWindow
+    */
+    constructor(name?: string) {
+        super(name);
+    }
+
+    protected _getTypeName(): string {
+        return "ScrollViewerWindow";
+    }
+
+    /** @hidden */
+    protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        super._additionalProcessing(parentMeasure, context);
+
+        this._measureForChildren.left = this._currentMeasure.left;
+        this._measureForChildren.top = this._currentMeasure.top;
+
+        this._measureForChildren.width = parentMeasure.width;
+        this._measureForChildren.height = parentMeasure.height;
+    }
+
+    protected _postMeasure(): void {
+        var maxWidth = this.parentClientWidth;
+        var maxHeight = this.parentClientHeight;
+        for (var child of this.children) {
+            if (!child.isVisible || child.notRenderable) {
+                continue;
+            }
+
+            if (child.horizontalAlignment === Control.HORIZONTAL_ALIGNMENT_CENTER) {
+                child._offsetLeft(this._currentMeasure.left - child._currentMeasure.left);
+            }
+
+            if (child.verticalAlignment === Control.VERTICAL_ALIGNMENT_CENTER) {
+                child._offsetTop(this._currentMeasure.top - child._currentMeasure.top);
+            }
+
+            maxWidth = Math.max(maxWidth, child._currentMeasure.left - this._currentMeasure.left + child._currentMeasure.width);
+            maxHeight = Math.max(maxHeight, child._currentMeasure.top - this._currentMeasure.top + child._currentMeasure.height);
+        }
+
+        if (this._currentMeasure.width !== maxWidth) {
+            this._width.updateInPlace(maxWidth, ValueAndUnit.UNITMODE_PIXEL);
+            this._currentMeasure.width = maxWidth;
+            this._rebuildLayout = true;
+            this._isDirty = true;
+        }
+
+        if (this._currentMeasure.height !== maxHeight) {
+            this._height.updateInPlace(maxHeight, ValueAndUnit.UNITMODE_PIXEL);
+            this._currentMeasure.height = maxHeight;
+            this._rebuildLayout = true;
+            this._isDirty = true;
+        }
+
+        super._postMeasure();
+    }
+
+}

+ 1 - 1
gui/src/2D/controls/selector.ts

@@ -4,7 +4,7 @@ import { Control } from "./control";
 import { TextBlock } from "./textBlock";
 import { Checkbox } from "./checkbox";
 import { RadioButton } from "./radioButton";
-import { Slider } from "./slider";
+import { Slider } from "./sliders/slider";
 import { Container } from "./container";
 
 /** Class used to create a RadioGroup

+ 0 - 227
gui/src/2D/controls/slider.ts

@@ -1,227 +0,0 @@
-import { Measure } from "../measure";
-import { BaseSlider } from "./baseSlider";
-
-/**
- * Class used to create slider controls
- */
-export class Slider extends BaseSlider {
-    private _background = "black";
-    private _borderColor = "white";
-    private _isThumbCircle = false;
-
-    /** Gets or sets border color */
-    public get borderColor(): string {
-        return this._borderColor;
-    }
-
-    public set borderColor(value: string) {
-        if (this._borderColor === value) {
-            return;
-        }
-
-        this._borderColor = value;
-        this._markAsDirty();
-    }
-
-    /** Gets or sets background color */
-    public get background(): string {
-        return this._background;
-    }
-
-    public set background(value: string) {
-        if (this._background === value) {
-            return;
-        }
-
-        this._background = value;
-        this._markAsDirty();
-    }
-
-    /** Gets or sets a boolean indicating if the thumb should be round or square */
-    public get isThumbCircle(): boolean {
-        return this._isThumbCircle;
-    }
-
-    public set isThumbCircle(value: boolean) {
-        if (this._isThumbCircle === value) {
-            return;
-        }
-
-        this._isThumbCircle = value;
-        this._markAsDirty();
-    }
-
-    /**
-     * Creates a new Slider
-     * @param name defines the control name
-     */
-    constructor(public name?: string) {
-        super(name);
-    }
-
-    protected _getTypeName(): string {
-        return "Slider";
-    }
-
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
-        context.save();
-
-        this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-
-            this._prepareRenderingData(this.isThumbCircle ? "circle" : "rectangle");
-            var left = this._renderLeft;
-            var top = this._renderTop;
-            var width = this._renderWidth;
-            var height = this._renderHeight;
-
-            var radius = 0;
-
-            if (this.isThumbClamped && this.isThumbCircle) {
-                if (this.isVertical) {
-                    top += (this._effectiveThumbThickness / 2);
-                }
-                else {
-                    left += (this._effectiveThumbThickness / 2);
-                }
-
-                radius = this._backgroundBoxThickness / 2;
-            }
-            else {
-                radius = (this._effectiveThumbThickness - this._effectiveBarOffset) / 2;
-            }
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowColor = this.shadowColor;
-                context.shadowBlur = this.shadowBlur;
-                context.shadowOffsetX = this.shadowOffsetX;
-                context.shadowOffsetY = this.shadowOffsetY;
-            }
-
-            const thumbPosition = this._getThumbPosition();
-            context.fillStyle = this._background;
-
-            if (this.isVertical) {
-                if (this.isThumbClamped) {
-                    if (this.isThumbCircle) {
-                        context.beginPath();
-                        context.arc(left + this._backgroundBoxThickness / 2, top, radius, Math.PI, 2 * Math.PI);
-                        context.fill();
-                        context.fillRect(left, top, width, height);
-                    }
-                    else {
-                        context.fillRect(left, top, width, height + this._effectiveThumbThickness);
-                    }
-                }
-                else {
-                    context.fillRect(left, top, width, height);
-                }
-            }
-            else {
-                if (this.isThumbClamped) {
-                    if (this.isThumbCircle) {
-                        context.beginPath();
-                        context.arc(left + this._backgroundBoxLength, top + (this._backgroundBoxThickness / 2), radius, 0, 2 * Math.PI);
-                        context.fill();
-                        context.fillRect(left, top, width, height);
-                    }
-                    else {
-                        context.fillRect(left, top, width + this._effectiveThumbThickness, height);
-                    }
-                }
-                else {
-                    context.fillRect(left, top, width, height);
-                }
-            }
-
-            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                context.shadowBlur = 0;
-                context.shadowOffsetX = 0;
-                context.shadowOffsetY = 0;
-            }
-
-            // Value bar
-            context.fillStyle = this.color;
-            if (this.isVertical) {
-                if (this.isThumbClamped) {
-                    if (this.isThumbCircle) {
-                        context.beginPath();
-                        context.arc(left + this._backgroundBoxThickness / 2, top + this._backgroundBoxLength, radius, 0, 2 * Math.PI);
-                        context.fill();
-                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
-                    }
-                    else {
-                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
-                    }
-                }
-                else {
-                    context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
-                }
-            }
-            else {
-                if (this.isThumbClamped) {
-                    if (this.isThumbCircle) {
-                        context.beginPath();
-                        context.arc(left, top + this._backgroundBoxThickness / 2, radius, 0, 2 * Math.PI);
-                        context.fill();
-                        context.fillRect(left, top, thumbPosition, height);
-                    }
-                    else {
-                        context.fillRect(left, top, thumbPosition, height);
-                    }
-                }
-                else {
-                    context.fillRect(left, top, thumbPosition, height);
-                }
-            }
-
-            // Thumb
-            if (this.displayThumb) {
-                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                    context.shadowColor = this.shadowColor;
-                    context.shadowBlur = this.shadowBlur;
-                    context.shadowOffsetX = this.shadowOffsetX;
-                    context.shadowOffsetY = this.shadowOffsetY;
-                }
-                if (this._isThumbCircle) {
-                    context.beginPath();
-                    if (this.isVertical) {
-                        context.arc(left + this._backgroundBoxThickness / 2, top + thumbPosition, radius, 0, 2 * Math.PI);
-                    }
-                    else {
-                        context.arc(left + thumbPosition, top + (this._backgroundBoxThickness / 2), radius, 0, 2 * Math.PI);
-                    }
-                    context.fill();
-                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                        context.shadowBlur = 0;
-                        context.shadowOffsetX = 0;
-                        context.shadowOffsetY = 0;
-                    }
-                    context.strokeStyle = this._borderColor;
-                    context.stroke();
-                }
-                else {
-                    if (this.isVertical) {
-                        context.fillRect(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
-                    }
-                    else {
-                        context.fillRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, this._effectiveThumbThickness, this._currentMeasure.height);
-                    }
-                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
-                        context.shadowBlur = 0;
-                        context.shadowOffsetX = 0;
-                        context.shadowOffsetY = 0;
-                    }
-                    context.strokeStyle = this._borderColor;
-                    if (this.isVertical) {
-                        context.strokeRect(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
-                    }
-                    else {
-                        context.strokeRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, this._effectiveThumbThickness, this._currentMeasure.height);
-                    }
-                }
-            }
-        }
-        context.restore();
-    }
-}

+ 4 - 3
gui/src/2D/controls/baseSlider.ts

@@ -1,5 +1,5 @@
-import { Control } from "./control";
-import { ValueAndUnit } from "../valueAndUnit";
+import { Control } from "../control";
+import { ValueAndUnit } from "../../valueAndUnit";
 import { Observable, Vector2 } from "babylonjs";
 
 /**
@@ -255,7 +255,8 @@ export class BaseSlider extends Control {
     // Events
     private _pointerIsDown = false;
 
-    private _updateValueFromPointer(x: number, y: number): void {
+    /** @hidden */
+    protected _updateValueFromPointer(x: number, y: number): void {
         if (this.rotation != 0) {
             this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
             x = this._transformedPosition.x;

+ 46 - 44
gui/src/2D/controls/imageBasedSlider.ts

@@ -1,6 +1,6 @@
 import { BaseSlider } from "./baseSlider";
-import { Measure } from "../measure";
-import { Image } from "./image";
+import { Measure } from "../../measure";
+import { Image } from "../image";
 
 /**
  * Class used to create slider controls based on images
@@ -100,60 +100,62 @@ export class ImageBasedSlider extends BaseSlider {
         return "ImageBasedSlider";
     }
 
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
-        if (this._processMeasures(parentMeasure, context)) {
-
-            this._prepareRenderingData("rectangle");
-            const thumbPosition = this._getThumbPosition();
-            var left = this._renderLeft;
-            var top = this._renderTop;
-            var width = this._renderWidth;
-            var height = this._renderHeight;
-
-            // Background
-            if (this._backgroundImage) {
-                this._tempMeasure.copyFromFloats(left, top, width, height);
-                if (this.isThumbClamped && this.displayThumb) {
-                    if (this.isVertical) {
-                        this._tempMeasure.height += this._effectiveThumbThickness;
-                    } else {
-                        this._tempMeasure.width += this._effectiveThumbThickness;
-                    }
-                }
-                this._backgroundImage._draw(this._tempMeasure, context);
-            }
 
-            // Bar
-            if (this._valueBarImage) {
+        this._prepareRenderingData("rectangle");
+        const thumbPosition = this._getThumbPosition();
+        var left = this._renderLeft;
+        var top = this._renderTop;
+        var width = this._renderWidth;
+        var height = this._renderHeight;
+
+        // Background
+        if (this._backgroundImage) {
+            this._tempMeasure.copyFromFloats(left, top, width, height);
+            if (this.isThumbClamped && this.displayThumb) {
                 if (this.isVertical) {
-                    if (this.isThumbClamped && this.displayThumb) {
-                        this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
-                    } else {
-                        this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition);
-                    }
+                    this._tempMeasure.height += this._effectiveThumbThickness;
                 } else {
-                    if (this.isThumbClamped && this.displayThumb) {
-                        this._tempMeasure.copyFromFloats(left, top, thumbPosition + this._effectiveThumbThickness / 2, height);
-                    }
-                    else {
-                        this._tempMeasure.copyFromFloats(left, top, thumbPosition, height);
-                    }
+                    this._tempMeasure.width += this._effectiveThumbThickness;
                 }
-                this._valueBarImage._draw(this._tempMeasure, context);
             }
+            this._backgroundImage._currentMeasure.copyFrom(this._tempMeasure);
+            this._backgroundImage._draw(context);
+        }
 
-            // Thumb
-            if (this.displayThumb) {
-                if (this.isVertical) {
-                    this._tempMeasure.copyFromFloats(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
+        // Bar
+        if (this._valueBarImage) {
+            if (this.isVertical) {
+                if (this.isThumbClamped && this.displayThumb) {
+                    this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
                 } else {
-                    this._tempMeasure.copyFromFloats(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, this._effectiveThumbThickness, this._currentMeasure.height);
+                    this._tempMeasure.copyFromFloats(left, top + thumbPosition, width, height - thumbPosition);
                 }
-                this._thumbImage._draw(this._tempMeasure, context);
+            } else {
+                if (this.isThumbClamped && this.displayThumb) {
+                    this._tempMeasure.copyFromFloats(left, top, thumbPosition + this._effectiveThumbThickness / 2, height);
+                }
+                else {
+                    this._tempMeasure.copyFromFloats(left, top, thumbPosition, height);
+                }
+            }
+            this._valueBarImage._currentMeasure.copyFrom(this._tempMeasure);
+            this._valueBarImage._draw(context);
+        }
+
+        // Thumb
+        if (this.displayThumb) {
+            if (this.isVertical) {
+                this._tempMeasure.copyFromFloats(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
+            } else {
+                this._tempMeasure.copyFromFloats(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, this._effectiveThumbThickness, this._currentMeasure.height);
             }
+
+            this._thumbImage._currentMeasure.copyFrom(this._tempMeasure);
+            this._thumbImage._draw(context);
         }
 
         context.restore();

+ 155 - 0
gui/src/2D/controls/sliders/scrollBar.ts

@@ -0,0 +1,155 @@
+import { BaseSlider } from "./baseSlider";
+import { Control } from "..";
+import { Vector2 } from "babylonjs";
+import { Measure } from "../../measure";
+
+/**
+ * Class used to create slider controls
+ */
+export class ScrollBar extends BaseSlider {
+    private _background = "black";
+    private _borderColor = "white";
+    private _thumbMeasure = new Measure(0, 0, 0, 0);
+
+    /** Gets or sets border color */
+    public get borderColor(): string {
+        return this._borderColor;
+    }
+
+    public set borderColor(value: string) {
+        if (this._borderColor === value) {
+            return;
+        }
+
+        this._borderColor = value;
+        this._markAsDirty();
+    }
+
+    /** Gets or sets background color */
+    public get background(): string {
+        return this._background;
+    }
+
+    public set background(value: string) {
+        if (this._background === value) {
+            return;
+        }
+
+        this._background = value;
+        this._markAsDirty();
+    }
+
+    /**
+     * Creates a new Slider
+     * @param name defines the control name
+     */
+    constructor(public name?: string) {
+        super(name);
+    }
+
+    protected _getTypeName(): string {
+        return "Scrollbar";
+    }
+
+    protected _getThumbThickness(): number {
+        var thumbThickness = 0;
+        if (this._thumbWidth.isPixel) {
+            thumbThickness = this._thumbWidth.getValue(this._host);
+        }
+        else {
+            thumbThickness = this._backgroundBoxThickness * this._thumbWidth.getValue(this._host);
+        }
+        return thumbThickness;
+    }
+
+    public _draw(context: CanvasRenderingContext2D): void {
+        context.save();
+
+        this._applyStates(context);
+        this._prepareRenderingData("rectangle");
+        var left = this._renderLeft;
+        var top = this._renderTop;
+        var width = this._renderWidth;
+        var height = this._renderHeight;
+
+        const thumbPosition = this._getThumbPosition();
+        context.fillStyle = this._background;
+
+        if (this.isVertical) {
+            context.fillRect(left, top, width, height + this._effectiveThumbThickness);
+        }
+        else {
+            context.fillRect(left, top, width + this._effectiveThumbThickness, height);
+        }
+
+        // Value bar
+        context.fillStyle = this.color;
+
+        // Thumb
+        if (this.isVertical) {
+            this._thumbMeasure.left = left - this._effectiveBarOffset;
+            this._thumbMeasure.top = this._currentMeasure.top + thumbPosition;
+            this._thumbMeasure.width = this._currentMeasure.width;
+            this._thumbMeasure.height = this._effectiveThumbThickness;
+        }
+        else {
+            this._thumbMeasure.left = this._currentMeasure.left + thumbPosition;
+            this._thumbMeasure.top = this._currentMeasure.top;
+            this._thumbMeasure.width = this._effectiveThumbThickness;
+            this._thumbMeasure.height = this._currentMeasure.height;
+        }
+
+        context.fillRect(this._thumbMeasure.left, this._thumbMeasure.top, this._thumbMeasure.width , this._thumbMeasure.height);
+
+        context.restore();
+    }
+
+    private _first: boolean;
+    private _originX: number;
+    private _originY: number;
+
+    /** @hidden */
+    protected _updateValueFromPointer(x: number, y: number): void {
+        if (this.rotation != 0) {
+            this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
+            x = this._transformedPosition.x;
+            y = this._transformedPosition.y;
+        }
+
+        if (this._first) {
+            this._first = false;
+            this._originX = x;
+            this._originY = y;
+
+            // Check if move is required
+            if (x < this._thumbMeasure.left || x > this._thumbMeasure.left + this._thumbMeasure.width || y < this._thumbMeasure.top || y > this._thumbMeasure.top + this._thumbMeasure.height) {
+                if (this.isVertical) {
+                    this.value = this.minimum + (1 - ((y - this._currentMeasure.top) / this._currentMeasure.height)) * (this.maximum - this.minimum);
+                }
+                else {
+                    this.value = this.minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this.maximum - this.minimum);
+                }
+            }
+        }
+
+        // Delta mode
+        let delta = 0;
+        if (this.isVertical) {
+            delta = -((y - this._originY) / (this._currentMeasure.height - this._effectiveThumbThickness));
+        }
+        else {
+            delta = (x - this._originX) / (this._currentMeasure.width - this._effectiveThumbThickness);
+        }
+
+        this.value += delta * (this.maximum - this.minimum);
+
+        this._originX = x;
+        this._originY = y;
+    }
+
+    public _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean {
+        this._first = true;
+
+        return super._onPointerDown(target, coordinates, pointerId, buttonIndex);
+    }
+}

+ 240 - 0
gui/src/2D/controls/sliders/slider.ts

@@ -0,0 +1,240 @@
+import { BaseSlider } from "./baseSlider";
+
+/**
+ * Class used to create slider controls
+ */
+export class Slider extends BaseSlider {
+    private _background = "black";
+    private _borderColor = "white";
+    private _isThumbCircle = false;
+    protected _displayValueBar = true;
+
+    /** Gets or sets a boolean indicating if the value bar must be rendered */
+    public get displayValueBar(): boolean {
+        return this._displayValueBar;
+    }
+
+    public set displayValueBar(value: boolean) {
+        if (this._displayValueBar === value) {
+            return;
+        }
+
+        this._displayValueBar = value;
+        this._markAsDirty();
+    }
+
+    /** Gets or sets border color */
+    public get borderColor(): string {
+        return this._borderColor;
+    }
+
+    public set borderColor(value: string) {
+        if (this._borderColor === value) {
+            return;
+        }
+
+        this._borderColor = value;
+        this._markAsDirty();
+    }
+
+    /** Gets or sets background color */
+    public get background(): string {
+        return this._background;
+    }
+
+    public set background(value: string) {
+        if (this._background === value) {
+            return;
+        }
+
+        this._background = value;
+        this._markAsDirty();
+    }
+
+    /** Gets or sets a boolean indicating if the thumb should be round or square */
+    public get isThumbCircle(): boolean {
+        return this._isThumbCircle;
+    }
+
+    public set isThumbCircle(value: boolean) {
+        if (this._isThumbCircle === value) {
+            return;
+        }
+
+        this._isThumbCircle = value;
+        this._markAsDirty();
+    }
+
+    /**
+     * Creates a new Slider
+     * @param name defines the control name
+     */
+    constructor(public name?: string) {
+        super(name);
+    }
+
+    protected _getTypeName(): string {
+        return "Slider";
+    }
+
+    public _draw(context: CanvasRenderingContext2D): void {
+        context.save();
+
+        this._applyStates(context);
+        this._prepareRenderingData(this.isThumbCircle ? "circle" : "rectangle");
+        var left = this._renderLeft;
+        var top = this._renderTop;
+        var width = this._renderWidth;
+        var height = this._renderHeight;
+
+        var radius = 0;
+
+        if (this.isThumbClamped && this.isThumbCircle) {
+            if (this.isVertical) {
+                top += (this._effectiveThumbThickness / 2);
+            }
+            else {
+                left += (this._effectiveThumbThickness / 2);
+            }
+
+            radius = this._backgroundBoxThickness / 2;
+        }
+        else {
+            radius = (this._effectiveThumbThickness - this._effectiveBarOffset) / 2;
+        }
+
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowColor = this.shadowColor;
+            context.shadowBlur = this.shadowBlur;
+            context.shadowOffsetX = this.shadowOffsetX;
+            context.shadowOffsetY = this.shadowOffsetY;
+        }
+
+        const thumbPosition = this._getThumbPosition();
+        context.fillStyle = this._background;
+
+        if (this.isVertical) {
+            if (this.isThumbClamped) {
+                if (this.isThumbCircle) {
+                    context.beginPath();
+                    context.arc(left + this._backgroundBoxThickness / 2, top, radius, Math.PI, 2 * Math.PI);
+                    context.fill();
+                    context.fillRect(left, top, width, height);
+                }
+                else {
+                    context.fillRect(left, top, width, height + this._effectiveThumbThickness);
+                }
+            }
+            else {
+                context.fillRect(left, top, width, height);
+            }
+        }
+        else {
+            if (this.isThumbClamped) {
+                if (this.isThumbCircle) {
+                    context.beginPath();
+                    context.arc(left + this._backgroundBoxLength, top + (this._backgroundBoxThickness / 2), radius, 0, 2 * Math.PI);
+                    context.fill();
+                    context.fillRect(left, top, width, height);
+                }
+                else {
+                    context.fillRect(left, top, width + this._effectiveThumbThickness, height);
+                }
+            }
+            else {
+                context.fillRect(left, top, width, height);
+            }
+        }
+
+        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+            context.shadowBlur = 0;
+            context.shadowOffsetX = 0;
+            context.shadowOffsetY = 0;
+        }
+
+        // Value bar
+        context.fillStyle = this.color;
+        if (this._displayValueBar) {
+            if (this.isVertical) {
+                if (this.isThumbClamped) {
+                    if (this.isThumbCircle) {
+                        context.beginPath();
+                        context.arc(left + this._backgroundBoxThickness / 2, top + this._backgroundBoxLength, radius, 0, 2 * Math.PI);
+                        context.fill();
+                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+                    }
+                    else {
+                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
+                    }
+                }
+                else {
+                    context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+                }
+            }
+            else {
+                if (this.isThumbClamped) {
+                    if (this.isThumbCircle) {
+                        context.beginPath();
+                        context.arc(left, top + this._backgroundBoxThickness / 2, radius, 0, 2 * Math.PI);
+                        context.fill();
+                        context.fillRect(left, top, thumbPosition, height);
+                    }
+                    else {
+                        context.fillRect(left, top, thumbPosition, height);
+                    }
+                }
+                else {
+                    context.fillRect(left, top, thumbPosition, height);
+                }
+            }
+        }
+
+        // Thumb
+        if (this.displayThumb) {
+            if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                context.shadowColor = this.shadowColor;
+                context.shadowBlur = this.shadowBlur;
+                context.shadowOffsetX = this.shadowOffsetX;
+                context.shadowOffsetY = this.shadowOffsetY;
+            }
+            if (this._isThumbCircle) {
+                context.beginPath();
+                if (this.isVertical) {
+                    context.arc(left + this._backgroundBoxThickness / 2, top + thumbPosition, radius, 0, 2 * Math.PI);
+                }
+                else {
+                    context.arc(left + thumbPosition, top + (this._backgroundBoxThickness / 2), radius, 0, 2 * Math.PI);
+                }
+                context.fill();
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
+                context.strokeStyle = this._borderColor;
+                context.stroke();
+            }
+            else {
+                if (this.isVertical) {
+                    context.fillRect(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
+                }
+                else {
+                    context.fillRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, this._effectiveThumbThickness, this._currentMeasure.height);
+                }
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
+                context.strokeStyle = this._borderColor;
+                if (this.isVertical) {
+                    context.strokeRect(left - this._effectiveBarOffset, this._currentMeasure.top + thumbPosition, this._currentMeasure.width, this._effectiveThumbThickness);
+                }
+                else {
+                    context.strokeRect(this._currentMeasure.left + thumbPosition, this._currentMeasure.top, this._effectiveThumbThickness, this._currentMeasure.height);
+                }
+            }
+        }
+        context.restore();
+    }
+}

+ 41 - 22
gui/src/2D/controls/stackPanel.ts

@@ -10,7 +10,6 @@ export class StackPanel extends Container {
     private _manualWidth = false;
     private _manualHeight = false;
     private _doNotTrackManualChanges = false;
-    private _tempMeasureStore = Measure.Empty();
 
     /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
     public get isVertical(): boolean {
@@ -82,13 +81,41 @@ export class StackPanel extends Container {
         return "StackPanel";
     }
 
+    /** @hidden */
     protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        for (var child of this._children) {
+            if (this._isVertical) {
+                child.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
+            } else {
+                child.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
+            }
+        }
+
+        super._preMeasure(parentMeasure, context);
+    }
+
+    protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        super._additionalProcessing(parentMeasure, context);
+
+        this._measureForChildren.copyFrom(parentMeasure);
+
+        this._measureForChildren.left = this._currentMeasure.left;
+        this._measureForChildren.top = this._currentMeasure.top;
+
+        if (this.isVertical || this._manualWidth) {
+            this._measureForChildren.width = this._currentMeasure.width;
+        } else if (!this.isVertical || this._manualHeight) {
+            this._measureForChildren.height = this._currentMeasure.height;
+        }
+    }
+
+    protected _postMeasure(): void {
         var stackWidth = 0;
         var stackHeight = 0;
         for (var child of this._children) {
-            this._tempMeasureStore.copyFrom(child._currentMeasure);
-            child._currentMeasure.copyFrom(parentMeasure);
-            child._measure();
+            if (!child.isVisible || child.notRenderable) {
+                continue;
+            }
 
             if (this._isVertical) {
                 child.top = stackHeight + "px";
@@ -96,25 +123,21 @@ export class StackPanel extends Container {
                     child._markAsDirty();
                 }
                 child._top.ignoreAdaptiveScaling = true;
-                stackHeight += child._currentMeasure.height;
+                stackHeight += child._currentMeasure.height + child.paddingTopInPixels;
                 if (child._currentMeasure.width > stackWidth) {
                     stackWidth = child._currentMeasure.width;
                 }
-                child.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
             } else {
                 child.left = stackWidth + "px";
                 if (!child._left.ignoreAdaptiveScaling) {
                     child._markAsDirty();
                 }
                 child._left.ignoreAdaptiveScaling = true;
-                stackWidth += child._currentMeasure.width;
+                stackWidth += child._currentMeasure.width + child.paddingLeftInPixels;
                 if (child._currentMeasure.height > stackHeight) {
                     stackHeight = child._currentMeasure.height;
                 }
-                child.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
             }
-
-            child._currentMeasure.copyFrom(this._tempMeasureStore);
         }
 
         this._doNotTrackManualChanges = true;
@@ -125,21 +148,17 @@ export class StackPanel extends Container {
         let panelWidthChanged = false;
         let panelHeightChanged = false;
 
-        let previousHeight = this.height;
-        let previousWidth = this.width;
-
-        if (!this._manualHeight) {
-            // do not specify height if strictly defined by user
+        if (!this._manualHeight) { // do not specify height if strictly defined by user
+            let previousHeight = this.height;
             this.height = stackHeight + "px";
+            panelHeightChanged = previousHeight !== this.height || !this._height.ignoreAdaptiveScaling;
         }
-        if (!this._manualWidth) {
-            // do not specify width if strictly defined by user
+        if (!this._manualWidth) { // do not specify width if strictly defined by user
+            let previousWidth = this.width;
             this.width = stackWidth + "px";
+            panelWidthChanged = previousWidth !== this.width || !this._width.ignoreAdaptiveScaling;
         }
 
-        panelWidthChanged = previousWidth !== this.width || !this._width.ignoreAdaptiveScaling;
-        panelHeightChanged = previousHeight !== this.height || !this._height.ignoreAdaptiveScaling;
-
         if (panelHeightChanged) {
             this._height.ignoreAdaptiveScaling = true;
         }
@@ -151,9 +170,9 @@ export class StackPanel extends Container {
         this._doNotTrackManualChanges = false;
 
         if (panelWidthChanged || panelHeightChanged) {
-            this._markAllAsDirty();
+            this._rebuildLayout = true;
         }
 
-        super._preMeasure(parentMeasure, context);
+        super._postMeasure();
     }
 }

+ 49 - 25
gui/src/2D/controls/textBlock.ts

@@ -65,12 +65,17 @@ export class TextBlock extends Control {
      * Gets or sets an boolean indicating that the TextBlock will be resized to fit container
      */
     public set resizeToFit(value: boolean) {
+        if (this._resizeToFit === value) {
+            return;
+        }
         this._resizeToFit = value;
 
         if (this._resizeToFit) {
             this._width.ignoreAdaptiveScaling = true;
             this._height.ignoreAdaptiveScaling = true;
         }
+
+        this._markAsDirty();
     }
 
     /**
@@ -221,6 +226,44 @@ export class TextBlock extends Control {
         return "TextBlock";
     }
 
+    protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        if (!this._fontOffset) {
+            this._fontOffset = Control._GetFontOffset(context.font);
+        }
+
+        super._processMeasures(parentMeasure, context);
+
+        // Prepare lines
+        this._lines = this._breakLines(this._currentMeasure.width, context);
+        this.onLinesReadyObservable.notifyObservers(this);
+
+        let maxLineWidth: number = 0;
+
+        for (let i = 0; i < this._lines.length; i++) {
+            const line = this._lines[i];
+
+            if (line.width > maxLineWidth) {
+                maxLineWidth = line.width;
+            }
+        }
+
+        if (this._resizeToFit) {
+            if (this._textWrapping === TextWrapping.Clip) {
+                let newWidth = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth;
+                if (newWidth !== this._width.internalValue) {
+                    this._width.updateInPlace(newWidth, ValueAndUnit.UNITMODE_PIXEL);
+                    this._isDirty = true;
+                }
+            }
+            let newHeight = this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * this._lines.length;
+
+            if (newHeight !== this._height.internalValue) {
+                this._height.updateInPlace(newHeight, ValueAndUnit.UNITMODE_PIXEL);
+                this._isDirty = true;
+            }
+        }
+    }
+
     private _drawText(text: string, textWidth: number, y: number, context: CanvasRenderingContext2D): void {
         var width = this._currentMeasure.width;
         var x = 0;
@@ -250,15 +293,14 @@ export class TextBlock extends Control {
     }
 
     /** @hidden */
-    public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+    public _draw(context: CanvasRenderingContext2D): void {
         context.save();
 
         this._applyStates(context);
 
-        if (this._processMeasures(parentMeasure, context)) {
-            // Render lines
-            this._renderLines(context);
-        }
+        // Render lines
+        this._renderLines(context);
+
         context.restore();
     }
 
@@ -270,20 +312,15 @@ export class TextBlock extends Control {
         }
     }
 
-    protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
-        this._lines = this._breakLines(this._currentMeasure.width, context);
-        this.onLinesReadyObservable.notifyObservers(this);
-    }
-
     protected _breakLines(refWidth: number, context: CanvasRenderingContext2D): object[] {
         var lines = [];
         var _lines = this.text.split("\n");
 
-        if (this._textWrapping === TextWrapping.Ellipsis && !this._resizeToFit) {
+        if (this._textWrapping === TextWrapping.Ellipsis) {
             for (var _line of _lines) {
                 lines.push(this._parseLineEllipsis(_line, refWidth, context));
             }
-        } else if (this._textWrapping === TextWrapping.WordWrap && !this._resizeToFit) {
+        } else if (this._textWrapping === TextWrapping.WordWrap) {
             for (var _line of _lines) {
                 lines.push(...this._parseLineWordWrap(_line, refWidth, context));
             }
@@ -342,10 +379,6 @@ export class TextBlock extends Control {
 
     protected _renderLines(context: CanvasRenderingContext2D): void {
         var height = this._currentMeasure.height;
-
-        if (!this._fontOffset) {
-            this._fontOffset = Control._GetFontOffset(context.font);
-        }
         var rootY = 0;
         switch (this._textVerticalAlignment) {
             case Control.VERTICAL_ALIGNMENT_TOP:
@@ -361,8 +394,6 @@ export class TextBlock extends Control {
 
         rootY += this._currentMeasure.top;
 
-        var maxLineWidth: number = 0;
-
         for (let i = 0; i < this._lines.length; i++) {
             const line = this._lines[i];
 
@@ -377,13 +408,6 @@ export class TextBlock extends Control {
 
             this._drawText(line.text, line.width, rootY, context);
             rootY += this._fontOffset.height;
-
-            if (line.width > maxLineWidth) { maxLineWidth = line.width; }
-        }
-
-        if (this._resizeToFit) {
-            this.width = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth + 'px';
-            this.height = this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * this._lines.length + 'px';
         }
     }
 

+ 13 - 0
gui/src/2D/valueAndUnit.ts

@@ -57,6 +57,19 @@ export class ValueAndUnit {
     }
 
     /**
+     * Update the current value and unit. This should be done cautiously as the GUi won't be marked as dirty with this function.
+     * @param value defines the value to store
+     * @param unit defines the unit to store
+     * @returns the current ValueAndUnit
+     */
+    public updateInPlace(value: number, unit = ValueAndUnit.UNITMODE_PIXEL): ValueAndUnit {
+        this._value = value;
+        this.unit = unit;
+
+        return this;
+    }
+
+    /**
      * Gets the value accordingly to its unit
      * @param host  defines the root host
      * @returns the value

+ 9 - 1
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -19,7 +19,7 @@ import { TextBlockPropertyGridComponent } from "./propertyGrids/gui/textBlockPro
 import { TextBlock } from "babylonjs-gui/2D/controls/textBlock";
 import { InputText } from "babylonjs-gui/2D/controls/inputText";
 import { InputTextPropertyGridComponent } from "./propertyGrids/gui/inputTextPropertyGridComponent";
-import { ColorPicker, Image, Slider, ImageBasedSlider, Rectangle, Ellipse, Checkbox, RadioButton, Line } from "babylonjs-gui";
+import { ColorPicker, Image, Slider, ImageBasedSlider, Rectangle, Ellipse, Checkbox, RadioButton, Line, ScrollViewer } from "babylonjs-gui";
 import { ColorPickerPropertyGridComponent } from "./propertyGrids/gui/colorPickerPropertyGridComponent";
 import { AnimationGroupGridComponent } from "./propertyGrids/animationGroupPropertyGridComponent";
 import { LockObject } from "./propertyGrids/lockObject";
@@ -31,6 +31,7 @@ import { EllipsePropertyGridComponent } from "./propertyGrids/gui/ellipsePropert
 import { CheckboxPropertyGridComponent } from "./propertyGrids/gui/checkboxPropertyGridComponent";
 import { RadioButtonPropertyGridComponent } from "./propertyGrids/gui/radioButtonPropertyGridComponent";
 import { LinePropertyGridComponent } from "./propertyGrids/gui/linePropertyGridComponent";
+import { ScrollViewerPropertyGridComponent } from "./propertyGrids/gui/scrollViewerPropertyGridComponent";
 
 export class PropertyGridTabComponent extends PaneComponent {
     private _timerIntervalId: number;
@@ -214,6 +215,13 @@ export class PropertyGridTabComponent extends PaneComponent {
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className === "ScrollViewer") {
+                const scrollViewer = entity as ScrollViewer;
+                return (<ScrollViewerPropertyGridComponent scrollViewer={scrollViewer}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }            
+
             if (className === "Ellipse") {
                 const ellipse = entity as Ellipse;
                 return (<EllipsePropertyGridComponent ellipse={ellipse}

+ 1 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx

@@ -42,7 +42,7 @@ export class CommonControlPropertyGridComponent extends React.Component<ICommonC
                     <TextLineComponent label="Class" value={control.getClassName()} />
                     <SliderLineComponent label="Alpha" target={control} propertyName="alpha" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
-                        control.color &&
+                        (control as any).color !== undefined &&
                         <TextInputLineComponent lockObject={this.props.lockObject} label="Color" target={control} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     }
                     {

+ 42 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/scrollViewerPropertyGridComponent.tsx

@@ -0,0 +1,42 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { ScrollViewer } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface IScrollViewerPropertyGridComponentProps {
+    scrollViewer: ScrollViewer,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ScrollViewerPropertyGridComponent extends React.Component<IScrollViewerPropertyGridComponentProps> {
+    constructor(props: IScrollViewerPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const scrollViewer = this.props.scrollViewer;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={scrollViewer} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="RECTANGLE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={scrollViewer} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Corner radius" target={scrollViewer} propertyName="cornerRadius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent title="SCROLLVIEWER">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Bar size" target={scrollViewer} propertyName="barSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar color" target={scrollViewer} propertyName="barColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar border color" target={scrollViewer} propertyName="barBorderColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar background" target={scrollViewer} propertyName="barBackground" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Wheel precision" target={scrollViewer} propertyName="wheelPrecision" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 1 - 0
inspector/src/components/actionTabs/tabs/toolsTabComponent.tsx

@@ -100,6 +100,7 @@ export class ToolsTabComponent extends PaneComponent {
             })
             .catch((error: any) => {
                 console.error(error);
+                alert(error);
             });
     }
 

+ 5 - 0
inspector/src/components/sceneExplorer/entities/gui/advancedDynamicTextureTreeItemComponent.tsx

@@ -44,6 +44,11 @@ export class AdvancedDynamicTextureTreeItemComponent extends React.Component<IAd
                 if (!this.props.onSelectionChangedObservable) {
                     return;
                 }
+
+                if (control.getClassName() === "ScrollViewerWindow") {
+                    control = control.getAscendantOfClass("ScrollViewer")!;
+                }
+
                 this.props.onSelectionChangedObservable.notifyObservers(control);
             });
         }

+ 23 - 18
inspector/src/inspector.ts

@@ -88,7 +88,6 @@ export class Inspector {
             }
 
             if (!options.overlay) {
-                this._SceneExplorerHost.style.gridColumn = "1";
                 this._SceneExplorerHost.style.position = "relative";
             }
         }
@@ -146,7 +145,6 @@ export class Inspector {
             this._ActionTabsHost = host;
 
             if (!options.overlay) {
-                this._ActionTabsHost.style.gridColumn = "3";
                 this._ActionTabsHost.style.position = "relative";
             }
         }
@@ -201,7 +199,6 @@ export class Inspector {
             this._EmbedHost = host;
 
             if (!options.overlay) {
-                this._EmbedHost.style.gridColumn = "2";
                 this._EmbedHost.style.position = "relative";
             }
         }
@@ -210,9 +207,9 @@ export class Inspector {
             this._OpenedPane++;
             const embedHostElement = React.createElement(EmbedHostComponent, {
                 globalState: this._GlobalState, scene: scene,
-                    noExpand: !options.enablePopup,
-                    noClose: !options.enableClose,
-                    popupMode: options.popup, onPopup: () => {
+                noExpand: !options.enablePopup,
+                noClose: !options.enableClose,
+                popupMode: options.popup, onPopup: () => {
                     ReactDOM.unmountComponentAtNode(this._EmbedHost!);
 
                     if (options.popup) {
@@ -342,11 +339,7 @@ export class Inspector {
                 let parentControl = (options.globalRoot ? options.globalRoot : canvas!.parentElement) as HTMLElement;
 
                 if (!options.overlay && !this._NewCanvasContainer) {
-
                     this._CreateCanvasContainer(parentControl);
-                    parentControl.style.gridTemplateColumns = "1fr auto";
-                    this._NewCanvasContainer!.style.gridColumn = "1";
-
                 } else if (!options.overlay && this._NewCanvasContainer && this._NewCanvasContainer.parentElement) {
                     // the root is now the parent of the canvas container
                     parentControl = this._NewCanvasContainer.parentElement;
@@ -381,9 +374,7 @@ export class Inspector {
             let parentControl = (options.globalRoot ? options.globalRoot : canvas!.parentElement) as HTMLElement;
 
             if (!options.overlay && !this._NewCanvasContainer) {
-
                 this._CreateCanvasContainer(parentControl);
-
             } else if (!options.overlay && this._NewCanvasContainer && this._NewCanvasContainer.parentElement) {
                 // the root is now the parent of the canvas container
                 parentControl = this._NewCanvasContainer.parentElement;
@@ -410,11 +401,9 @@ export class Inspector {
 
     private static _CreateCanvasContainer(parentControl: HTMLElement) {
         // Create a container for previous elements
-        parentControl.style.display = "grid";
-        parentControl.style.gridTemplateColumns = "auto 1fr auto";
-        parentControl.style.gridTemplateRows = "100%";
-
         this._NewCanvasContainer = parentControl.ownerDocument!.createElement("div");
+        this._NewCanvasContainer.style.display = parentControl.style.display;
+        parentControl.style.display = "flex";
 
         while (parentControl.childElementCount > 0) {
             var child = parentControl.childNodes[0];
@@ -424,13 +413,29 @@ export class Inspector {
 
         parentControl.appendChild(this._NewCanvasContainer);
 
-        this._NewCanvasContainer.style.gridRow = "1";
-        this._NewCanvasContainer.style.gridColumn = "2";
         this._NewCanvasContainer.style.width = "100%";
         this._NewCanvasContainer.style.height = "100%";
     }
 
+    private static _DestroyCanvasContainer() {
+        const parentControl = this._NewCanvasContainer.parentElement!;
+
+        while (this._NewCanvasContainer.childElementCount > 0) {
+            const child = this._NewCanvasContainer.childNodes[0];
+            this._NewCanvasContainer.removeChild(child);
+            parentControl.appendChild(child);
+        }
+
+        parentControl.removeChild(this._NewCanvasContainer);
+        parentControl.style.display = this._NewCanvasContainer.style.display;
+        delete this._NewCanvasContainer;
+    }
+
     private static _Cleanup() {
+        if (this._NewCanvasContainer) {
+            this._DestroyCanvasContainer();
+        }
+
         if (Inspector._OpenedPane === 0 && this._OnBeforeRenderObserver && this._Scene) {
             this._Scene.onBeforeRenderObservable.remove(this._OnBeforeRenderObserver);
             this._OnBeforeRenderObserver = null;

+ 24 - 18
src/Behaviors/Mesh/babylon.sixDofDragBehavior.ts

@@ -16,7 +16,11 @@ module BABYLON {
         /**
          * How much faster the object should move when the controller is moving towards it. This is useful to bring objects that are far away from the user to them faster. Set this to 0 to avoid any speed increase. (Default: 3)
          */
-         private zDragFactor = 3;
+        private zDragFactor = 3;
+        /**
+         * If the object should rotate to face the drag origin
+         */
+        public rotateDraggedObject = true;
         /**
          * If the behavior is currently in a dragging state
          */
@@ -90,7 +94,7 @@ module BABYLON {
                 if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
                     if (!this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(pointerInfo.pickInfo.pickedMesh)) {
                         if (this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.globalPosition);
                         }
 
                         pickedMesh = this._ownerNode;
@@ -149,7 +153,7 @@ module BABYLON {
                     if (this.currentDraggingPointerID == (<PointerEvent>pointerInfo.event).pointerId && this.dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray && pickedMesh) {
                         var zDragFactor = this.zDragFactor;
                         if (this._scene.activeCamera && this._scene.activeCamera.cameraRigMode == Camera.RIG_MODE_NONE) {
-                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.position);
+                            pointerInfo.pickInfo.ray.origin.copyFrom(this._scene.activeCamera!.globalPosition);
                             zDragFactor = 0;
                         }
 
@@ -192,21 +196,23 @@ module BABYLON {
                     // Slowly move mesh to avoid jitter
                     pickedMesh.position.addInPlace(this._targetPosition.subtract(pickedMesh.position).scale(this.dragDeltaRatio));
 
-                    // Get change in rotation
-                    tmpQuaternion.copyFrom(this._startingOrientation);
-                    tmpQuaternion.x = -tmpQuaternion.x;
-                    tmpQuaternion.y = -tmpQuaternion.y;
-                    tmpQuaternion.z = -tmpQuaternion.z;
-                    this._virtualDragMesh.rotationQuaternion!.multiplyToRef(tmpQuaternion, tmpQuaternion);
-                    // Convert change in rotation to only y axis rotation
-                    Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
-                    tmpQuaternion.multiplyToRef(this._startingOrientation, tmpQuaternion);
-                    // Slowly move mesh to avoid jitter
-                    var oldParent = pickedMesh.parent;
-                    pickedMesh.setParent(null);
-                    Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
-                    pickedMesh.setParent(oldParent);
-                    BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    if (this.rotateDraggedObject) {
+                        // Get change in rotation
+                        tmpQuaternion.copyFrom(this._startingOrientation);
+                        tmpQuaternion.x = -tmpQuaternion.x;
+                        tmpQuaternion.y = -tmpQuaternion.y;
+                        tmpQuaternion.z = -tmpQuaternion.z;
+                        this._virtualDragMesh.rotationQuaternion!.multiplyToRef(tmpQuaternion, tmpQuaternion);
+                        // Convert change in rotation to only y axis rotation
+                        Quaternion.RotationYawPitchRollToRef(tmpQuaternion.toEulerAngles("xyz").y, 0, 0, tmpQuaternion);
+                        tmpQuaternion.multiplyToRef(this._startingOrientation, tmpQuaternion);
+                        // Slowly move mesh to avoid jitter
+                        var oldParent = pickedMesh.parent;
+                        pickedMesh.setParent(null);
+                        Quaternion.SlerpToRef(pickedMesh.rotationQuaternion!, tmpQuaternion, this.dragDeltaRatio, pickedMesh.rotationQuaternion!);
+                        pickedMesh.setParent(oldParent);
+                        BoundingBoxGizmo._RestorePivotPoint(pickedMesh);
+                    }
                 }
             });
         }

+ 22 - 0
src/Cameras/babylon.arcRotateCamera.ts

@@ -544,6 +544,8 @@ module BABYLON {
         protected _targetBoundingCenter: Nullable<Vector3>;
 
         private _computationVector: Vector3 = Vector3.Zero();
+        private _tempAxisVector: Vector3;
+        private _tempAxisRotationMatrix: Matrix;
 
         /**
          * Instantiates a new ArcRotateCamera in a given scene
@@ -907,6 +909,26 @@ module BABYLON {
 
             var target = this._getTargetPosition();
             this._computationVector.copyFromFloats(this.radius * cosa * sinb, this.radius * cosb, this.radius * sina * sinb);
+
+            // Rotate according to up vector
+            if (this.upVector.x !== 0 || this.upVector.y !== 1.0 || this.upVector.z !== 0) {
+
+                if (!this._tempAxisVector) {
+                    this._tempAxisVector = new Vector3();
+                    this._tempAxisRotationMatrix = new Matrix();
+                }
+
+                Vector3.CrossToRef(Vector3.Up(), this.upVector, this._tempAxisVector);
+                this._tempAxisVector.normalize();
+
+                let angle = Math.acos(Vector3.Dot(Vector3.UpReadOnly, this.upVector));
+
+                Matrix.RotationAxisToRef(this._tempAxisVector, angle, this._tempAxisRotationMatrix);
+
+                this._tempAxisVector.copyFrom(this._computationVector);
+                Vector3.TransformCoordinatesToRef(this._tempAxisVector, this._tempAxisRotationMatrix, this._computationVector);
+            }
+
             target.addToRef(this._computationVector, this._newPosition);
             if (this.getScene().collisionsEnabled && this.checkCollisions) {
                 if (!this._collider) {

+ 20 - 0
src/Math/babylon.math.ts

@@ -1540,6 +1540,8 @@ module BABYLON {
      * Reminder: Babylon.js uses a left handed forward facing system
      */
     export class Vector3 {
+        private static _UpReadOnly = Vector3.Up() as DeepImmutable<Vector3>;
+
         /**
          * Creates a new Vector3 object from the given x, y, z (floats) coordinates.
          * @param x defines the first coordinates (on X axis)
@@ -2224,6 +2226,14 @@ module BABYLON {
         public static Up(): Vector3 {
             return new Vector3(0.0, 1.0, 0.0);
         }
+
+        /**
+         * Gets a up Vector3 that must not be updated
+         */
+        public static get UpReadOnly(): DeepImmutable<Vector3> {
+            return Vector3._UpReadOnly;
+        }
+
         /**
          * Returns a new Vector3 set to (0.0, -1.0, 0.0)
          * @returns a new down Vector3
@@ -3430,6 +3440,16 @@ module BABYLON {
             result.z = (x * m[2]) + (y * m[6]) + (z * m[10]);
             result.w = w;
         }
+
+        /**
+         * Creates a new Vector4 from a Vector3
+         * @param source defines the source data
+         * @param w defines the 4th component (default is 0)
+         * @returns a new Vector4
+         */
+        public static FromVector3(source: Vector3, w: number = 0) {
+            return new Vector4(source.x, source.y, source.z, w);
+        }
     }
 
     /**

+ 38 - 4
src/Physics/Plugins/babylon.ammoJSPlugin.ts

@@ -21,6 +21,7 @@ module BABYLON {
         public name: string = "AmmoJSPlugin";
 
         private _timeStep: number = 1 / 60;
+        private _fixedTimeStep: number = 1 / 60;
         private _maxSteps = 5;
         private _tmpQuaternion = new BABYLON.Quaternion();
         private _tmpAmmoTransform: any;
@@ -35,6 +36,7 @@ module BABYLON {
         private _tmpAmmoVectorC: any;
         private _tmpContactCallbackResult = false;
 
+        private static readonly DISABLE_COLLISION_FLAG = 4;
         private static readonly KINEMATIC_FLAG = 2;
         private static readonly DISABLE_DEACTIVATION_FLAG = 4;
 
@@ -88,6 +90,22 @@ module BABYLON {
         }
 
         /**
+         * Increment to step forward in the physics engine (If timeStep is set to 1/60 and fixedTimeStep is set to 1/120 the physics engine should run 2 steps per frame) (Default: 1/60)
+         * @param fixedTimeStep fixedTimeStep to use in seconds
+         */
+        public setFixedTimeStep(fixedTimeStep: number) {
+            this._fixedTimeStep = fixedTimeStep;
+        }
+
+        /**
+         * Sets the maximum number of steps by the physics engine per frame (Default: 5)
+         * @param maxSteps the maximum number of steps by the physics engine per frame
+         */
+        public setMaxSteps(maxSteps: number) {
+            this._maxSteps = maxSteps;
+        }
+
+        /**
          * Gets the current timestep (only used if useDeltaForWorldStep is false in the constructor)
          * @returns the current timestep in seconds
          */
@@ -146,7 +164,7 @@ module BABYLON {
                 impostor.beforeStep();
             }
 
-            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps);
+            this._stepSimulation(this._useDeltaForWorldStep ? delta : this._timeStep, this._maxSteps, this._fixedTimeStep);
 
             for (var mainImpostor of impostors) {
                 // After physics update make babylon world objects match physics world objects
@@ -240,6 +258,11 @@ module BABYLON {
                     body.setActivationState(AmmoJSPlugin.DISABLE_DEACTIVATION_FLAG);
                 }
 
+                // Disable collision if NoImpostor, but keep collision if shape is btCompoundShape
+                if (impostor.type == BABYLON.PhysicsImpostor.NoImpostor && !colShape.getChildShape) {
+                    body.setCollisionFlags(body.getCollisionFlags() | AmmoJSPlugin.DISABLE_COLLISION_FLAG);
+                }
+
                 body.setRestitution(impostor.getParam("restitution"));
                 this.world.addRigidBody(body);
                 impostor.physicsBody = body;
@@ -300,7 +323,9 @@ module BABYLON {
          * @param impostorJoint the imposter joint to remove the joint from
          */
         public removeJoint(impostorJoint: PhysicsImpostorJoint) {
-            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            if (this.world) {
+                this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            }
         }
 
         // adds all verticies (including child verticies) to the triangle mesh
@@ -362,7 +387,13 @@ module BABYLON {
                         var childImpostor = childMesh.getPhysicsImpostor();
                         if (childImpostor) {
                             var shape = this._createShape(childImpostor);
-                            this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x, childMesh.position.y, childMesh.position.z);
+
+                            // Position needs to be scaled based on parent's scaling
+                            var parentMat = childMesh.parent!.getWorldMatrix().clone();
+                            var s = new BABYLON.Vector3();
+                            parentMat.decompose(s);
+                            this._tmpAmmoTransform.getOrigin().setValue(childMesh.position.x * s.x, childMesh.position.y * s.y, childMesh.position.z * s.z);
+
                             this._tmpAmmoQuaternion.setValue(childMesh.rotationQuaternion!.x, childMesh.rotationQuaternion!.y, childMesh.rotationQuaternion!.z, childMesh.rotationQuaternion!.w);
                             this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
                             returnValue.addChildShape(this._tmpAmmoTransform, shape);
@@ -374,7 +405,6 @@ module BABYLON {
                     var shape = this._createShape(impostor, true);
                     if (shape) {
                         this._tmpAmmoTransform.getOrigin().setValue(0, 0, 0);
-                        //this._tmpAmmoQuaternion = new this.BJSAMMO.btQuaternion(0,0,0,1);
                         this._tmpAmmoQuaternion.setValue(0, 0, 0, 1);
                         this._tmpAmmoTransform.setRotation(this._tmpAmmoQuaternion);
 
@@ -408,6 +438,10 @@ module BABYLON {
                         returnValue = new Ammo.btBvhTriangleMeshShape(tetraMesh);
                     }
                     break;
+                case PhysicsImpostor.NoImpostor:
+                    // Fill with sphere but collision is disabled on the rigid body in generatePhysicsBody, using an empty shape caused unexpected movement with joints
+                    returnValue = new Ammo.btSphereShape(extendSize.x / 2);
+                    break;
             }
 
             return returnValue;