Browse Source

Nightly
Improved fluent shader for GUI3D

David Catuhe 7 years ago
parent
commit
ffbc650e84

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


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


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


+ 197 - 1
dist/preview release/babylon.max.js

@@ -25028,6 +25028,20 @@ var BABYLON;
             this._alternateSceneUbo.addUniform("view", 16);
             this._alternateSceneUbo.addUniform("view", 16);
         };
         };
         // Pointers handling
         // Pointers handling
+        Scene.prototype._pickSpriteButKeepRay = function (originalPointerInfo, x, y, predicate, fastCheck, camera) {
+            var result = this.pickSprite(x, y, predicate, fastCheck, camera);
+            if (result) {
+                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
+            }
+            return result;
+        };
+        Scene.prototype._setRayOnPointerInfo = function (pointerInfo) {
+            if (pointerInfo.pickInfo) {
+                if (!pointerInfo.pickInfo.ray) {
+                    pointerInfo.pickInfo.ray = this.createPickingRay(pointerInfo.event.offsetX, pointerInfo.event.offsetY, BABYLON.Matrix.Identity(), this.activeCamera);
+                }
+            }
+        };
         /**
         /**
          * Use this method to simulate a pointer move on a mesh
          * Use this method to simulate a pointer move on a mesh
          * The pickResult parameter can be obtained from a scene.pick or scene.pickWithRay
          * The pickResult parameter can be obtained from a scene.pick or scene.pickWithRay
@@ -25062,7 +25076,7 @@ var BABYLON;
             else {
             else {
                 this.setPointerOverMesh(null);
                 this.setPointerOverMesh(null);
                 // Sprites
                 // Sprites
-                pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
+                pickResult = this._pickSpriteButKeepRay(pickResult, this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
                 if (pickResult && pickResult.hit && pickResult.pickedSprite) {
                 if (pickResult && pickResult.hit && pickResult.pickedSprite) {
                     this.setPointerOverSprite(pickResult.pickedSprite);
                     this.setPointerOverSprite(pickResult.pickedSprite);
                     if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
                     if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
@@ -25085,6 +25099,7 @@ var BABYLON;
                 }
                 }
                 if (this.onPointerObservable.hasObservers()) {
                 if (this.onPointerObservable.hasObservers()) {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -25144,6 +25159,7 @@ var BABYLON;
                 }
                 }
                 if (this.onPointerObservable.hasObservers()) {
                 if (this.onPointerObservable.hasObservers()) {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -25173,6 +25189,7 @@ var BABYLON;
                     if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
                     if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
                         var type_1 = BABYLON.PointerEventTypes.POINTERPICK;
                         var type_1 = BABYLON.PointerEventTypes.POINTERPICK;
                         var pi = new BABYLON.PointerInfo(type_1, evt, pickResult);
                         var pi = new BABYLON.PointerInfo(type_1, evt, pickResult);
+                        this._setRayOnPointerInfo(pi);
                         this.onPointerObservable.notifyObservers(pi, type_1);
                         this.onPointerObservable.notifyObservers(pi, type_1);
                     }
                     }
                 }
                 }
@@ -25201,17 +25218,20 @@ var BABYLON;
                         if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
                         if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
                             var type_2 = BABYLON.PointerEventTypes.POINTERTAP;
                             var type_2 = BABYLON.PointerEventTypes.POINTERTAP;
                             var pi = new BABYLON.PointerInfo(type_2, evt, pickResult);
                             var pi = new BABYLON.PointerInfo(type_2, evt, pickResult);
+                            this._setRayOnPointerInfo(pi);
                             this.onPointerObservable.notifyObservers(pi, type_2);
                             this.onPointerObservable.notifyObservers(pi, type_2);
                         }
                         }
                         if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
                         if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
                             var type_3 = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
                             var type_3 = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
                             var pi = new BABYLON.PointerInfo(type_3, evt, pickResult);
                             var pi = new BABYLON.PointerInfo(type_3, evt, pickResult);
+                            this._setRayOnPointerInfo(pi);
                             this.onPointerObservable.notifyObservers(pi, type_3);
                             this.onPointerObservable.notifyObservers(pi, type_3);
                         }
                         }
                     }
                     }
                 }
                 }
                 else {
                 else {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -96328,6 +96348,182 @@ var BABYLON;
 
 
 //# sourceMappingURL=babylon.autoRotationBehavior.js.map
 //# sourceMappingURL=babylon.autoRotationBehavior.js.map
 
 
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
+     */
+    var PointerDragBehavior = /** @class */ (function () {
+        /**
+         * Creates a pointer drag behavior that can be attached to a mesh
+         * @param options The drag axis or normal of the plane that will be dragged accross
+         */
+        function PointerDragBehavior(options) {
+            this.options = options;
+            this._draggingID = -1;
+            /**
+             *  Fires each time the attached mesh is dragged with the pointer
+             */
+            this.onDragObservable = new BABYLON.Observable();
+            /**
+             *  Fires each time a drag begins (eg. mouse down on mesh)
+             */
+            this.onDragStartObservable = new BABYLON.Observable();
+            /**
+             *  Fires each time a drag ends (eg. mouse release after drag)
+             */
+            this.onDragEndObservable = new BABYLON.Observable();
+            /**
+             *  If the attached mesh should be moved when dragged
+             */
+            this.moveAttached = true;
+            /**
+             *  Mesh with the position where the drag plane should be placed
+             */
+            this._dragPlaneParent = null;
+            var optionCount = 0;
+            if (options.dragAxis) {
+                optionCount++;
+            }
+            if (options.dragPlaneNormal) {
+                optionCount++;
+            }
+            if (optionCount > 1) {
+                throw "Multiple drag modes specified in dragBehavior options. Only one expected";
+            }
+            if (optionCount < 1) {
+                throw "At least one drag mode option must be specified";
+            }
+        }
+        Object.defineProperty(PointerDragBehavior.prototype, "name", {
+            /**
+             *  The name of the behavior
+             */
+            get: function () {
+                return "PointerDrag";
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         *  Initializes the behavior
+         */
+        PointerDragBehavior.prototype.init = function () { };
+        /**
+         * Attaches the drag behavior the passed in mesh
+         * @param ownerNode The mesh that will be dragged around once attached
+         */
+        PointerDragBehavior.prototype.attach = function (ownerNode) {
+            var _this = this;
+            this._scene = ownerNode.getScene();
+            this._attachedNode = ownerNode;
+            // Initialize drag plane to not interfere with existing scene
+            if (!PointerDragBehavior._planeScene) {
+                PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
+                this._scene.getEngine().scenes.pop();
+            }
+            this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", 1000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
+            // State of the drag
+            var dragging = false;
+            var lastPosition = new BABYLON.Vector3(0, 0, 0);
+            var delta = new BABYLON.Vector3(0, 0, 0);
+            this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo) {
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray) {
+                        if (_this._attachedNode == pointerInfo.pickInfo.pickedMesh) {
+                            _this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                            var pickedPoint = _this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                            if (pickedPoint) {
+                                dragging = true;
+                                _this._draggingID = pointerInfo.event.pointerId;
+                                lastPosition.copyFrom(pickedPoint);
+                                _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint });
+                            }
+                        }
+                    }
+                }
+                else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
+                    if (_this._draggingID == pointerInfo.event.pointerId) {
+                        dragging = false;
+                        _this._draggingID = -1;
+                        _this.onDragEndObservable.notifyObservers({ dragPlanePoint: lastPosition });
+                    }
+                }
+                else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
+                    if (_this._draggingID == pointerInfo.event.pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                        var pickedPoint = _this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                        _this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                        if (pickedPoint) {
+                            // depending on the drag mode option drag accordingly
+                            if (_this.options.dragAxis) {
+                                //get the closest point on the dragaxis from the selected mesh to the picked point location
+                                // https://www.opengl.org/discussion_boards/showthread.php/159717-Closest-point-on-a-Vector-to-a-point
+                                _this.options.dragAxis.scaleToRef(BABYLON.Vector3.Dot(pickedPoint.subtract(lastPosition), _this.options.dragAxis), delta);
+                            }
+                            else {
+                                pickedPoint.subtractToRef(lastPosition, delta);
+                            }
+                            if (_this.moveAttached) {
+                                _this._attachedNode.position.addInPlace(delta);
+                            }
+                            _this.onDragObservable.notifyObservers({ delta: delta, dragPlanePoint: pickedPoint });
+                            lastPosition.copyFrom(pickedPoint);
+                        }
+                    }
+                }
+            });
+        };
+        PointerDragBehavior.prototype._pickWithRayOnDragPlane = function (ray) {
+            var _this = this;
+            if (!ray) {
+                return null;
+            }
+            var pickResult = PointerDragBehavior._planeScene.pickWithRay(ray, function (m) { return m == _this._dragPlane; });
+            if (pickResult && pickResult.hit && pickResult.pickedMesh && pickResult.pickedPoint) {
+                return pickResult.pickedPoint;
+            }
+            else {
+                return null;
+            }
+        };
+        // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
+        PointerDragBehavior.prototype._updateDragPlanePosition = function (ray) {
+            var pointA = this._dragPlaneParent ? this._dragPlaneParent.position : this._attachedNode.position; // center
+            if (this.options.dragAxis) {
+                var camPos = ray.origin;
+                // Calculate plane normal in direction of camera but perpendicular to drag axis
+                var pointB = pointA.add(this.options.dragAxis); // towards drag axis
+                var pointC = pointA.add(camPos.subtract(pointA).normalize()); // towards camera
+                // Get perpendicular line from direction to camera and drag axis
+                var lineA = pointB.subtract(pointA);
+                var lineB = pointC.subtract(pointA);
+                var perpLine = BABYLON.Vector3.Cross(lineA, lineB);
+                // Get perpendicular line from previous result and drag axis to adjust lineB to be perpendiculat to camera
+                var norm = BABYLON.Vector3.Cross(lineA, perpLine).normalize();
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(pointA.add(norm));
+            }
+            else if (this.options.dragPlaneNormal) {
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(pointA.add(this.options.dragPlaneNormal));
+            }
+            this._dragPlane.computeWorldMatrix(true);
+        };
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        PointerDragBehavior.prototype.detach = function () {
+            if (this._pointerObserver) {
+                this._scene.onPointerObservable.remove(this._pointerObserver);
+            }
+        };
+        return PointerDragBehavior;
+    }());
+    BABYLON.PointerDragBehavior = PointerDragBehavior;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.pointerDragBehavior.js.map
+
 
 
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {

+ 197 - 1
dist/preview release/babylon.no-module.max.js

@@ -24995,6 +24995,20 @@ var BABYLON;
             this._alternateSceneUbo.addUniform("view", 16);
             this._alternateSceneUbo.addUniform("view", 16);
         };
         };
         // Pointers handling
         // Pointers handling
+        Scene.prototype._pickSpriteButKeepRay = function (originalPointerInfo, x, y, predicate, fastCheck, camera) {
+            var result = this.pickSprite(x, y, predicate, fastCheck, camera);
+            if (result) {
+                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
+            }
+            return result;
+        };
+        Scene.prototype._setRayOnPointerInfo = function (pointerInfo) {
+            if (pointerInfo.pickInfo) {
+                if (!pointerInfo.pickInfo.ray) {
+                    pointerInfo.pickInfo.ray = this.createPickingRay(pointerInfo.event.offsetX, pointerInfo.event.offsetY, BABYLON.Matrix.Identity(), this.activeCamera);
+                }
+            }
+        };
         /**
         /**
          * Use this method to simulate a pointer move on a mesh
          * Use this method to simulate a pointer move on a mesh
          * The pickResult parameter can be obtained from a scene.pick or scene.pickWithRay
          * The pickResult parameter can be obtained from a scene.pick or scene.pickWithRay
@@ -25029,7 +25043,7 @@ var BABYLON;
             else {
             else {
                 this.setPointerOverMesh(null);
                 this.setPointerOverMesh(null);
                 // Sprites
                 // Sprites
-                pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
+                pickResult = this._pickSpriteButKeepRay(pickResult, this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
                 if (pickResult && pickResult.hit && pickResult.pickedSprite) {
                 if (pickResult && pickResult.hit && pickResult.pickedSprite) {
                     this.setPointerOverSprite(pickResult.pickedSprite);
                     this.setPointerOverSprite(pickResult.pickedSprite);
                     if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
                     if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
@@ -25052,6 +25066,7 @@ var BABYLON;
                 }
                 }
                 if (this.onPointerObservable.hasObservers()) {
                 if (this.onPointerObservable.hasObservers()) {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -25111,6 +25126,7 @@ var BABYLON;
                 }
                 }
                 if (this.onPointerObservable.hasObservers()) {
                 if (this.onPointerObservable.hasObservers()) {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -25140,6 +25156,7 @@ var BABYLON;
                     if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
                     if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
                         var type_1 = BABYLON.PointerEventTypes.POINTERPICK;
                         var type_1 = BABYLON.PointerEventTypes.POINTERPICK;
                         var pi = new BABYLON.PointerInfo(type_1, evt, pickResult);
                         var pi = new BABYLON.PointerInfo(type_1, evt, pickResult);
+                        this._setRayOnPointerInfo(pi);
                         this.onPointerObservable.notifyObservers(pi, type_1);
                         this.onPointerObservable.notifyObservers(pi, type_1);
                     }
                     }
                 }
                 }
@@ -25168,17 +25185,20 @@ var BABYLON;
                         if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
                         if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
                             var type_2 = BABYLON.PointerEventTypes.POINTERTAP;
                             var type_2 = BABYLON.PointerEventTypes.POINTERTAP;
                             var pi = new BABYLON.PointerInfo(type_2, evt, pickResult);
                             var pi = new BABYLON.PointerInfo(type_2, evt, pickResult);
+                            this._setRayOnPointerInfo(pi);
                             this.onPointerObservable.notifyObservers(pi, type_2);
                             this.onPointerObservable.notifyObservers(pi, type_2);
                         }
                         }
                         if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
                         if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
                             var type_3 = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
                             var type_3 = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
                             var pi = new BABYLON.PointerInfo(type_3, evt, pickResult);
                             var pi = new BABYLON.PointerInfo(type_3, evt, pickResult);
+                            this._setRayOnPointerInfo(pi);
                             this.onPointerObservable.notifyObservers(pi, type_3);
                             this.onPointerObservable.notifyObservers(pi, type_3);
                         }
                         }
                     }
                     }
                 }
                 }
                 else {
                 else {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -96295,6 +96315,182 @@ var BABYLON;
 
 
 //# sourceMappingURL=babylon.autoRotationBehavior.js.map
 //# sourceMappingURL=babylon.autoRotationBehavior.js.map
 
 
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
+     */
+    var PointerDragBehavior = /** @class */ (function () {
+        /**
+         * Creates a pointer drag behavior that can be attached to a mesh
+         * @param options The drag axis or normal of the plane that will be dragged accross
+         */
+        function PointerDragBehavior(options) {
+            this.options = options;
+            this._draggingID = -1;
+            /**
+             *  Fires each time the attached mesh is dragged with the pointer
+             */
+            this.onDragObservable = new BABYLON.Observable();
+            /**
+             *  Fires each time a drag begins (eg. mouse down on mesh)
+             */
+            this.onDragStartObservable = new BABYLON.Observable();
+            /**
+             *  Fires each time a drag ends (eg. mouse release after drag)
+             */
+            this.onDragEndObservable = new BABYLON.Observable();
+            /**
+             *  If the attached mesh should be moved when dragged
+             */
+            this.moveAttached = true;
+            /**
+             *  Mesh with the position where the drag plane should be placed
+             */
+            this._dragPlaneParent = null;
+            var optionCount = 0;
+            if (options.dragAxis) {
+                optionCount++;
+            }
+            if (options.dragPlaneNormal) {
+                optionCount++;
+            }
+            if (optionCount > 1) {
+                throw "Multiple drag modes specified in dragBehavior options. Only one expected";
+            }
+            if (optionCount < 1) {
+                throw "At least one drag mode option must be specified";
+            }
+        }
+        Object.defineProperty(PointerDragBehavior.prototype, "name", {
+            /**
+             *  The name of the behavior
+             */
+            get: function () {
+                return "PointerDrag";
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         *  Initializes the behavior
+         */
+        PointerDragBehavior.prototype.init = function () { };
+        /**
+         * Attaches the drag behavior the passed in mesh
+         * @param ownerNode The mesh that will be dragged around once attached
+         */
+        PointerDragBehavior.prototype.attach = function (ownerNode) {
+            var _this = this;
+            this._scene = ownerNode.getScene();
+            this._attachedNode = ownerNode;
+            // Initialize drag plane to not interfere with existing scene
+            if (!PointerDragBehavior._planeScene) {
+                PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
+                this._scene.getEngine().scenes.pop();
+            }
+            this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", 1000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
+            // State of the drag
+            var dragging = false;
+            var lastPosition = new BABYLON.Vector3(0, 0, 0);
+            var delta = new BABYLON.Vector3(0, 0, 0);
+            this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo) {
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray) {
+                        if (_this._attachedNode == pointerInfo.pickInfo.pickedMesh) {
+                            _this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                            var pickedPoint = _this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                            if (pickedPoint) {
+                                dragging = true;
+                                _this._draggingID = pointerInfo.event.pointerId;
+                                lastPosition.copyFrom(pickedPoint);
+                                _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint });
+                            }
+                        }
+                    }
+                }
+                else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
+                    if (_this._draggingID == pointerInfo.event.pointerId) {
+                        dragging = false;
+                        _this._draggingID = -1;
+                        _this.onDragEndObservable.notifyObservers({ dragPlanePoint: lastPosition });
+                    }
+                }
+                else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
+                    if (_this._draggingID == pointerInfo.event.pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                        var pickedPoint = _this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                        _this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                        if (pickedPoint) {
+                            // depending on the drag mode option drag accordingly
+                            if (_this.options.dragAxis) {
+                                //get the closest point on the dragaxis from the selected mesh to the picked point location
+                                // https://www.opengl.org/discussion_boards/showthread.php/159717-Closest-point-on-a-Vector-to-a-point
+                                _this.options.dragAxis.scaleToRef(BABYLON.Vector3.Dot(pickedPoint.subtract(lastPosition), _this.options.dragAxis), delta);
+                            }
+                            else {
+                                pickedPoint.subtractToRef(lastPosition, delta);
+                            }
+                            if (_this.moveAttached) {
+                                _this._attachedNode.position.addInPlace(delta);
+                            }
+                            _this.onDragObservable.notifyObservers({ delta: delta, dragPlanePoint: pickedPoint });
+                            lastPosition.copyFrom(pickedPoint);
+                        }
+                    }
+                }
+            });
+        };
+        PointerDragBehavior.prototype._pickWithRayOnDragPlane = function (ray) {
+            var _this = this;
+            if (!ray) {
+                return null;
+            }
+            var pickResult = PointerDragBehavior._planeScene.pickWithRay(ray, function (m) { return m == _this._dragPlane; });
+            if (pickResult && pickResult.hit && pickResult.pickedMesh && pickResult.pickedPoint) {
+                return pickResult.pickedPoint;
+            }
+            else {
+                return null;
+            }
+        };
+        // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
+        PointerDragBehavior.prototype._updateDragPlanePosition = function (ray) {
+            var pointA = this._dragPlaneParent ? this._dragPlaneParent.position : this._attachedNode.position; // center
+            if (this.options.dragAxis) {
+                var camPos = ray.origin;
+                // Calculate plane normal in direction of camera but perpendicular to drag axis
+                var pointB = pointA.add(this.options.dragAxis); // towards drag axis
+                var pointC = pointA.add(camPos.subtract(pointA).normalize()); // towards camera
+                // Get perpendicular line from direction to camera and drag axis
+                var lineA = pointB.subtract(pointA);
+                var lineB = pointC.subtract(pointA);
+                var perpLine = BABYLON.Vector3.Cross(lineA, lineB);
+                // Get perpendicular line from previous result and drag axis to adjust lineB to be perpendiculat to camera
+                var norm = BABYLON.Vector3.Cross(lineA, perpLine).normalize();
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(pointA.add(norm));
+            }
+            else if (this.options.dragPlaneNormal) {
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(pointA.add(this.options.dragPlaneNormal));
+            }
+            this._dragPlane.computeWorldMatrix(true);
+        };
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        PointerDragBehavior.prototype.detach = function () {
+            if (this._pointerObserver) {
+                this._scene.onPointerObservable.remove(this._pointerObserver);
+            }
+        };
+        return PointerDragBehavior;
+    }());
+    BABYLON.PointerDragBehavior = PointerDragBehavior;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.pointerDragBehavior.js.map
+
 
 
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {

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


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


+ 59 - 6
dist/preview release/gui/babylon.gui.d.ts

@@ -1027,13 +1027,48 @@ declare module BABYLON.GUI {
 
 
 
 
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
+    /** @hidden */
+    class FluentMaterialDefines extends MaterialDefines {
+        INNERGLOW: boolean;
+        BORDER: boolean;
+        constructor();
+    }
     /**
     /**
      * Class used to render controls with fluent desgin
      * Class used to render controls with fluent desgin
      */
      */
     class FluentMaterial extends PushMaterial {
     class FluentMaterial extends PushMaterial {
-        private _emissiveTexture;
-        emissiveTexture: BaseTexture;
-        private _renderId;
+        /**
+         * Gets or sets inner glow intensity. A value of 0 means no glow (default is 0.5)
+         */
+        innerGlowColorIntensity: number;
+        /**
+         * Gets or sets the inner glow color (white by default)
+         */
+        innerGlowColor: Color3;
+        /**
+         * Gets or sets alpha value (default is 1.0)
+         */
+        alpha: number;
+        /**
+         * Gets or sets the albedo color (Default is Color3(0.3, 0.35, 0.4))
+         */
+        albedoColor: Color3;
+        /**
+         * Gets or sets a boolean indicating if borders must be rendered (default is false)
+         */
+        renderBorders: boolean;
+        /**
+         * Gets or sets border width (default is 0.5)
+         */
+        borderWidth: number;
+        /**
+         * Gets or sets a value indicating the smoothing value applied to border edges (0.02 by default)
+         */
+        edgeSmoothingValue: number;
+        /**
+         * Gets or sets the minimum value that can be applied to border width (default is 0.1)
+         */
+        borderMinValue: number;
         /**
         /**
          * Creates a new Fluent material
          * Creates a new Fluent material
          * @param name defines the name of the material
          * @param name defines the name of the material
@@ -1166,7 +1201,7 @@ declare module BABYLON.GUI {
         /**
         /**
          * Gets the mesh used to render this control
          * Gets the mesh used to render this control
          */
          */
-        readonly mesh: Nullable<Mesh>;
+        readonly mesh: Nullable<AbstractMesh>;
         /**
         /**
          * Link the control as child of the given node
          * Link the control as child of the given node
          * @param node defines the node to link to. Use null to unlink the control
          * @param node defines the node to link to. Use null to unlink the control
@@ -1186,7 +1221,7 @@ declare module BABYLON.GUI {
          * Affect a material to the given mesh
          * Affect a material to the given mesh
          * @param mesh defines the mesh which will represent the control
          * @param mesh defines the mesh which will represent the control
          */
          */
-        protected _affectMaterial(mesh: Mesh): void;
+        protected _affectMaterial(mesh: AbstractMesh): void;
         /** @hidden */
         /** @hidden */
         _onPointerMove(target: Control3D, coordinates: Vector3): void;
         _onPointerMove(target: Control3D, coordinates: Vector3): void;
         /** @hidden */
         /** @hidden */
@@ -1273,9 +1308,15 @@ declare module BABYLON.GUI {
          * Gets or sets the GUI 2D content used to display the button's facade
          * Gets or sets the GUI 2D content used to display the button's facade
          */
          */
         content: Control;
         content: Control;
+        /**
+         * Apply the facade texture (created from the content property).
+         * This function can be overloaded by child classes
+         * @param facadeTexture defines the AdvancedDynamicTexture to use
+         */
+        protected _applyFacade(facadeTexture: AdvancedDynamicTexture): void;
         protected _getTypeName(): string;
         protected _getTypeName(): string;
         protected _createNode(scene: Scene): TransformNode;
         protected _createNode(scene: Scene): TransformNode;
-        protected _affectMaterial(mesh: Mesh): void;
+        protected _affectMaterial(mesh: AbstractMesh): void;
     }
     }
 }
 }
 
 
@@ -1285,14 +1326,26 @@ declare module BABYLON.GUI {
      * Class used to create a holographic button in 3D
      * Class used to create a holographic button in 3D
      */
      */
     class HolographicButton extends Button3D {
     class HolographicButton extends Button3D {
+        private _backPlate;
+        private _textPlate;
         private _frontPlate;
         private _frontPlate;
+        private _backFluentMaterial;
+        private _frontFluentMaterial;
+        private _text;
+        private _imageUrl;
+        /**
+         * Gets or sets text for the button
+         */
+        text: string;
         /**
         /**
          * Creates a new button
          * Creates a new button
          * @param name defines the control name
          * @param name defines the control name
          */
          */
         constructor(name?: string);
         constructor(name?: string);
         protected _getTypeName(): string;
         protected _getTypeName(): string;
+        private _rebuildContent();
         protected _createNode(scene: Scene): TransformNode;
         protected _createNode(scene: Scene): TransformNode;
+        protected _applyFacade(facadeTexture: AdvancedDynamicTexture): void;
         protected _affectMaterial(mesh: Mesh): void;
         protected _affectMaterial(mesh: Mesh): void;
     }
     }
 }
 }

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


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


+ 59 - 6
dist/preview release/gui/babylon.gui.module.d.ts

@@ -1032,13 +1032,48 @@ declare module BABYLON.GUI {
 
 
 
 
 declare module BABYLON.GUI {
 declare module BABYLON.GUI {
+    /** @hidden */
+    class FluentMaterialDefines extends MaterialDefines {
+        INNERGLOW: boolean;
+        BORDER: boolean;
+        constructor();
+    }
     /**
     /**
      * Class used to render controls with fluent desgin
      * Class used to render controls with fluent desgin
      */
      */
     class FluentMaterial extends PushMaterial {
     class FluentMaterial extends PushMaterial {
-        private _emissiveTexture;
-        emissiveTexture: BaseTexture;
-        private _renderId;
+        /**
+         * Gets or sets inner glow intensity. A value of 0 means no glow (default is 0.5)
+         */
+        innerGlowColorIntensity: number;
+        /**
+         * Gets or sets the inner glow color (white by default)
+         */
+        innerGlowColor: Color3;
+        /**
+         * Gets or sets alpha value (default is 1.0)
+         */
+        alpha: number;
+        /**
+         * Gets or sets the albedo color (Default is Color3(0.3, 0.35, 0.4))
+         */
+        albedoColor: Color3;
+        /**
+         * Gets or sets a boolean indicating if borders must be rendered (default is false)
+         */
+        renderBorders: boolean;
+        /**
+         * Gets or sets border width (default is 0.5)
+         */
+        borderWidth: number;
+        /**
+         * Gets or sets a value indicating the smoothing value applied to border edges (0.02 by default)
+         */
+        edgeSmoothingValue: number;
+        /**
+         * Gets or sets the minimum value that can be applied to border width (default is 0.1)
+         */
+        borderMinValue: number;
         /**
         /**
          * Creates a new Fluent material
          * Creates a new Fluent material
          * @param name defines the name of the material
          * @param name defines the name of the material
@@ -1171,7 +1206,7 @@ declare module BABYLON.GUI {
         /**
         /**
          * Gets the mesh used to render this control
          * Gets the mesh used to render this control
          */
          */
-        readonly mesh: Nullable<Mesh>;
+        readonly mesh: Nullable<AbstractMesh>;
         /**
         /**
          * Link the control as child of the given node
          * Link the control as child of the given node
          * @param node defines the node to link to. Use null to unlink the control
          * @param node defines the node to link to. Use null to unlink the control
@@ -1191,7 +1226,7 @@ declare module BABYLON.GUI {
          * Affect a material to the given mesh
          * Affect a material to the given mesh
          * @param mesh defines the mesh which will represent the control
          * @param mesh defines the mesh which will represent the control
          */
          */
-        protected _affectMaterial(mesh: Mesh): void;
+        protected _affectMaterial(mesh: AbstractMesh): void;
         /** @hidden */
         /** @hidden */
         _onPointerMove(target: Control3D, coordinates: Vector3): void;
         _onPointerMove(target: Control3D, coordinates: Vector3): void;
         /** @hidden */
         /** @hidden */
@@ -1278,9 +1313,15 @@ declare module BABYLON.GUI {
          * Gets or sets the GUI 2D content used to display the button's facade
          * Gets or sets the GUI 2D content used to display the button's facade
          */
          */
         content: Control;
         content: Control;
+        /**
+         * Apply the facade texture (created from the content property).
+         * This function can be overloaded by child classes
+         * @param facadeTexture defines the AdvancedDynamicTexture to use
+         */
+        protected _applyFacade(facadeTexture: AdvancedDynamicTexture): void;
         protected _getTypeName(): string;
         protected _getTypeName(): string;
         protected _createNode(scene: Scene): TransformNode;
         protected _createNode(scene: Scene): TransformNode;
-        protected _affectMaterial(mesh: Mesh): void;
+        protected _affectMaterial(mesh: AbstractMesh): void;
     }
     }
 }
 }
 
 
@@ -1290,14 +1331,26 @@ declare module BABYLON.GUI {
      * Class used to create a holographic button in 3D
      * Class used to create a holographic button in 3D
      */
      */
     class HolographicButton extends Button3D {
     class HolographicButton extends Button3D {
+        private _backPlate;
+        private _textPlate;
         private _frontPlate;
         private _frontPlate;
+        private _backFluentMaterial;
+        private _frontFluentMaterial;
+        private _text;
+        private _imageUrl;
+        /**
+         * Gets or sets text for the button
+         */
+        text: string;
         /**
         /**
          * Creates a new button
          * Creates a new button
          * @param name defines the control name
          * @param name defines the control name
          */
          */
         constructor(name?: string);
         constructor(name?: string);
         protected _getTypeName(): string;
         protected _getTypeName(): string;
+        private _rebuildContent();
         protected _createNode(scene: Scene): TransformNode;
         protected _createNode(scene: Scene): TransformNode;
+        protected _applyFacade(facadeTexture: AdvancedDynamicTexture): void;
         protected _affectMaterial(mesh: Mesh): void;
         protected _affectMaterial(mesh: Mesh): void;
     }
     }
 }
 }

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

@@ -1360,16 +1360,17 @@ declare module BabylonViewer {
             exposure?: number;
             exposure?: number;
             pinchPrecision?: number;
             pinchPrecision?: number;
             behaviors?: {
             behaviors?: {
-                    [name: string]: number | {
-                            type: number;
-                            [propName: string]: any;
-                    };
+                    [name: string]: boolean | number | ICameraBehaviorConfiguration;
             };
             };
             disableCameraControl?: boolean;
             disableCameraControl?: boolean;
             disableCtrlForPanning?: boolean;
             disableCtrlForPanning?: boolean;
             disableAutoFocus?: boolean;
             disableAutoFocus?: boolean;
             [propName: string]: any;
             [propName: string]: any;
     }
     }
+    export interface ICameraBehaviorConfiguration {
+            type: number;
+            [propName: string]: any;
+    }
     export interface ILightConfiguration {
     export interface ILightConfiguration {
             type: number;
             type: number;
             name?: string;
             name?: string;

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


+ 221 - 8
dist/preview release/viewer/babylon.viewer.max.js

@@ -25116,6 +25116,20 @@ var BABYLON;
             this._alternateSceneUbo.addUniform("view", 16);
             this._alternateSceneUbo.addUniform("view", 16);
         };
         };
         // Pointers handling
         // Pointers handling
+        Scene.prototype._pickSpriteButKeepRay = function (originalPointerInfo, x, y, predicate, fastCheck, camera) {
+            var result = this.pickSprite(x, y, predicate, fastCheck, camera);
+            if (result) {
+                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
+            }
+            return result;
+        };
+        Scene.prototype._setRayOnPointerInfo = function (pointerInfo) {
+            if (pointerInfo.pickInfo) {
+                if (!pointerInfo.pickInfo.ray) {
+                    pointerInfo.pickInfo.ray = this.createPickingRay(pointerInfo.event.offsetX, pointerInfo.event.offsetY, BABYLON.Matrix.Identity(), this.activeCamera);
+                }
+            }
+        };
         /**
         /**
          * Use this method to simulate a pointer move on a mesh
          * Use this method to simulate a pointer move on a mesh
          * The pickResult parameter can be obtained from a scene.pick or scene.pickWithRay
          * The pickResult parameter can be obtained from a scene.pick or scene.pickWithRay
@@ -25150,7 +25164,7 @@ var BABYLON;
             else {
             else {
                 this.setPointerOverMesh(null);
                 this.setPointerOverMesh(null);
                 // Sprites
                 // Sprites
-                pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
+                pickResult = this._pickSpriteButKeepRay(pickResult, this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
                 if (pickResult && pickResult.hit && pickResult.pickedSprite) {
                 if (pickResult && pickResult.hit && pickResult.pickedSprite) {
                     this.setPointerOverSprite(pickResult.pickedSprite);
                     this.setPointerOverSprite(pickResult.pickedSprite);
                     if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
                     if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
@@ -25173,6 +25187,7 @@ var BABYLON;
                 }
                 }
                 if (this.onPointerObservable.hasObservers()) {
                 if (this.onPointerObservable.hasObservers()) {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -25232,6 +25247,7 @@ var BABYLON;
                 }
                 }
                 if (this.onPointerObservable.hasObservers()) {
                 if (this.onPointerObservable.hasObservers()) {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -25261,6 +25277,7 @@ var BABYLON;
                     if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
                     if (clickInfo.singleClick && !clickInfo.ignore && this.onPointerObservable.hasObservers()) {
                         var type_1 = BABYLON.PointerEventTypes.POINTERPICK;
                         var type_1 = BABYLON.PointerEventTypes.POINTERPICK;
                         var pi = new BABYLON.PointerInfo(type_1, evt, pickResult);
                         var pi = new BABYLON.PointerInfo(type_1, evt, pickResult);
+                        this._setRayOnPointerInfo(pi);
                         this.onPointerObservable.notifyObservers(pi, type_1);
                         this.onPointerObservable.notifyObservers(pi, type_1);
                     }
                     }
                 }
                 }
@@ -25289,17 +25306,20 @@ var BABYLON;
                         if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
                         if (clickInfo.singleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERTAP)) {
                             var type_2 = BABYLON.PointerEventTypes.POINTERTAP;
                             var type_2 = BABYLON.PointerEventTypes.POINTERTAP;
                             var pi = new BABYLON.PointerInfo(type_2, evt, pickResult);
                             var pi = new BABYLON.PointerInfo(type_2, evt, pickResult);
+                            this._setRayOnPointerInfo(pi);
                             this.onPointerObservable.notifyObservers(pi, type_2);
                             this.onPointerObservable.notifyObservers(pi, type_2);
                         }
                         }
                         if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
                         if (clickInfo.doubleClick && this.onPointerObservable.hasSpecificMask(BABYLON.PointerEventTypes.POINTERDOUBLETAP)) {
                             var type_3 = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
                             var type_3 = BABYLON.PointerEventTypes.POINTERDOUBLETAP;
                             var pi = new BABYLON.PointerInfo(type_3, evt, pickResult);
                             var pi = new BABYLON.PointerInfo(type_3, evt, pickResult);
+                            this._setRayOnPointerInfo(pi);
                             this.onPointerObservable.notifyObservers(pi, type_3);
                             this.onPointerObservable.notifyObservers(pi, type_3);
                         }
                         }
                     }
                     }
                 }
                 }
                 else {
                 else {
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
                     var pi = new BABYLON.PointerInfo(type, evt, pickResult);
+                    this._setRayOnPointerInfo(pi);
                     this.onPointerObservable.notifyObservers(pi, type);
                     this.onPointerObservable.notifyObservers(pi, type);
                 }
                 }
             }
             }
@@ -96416,6 +96436,182 @@ var BABYLON;
 
 
 //# sourceMappingURL=babylon.autoRotationBehavior.js.map
 //# sourceMappingURL=babylon.autoRotationBehavior.js.map
 
 
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
+     */
+    var PointerDragBehavior = /** @class */ (function () {
+        /**
+         * Creates a pointer drag behavior that can be attached to a mesh
+         * @param options The drag axis or normal of the plane that will be dragged accross
+         */
+        function PointerDragBehavior(options) {
+            this.options = options;
+            this._draggingID = -1;
+            /**
+             *  Fires each time the attached mesh is dragged with the pointer
+             */
+            this.onDragObservable = new BABYLON.Observable();
+            /**
+             *  Fires each time a drag begins (eg. mouse down on mesh)
+             */
+            this.onDragStartObservable = new BABYLON.Observable();
+            /**
+             *  Fires each time a drag ends (eg. mouse release after drag)
+             */
+            this.onDragEndObservable = new BABYLON.Observable();
+            /**
+             *  If the attached mesh should be moved when dragged
+             */
+            this.moveAttached = true;
+            /**
+             *  Mesh with the position where the drag plane should be placed
+             */
+            this._dragPlaneParent = null;
+            var optionCount = 0;
+            if (options.dragAxis) {
+                optionCount++;
+            }
+            if (options.dragPlaneNormal) {
+                optionCount++;
+            }
+            if (optionCount > 1) {
+                throw "Multiple drag modes specified in dragBehavior options. Only one expected";
+            }
+            if (optionCount < 1) {
+                throw "At least one drag mode option must be specified";
+            }
+        }
+        Object.defineProperty(PointerDragBehavior.prototype, "name", {
+            /**
+             *  The name of the behavior
+             */
+            get: function () {
+                return "PointerDrag";
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         *  Initializes the behavior
+         */
+        PointerDragBehavior.prototype.init = function () { };
+        /**
+         * Attaches the drag behavior the passed in mesh
+         * @param ownerNode The mesh that will be dragged around once attached
+         */
+        PointerDragBehavior.prototype.attach = function (ownerNode) {
+            var _this = this;
+            this._scene = ownerNode.getScene();
+            this._attachedNode = ownerNode;
+            // Initialize drag plane to not interfere with existing scene
+            if (!PointerDragBehavior._planeScene) {
+                PointerDragBehavior._planeScene = new BABYLON.Scene(this._scene.getEngine());
+                this._scene.getEngine().scenes.pop();
+            }
+            this._dragPlane = BABYLON.Mesh.CreatePlane("pointerDragPlane", 1000, PointerDragBehavior._planeScene, false, BABYLON.Mesh.DOUBLESIDE);
+            // State of the drag
+            var dragging = false;
+            var lastPosition = new BABYLON.Vector3(0, 0, 0);
+            var delta = new BABYLON.Vector3(0, 0, 0);
+            this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo) {
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray) {
+                        if (_this._attachedNode == pointerInfo.pickInfo.pickedMesh) {
+                            _this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                            var pickedPoint = _this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                            if (pickedPoint) {
+                                dragging = true;
+                                _this._draggingID = pointerInfo.event.pointerId;
+                                lastPosition.copyFrom(pickedPoint);
+                                _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint });
+                            }
+                        }
+                    }
+                }
+                else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERUP) {
+                    if (_this._draggingID == pointerInfo.event.pointerId) {
+                        dragging = false;
+                        _this._draggingID = -1;
+                        _this.onDragEndObservable.notifyObservers({ dragPlanePoint: lastPosition });
+                    }
+                }
+                else if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERMOVE) {
+                    if (_this._draggingID == pointerInfo.event.pointerId && dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                        var pickedPoint = _this._pickWithRayOnDragPlane(pointerInfo.pickInfo.ray);
+                        _this._updateDragPlanePosition(pointerInfo.pickInfo.ray);
+                        if (pickedPoint) {
+                            // depending on the drag mode option drag accordingly
+                            if (_this.options.dragAxis) {
+                                //get the closest point on the dragaxis from the selected mesh to the picked point location
+                                // https://www.opengl.org/discussion_boards/showthread.php/159717-Closest-point-on-a-Vector-to-a-point
+                                _this.options.dragAxis.scaleToRef(BABYLON.Vector3.Dot(pickedPoint.subtract(lastPosition), _this.options.dragAxis), delta);
+                            }
+                            else {
+                                pickedPoint.subtractToRef(lastPosition, delta);
+                            }
+                            if (_this.moveAttached) {
+                                _this._attachedNode.position.addInPlace(delta);
+                            }
+                            _this.onDragObservable.notifyObservers({ delta: delta, dragPlanePoint: pickedPoint });
+                            lastPosition.copyFrom(pickedPoint);
+                        }
+                    }
+                }
+            });
+        };
+        PointerDragBehavior.prototype._pickWithRayOnDragPlane = function (ray) {
+            var _this = this;
+            if (!ray) {
+                return null;
+            }
+            var pickResult = PointerDragBehavior._planeScene.pickWithRay(ray, function (m) { return m == _this._dragPlane; });
+            if (pickResult && pickResult.hit && pickResult.pickedMesh && pickResult.pickedPoint) {
+                return pickResult.pickedPoint;
+            }
+            else {
+                return null;
+            }
+        };
+        // Position the drag plane based on the attached mesh position, for single axis rotate the plane along the axis to face the camera
+        PointerDragBehavior.prototype._updateDragPlanePosition = function (ray) {
+            var pointA = this._dragPlaneParent ? this._dragPlaneParent.position : this._attachedNode.position; // center
+            if (this.options.dragAxis) {
+                var camPos = ray.origin;
+                // Calculate plane normal in direction of camera but perpendicular to drag axis
+                var pointB = pointA.add(this.options.dragAxis); // towards drag axis
+                var pointC = pointA.add(camPos.subtract(pointA).normalize()); // towards camera
+                // Get perpendicular line from direction to camera and drag axis
+                var lineA = pointB.subtract(pointA);
+                var lineB = pointC.subtract(pointA);
+                var perpLine = BABYLON.Vector3.Cross(lineA, lineB);
+                // Get perpendicular line from previous result and drag axis to adjust lineB to be perpendiculat to camera
+                var norm = BABYLON.Vector3.Cross(lineA, perpLine).normalize();
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(pointA.add(norm));
+            }
+            else if (this.options.dragPlaneNormal) {
+                this._dragPlane.position.copyFrom(pointA);
+                this._dragPlane.lookAt(pointA.add(this.options.dragPlaneNormal));
+            }
+            this._dragPlane.computeWorldMatrix(true);
+        };
+        /**
+         *  Detaches the behavior from the mesh
+         */
+        PointerDragBehavior.prototype.detach = function () {
+            if (this._pointerObserver) {
+                this._scene.onPointerObservable.remove(this._pointerObserver);
+            }
+        };
+        return PointerDragBehavior;
+    }());
+    BABYLON.PointerDragBehavior = PointerDragBehavior;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.pointerDragBehavior.js.map
+
 
 
 var BABYLON;
 var BABYLON;
 (function (BABYLON) {
 (function (BABYLON) {
@@ -108445,6 +108641,7 @@ var SceneManager = /** @class */ (function () {
             _this.camera.beta = (_this._viewer.configuration.camera && _this._viewer.configuration.camera.beta) || _this.camera.beta;
             _this.camera.beta = (_this._viewer.configuration.camera && _this._viewer.configuration.camera.beta) || _this.camera.beta;
             _this.camera.radius = (_this._viewer.configuration.camera && _this._viewer.configuration.camera.radius) || _this.camera.radius;
             _this.camera.radius = (_this._viewer.configuration.camera && _this._viewer.configuration.camera.radius) || _this.camera.radius;
         };
         };
+        this._cameraBehaviorMapping = {};
         this.models = [];
         this.models = [];
         this.onCameraConfiguredObservable = new babylonjs_1.Observable();
         this.onCameraConfiguredObservable = new babylonjs_1.Observable();
         this.onLightsConfiguredObservable = new babylonjs_1.Observable();
         this.onLightsConfiguredObservable = new babylonjs_1.Observable();
@@ -109065,8 +109262,8 @@ var SceneManager = /** @class */ (function () {
         }
         }
         if (cameraConfig.behaviors) {
         if (cameraConfig.behaviors) {
             for (var name_1 in cameraConfig.behaviors) {
             for (var name_1 in cameraConfig.behaviors) {
-                if (cameraConfig.behaviors[name_1]) {
-                    this._setCameraBehavior(cameraConfig.behaviors[name_1]);
+                if (cameraConfig.behaviors[name_1] !== undefined) {
+                    this._setCameraBehavior(name_1, cameraConfig.behaviors[name_1]);
                 }
                 }
             }
             }
         }
         }
@@ -109523,22 +109720,37 @@ var SceneManager = /** @class */ (function () {
             this.scene.dispose();
             this.scene.dispose();
         }
         }
     };
     };
-    SceneManager.prototype._setCameraBehavior = function (behaviorConfig, payload) {
+    SceneManager.prototype._setCameraBehavior = function (name, behaviorConfig, payload) {
         var behavior;
         var behavior;
-        var type = (typeof behaviorConfig !== "object") ? behaviorConfig : behaviorConfig.type;
+        var type;
+        if (typeof behaviorConfig === 'object') {
+            type = behaviorConfig.type;
+        }
+        else if (typeof behaviorConfig === 'number') {
+            type = behaviorConfig;
+        }
+        else {
+            type = this._cameraBehaviorMapping[name];
+        }
+        if (type === undefined)
+            return;
         var config = (typeof behaviorConfig === "object") ? behaviorConfig : {};
         var config = (typeof behaviorConfig === "object") ? behaviorConfig : {};
+        var enabled = true;
+        if (typeof behaviorConfig === 'boolean') {
+            enabled = behaviorConfig;
+        }
         // constructing behavior
         // constructing behavior
         switch (type) {
         switch (type) {
             case 0 /* AUTOROTATION */:
             case 0 /* AUTOROTATION */:
-                this.camera.useAutoRotationBehavior = true;
+                this.camera.useAutoRotationBehavior = enabled;
                 behavior = this.camera.autoRotationBehavior;
                 behavior = this.camera.autoRotationBehavior;
                 break;
                 break;
             case 1 /* BOUNCING */:
             case 1 /* BOUNCING */:
-                this.camera.useBouncingBehavior = true;
+                this.camera.useBouncingBehavior = enabled;
                 behavior = this.camera.bouncingBehavior;
                 behavior = this.camera.bouncingBehavior;
                 break;
                 break;
             case 2 /* FRAMING */:
             case 2 /* FRAMING */:
-                this.camera.useFramingBehavior = true;
+                this.camera.useFramingBehavior = enabled;
                 behavior = this.camera.framingBehavior;
                 behavior = this.camera.framingBehavior;
                 break;
                 break;
             default:
             default:
@@ -109546,6 +109758,7 @@ var SceneManager = /** @class */ (function () {
                 break;
                 break;
         }
         }
         if (behavior) {
         if (behavior) {
+            this._cameraBehaviorMapping[name] = type;
             if (typeof behaviorConfig === "object") {
             if (typeof behaviorConfig === "object") {
                 helper_1.extendClassWithConfig(behavior, behaviorConfig);
                 helper_1.extendClassWithConfig(behavior, behaviorConfig);
             }
             }

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

@@ -1360,16 +1360,17 @@ declare module 'babylonjs-viewer/configuration/configuration' {
             exposure?: number;
             exposure?: number;
             pinchPrecision?: number;
             pinchPrecision?: number;
             behaviors?: {
             behaviors?: {
-                    [name: string]: number | {
-                            type: number;
-                            [propName: string]: any;
-                    };
+                    [name: string]: boolean | number | ICameraBehaviorConfiguration;
             };
             };
             disableCameraControl?: boolean;
             disableCameraControl?: boolean;
             disableCtrlForPanning?: boolean;
             disableCtrlForPanning?: boolean;
             disableAutoFocus?: boolean;
             disableAutoFocus?: boolean;
             [propName: string]: any;
             [propName: string]: any;
     }
     }
+    export interface ICameraBehaviorConfiguration {
+            type: number;
+            [propName: string]: any;
+    }
     export interface ILightConfiguration {
     export interface ILightConfiguration {
             type: number;
             type: number;
             name?: string;
             name?: string;

+ 3 - 3
gui/src/3D/controls/button3D.ts

@@ -93,17 +93,17 @@ module BABYLON.GUI {
             }
             }
             faceUV[1] = new BABYLON.Vector4(0, 0, 1, 1);
             faceUV[1] = new BABYLON.Vector4(0, 0, 1, 1);
 
 
-            let mesh = MeshBuilder.CreateBox(this.name + "Mesh", {
+            let mesh = MeshBuilder.CreateBox(this.name + "_rootMesh", {
                 width: 1.0, 
                 width: 1.0, 
                 height: 1.0,
                 height: 1.0,
-                depth: 0.05,
+                depth: 0.08,
                 faceUV: faceUV
                 faceUV: faceUV
             }, scene); 
             }, scene); 
            
            
             return mesh;
             return mesh;
         }
         }
 
 
-        protected _affectMaterial(mesh: Mesh) {
+        protected _affectMaterial(mesh: AbstractMesh) {
             let material = new StandardMaterial(this.name + "Material", mesh.getScene());
             let material = new StandardMaterial(this.name + "Material", mesh.getScene());
             material.specularColor = Color3.Black();
             material.specularColor = Color3.Black();
 
 

+ 8 - 4
gui/src/3D/controls/control3D.ts

@@ -184,8 +184,12 @@ module BABYLON.GUI {
         /**
         /**
          * Gets the mesh used to render this control
          * Gets the mesh used to render this control
          */
          */
-        public get mesh(): Nullable<Mesh> {
-            return this._node as Mesh;
+        public get mesh(): Nullable<AbstractMesh> {
+            if (this._node instanceof AbstractMesh) {
+                return this._node as AbstractMesh;
+            }
+
+            return null;
         }
         }
 
 
         /**
         /**
@@ -225,7 +229,7 @@ module BABYLON.GUI {
          * Node creation.
          * Node creation.
          * Can be overriden by children
          * Can be overriden by children
          * @param scene defines the scene where the node must be attached
          * @param scene defines the scene where the node must be attached
-         * @returns the attached node or null if none
+         * @returns the attached node or null if none. Must return a Mesh or AbstractMesh if there is an atttached visible object
          */
          */
         protected _createNode(scene: Scene): Nullable<TransformNode> {
         protected _createNode(scene: Scene): Nullable<TransformNode> {
             // Do nothing by default
             // Do nothing by default
@@ -236,7 +240,7 @@ module BABYLON.GUI {
          * Affect a material to the given mesh
          * Affect a material to the given mesh
          * @param mesh defines the mesh which will represent the control
          * @param mesh defines the mesh which will represent the control
          */
          */
-        protected _affectMaterial(mesh: Mesh) {
+        protected _affectMaterial(mesh: AbstractMesh) {
             mesh.material = null;
             mesh.material = null;
         }
         }
 
 

+ 72 - 16
gui/src/3D/controls/holographicButton.ts

@@ -5,8 +5,29 @@ module BABYLON.GUI {
      * Class used to create a holographic button in 3D
      * Class used to create a holographic button in 3D
      */
      */
     export class HolographicButton extends Button3D {
     export class HolographicButton extends Button3D {
+        private _backPlate: Mesh;
+        private _textPlate: Mesh;
         private _frontPlate: Mesh;
         private _frontPlate: Mesh;
-        private _fluentMaterial: FluentMaterial;
+        private _backFluentMaterial: FluentMaterial;
+        private _frontFluentMaterial: FluentMaterial;
+        private _text: string;
+        // private _imageUrl: string;
+
+        /**
+         * Gets or sets text for the button
+         */
+        public get text(): string {
+            return this._text;
+        }
+
+        public set text(value: string) {
+            if (this._text === value) {
+                return;
+            }
+
+            this._text = value;
+            this._rebuildContent();
+        }
 
 
         /**
         /**
          * Creates a new button
          * Creates a new button
@@ -21,14 +42,14 @@ module BABYLON.GUI {
                 if (!this.mesh) {
                 if (!this.mesh) {
                     return;
                     return;
                 }
                 }
-                this._frontPlate.edgesRenderer!.isEnabled = true;
+                this._frontPlate.setEnabled(true);
             }
             }
 
 
             this.pointerOutAnimation = () => {
             this.pointerOutAnimation = () => {
                 if (!this.mesh) {
                 if (!this.mesh) {
                     return;
                     return;
                 }
                 }
-                this._frontPlate.edgesRenderer!.isEnabled = false;
+                this._frontPlate.setEnabled(false);
             }                      
             }                      
         }
         }
     
     
@@ -36,21 +57,49 @@ module BABYLON.GUI {
             return "HolographicButton";
             return "HolographicButton";
         }        
         }        
 
 
+        private _rebuildContent(): void {
+            let panel = new StackPanel();
+            panel.isVertical = true;
+
+            if (this._text) {
+                let text = new BABYLON.GUI.TextBlock();
+                text.text = this._text;
+                text.color = "white";
+                text.height = "30px";
+                text.fontSize = 24;
+                panel.addControl(text);
+            }
+
+            if (this._frontPlate) {
+                this.content = panel;
+            }
+        }
+
         // Mesh association
         // Mesh association
         protected _createNode(scene: Scene): TransformNode {
         protected _createNode(scene: Scene): TransformNode {
-            var mesh = <Mesh>super._createNode(scene);
+            this._backPlate = MeshBuilder.CreateBox(this.name + "BackMesh", {
+                width: 1.0, 
+                height: 1.0,
+                depth: 0.08
+            }, scene); 
 
 
-            this._frontPlate= <Mesh>super._createNode(scene);
-            this._frontPlate.parent = mesh;
-            this._frontPlate.position.z = -0.05;
+            this._frontPlate = MeshBuilder.CreateBox(this.name + "FrontMesh", {
+                width: 1.0, 
+                height: 1.0,
+                depth: 0.08
+            }, scene); 
+
+            this._frontPlate.parent = this._backPlate;
+            this._frontPlate.position.z = -0.08;
             this._frontPlate.isPickable = false;
             this._frontPlate.isPickable = false;
+            this._frontPlate.setEnabled(false);
 
 
-            this._frontPlate.edgesWidth = 1.0;
-            this._frontPlate.edgesColor = new Color4(1.0, 1.0, 1.0, 1.0);
-            this._frontPlate.enableEdgesRendering();
-            this._frontPlate.edgesRenderer!.isEnabled = false;
-            
-            return mesh;
+            this._textPlate= <Mesh>super._createNode(scene);
+            this._textPlate.parent = this._backPlate;
+            this._textPlate.position.z = -0.08;
+            this._textPlate.isPickable = false;            
+           
+            return this._backPlate;
         }
         }
 
 
         protected _applyFacade(facadeTexture: AdvancedDynamicTexture) {
         protected _applyFacade(facadeTexture: AdvancedDynamicTexture) {
@@ -59,10 +108,17 @@ module BABYLON.GUI {
         }        
         }        
 
 
         protected _affectMaterial(mesh: Mesh) {
         protected _affectMaterial(mesh: Mesh) {
-            super._affectMaterial(this._frontPlate);
-            this._fluentMaterial = new FluentMaterial(this.name + "Material", mesh.getScene());
+            this._backFluentMaterial = new FluentMaterial(this.name + "Back Material", mesh.getScene());
+            mesh.material = this._backFluentMaterial;
+
+            this._frontFluentMaterial = new FluentMaterial(this.name + "Front Material", mesh.getScene());
+            this._frontPlate.material = this._frontFluentMaterial;
+            this._frontFluentMaterial.innerGlowColorIntensity = 0; // No inner glow
+            this._frontFluentMaterial.alpha = 0.5; // Additive
+            this._frontFluentMaterial.renderBorders = true;
 
 
-            mesh.material = this._fluentMaterial;
+            super._affectMaterial(this._textPlate);
+            this._rebuildContent();
         }
         }
     }
     }
 }
 }

+ 4 - 1
gui/src/3D/controls/stackPanel3D.ts

@@ -20,7 +20,10 @@ module BABYLON.GUI {
             }
             }
 
 
             this._isVertical = value;
             this._isVertical = value;
-            this._arrangeChildren();
+
+            Tools.SetImmediate(() => {
+                this._arrangeChildren();               
+            });
         }
         }
 
 
         /**
         /**

+ 125 - 55
gui/src/3D/materials/fluentMaterial.ts

@@ -1,18 +1,74 @@
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
 
 module BABYLON.GUI {
 module BABYLON.GUI {
+
+    /** @hidden */
+    export class FluentMaterialDefines extends MaterialDefines {
+        public INNERGLOW = false;
+        public BORDER = false;
+    
+        constructor() {
+            super();
+            this.rebuild();
+        }
+    }
+
     /**
     /**
      * Class used to render controls with fluent desgin
      * Class used to render controls with fluent desgin
      */
      */
     export class FluentMaterial extends PushMaterial {    
     export class FluentMaterial extends PushMaterial {    
-        @serializeAsTexture("emissiveTexture")
-        private _emissiveTexture: BaseTexture;
+
+        /**
+         * Gets or sets inner glow intensity. A value of 0 means no glow (default is 0.5)
+         */
+        @serialize()
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-        public emissiveTexture: BaseTexture;
+        public innerGlowColorIntensity = 0.5;
 
 
-        private _renderId: number;
+        /**
+         * Gets or sets the inner glow color (white by default)
+         */
+        @serializeAsColor3()
+        public innerGlowColor = new Color3(1.0, 1.0, 1.0);
+
+        /**
+         * Gets or sets alpha value (default is 1.0)
+         */
+        @serialize()
+        public alpha = 1.0;        
 
 
         /**
         /**
+         * Gets or sets the albedo color (Default is Color3(0.3, 0.35, 0.4))
+         */
+        @serializeAsColor3()
+        public albedoColor = new Color3(0.3, 0.35, 0.4);
+
+        /**
+         * Gets or sets a boolean indicating if borders must be rendered (default is false)
+         */
+        @serialize()
+        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+        public renderBorders = false;        
+
+        /**
+         * Gets or sets border width (default is 0.5)
+         */
+        @serialize()
+        public borderWidth = 0.5;      
+        
+        /**
+         * Gets or sets a value indicating the smoothing value applied to border edges (0.02 by default)
+         */
+        @serialize()
+        public edgeSmoothingValue = 0.02;       
+        
+        /**
+         * Gets or sets the minimum value that can be applied to border width (default is 0.1)
+         */
+        @serialize()
+        public borderMinValue = 0.1;        
+        
+        /**
          * Creates a new Fluent material
          * Creates a new Fluent material
          * @param name defines the name of the material
          * @param name defines the name of the material
          * @param scene defines the hosting scene
          * @param scene defines the hosting scene
@@ -22,7 +78,7 @@ module BABYLON.GUI {
         }
         }
 
 
         public needAlphaBlending(): boolean {
         public needAlphaBlending(): boolean {
-            return false;
+            return this.alpha !== 1.0;
         }
         }
 
 
         public needAlphaTesting(): boolean {
         public needAlphaTesting(): boolean {
@@ -40,56 +96,70 @@ module BABYLON.GUI {
                 }
                 }
             }
             }
 
 
-            var scene = this.getScene();
+            if (!subMesh._materialDefines) {
+                subMesh._materialDefines = new FluentMaterialDefines();
+            }
 
 
+            var scene = this.getScene();
+            var defines = <FluentMaterialDefines>subMesh._materialDefines;
             if (!this.checkReadyOnEveryCall && subMesh.effect) {
             if (!this.checkReadyOnEveryCall && subMesh.effect) {
-                if (this._renderId === scene.getRenderId()) {
+                if (defines._renderId === scene.getRenderId()) {
                     return true;
                     return true;
                 }
                 }
+            }            
+
+            if (defines._areTexturesDirty) {
+                defines.INNERGLOW = this.innerGlowColorIntensity > 0;
+                defines.BORDER = this.renderBorders;
             }
             }
 
 
             var engine = scene.getEngine();
             var engine = scene.getEngine();
+            // Get correct effect      
+            if (defines.isDirty) {
+                defines.markAsProcessed();
+                scene.resetCachedMaterial();
 
 
-            scene.resetCachedMaterial();
-
-            //Attributes
-            var attribs = [VertexBuffer.PositionKind];
-            attribs.push(VertexBuffer.NormalKind);
-            attribs.push(VertexBuffer.UVKind);
-
-            var shaderName = "fluent";
+                //Attributes
+                var attribs = [VertexBuffer.PositionKind];
+                attribs.push(VertexBuffer.NormalKind);
+                attribs.push(VertexBuffer.UVKind);
 
 
-            var uniforms = ["world", "viewProjection", "emissiveMatrix"];
+                var shaderName = "fluent";
 
 
-            var samplers = ["emissiveSampler"]
-            var uniformBuffers = new Array<string>();
+                var uniforms = ["world", "viewProjection", "innerGlowColor", "albedoColor", "borderWidth", "edgeSmoothingValue", "scaleFactor", "borderMinValue"];
 
 
-            MaterialHelper.PrepareUniformsAndSamplersList(<EffectCreationOptions>{
-                uniformsNames: uniforms,
-                uniformBuffersNames: uniformBuffers,
-                samplers: samplers,
-                defines: "",
-                maxSimultaneousLights: 4
-            });            
+                var samplers = new Array<String>();
+                var uniformBuffers = new Array<string>();
 
 
-            subMesh.setEffect(scene.getEngine().createEffect(shaderName,
-                <EffectCreationOptions>{
-                    attributes: attribs,
+                MaterialHelper.PrepareUniformsAndSamplersList(<EffectCreationOptions>{
                     uniformsNames: uniforms,
                     uniformsNames: uniforms,
                     uniformBuffersNames: uniformBuffers,
                     uniformBuffersNames: uniformBuffers,
                     samplers: samplers,
                     samplers: samplers,
-                    defines: "",
-                    fallbacks: null,
-                    onCompiled: this.onCompiled,
-                    onError: this.onError,
-                    indexParameters: { maxSimultaneousLights: 4 }
-                }, engine));
-
-                if (!subMesh.effect || !subMesh.effect.isReady()) {
+                    defines: defines,
+                    maxSimultaneousLights: 4
+                });            
+
+                var join = defines.toString();
+                subMesh.setEffect(scene.getEngine().createEffect(shaderName,
+                    <EffectCreationOptions>{
+                        attributes: attribs,
+                        uniformsNames: uniforms,
+                        uniformBuffersNames: uniformBuffers,
+                        samplers: samplers,
+                        defines: join,
+                        fallbacks: null,
+                        onCompiled: this.onCompiled,
+                        onError: this.onError,
+                        indexParameters: { maxSimultaneousLights: 4 }
+                    }, engine));            
+                
+            }
+
+            if (!subMesh.effect || !subMesh.effect.isReady()) {
                 return false;
                 return false;
             }
             }
 
 
-            this._renderId = scene.getRenderId();
+            defines._renderId = scene.getRenderId();
             this._wasPreviouslyReady = true;
             this._wasPreviouslyReady = true;
 
 
             return true;
             return true;
@@ -98,6 +168,11 @@ module BABYLON.GUI {
         public bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void {
         public bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void {
             var scene = this.getScene();
             var scene = this.getScene();
 
 
+            var defines = <FluentMaterialDefines>subMesh._materialDefines;
+            if (!defines) {
+                return;
+            }            
+
             var effect = subMesh.effect;
             var effect = subMesh.effect;
             if (!effect) {
             if (!effect) {
                 return;
                 return;
@@ -109,13 +184,20 @@ module BABYLON.GUI {
             this._activeEffect.setMatrix("viewProjection", scene.getTransformMatrix());
             this._activeEffect.setMatrix("viewProjection", scene.getTransformMatrix());
 
 
 
 
-            if (this._mustRebind(scene, effect)) {
-                // Textures        
-                if (this._emissiveTexture && StandardMaterial.DiffuseTextureEnabled) {
-                    this._activeEffect.setTexture("emissiveSampler", this._emissiveTexture);
+            if (this._mustRebind(scene, effect)) {   
+                this._activeEffect.setColor4("albedoColor", this.albedoColor, this.alpha);
+
+                if (defines.INNERGLOW) {
+                    this._activeEffect.setColor4("innerGlowColor", this.innerGlowColor, this.innerGlowColorIntensity);
+                }
+
+                if (defines.BORDER) {
+                    this._activeEffect.setFloat("borderWidth", this.borderWidth);
+                    this._activeEffect.setFloat("edgeSmoothingValue", this.edgeSmoothingValue);
+                    this._activeEffect.setFloat("borderMinValue", this.borderMinValue);
 
 
-                    this._activeEffect.setMatrix("emissiveMatrix", this._emissiveTexture.getTextureMatrix());
-                }                
+                    this._activeEffect.setVector3("scaleFactor", mesh.getBoundingInfo().boundingBox.extendSizeWorld);
+                }
             }
             }
 
 
             this._afterBind(mesh, this._activeEffect);
             this._afterBind(mesh, this._activeEffect);
@@ -124,10 +206,6 @@ module BABYLON.GUI {
         public getActiveTextures(): BaseTexture[] {
         public getActiveTextures(): BaseTexture[] {
             var activeTextures = super.getActiveTextures();
             var activeTextures = super.getActiveTextures();
 
 
-            if (this._emissiveTexture) {
-                activeTextures.push(this._emissiveTexture);
-            }
-
             return activeTextures;
             return activeTextures;
         }        
         }        
 
 
@@ -136,18 +214,10 @@ module BABYLON.GUI {
                 return true;
                 return true;
             }
             }
 
 
-            if (this._emissiveTexture === texture) {
-                return true;
-            }
-
             return false;
             return false;
         }        
         }        
         
         
         public dispose(forceDisposeEffect?: boolean): void {
         public dispose(forceDisposeEffect?: boolean): void {
-            if (this._emissiveTexture) {
-                this._emissiveTexture.dispose();
-            }
-
             super.dispose(forceDisposeEffect);
             super.dispose(forceDisposeEffect);
         }
         }
 
 

+ 45 - 4
gui/src/3D/materials/shaders/fluent.fragment.fx

@@ -1,10 +1,51 @@
 precision highp float;
 precision highp float;
 
 
-varying vec2 vEmissiveUV;
-uniform sampler2D emissiveSampler;
+varying vec2 vUV;
 
 
+uniform vec4 albedoColor;
+
+#ifdef INNERGLOW
+uniform vec4 innerGlowColor;
+#endif
+
+#ifdef BORDER
+varying vec2 scaleInfo;
+uniform float edgeSmoothingValue;
+uniform float borderMinValue;
+
+#endif
 
 
 void main(void) {
 void main(void) {
-	vec3 emissiveColor = texture2D(emissiveSampler, vEmissiveUV).rgb;
-	gl_FragColor = vec4(emissiveColor, 1.0);
+
+	vec3 albedo = albedoColor.rgb;
+	float alpha = albedoColor.a;
+
+#ifdef BORDER	
+	float borderPower = 10.0;
+	float inverseBorderPower = 1.0 / borderPower;
+	float pointToHover = 1.0;
+	vec3 borderColor = albedo * borderPower;
+
+	vec2 distanceToEdge;
+    distanceToEdge.x = abs(vUV.x - 0.5) * 2.0;
+    distanceToEdge.y = abs(vUV.y - 0.5) * 2.0;
+
+    float borderValue = max(smoothstep(scaleInfo.x - edgeSmoothingValue, scaleInfo.x + edgeSmoothingValue, distanceToEdge.x),
+                            smoothstep(scaleInfo.y - edgeSmoothingValue, scaleInfo.y + edgeSmoothingValue, distanceToEdge.y));
+
+    borderColor = borderColor * borderValue * max(borderMinValue * inverseBorderPower, pointToHover);	
+	albedo += borderColor;
+	alpha = max(alpha, borderValue);
+#endif
+
+#ifdef INNERGLOW
+	// Inner glow color
+	vec2 uvGlow = (vUV - vec2(0.5, 0.5)) * (innerGlowColor.a * 2.0);
+	uvGlow = uvGlow * uvGlow;
+    uvGlow = uvGlow * uvGlow;
+
+    albedo += mix(vec3(0.0, 0.0, 0.0), innerGlowColor.rgb, uvGlow.x + uvGlow.y);	
+#endif
+
+	gl_FragColor = vec4(albedo, alpha);
 }
 }

+ 58 - 3
gui/src/3D/materials/shaders/fluent.vertex.fx

@@ -8,12 +8,67 @@ attribute vec2 uv;
 // Uniforms
 // Uniforms
 uniform mat4 world;
 uniform mat4 world;
 uniform mat4 viewProjection;
 uniform mat4 viewProjection;
-uniform mat4 emissiveMatrix;
 
 
-varying vec2 vEmissiveUV;
+varying vec2 vUV;
+
+#ifdef BORDER
+varying vec2 scaleInfo;
+uniform float borderWidth;
+uniform vec3 scaleFactor;
+#endif
 
 
 void main(void) {
 void main(void) {
-	vEmissiveUV = vec2(emissiveMatrix * vec4(uv, 1.0, 0.0));
+	vUV = uv;
+
+#ifdef BORDER
+	vec3 scale = scaleFactor;
+	float minScale = min(min(scale.x, scale.y), scale.z);
+    float maxScale = max(max(scale.x, scale.y), scale.z);
+	float minOverMiddleScale = minScale / (scale.x + scale.y + scale.z - minScale - maxScale);
+    float areaYZ = scale.y * scale.z;
+    float areaXZ = scale.x * scale.z;
+    float areaXY = scale.x * scale.y;
+    float scaledBorderWidth = borderWidth;	
+
+	if (abs(normal.x) == 1.0) // Y,Z plane.
+	{
+		scale.x = scale.y;
+		scale.y = scale.z;
+		
+		if (areaYZ > areaXZ && areaYZ > areaXY)
+		{
+			scaledBorderWidth *= minOverMiddleScale;
+		}
+	}
+	else if (abs(normal.y) == 1.0) // X,Z plane.
+	{
+		scale.x = scale.z;
+
+		if (areaXZ > areaXY && areaXZ > areaYZ)
+		{
+			scaledBorderWidth *= minOverMiddleScale;
+		}
+	}
+	else  // X,Y plane.
+	{
+		if (areaXY > areaYZ && areaXY > areaXZ)
+		{
+			scaledBorderWidth *= minOverMiddleScale;
+		}
+	}
+
+ 	float scaleRatio = min(scale.x, scale.y) / max(scale.x, scale.y);
+	if (scale.x > scale.y)
+	{
+		scaleInfo.x = 1.0 - (scaledBorderWidth * scaleRatio);
+		scaleInfo.y = 1.0 - scaledBorderWidth;
+	}
+	else
+	{
+		scaleInfo.x = 1.0 - scaledBorderWidth;
+		scaleInfo.y = 1.0 - (scaledBorderWidth * scaleRatio);
+	}	
+#endif		
 
 
 	gl_Position = viewProjection * world * vec4(position, 1.0);
 	gl_Position = viewProjection * world * vec4(position, 1.0);
 }
 }

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

@@ -25,7 +25,6 @@ module BABYLON {
             originalScene.getEngine().scenes.pop();
             originalScene.getEngine().scenes.pop();
 
 
             // Render directly on top of existing scene without clearing
             // Render directly on top of existing scene without clearing
-            this.utilityLayerScene.clearColor = new BABYLON.Color4(0,0,0,0);
             this.utilityLayerScene.autoClear = false;
             this.utilityLayerScene.autoClear = false;
 
 
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(()=>{
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(()=>{
@@ -61,7 +60,7 @@ module BABYLON {
         }
         }
 
 
         private _updateCamera(){
         private _updateCamera(){
-            this.utilityLayerScene.activeCamera=this.originalScene.activeCamera;
+            this.utilityLayerScene.activeCamera = this.originalScene.activeCamera;
         }
         }
     }
     }
 }
 }