David Catuhe 8 éve
szülő
commit
75a466bbce

+ 1 - 0
Tools/Gulp/config.json

@@ -1245,6 +1245,7 @@
                 "files": [
                     "../../gui/src/advancedDynamicTexture.ts",
                     "../../gui/src/measure.ts",
+                    "../../gui/src/math2D.ts",
                     "../../gui/src/valueAndUnit.ts",
                     "../../gui/src/controls/control.ts",
                     "../../gui/src/controls/container.ts",

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2790 - 2790
dist/preview release/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 37 - 37
dist/preview release/babylon.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2790 - 2790
dist/preview release/babylon.module.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 38 - 38
dist/preview release/babylon.worker.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 11 - 11
dist/preview release/canvas2D/babylon.canvas2d.min.js


+ 54 - 2
dist/preview release/gui/babylon.gui.d.ts

@@ -8,6 +8,8 @@ declare module BABYLON.GUI {
         private _background;
         private _rootContainer;
         _lastControlOver: Control;
+        _lastControlDown: Control;
+        _shouldBlockPointer: boolean;
         _toDispose: IDisposable;
         background: string;
         constructor(name: string, width: number, height: number, scene: Scene, generateMipMaps?: boolean, samplingMode?: number);
@@ -41,6 +43,27 @@ declare module BABYLON.GUI {
 
 /// <reference path="../../dist/preview release/babylon.d.ts" />
 declare module BABYLON.GUI {
+    class Matrix2D {
+        m: Float32Array;
+        constructor(m00: number, m01: number, m10: number, m11: number, m20: number, m21: number);
+        fromValues(m00: number, m01: number, m10: number, m11: number, m20: number, m21: number): Matrix2D;
+        determinant(): number;
+        invertToRef(result: Matrix2D): Matrix2D;
+        multiplyToRef(other: Matrix2D, result: Matrix2D): Matrix2D;
+        static Identity(): Matrix2D;
+        static TranslationToRef(x: number, y: number, result: Matrix2D): void;
+        static ScalingToRef(x: number, y: number, result: Matrix2D): void;
+        static RotationToRef(angle: number, result: Matrix2D): void;
+        private static _TempPreTranslationMatrix;
+        private static _TempPostTranslationMatrix;
+        private static _TempRotationMatrix;
+        private static _TempScalingMatrix;
+        static ComposeToRef(tx: number, ty: number, angle: number, scaleX: number, scaleY: number, parentMatrix: Matrix2D, result: Matrix2D): void;
+    }
+}
+
+/// <reference path="../../dist/preview release/babylon.d.ts" />
+declare module BABYLON.GUI {
     class ValueAndUnit {
         value: number;
         unit: number;
@@ -88,6 +111,18 @@ declare module BABYLON.GUI {
         private _marginBottom;
         private _left;
         private _top;
+        private _scaleX;
+        private _scaleY;
+        private _rotation;
+        private _transformCenterX;
+        private _transformCenterY;
+        private _transformMatrix;
+        private _invertTransformMatrix;
+        private _isMatrixDirty;
+        private _cachedOffsetX;
+        private _cachedOffsetY;
+        isHitTestVisible: boolean;
+        isPointerBlocker: boolean;
         /**
         * An event triggered when the pointer move over the control.
         * @type {BABYLON.Observable}
@@ -108,6 +143,16 @@ declare module BABYLON.GUI {
         * @type {BABYLON.Observable}
         */
         onPointerUpObservable: Observable<Control>;
+        /**
+        * An event triggered when pointer enters the control
+        * @type {BABYLON.Observable}
+        */
+        onPointerEnterObservable: Observable<Control>;
+        scaleX: number;
+        scaleY: number;
+        rotation: number;
+        transformCenterY: number;
+        transformCenterX: number;
         horizontalAlignment: number;
         verticalAlignment: number;
         width: string;
@@ -126,16 +171,18 @@ declare module BABYLON.GUI {
         constructor(name: string);
         protected _markAsDirty(): void;
         _link(root: Container, host: AdvancedDynamicTexture): void;
-        protected applyStates(context: CanvasRenderingContext2D): void;
+        protected _transform(context: CanvasRenderingContext2D): void;
+        protected _applyStates(context: CanvasRenderingContext2D): void;
         protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         protected _clip(context: CanvasRenderingContext2D): void;
         protected _measure(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        protected _contains(x: number, y: number): boolean;
+        contains(x: number, y: number): boolean;
         _processPicking(x: number, y: number, type: number): boolean;
         protected _onPointerMove(): void;
+        protected _onPointerEnter(): void;
         protected _onPointerOut(): void;
         protected _onPointerDown(): void;
         protected _onPointerUp(): void;
@@ -250,6 +297,11 @@ declare module BABYLON.GUI {
         name: string;
         constructor(name: string);
         _processPicking(x: number, y: number, type: number): boolean;
+        protected _onPointerEnter(): void;
+        protected _onPointerOut(): void;
+        protected _onPointerDown(): void;
+        protected _onPointerUp(): void;
         static CreateImageButton(name: string, text: string, imageUrl: string): Button;
+        static CreateSimpleButton(name: string, text: string): Button;
     }
 }

+ 282 - 14
dist/preview release/gui/babylon.gui.js

@@ -63,7 +63,7 @@ var BABYLON;
                     this.getScene().getEngine().onResizeObservable.remove(this._resizeObserver);
                 }
                 if (this._pointerMoveObserver) {
-                    this.getScene().onPointerObservable.remove(this._pointerMoveObserver);
+                    this.getScene().onPrePointerObservable.remove(this._pointerMoveObserver);
                 }
                 if (this._toDispose) {
                     this._toDispose.dispose();
@@ -120,13 +120,15 @@ var BABYLON;
             AdvancedDynamicTexture.prototype.attach = function () {
                 var _this = this;
                 var scene = this.getScene();
-                this._pointerMoveObserver = scene.onPointerObservable.add(function (pi, state) {
+                this._pointerMoveObserver = scene.onPrePointerObservable.add(function (pi, state) {
                     if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE
                         && pi.type !== BABYLON.PointerEventTypes.POINTERUP
                         && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
                         return;
                     }
+                    _this._shouldBlockPointer = false;
                     _this._doPicking(scene.pointerX, scene.pointerY, pi.type);
+                    pi.skipOnPointerObservable = _this._shouldBlockPointer;
                 });
             };
             // Statics
@@ -209,6 +211,104 @@ var BABYLON;
 (function (BABYLON) {
     var GUI;
     (function (GUI) {
+        var Matrix2D = (function () {
+            function Matrix2D(m00, m01, m10, m11, m20, m21) {
+                this.m = new Float32Array(6);
+                this.fromValues(m00, m01, m10, m11, m20, m21);
+            }
+            Matrix2D.prototype.fromValues = function (m00, m01, m10, m11, m20, m21) {
+                this.m[0] = m00;
+                this.m[1] = m01;
+                this.m[2] = m10;
+                this.m[3] = m11;
+                this.m[4] = m20;
+                this.m[5] = m21;
+                return this;
+            };
+            Matrix2D.prototype.determinant = function () {
+                return this.m[0] * this.m[3] - this.m[1] * this.m[2];
+            };
+            Matrix2D.prototype.invertToRef = function (result) {
+                var l0 = this.m[0];
+                var l1 = this.m[1];
+                var l2 = this.m[2];
+                var l3 = this.m[3];
+                var l4 = this.m[4];
+                var l5 = this.m[5];
+                var det = this.determinant();
+                if (det < (BABYLON.Epsilon * BABYLON.Epsilon)) {
+                    throw new Error("Can't invert matrix, near null determinant");
+                }
+                var detDiv = 1 / det;
+                var det4 = l2 * l5 - l3 * l4;
+                var det5 = l1 * l4 - l0 * l5;
+                result.m[0] = l3 * detDiv;
+                result.m[1] = -l1 * detDiv;
+                result.m[2] = -l2 * detDiv;
+                result.m[3] = l0 * detDiv;
+                result.m[4] = det4 * detDiv;
+                result.m[5] = det5 * detDiv;
+                return this;
+            };
+            Matrix2D.prototype.multiplyToRef = function (other, result) {
+                var l0 = this.m[0];
+                var l1 = this.m[1];
+                var l2 = this.m[2];
+                var l3 = this.m[3];
+                var l4 = this.m[4];
+                var l5 = this.m[5];
+                var r0 = other.m[0];
+                var r1 = other.m[1];
+                var r2 = other.m[2];
+                var r3 = other.m[3];
+                var r4 = other.m[4];
+                var r5 = other.m[5];
+                result.m[0] = l0 * r0 + l1 * r2;
+                result.m[1] = l0 * r1 + l1 * r3;
+                result.m[2] = l2 * r0 + l3 * r2;
+                result.m[3] = l2 * r1 + l3 * r3;
+                result.m[4] = l4 * r0 + l5 * r2 + r4;
+                result.m[5] = l4 * r1 + l5 * r3 + r5;
+                return this;
+            };
+            // Statics
+            Matrix2D.Identity = function () {
+                return new Matrix2D(1, 0, 0, 1, 0, 0);
+            };
+            Matrix2D.TranslationToRef = function (x, y, result) {
+                result.fromValues(1, 0, 0, 1, x, y);
+            };
+            Matrix2D.ScalingToRef = function (x, y, result) {
+                result.fromValues(x, 0, 0, y, 0, 0);
+            };
+            Matrix2D.RotationToRef = function (angle, result) {
+                var s = Math.sin(angle);
+                var c = Math.cos(angle);
+                result.fromValues(c, s, -s, c, 0, 0);
+            };
+            Matrix2D.ComposeToRef = function (tx, ty, angle, scaleX, scaleY, parentMatrix, result) {
+                Matrix2D.TranslationToRef(tx, ty, Matrix2D._TempPreTranslationMatrix);
+                Matrix2D.ScalingToRef(scaleX, scaleY, Matrix2D._TempScalingMatrix);
+                Matrix2D.RotationToRef(angle, Matrix2D._TempRotationMatrix);
+                Matrix2D.TranslationToRef(-tx, -ty, Matrix2D._TempPostTranslationMatrix);
+            };
+            return Matrix2D;
+        }());
+        Matrix2D._TempPreTranslationMatrix = Matrix2D.Identity();
+        Matrix2D._TempPostTranslationMatrix = Matrix2D.Identity();
+        Matrix2D._TempRotationMatrix = Matrix2D.Identity();
+        Matrix2D._TempScalingMatrix = Matrix2D.Identity();
+        GUI.Matrix2D = Matrix2D;
+    })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=math2D.js.map
+
+/// <reference path="../../dist/preview release/babylon.d.ts"/>
+var BABYLON;
+(function (BABYLON) {
+    var GUI;
+    (function (GUI) {
         var ValueAndUnit = (function () {
             function ValueAndUnit(value, unit, negativeValueAllowed) {
                 if (value === void 0) { value = 1; }
@@ -321,6 +421,16 @@ var BABYLON;
                 this._marginBottom = new GUI.ValueAndUnit(0);
                 this._left = new GUI.ValueAndUnit(0);
                 this._top = new GUI.ValueAndUnit(0);
+                this._scaleX = 1.0;
+                this._scaleY = 1.0;
+                this._rotation = 0;
+                this._transformCenterX = 0.5;
+                this._transformCenterY = 0.5;
+                this._transformMatrix = GUI.Matrix2D.Identity();
+                this._invertTransformMatrix = GUI.Matrix2D.Identity();
+                this._isMatrixDirty = true;
+                this.isHitTestVisible = true;
+                this.isPointerBlocker = false;
                 // Properties
                 /**
                 * An event triggered when the pointer move over the control.
@@ -342,8 +452,88 @@ var BABYLON;
                 * @type {BABYLON.Observable}
                 */
                 this.onPointerUpObservable = new BABYLON.Observable();
+                /**
+                * An event triggered when pointer enters the control
+                * @type {BABYLON.Observable}
+                */
+                this.onPointerEnterObservable = new BABYLON.Observable();
                 this.fontFamily = "Arial";
             }
+            Object.defineProperty(Control.prototype, "scaleX", {
+                get: function () {
+                    return this._scaleX;
+                },
+                set: function (value) {
+                    if (this._scaleX === value) {
+                        return;
+                    }
+                    this._scaleX = value;
+                    this._markAsDirty();
+                    this._isMatrixDirty = true;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control.prototype, "scaleY", {
+                get: function () {
+                    return this._scaleY;
+                },
+                set: function (value) {
+                    if (this._scaleY === value) {
+                        return;
+                    }
+                    this._scaleY = value;
+                    this._markAsDirty();
+                    this._isMatrixDirty = true;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control.prototype, "rotation", {
+                get: function () {
+                    return this._rotation;
+                },
+                set: function (value) {
+                    if (this._rotation === value) {
+                        return;
+                    }
+                    this._rotation = value;
+                    this._markAsDirty();
+                    this._isMatrixDirty = true;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control.prototype, "transformCenterY", {
+                get: function () {
+                    return this._transformCenterY;
+                },
+                set: function (value) {
+                    if (this._transformCenterY === value) {
+                        return;
+                    }
+                    this._transformCenterY = value;
+                    this._markAsDirty();
+                    this._isMatrixDirty = true;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control.prototype, "transformCenterX", {
+                get: function () {
+                    return this._transformCenterX;
+                },
+                set: function (value) {
+                    if (this._transformCenterX === value) {
+                        return;
+                    }
+                    this._transformCenterX = value;
+                    this._markAsDirty();
+                    this._isMatrixDirty = true;
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(Control.prototype, "horizontalAlignment", {
                 get: function () {
                     return this._horizontalAlignment;
@@ -548,7 +738,30 @@ var BABYLON;
                 this._root = root;
                 this._host = host;
             };
-            Control.prototype.applyStates = function (context) {
+            Control.prototype._transform = function (context) {
+                if (this._scaleX === 1 && this._scaleY === 1 && this._rotation === 0) {
+                    return;
+                }
+                // preTranslate
+                var offsetX = this._currentMeasure.width * this._transformCenterX + this._currentMeasure.left;
+                var offsetY = this._currentMeasure.height * this._transformCenterY + this._currentMeasure.top;
+                context.translate(offsetX, offsetY);
+                // scale
+                context.scale(this._scaleX, this._scaleY);
+                // rotate
+                context.rotate(this._rotation);
+                // postTranslate
+                context.translate(-offsetX, -offsetY);
+                // Need to update matrices?
+                if (this._isMatrixDirty || this._cachedOffsetX !== offsetX || this._cachedOffsetY !== offsetY) {
+                    this._cachedOffsetX = offsetX;
+                    this._cachedOffsetY = offsetY;
+                    this._isMatrixDirty = false;
+                    GUI.Matrix2D.ComposeToRef(offsetX, offsetY, this._rotation, this._scaleX, this._scaleY, this._root ? this._root._transformMatrix : null, this._transformMatrix);
+                    this._transformMatrix.invertToRef(this._invertTransformMatrix);
+                }
+            };
+            Control.prototype._applyStates = function (context) {
                 if (this._font) {
                     context.font = this._font;
                 }
@@ -571,6 +784,8 @@ var BABYLON;
                     this._isDirty = false;
                     this._cachedParentMeasure.copyFrom(parentMeasure);
                 }
+                // Transform
+                this._transform(context);
                 // Clip
                 this._clip(context);
                 context.clip();
@@ -673,7 +888,11 @@ var BABYLON;
             Control.prototype._draw = function (parentMeasure, context) {
                 // Do nothing
             };
-            Control.prototype._contains = function (x, y) {
+            Control.prototype.contains = function (x, y) {
+                // Invert transform
+                if (this._scaleX !== 1 || this._scaleY !== 1 || this.rotation !== 0) {
+                }
+                // Check
                 if (x < this._currentMeasure.left) {
                     return false;
                 }
@@ -686,10 +905,13 @@ var BABYLON;
                 if (y > this._currentMeasure.top + this._currentMeasure.height) {
                     return false;
                 }
+                if (this.isPointerBlocker) {
+                    this._host._shouldBlockPointer = true;
+                }
                 return true;
             };
             Control.prototype._processPicking = function (x, y, type) {
-                if (!this._contains(x, y)) {
+                if (!this.contains(x, y)) {
                     return false;
                 }
                 this._processObservables(type);
@@ -700,10 +922,14 @@ var BABYLON;
                     this.onPointerMoveObservable.notifyObservers(this);
                 }
             };
+            Control.prototype._onPointerEnter = function () {
+                if (this.onPointerEnterObservable.hasObservers()) {
+                    this.onPointerEnterObservable.notifyObservers(this);
+                }
+            };
             Control.prototype._onPointerOut = function () {
-                var previousControlOver = this._host._lastControlOver;
-                if (previousControlOver.onPointerOutObservable.hasObservers()) {
-                    previousControlOver.onPointerOutObservable.notifyObservers(previousControlOver);
+                if (this.onPointerOutObservable.hasObservers()) {
+                    this.onPointerOutObservable.notifyObservers(this);
                 }
             };
             Control.prototype._onPointerDown = function () {
@@ -717,21 +943,32 @@ var BABYLON;
                 }
             };
             Control.prototype._processObservables = function (type) {
+                if (!this.isHitTestVisible) {
+                    return false;
+                }
                 if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                     this._onPointerMove();
                     var previousControlOver = this._host._lastControlOver;
                     if (previousControlOver && previousControlOver !== this) {
-                        this._onPointerOut();
+                        previousControlOver._onPointerOut();
+                    }
+                    if (previousControlOver !== this) {
+                        this._onPointerEnter();
                     }
                     this._host._lastControlOver = this;
                     return true;
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
                     this._onPointerDown();
+                    this._host._lastControlDown = this;
                     return true;
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERUP) {
                     this._onPointerUp();
+                    if (this._host._lastControlDown !== this) {
+                        this._host._lastControlDown._onPointerUp();
+                        this._host._lastControlDown = null;
+                    }
                     return true;
                 }
                 return false;
@@ -899,7 +1136,7 @@ var BABYLON;
             Container.prototype._draw = function (parentMeasure, context) {
                 context.save();
                 _super.prototype._processMeasures.call(this, parentMeasure, context);
-                this.applyStates(context);
+                this._applyStates(context);
                 this._localDraw(context);
                 this._clipForChildren(context);
                 for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
@@ -909,7 +1146,7 @@ var BABYLON;
                 context.restore();
             };
             Container.prototype._processPicking = function (x, y, type) {
-                if (!_super.prototype._contains.call(this, x, y)) {
+                if (!_super.prototype.contains.call(this, x, y)) {
                     return false;
                 }
                 // Checking backwards to pick closest first
@@ -1172,7 +1409,7 @@ var BABYLON;
             };
             TextBlock.prototype._draw = function (parentMeasure, context) {
                 context.save();
-                this.applyStates(context);
+                this._applyStates(context);
                 _super.prototype._processMeasures.call(this, parentMeasure, context);
                 // Render lines
                 this._renderLines(context);
@@ -1287,7 +1524,7 @@ var BABYLON;
             });
             Image.prototype._draw = function (parentMeasure, context) {
                 context.save();
-                this.applyStates(context);
+                this._applyStates(context);
                 _super.prototype._processMeasures.call(this, parentMeasure, context);
                 if (this._loaded) {
                     switch (this._stretch) {
@@ -1362,16 +1599,38 @@ var BABYLON;
             function Button(name) {
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
+                _this.thickness = 1;
+                _this.isPointerBlocker = true;
                 return _this;
             }
             // While being a container, the button behaves like a control.
             Button.prototype._processPicking = function (x, y, type) {
-                if (!this._contains(x, y)) {
+                if (!this.contains(x, y)) {
                     return false;
                 }
                 this._processObservables(type);
                 return true;
             };
+            Button.prototype._onPointerEnter = function () {
+                this.scaleX += 0.01;
+                this.scaleY += 0.01;
+                _super.prototype._onPointerEnter.call(this);
+            };
+            Button.prototype._onPointerOut = function () {
+                this.scaleX -= 0.01;
+                this.scaleY -= 0.01;
+                _super.prototype._onPointerOut.call(this);
+            };
+            Button.prototype._onPointerDown = function () {
+                this.scaleX -= 0.05;
+                this.scaleY -= 0.05;
+                _super.prototype._onPointerDown.call(this);
+            };
+            Button.prototype._onPointerUp = function () {
+                this.scaleX += 0.05;
+                this.scaleY += 0.05;
+                _super.prototype._onPointerUp.call(this);
+            };
             // Statics
             Button.CreateImageButton = function (name, text, imageUrl) {
                 var result = new Button(name);
@@ -1389,6 +1648,15 @@ var BABYLON;
                 result.addControl(iconImage);
                 return result;
             };
+            Button.CreateSimpleButton = function (name, text) {
+                var result = new Button(name);
+                // Adding text
+                var textBlock = new BABYLON.GUI.TextBlock(name + "_button", text);
+                textBlock.textWrapping = true;
+                textBlock.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
+                result.addControl(textBlock);
+                return result;
+            };
             return Button;
         }(GUI.Rectangle));
         GUI.Button = Button;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 17 - 17
dist/preview release/inspector/babylon.inspector.bundle.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


+ 8 - 3
gui/src/advancedDynamicTexture.ts

@@ -5,10 +5,12 @@ module BABYLON.GUI {
         private _isDirty = false;
         private _renderObserver: Observer<Scene>;
         private _resizeObserver: Observer<Engine>;
-        private _pointerMoveObserver: Observer<PointerInfo>;
+        private _pointerMoveObserver: Observer<PointerInfoPre>;
         private _background: string;
         private _rootContainer = new Container("root");
         public _lastControlOver: Control;
+        public _lastControlDown: Control;
+        public _shouldBlockPointer: boolean;
         public _toDispose: IDisposable;
 
         public get background(): string {
@@ -62,7 +64,7 @@ module BABYLON.GUI {
             }
 
             if (this._pointerMoveObserver) {
-                this.getScene().onPointerObservable.remove(this._pointerMoveObserver);
+                this.getScene().onPrePointerObservable.remove(this._pointerMoveObserver);
             }
 
             if (this._toDispose) {
@@ -131,14 +133,17 @@ module BABYLON.GUI {
 
         public attach(): void {
             var scene = this.getScene();
-            this._pointerMoveObserver = scene.onPointerObservable.add((pi, state) => {
+            this._pointerMoveObserver = scene.onPrePointerObservable.add((pi, state) => {
                 if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE 
                     && pi.type !== BABYLON.PointerEventTypes.POINTERUP
                     && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
                     return;
                 }
 
+                this._shouldBlockPointer = false;
                 this._doPicking(scene.pointerX, scene.pointerY, pi.type);
+
+                pi.skipOnPointerObservable = this._shouldBlockPointer;
             });
         }
 

+ 42 - 1
gui/src/controls/button.ts

@@ -4,11 +4,13 @@ module BABYLON.GUI {
     export class Button extends Rectangle {      
         constructor(public name: string) {
             super(name);
+            this.thickness = 1;
+            this.isPointerBlocker = true;
         }
 
         // While being a container, the button behaves like a control.
         public _processPicking(x: number, y: number, type: number): boolean {
-            if (!this._contains(x, y)) {
+            if (!this.contains(x, y)) {
                 return false;
             }
 
@@ -17,6 +19,33 @@ module BABYLON.GUI {
             return true;
         }
 
+        protected _onPointerEnter(): void {
+            this.scaleX += 0.01;
+            this.scaleY += 0.01;
+            super._onPointerEnter();
+        }
+
+        protected _onPointerOut(): void {
+            this.scaleX -= 0.01;
+            this.scaleY -= 0.01;
+
+            super._onPointerOut();
+        }
+
+        protected _onPointerDown(): void {
+            this.scaleX -= 0.05;
+            this.scaleY -= 0.05;
+
+            super._onPointerDown();
+        }
+
+        protected _onPointerUp (): void {
+            this.scaleX += 0.05;
+            this.scaleY += 0.05;
+
+            super._onPointerUp();
+        }        
+
         // Statics
         public static CreateImageButton(name: string, text: string, imageUrl: string): Button {
             var result = new Button(name);
@@ -37,5 +66,17 @@ module BABYLON.GUI {
 
             return result;
         }
+
+        public static CreateSimpleButton(name: string, text: string): Button {
+            var result = new Button(name);
+
+            // Adding text
+            var textBlock = new BABYLON.GUI.TextBlock(name + "_button", text);
+            textBlock.textWrapping = true;
+            textBlock.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
+            result.addControl(textBlock);           
+
+            return result;
+        }
     }    
 }

+ 2 - 2
gui/src/controls/container.ts

@@ -65,7 +65,7 @@ module BABYLON.GUI {
             context.save();
             super._processMeasures(parentMeasure, context);
            
-            this.applyStates(context);
+            this._applyStates(context);
 
             this._localDraw(context);
 
@@ -77,7 +77,7 @@ module BABYLON.GUI {
         }
 
         public _processPicking(x: number, y: number, type: number): boolean {
-            if (!super._contains(x, y)) {
+            if (!super.contains(x, y)) {
                 return false;
             }
 

+ 160 - 9
gui/src/controls/control.ts

@@ -24,6 +24,19 @@ module BABYLON.GUI {
         private _marginBottom = new ValueAndUnit(0);        
         private _left = new ValueAndUnit(0);
         private _top = new ValueAndUnit(0);
+        private _scaleX = 1.0;
+        private _scaleY = 1.0;
+        private _rotation = 0;
+        private _transformCenterX = 0.5;
+        private _transformCenterY = 0.5;
+        private _transformMatrix = Matrix2D.Identity();
+        private _invertTransformMatrix = Matrix2D.Identity();
+        private _isMatrixDirty = true;
+        private _cachedOffsetX: number;
+        private _cachedOffsetY: number;
+
+        public isHitTestVisible = true;
+        public isPointerBlocker = false;
         
         // Properties
 
@@ -51,6 +64,82 @@ module BABYLON.GUI {
         */
         public onPointerUpObservable = new Observable<Control>();     
 
+        /**
+        * An event triggered when pointer enters the control
+        * @type {BABYLON.Observable}
+        */
+        public onPointerEnterObservable = new Observable<Control>();           
+
+        public get scaleX(): number {
+            return this._scaleX;
+        }
+
+        public set scaleX(value: number) {
+            if (this._scaleX === value) {
+                return;
+            }
+
+            this._scaleX = value;
+            this._markAsDirty();
+            this._isMatrixDirty = true;
+        }     
+
+        public get scaleY(): number {
+            return this._scaleY;
+        }
+
+        public set scaleY(value: number) {
+            if (this._scaleY === value) {
+                return;
+            }
+
+            this._scaleY = value;
+            this._markAsDirty();
+            this._isMatrixDirty = true;
+        }  
+
+        public get rotation(): number {
+            return this._rotation;
+        }
+
+        public set rotation(value: number) {
+            if (this._rotation === value) {
+                return;
+            }
+
+            this._rotation = value;
+            this._markAsDirty();
+            this._isMatrixDirty = true;
+        }    
+
+        public get transformCenterY(): number {
+            return this._transformCenterY;
+        }
+
+        public set transformCenterY(value: number) {
+            if (this._transformCenterY === value) {
+                return;
+            }
+
+            this._transformCenterY = value;
+            this._markAsDirty();
+            this._isMatrixDirty = true;
+        }     
+
+        public get transformCenterX(): number {
+            return this._transformCenterX;
+        }
+
+        public set transformCenterX(value: number) {
+            if (this._transformCenterX === value) {
+                return;
+            }
+
+            this._transformCenterX = value;
+            this._markAsDirty();
+            this._isMatrixDirty = true;
+        }    
+
         public get horizontalAlignment(): number {
             return this._horizontalAlignment;
         }
@@ -240,7 +329,39 @@ module BABYLON.GUI {
             this._host = host;
         }
 
-        protected applyStates(context: CanvasRenderingContext2D): void {
+        protected _transform(context: CanvasRenderingContext2D): void {
+            if (this._scaleX === 1 && this._scaleY ===1 && this._rotation === 0) {
+                return;
+            }
+
+            // preTranslate
+            var offsetX = this._currentMeasure.width * this._transformCenterX + this._currentMeasure.left;
+            var offsetY = this._currentMeasure.height * this._transformCenterY + this._currentMeasure.top;
+            context.translate(offsetX, offsetY);
+
+            // scale
+            context.scale(this._scaleX, this._scaleY);
+
+            // rotate
+            context.rotate(this._rotation);
+
+            // postTranslate
+            context.translate(-offsetX, -offsetY);    
+
+
+            // Need to update matrices?
+            if (this._isMatrixDirty || this._cachedOffsetX !== offsetX || this._cachedOffsetY !== offsetY) {
+                this._cachedOffsetX = offsetX;
+                this._cachedOffsetY = offsetY;
+                this._isMatrixDirty = false;
+
+                Matrix2D.ComposeToRef(offsetX, offsetY, this._rotation, this._scaleX, this._scaleY, this._root ? this._root._transformMatrix : null, this._transformMatrix);
+
+                this._transformMatrix.invertToRef(this._invertTransformMatrix);
+            }
+        }
+
+        protected _applyStates(context: CanvasRenderingContext2D): void {
             if (this._font) {
                 context.font = this._font;
             }
@@ -268,7 +389,10 @@ module BABYLON.GUI {
 
                 this._isDirty = false;
                 this._cachedParentMeasure.copyFrom(parentMeasure);
-            }      
+            }     
+
+            // Transform
+            this._transform(context); 
                         
             // Clip
             this._clip(context);
@@ -382,7 +506,13 @@ module BABYLON.GUI {
             // Do nothing
         }
 
-        protected _contains(x: number, y: number) : boolean {
+        public contains(x: number, y: number) : boolean {
+            // Invert transform
+            if (this._scaleX !== 1 || this._scaleY !== 1 || this.rotation !== 0) {
+               
+            }
+
+            // Check
             if (x < this._currentMeasure.left) {
                 return false;
             }
@@ -399,11 +529,14 @@ module BABYLON.GUI {
                 return false;
             } 
 
+            if (this.isPointerBlocker) {
+                this._host._shouldBlockPointer = true;
+            }
             return true;
         }
 
         public _processPicking(x: number, y: number, type: number): boolean {
-            if (!this._contains(x, y)) {
+            if (!this.contains(x, y)) {
                 return false;
             }
 
@@ -418,11 +551,15 @@ module BABYLON.GUI {
             }
         }
 
-        protected _onPointerOut(): void {
-            var previousControlOver = this._host._lastControlOver;
+        protected _onPointerEnter(): void {
+            if (this.onPointerEnterObservable.hasObservers()) {
+                this.onPointerEnterObservable.notifyObservers(this);
+            }
+        }
 
-            if (previousControlOver.onPointerOutObservable.hasObservers()) {
-                previousControlOver.onPointerOutObservable.notifyObservers(previousControlOver);
+        protected _onPointerOut(): void {
+            if (this.onPointerOutObservable.hasObservers()) {
+                this.onPointerOutObservable.notifyObservers(this);
             }
         }
 
@@ -439,24 +576,38 @@ module BABYLON.GUI {
         }
 
         protected _processObservables(type: number): boolean {
+            if (!this.isHitTestVisible) {
+                return false;
+            }
+
             if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                 this._onPointerMove();
 
                 var previousControlOver = this._host._lastControlOver;
                 if (previousControlOver && previousControlOver !== this) {
-                    this._onPointerOut();
+                    previousControlOver._onPointerOut();
                 }
+
+                if (previousControlOver !== this) {
+                    this._onPointerEnter();
+                }
+
                 this._host._lastControlOver = this;
                 return true;
             }
 
             if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
                 this._onPointerDown();
+                this._host._lastControlDown = this;
                 return true;
             }
 
             if (type === BABYLON.PointerEventTypes.POINTERUP) {
                 this._onPointerUp();
+                if (this._host._lastControlDown !== this) {
+                    this._host._lastControlDown._onPointerUp();
+                    this._host._lastControlDown = null;
+                }
                 return true;
             }
         

+ 1 - 1
gui/src/controls/image.ts

@@ -42,7 +42,7 @@ module BABYLON.GUI {
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
 
-            this.applyStates(context);
+            this._applyStates(context);
             super._processMeasures(parentMeasure, context);
 
             if (this._loaded) {

+ 1 - 1
gui/src/controls/textBlock.ts

@@ -88,7 +88,7 @@ module BABYLON.GUI {
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
 
-            this.applyStates(context);
+            this._applyStates(context);
             super._processMeasures(parentMeasure, context);
             
             // Render lines

+ 96 - 0
gui/src/math2D.ts

@@ -0,0 +1,96 @@
+/// <reference path="../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GUI {
+    export class Matrix2D {
+        public m = new Float32Array(6);
+
+        constructor(m00: number, m01: number, m10: number, m11: number, m20: number, m21: number) {
+            this.fromValues(m00, m01, m10, m11, m20, m21);
+        }
+
+        public fromValues(m00: number, m01: number, m10: number, m11: number, m20: number, m21: number): Matrix2D {
+            this.m[0] = m00; this.m[1] = m01; 
+            this.m[2] = m10; this.m[3] = m11;
+            this.m[4] = m20; this.m[5] = m21;
+            return this;
+        }
+
+        public determinant(): number {
+            return this.m[0] * this.m[3] - this.m[1] * this.m[2];
+        }
+
+        public invertToRef(result: Matrix2D): Matrix2D {
+            let l0 = this.m[0]; let l1 = this.m[1];
+            let l2 = this.m[2]; let l3 = this.m[3];
+            let l4 = this.m[4]; let l5 = this.m[5];
+
+            let det = this.determinant();
+            if (det < (Epsilon * Epsilon)) {
+                throw new Error("Can't invert matrix, near null determinant");
+            }
+
+            let detDiv = 1 / det;
+
+            let det4 = l2 * l5 - l3 * l4;
+            let det5 = l1 * l4 - l0 * l5;
+
+            result.m[0] = l3 * detDiv;     result.m[1] = -l1 * detDiv;
+            result.m[2] = -l2 * detDiv;    result.m[3] = l0 * detDiv;
+            result.m[4] = det4 * detDiv;   result.m[5] = det5 * detDiv;
+
+            return this;
+        }
+
+        public multiplyToRef(other: Matrix2D, result: Matrix2D): Matrix2D {
+            let l0 = this.m[0];     let l1 = this.m[1];
+            let l2 = this.m[2];     let l3 = this.m[3];
+            let l4 = this.m[4];     let l5 = this.m[5];
+
+            let r0 = other.m[0];    let r1 = other.m[1];
+            let r2 = other.m[2];    let r3 = other.m[3];
+            let r4 = other.m[4];    let r5 = other.m[5];
+
+            result.m[0] = l0 * r0 + l1 * r2;        result.m[1] = l0 * r1 + l1 * r3;
+            result.m[2] = l2 * r0 + l3 * r2;        result.m[3] = l2 * r1 + l3 * r3;
+            result.m[4] = l4 * r0 + l5 * r2 + r4;   result.m[5] = l4 * r1 + l5 * r3 + r5;
+
+            return this;
+        }
+
+        // Statics
+        public static Identity(): Matrix2D {
+            return new Matrix2D(1, 0, 0, 1, 0, 0);
+        }
+
+        public static TranslationToRef(x: number, y: number, result: Matrix2D): void {
+            result.fromValues(1, 0, 0, 1, x, y);
+        }
+
+        public static ScalingToRef(x: number, y: number, result: Matrix2D): void {
+            result.fromValues(x, 0, 0, y,  0, 0);
+        }
+
+        public static RotationToRef(angle: number, result: Matrix2D): void {
+            var s = Math.sin(angle);
+            var c = Math.cos(angle);
+
+            result.fromValues(c, s, -s, c,  0, 0);
+        }
+
+
+        private static _TempPreTranslationMatrix = Matrix2D.Identity();
+        private static _TempPostTranslationMatrix = Matrix2D.Identity();
+        private static _TempRotationMatrix = Matrix2D.Identity();
+        private static _TempScalingMatrix = Matrix2D.Identity();
+
+        public static ComposeToRef(tx: number, ty: number, angle: number, scaleX: number, scaleY: number, parentMatrix: Matrix2D,  result: Matrix2D): void {
+            Matrix2D.TranslationToRef(tx, ty, Matrix2D._TempPreTranslationMatrix);
+
+            Matrix2D.ScalingToRef(scaleX, scaleY, Matrix2D._TempScalingMatrix);
+
+            Matrix2D.RotationToRef(angle, Matrix2D._TempRotationMatrix);
+
+            Matrix2D.TranslationToRef(-tx, -ty, Matrix2D._TempPostTranslationMatrix);
+        }
+    }    
+}