Selaa lähdekoodia

More GUI3D: sphere container

David Catuhe 7 vuotta sitten
vanhempi
commit
2ab5535bab

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 12579 - 12546
Playground/babylon.d.txt


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 16472 - 16468
dist/preview release/babylon.d.ts


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/babylon.js


+ 10 - 6
dist/preview release/babylon.max.js

@@ -86959,6 +86959,10 @@ var BABYLON;
              * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
              */
             this.processAllEvents = false;
+            /**
+             * Observable raised when the pointer move from the utility layer scene to the main scene
+             */
+            this.onPointerOutObservable = new BABYLON.Observable();
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             originalScene.getEngine().scenes.pop();
@@ -87003,9 +87007,6 @@ var BABYLON;
                             if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {
-                                _this._pointerCaptures[pointerEvent.pointerId] = false;
-                            }
                         }
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
                             if (!prePointerInfo.skipOnPointerObservable) {
@@ -87016,11 +87017,14 @@ var BABYLON;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
                             // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId] !== BABYLON.PointerEventTypes.POINTERUP) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(BABYLON.PointerEventTypes.POINTERUP, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = BABYLON.PointerEventTypes.POINTERUP;
+                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
                         }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        }
                     }
                 }
             });

+ 10 - 6
dist/preview release/babylon.no-module.max.js

@@ -86926,6 +86926,10 @@ var BABYLON;
              * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
              */
             this.processAllEvents = false;
+            /**
+             * Observable raised when the pointer move from the utility layer scene to the main scene
+             */
+            this.onPointerOutObservable = new BABYLON.Observable();
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             originalScene.getEngine().scenes.pop();
@@ -86970,9 +86974,6 @@ var BABYLON;
                             if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {
-                                _this._pointerCaptures[pointerEvent.pointerId] = false;
-                            }
                         }
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
                             if (!prePointerInfo.skipOnPointerObservable) {
@@ -86983,11 +86984,14 @@ var BABYLON;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
                             // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId] !== BABYLON.PointerEventTypes.POINTERUP) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(BABYLON.PointerEventTypes.POINTERUP, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = BABYLON.PointerEventTypes.POINTERUP;
+                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
                         }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        }
                     }
                 }
             });

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/babylon.worker.js


+ 10 - 6
dist/preview release/es6.js

@@ -86926,6 +86926,10 @@ var BABYLON;
              * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
              */
             this.processAllEvents = false;
+            /**
+             * Observable raised when the pointer move from the utility layer scene to the main scene
+             */
+            this.onPointerOutObservable = new BABYLON.Observable();
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             originalScene.getEngine().scenes.pop();
@@ -86970,9 +86974,6 @@ var BABYLON;
                             if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {
-                                _this._pointerCaptures[pointerEvent.pointerId] = false;
-                            }
                         }
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
                             if (!prePointerInfo.skipOnPointerObservable) {
@@ -86983,11 +86984,14 @@ var BABYLON;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
                             // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId] !== BABYLON.PointerEventTypes.POINTERUP) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(BABYLON.PointerEventTypes.POINTERUP, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = BABYLON.PointerEventTypes.POINTERUP;
+                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
                         }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        }
                     }
                 }
             });

+ 34 - 5
dist/preview release/gui/babylon.gui.d.ts

@@ -1876,6 +1876,7 @@ declare module BABYLON.GUI {
         private _utilityLayer;
         private _rootContainer;
         private _pointerObserver;
+        private _pointerOutObserver;
         /** @hidden */
         _lastPickedControl: Control3D;
         /** @hidden */
@@ -1903,6 +1904,7 @@ declare module BABYLON.GUI {
          * @param scene
          */
         constructor(scene?: Scene);
+        private _handlePointerOut(pointerId, isPointerUp);
         private _doPicking(pi);
         /**
          * Gets the root container
@@ -2193,6 +2195,10 @@ declare module BABYLON.GUI {
          */
         protected _children: Control3D[];
         /**
+         * Gets the list of child controls
+         */
+        readonly children: Array<Control3D>;
+        /**
          * Gets or sets a boolean indicating if the layout must be blocked (default is false).
          * This is helpful to optimize layout operation when adding multiple children in a row
          */
@@ -2230,6 +2236,11 @@ declare module BABYLON.GUI {
          * Releases all associated resources
          */
         dispose(): void;
+        static readonly UNSET_ORIENTATION: number;
+        static readonly FACEORIGIN_ORIENTATION: number;
+        static readonly FACEORIGINREVERSED_ORIENTATION: number;
+        static readonly FACEFORWARD_ORIENTATION: number;
+        static readonly FACEFORWARDREVERSED_ORIENTATION: number;
     }
 }
 
@@ -2372,21 +2383,39 @@ declare module BABYLON.GUI {
     class SpherePanel extends Container3D {
         private _radius;
         private _columns;
+        private _rows;
         private _rowThenColum;
+        private _orientation;
         /**
-         * Gets or sets a boolean indicating if the layout must first fill rows then columns or the opposite (true by default)
+         * Gets or sets the distance between elements
          */
-        rowThenColum: boolean;
+        margin: number;
         /**
-         * Gets or sets a the radius of the sphere where to project controls (5 by default)
+         * Gets or sets the orientation to apply to all controls (BABYLON.Container3D.FaceOriginReversedOrientation by default)
+        * | Value | Type                                | Description |
+        * | ----- | ----------------------------------- | ----------- |
+        * | 0     | UNSET_ORIENTATION                   |  Control rotation will remain unchanged |
+        * | 1     | FACEORIGIN_ORIENTATION              |  Control will rotate to make it look at sphere central axis |
+        * | 2     | FACEORIGINREVERSED_ORIENTATION      |  Control will rotate to make it look back at sphere central axis |
+        * | 3     | FACEFORWARD_ORIENTATION             |  Control will rotate to look at z axis (0, 0, 1) |
+        * | 4     | FACEFORWARDREVERSED_ORIENTATION     |  Control will rotate to look at negative z axis (0, 0, -1) |
+         */
+        orientation: number;
+        /**
+         * Gets or sets the radius of the sphere where to project controls (5 by default)
          */
         radius: float;
         /**
-         * Gets or sets a the number of columns requested (10 by default).
-         * The panel will automatically compute the number of rows based on number of child controls
+         * Gets or sets the number of columns requested (10 by default).
+         * The panel will automatically compute the number of rows based on number of child controls.
          */
         columns: int;
         /**
+         * Gets or sets a the number of rows requested.
+         * The panel will automatically compute the number of columns based on number of child controls.
+         */
+        rows: int;
+        /**
          * Creates new SpherePanel
          */
         constructor();

+ 112 - 33
dist/preview release/gui/babylon.gui.js

@@ -6517,12 +6517,10 @@ var BABYLON;
                 this._rootContainer._host = this;
                 var utilityLayerScene = this._utilityLayer.utilityLayerScene;
                 // Events
+                this._pointerOutObserver = this._utilityLayer.onPointerOutObservable.add(function (pointerId) {
+                    _this._handlePointerOut(pointerId, true);
+                });
                 this._pointerObserver = utilityLayerScene.onPointerObservable.add(function (pi, state) {
-                    if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE
-                        && pi.type !== BABYLON.PointerEventTypes.POINTERUP
-                        && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
-                        return;
-                    }
                     _this._doPicking(pi);
                 });
                 // Scene
@@ -6546,6 +6544,20 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            GUI3DManager.prototype._handlePointerOut = function (pointerId, isPointerUp) {
+                var previousControlOver = this._lastControlOver[pointerId];
+                if (previousControlOver) {
+                    previousControlOver._onPointerOut(previousControlOver);
+                    delete this._lastControlOver[pointerId];
+                }
+                if (isPointerUp) {
+                    if (this._lastControlDown[pointerId]) {
+                        this._lastControlDown[pointerId].forcePointerUp();
+                        delete this._lastControlDown[pointerId];
+                    }
+                }
+                this.onPickedPointChangedObservable.notifyObservers(null);
+            };
             GUI3DManager.prototype._doPicking = function (pi) {
                 if (!this._utilityLayer || !this._utilityLayer.utilityLayerScene.activeCamera) {
                     return false;
@@ -6555,18 +6567,7 @@ var BABYLON;
                 var buttonIndex = pointerEvent.button;
                 var pickingInfo = pi.pickInfo;
                 if (!pickingInfo || !pickingInfo.hit) {
-                    var previousControlOver = this._lastControlOver[pointerId];
-                    if (previousControlOver) {
-                        previousControlOver._onPointerOut(previousControlOver);
-                        delete this._lastControlOver[pointerId];
-                    }
-                    if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
-                        if (this._lastControlDown[pointerEvent.pointerId]) {
-                            this._lastControlDown[pointerEvent.pointerId].forcePointerUp();
-                            delete this._lastControlDown[pointerEvent.pointerId];
-                        }
-                    }
-                    this.onPickedPointChangedObservable.notifyObservers(null);
+                    this._handlePointerOut(pointerId, pi.type === BABYLON.PointerEventTypes.POINTERUP);
                     return false;
                 }
                 var control = (pickingInfo.pickedMesh.metadata);
@@ -6637,6 +6638,10 @@ var BABYLON;
                     this._sharedMaterials[materialName].dispose();
                 }
                 this._sharedMaterials = {};
+                if (this._pointerOutObserver && this._utilityLayer) {
+                    this._utilityLayer.onPointerOutObservable.remove(this._pointerOutObserver);
+                    this._pointerOutObserver = null;
+                }
                 this.onPickedPointChangedObservable.clear();
                 var utilityLayerScene = this._utilityLayer ? this._utilityLayer.utilityLayerScene : null;
                 if (utilityLayerScene) {
@@ -7352,6 +7357,16 @@ var BABYLON;
                 _this._children = new Array();
                 return _this;
             }
+            Object.defineProperty(Container3D.prototype, "children", {
+                /**
+                 * Gets the list of child controls
+                 */
+                get: function () {
+                    return this._children;
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(Container3D.prototype, "blockLayout", {
                 /**
                  * Gets or sets a boolean indicating if the layout must be blocked (default is false).
@@ -7440,6 +7455,11 @@ var BABYLON;
                 this._children = [];
                 _super.prototype.dispose.call(this);
             };
+            Container3D.UNSET_ORIENTATION = 0;
+            Container3D.FACEORIGIN_ORIENTATION = 1;
+            Container3D.FACEORIGINREVERSED_ORIENTATION = 2;
+            Container3D.FACEFORWARD_ORIENTATION = 3;
+            Container3D.FACEFORWARDREVERSED_ORIENTATION = 4;
             return Container3D;
         }(GUI.Control3D));
         GUI.Container3D = Container3D;
@@ -7973,22 +7993,35 @@ var BABYLON;
                 var _this = _super.call(this) || this;
                 _this._radius = 5.0;
                 _this._columns = 10;
+                _this._rows = 0;
                 _this._rowThenColum = true;
+                _this._orientation = GUI.Container3D.FACEORIGIN_ORIENTATION;
+                /**
+                 * Gets or sets the distance between elements
+                 */
+                _this.margin = 0.1;
                 return _this;
             }
-            Object.defineProperty(SpherePanel.prototype, "rowThenColum", {
+            Object.defineProperty(SpherePanel.prototype, "orientation", {
                 /**
-                 * Gets or sets a boolean indicating if the layout must first fill rows then columns or the opposite (true by default)
+                 * Gets or sets the orientation to apply to all controls (BABYLON.Container3D.FaceOriginReversedOrientation by default)
+                * | Value | Type                                | Description |
+                * | ----- | ----------------------------------- | ----------- |
+                * | 0     | UNSET_ORIENTATION                   |  Control rotation will remain unchanged |
+                * | 1     | FACEORIGIN_ORIENTATION              |  Control will rotate to make it look at sphere central axis |
+                * | 2     | FACEORIGINREVERSED_ORIENTATION      |  Control will rotate to make it look back at sphere central axis |
+                * | 3     | FACEFORWARD_ORIENTATION             |  Control will rotate to look at z axis (0, 0, 1) |
+                * | 4     | FACEFORWARDREVERSED_ORIENTATION     |  Control will rotate to look at negative z axis (0, 0, -1) |
                  */
                 get: function () {
-                    return this._rowThenColum;
+                    return this._orientation;
                 },
                 set: function (value) {
                     var _this = this;
-                    if (this._rowThenColum === value) {
+                    if (this._orientation === value) {
                         return;
                     }
-                    this._rowThenColum = value;
+                    this._orientation = value;
                     BABYLON.Tools.SetImmediate(function () {
                         _this._arrangeChildren();
                     });
@@ -7998,7 +8031,7 @@ var BABYLON;
             });
             Object.defineProperty(SpherePanel.prototype, "radius", {
                 /**
-                 * Gets or sets a the radius of the sphere where to project controls (5 by default)
+                 * Gets or sets the radius of the sphere where to project controls (5 by default)
                  */
                 get: function () {
                     return this._radius;
@@ -8018,8 +8051,8 @@ var BABYLON;
             });
             Object.defineProperty(SpherePanel.prototype, "columns", {
                 /**
-                 * Gets or sets a the number of columns requested (10 by default).
-                 * The panel will automatically compute the number of rows based on number of child controls
+                 * Gets or sets the number of columns requested (10 by default).
+                 * The panel will automatically compute the number of rows based on number of child controls.
                  */
                 get: function () {
                     return this._columns;
@@ -8030,6 +8063,29 @@ var BABYLON;
                         return;
                     }
                     this._columns = value;
+                    this._rowThenColum = true;
+                    BABYLON.Tools.SetImmediate(function () {
+                        _this._arrangeChildren();
+                    });
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(SpherePanel.prototype, "rows", {
+                /**
+                 * Gets or sets a the number of rows requested.
+                 * The panel will automatically compute the number of columns based on number of child controls.
+                 */
+                get: function () {
+                    return this._rows;
+                },
+                set: function (value) {
+                    var _this = this;
+                    if (this._rows === value) {
+                        return;
+                    }
+                    this._rows = value;
+                    this._rowThenColum = false;
                     BABYLON.Tools.SetImmediate(function () {
                         _this._arrangeChildren();
                     });
@@ -8041,6 +8097,7 @@ var BABYLON;
                 var cellWidth = 0;
                 var cellHeight = 0;
                 var rows = 0;
+                var columns = 0;
                 var controlCount = 0;
                 var currentInverseWorld = BABYLON.Matrix.Invert(this.node.computeWorldMatrix(true));
                 // Measure
@@ -8057,17 +8114,25 @@ var BABYLON;
                     cellWidth = Math.max(cellWidth, extendSize.x * 2);
                     cellHeight = Math.max(cellHeight, extendSize.y * 2);
                 }
-                console.log(cellWidth + "x" + cellHeight);
+                //     cellWidth += this.margin * 2;
+                //   cellHeight += this.margin * 2;
                 // Arrange
-                rows = Math.ceil(controlCount / this._columns);
-                var startOffsetX = (this._columns * 0.5) * cellWidth;
+                if (this._rowThenColum) {
+                    columns = this._columns;
+                    rows = Math.ceil(controlCount / this._columns);
+                }
+                else {
+                    rows = this._rows;
+                    columns = Math.ceil(controlCount / this._rows);
+                }
+                var startOffsetX = (columns * 0.5) * cellWidth;
                 var startOffsetY = (rows * 0.5) * cellHeight;
                 var nodeGrid = [];
                 var cellCounter = 0;
                 if (this._rowThenColum) {
                     for (var r = 0; r < rows; r++) {
-                        for (var c = 0; c < this._columns; c++) {
-                            nodeGrid.push(new BABYLON.Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, -(r * cellHeight) - startOffsetY - cellHeight / 2, 0));
+                        for (var c = 0; c < columns; c++) {
+                            nodeGrid.push(new BABYLON.Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, (r * cellHeight) - startOffsetY + cellHeight / 2, 0));
                             cellCounter++;
                             if (cellCounter > controlCount) {
                                 break;
@@ -8076,9 +8141,9 @@ var BABYLON;
                     }
                 }
                 else {
-                    for (var c = 0; c < this._columns; c++) {
+                    for (var c = 0; c < columns; c++) {
                         for (var r = 0; r < rows; r++) {
-                            nodeGrid.push(new BABYLON.Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, -(r * cellHeight) - startOffsetY - cellHeight / 2, 0));
+                            nodeGrid.push(new BABYLON.Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, (r * cellHeight) - startOffsetY + cellHeight / 2, 0));
                             cellCounter++;
                             if (cellCounter > controlCount) {
                                 break;
@@ -8093,6 +8158,20 @@ var BABYLON;
                         continue;
                     }
                     var newPos = this._sphericalMapping(nodeGrid[cellCounter]);
+                    switch (this._orientation) {
+                        case GUI.Container3D.FACEORIGIN_ORIENTATION:
+                            child.mesh.lookAt(new BABYLON.Vector3(-newPos.x, 0, -newPos.z));
+                            break;
+                        case GUI.Container3D.FACEORIGINREVERSED_ORIENTATION:
+                            child.mesh.lookAt(new BABYLON.Vector3(newPos.x, 0, newPos.z));
+                            break;
+                        case GUI.Container3D.FACEFORWARD_ORIENTATION:
+                            child.mesh.lookAt(new BABYLON.Vector3(0, 0, 1));
+                            break;
+                        case GUI.Container3D.FACEFORWARDREVERSED_ORIENTATION:
+                            child.mesh.lookAt(new BABYLON.Vector3(0, 0, -1));
+                            break;
+                    }
                     child.position = newPos;
                     cellCounter++;
                 }
@@ -8102,7 +8181,7 @@ var BABYLON;
                 var xAngle = (source.y / this._radius);
                 var yAngle = -(source.x / this._radius);
                 BABYLON.Matrix.RotationYawPitchRollToRef(yAngle, xAngle, 0, BABYLON.Tmp.Matrix[0]);
-                return BABYLON.Vector3.TransformCoordinates(newPos, BABYLON.Tmp.Matrix[0]);
+                return BABYLON.Vector3.TransformNormal(newPos, BABYLON.Tmp.Matrix[0]);
             };
             return SpherePanel;
         }(GUI.Container3D));

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


+ 34 - 5
dist/preview release/gui/babylon.gui.module.d.ts

@@ -1881,6 +1881,7 @@ declare module BABYLON.GUI {
         private _utilityLayer;
         private _rootContainer;
         private _pointerObserver;
+        private _pointerOutObserver;
         /** @hidden */
         _lastPickedControl: Control3D;
         /** @hidden */
@@ -1908,6 +1909,7 @@ declare module BABYLON.GUI {
          * @param scene
          */
         constructor(scene?: Scene);
+        private _handlePointerOut(pointerId, isPointerUp);
         private _doPicking(pi);
         /**
          * Gets the root container
@@ -2198,6 +2200,10 @@ declare module BABYLON.GUI {
          */
         protected _children: Control3D[];
         /**
+         * Gets the list of child controls
+         */
+        readonly children: Array<Control3D>;
+        /**
          * Gets or sets a boolean indicating if the layout must be blocked (default is false).
          * This is helpful to optimize layout operation when adding multiple children in a row
          */
@@ -2235,6 +2241,11 @@ declare module BABYLON.GUI {
          * Releases all associated resources
          */
         dispose(): void;
+        static readonly UNSET_ORIENTATION: number;
+        static readonly FACEORIGIN_ORIENTATION: number;
+        static readonly FACEORIGINREVERSED_ORIENTATION: number;
+        static readonly FACEFORWARD_ORIENTATION: number;
+        static readonly FACEFORWARDREVERSED_ORIENTATION: number;
     }
 }
 
@@ -2377,21 +2388,39 @@ declare module BABYLON.GUI {
     class SpherePanel extends Container3D {
         private _radius;
         private _columns;
+        private _rows;
         private _rowThenColum;
+        private _orientation;
         /**
-         * Gets or sets a boolean indicating if the layout must first fill rows then columns or the opposite (true by default)
+         * Gets or sets the distance between elements
          */
-        rowThenColum: boolean;
+        margin: number;
         /**
-         * Gets or sets a the radius of the sphere where to project controls (5 by default)
+         * Gets or sets the orientation to apply to all controls (BABYLON.Container3D.FaceOriginReversedOrientation by default)
+        * | Value | Type                                | Description |
+        * | ----- | ----------------------------------- | ----------- |
+        * | 0     | UNSET_ORIENTATION                   |  Control rotation will remain unchanged |
+        * | 1     | FACEORIGIN_ORIENTATION              |  Control will rotate to make it look at sphere central axis |
+        * | 2     | FACEORIGINREVERSED_ORIENTATION      |  Control will rotate to make it look back at sphere central axis |
+        * | 3     | FACEFORWARD_ORIENTATION             |  Control will rotate to look at z axis (0, 0, 1) |
+        * | 4     | FACEFORWARDREVERSED_ORIENTATION     |  Control will rotate to look at negative z axis (0, 0, -1) |
+         */
+        orientation: number;
+        /**
+         * Gets or sets the radius of the sphere where to project controls (5 by default)
          */
         radius: float;
         /**
-         * Gets or sets a the number of columns requested (10 by default).
-         * The panel will automatically compute the number of rows based on number of child controls
+         * Gets or sets the number of columns requested (10 by default).
+         * The panel will automatically compute the number of rows based on number of child controls.
          */
         columns: int;
         /**
+         * Gets or sets a the number of rows requested.
+         * The panel will automatically compute the number of columns based on number of child controls.
+         */
+        rows: int;
+        /**
          * Creates new SpherePanel
          */
         constructor();

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1 - 1
dist/preview release/viewer/babylon.viewer.js


+ 10 - 6
dist/preview release/viewer/babylon.viewer.max.js

@@ -87047,6 +87047,10 @@ var BABYLON;
              * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
              */
             this.processAllEvents = false;
+            /**
+             * Observable raised when the pointer move from the utility layer scene to the main scene
+             */
+            this.onPointerOutObservable = new BABYLON.Observable();
             // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
             this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
             originalScene.getEngine().scenes.pop();
@@ -87091,9 +87095,6 @@ var BABYLON;
                             if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 _this._pointerCaptures[pointerEvent.pointerId] = true;
                             }
-                            else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {
-                                _this._pointerCaptures[pointerEvent.pointerId] = false;
-                            }
                         }
                         if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
                             if (!prePointerInfo.skipOnPointerObservable) {
@@ -87104,11 +87105,14 @@ var BABYLON;
                         }
                         else if (!_this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
                             // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (_this._lastPointerEvents[pointerEvent.pointerId] !== BABYLON.PointerEventTypes.POINTERUP) {
-                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(BABYLON.PointerEventTypes.POINTERUP, prePointerInfo.event, utilityScenePick));
-                                _this._lastPointerEvents[pointerEvent.pointerId] = BABYLON.PointerEventTypes.POINTERUP;
+                            if (_this._lastPointerEvents[pointerEvent.pointerId]) {
+                                _this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete _this._lastPointerEvents[pointerEvent.pointerId];
                             }
                         }
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && _this._pointerCaptures[pointerEvent.pointerId]) {
+                            _this._pointerCaptures[pointerEvent.pointerId] = false;
+                        }
                     }
                 }
             });

+ 23 - 0
gui/src/3D/controls/container3D.ts

@@ -13,6 +13,13 @@ module BABYLON.GUI {
         protected _children = new Array<Control3D>();
 
         /**
+         * Gets the list of child controls
+         */
+        public get children(): Array<Control3D> {
+            return this._children;
+        }
+
+        /**
          * Gets or sets a boolean indicating if the layout must be blocked (default is false).
          * This is helpful to optimize layout operation when adding multiple children in a row
          */
@@ -125,5 +132,21 @@ module BABYLON.GUI {
 
             super.dispose();
         }
+
+        /** Control rotation will remain unchanged  */
+        public static readonly UNSET_ORIENTATION = 0;
+
+        /** Control will rotate to make it look at sphere central axis */
+        public static readonly FACEORIGIN_ORIENTATION = 1;
+
+        /** Control will rotate to make it look back at sphere central axis */
+        public static readonly FACEORIGINREVERSED_ORIENTATION = 2;
+
+        /** Control will rotate to look at z axis (0, 0, 1) */
+        public static readonly FACEFORWARD_ORIENTATION = 3;
+
+        /** Control will rotate to look at negative z axis (0, 0, -1) */
+        public static readonly FACEFORWARDREVERSED_ORIENTATION = 4;
+
     }
 }

+ 80 - 20
gui/src/3D/controls/spherePanel.ts

@@ -7,29 +7,44 @@ module BABYLON.GUI {
     export class SpherePanel extends Container3D {
         private _radius = 5.0;
         private _columns = 10;
+        private _rows = 0;
         private _rowThenColum = true;
+        
+        private _orientation = Container3D.FACEORIGIN_ORIENTATION;
 
         /**
-         * Gets or sets a boolean indicating if the layout must first fill rows then columns or the opposite (true by default)
+         * Gets or sets the distance between elements
          */
-        public get rowThenColum(): boolean {
-            return this._rowThenColum;
+        public margin = 0.1;        
+
+        /**
+         * Gets or sets the orientation to apply to all controls (BABYLON.Container3D.FaceOriginReversedOrientation by default)
+        * | Value | Type                                | Description |
+        * | ----- | ----------------------------------- | ----------- |
+        * | 0     | UNSET_ORIENTATION                   |  Control rotation will remain unchanged |
+        * | 1     | FACEORIGIN_ORIENTATION              |  Control will rotate to make it look at sphere central axis |
+        * | 2     | FACEORIGINREVERSED_ORIENTATION      |  Control will rotate to make it look back at sphere central axis |
+        * | 3     | FACEFORWARD_ORIENTATION             |  Control will rotate to look at z axis (0, 0, 1) |
+        * | 4     | FACEFORWARDREVERSED_ORIENTATION     |  Control will rotate to look at negative z axis (0, 0, -1) |
+         */
+        public get orientation(): number {
+            return this._orientation;
         }
 
-        public set rowThenColum(value: boolean) {
-            if (this._rowThenColum === value) {
+        public set orientation(value: number) {
+            if (this._orientation === value) {
                 return;
             }
 
-            this._rowThenColum = value;
+            this._orientation = value;
 
             Tools.SetImmediate(() => {
                 this._arrangeChildren();               
             });
-        }              
-
+        }   
+         
         /**
-         * Gets or sets a the radius of the sphere where to project controls (5 by default)
+         * Gets or sets the radius of the sphere where to project controls (5 by default)
          */
         public get radius(): float {
             return this._radius;
@@ -48,8 +63,8 @@ module BABYLON.GUI {
         }        
 
         /**
-         * Gets or sets a the number of columns requested (10 by default). 
-         * The panel will automatically compute the number of rows based on number of child controls 
+         * Gets or sets the number of columns requested (10 by default). 
+         * The panel will automatically compute the number of rows based on number of child controls. 
          */
         public get columns(): int {
             return this._columns;
@@ -61,11 +76,33 @@ module BABYLON.GUI {
             }
 
             this._columns = value;
+            this._rowThenColum = true;
+
+            Tools.SetImmediate(() => {
+                this._arrangeChildren();               
+            });
+        }     
+        
+        /**
+         * Gets or sets a the number of rows requested. 
+         * The panel will automatically compute the number of columns based on number of child controls. 
+         */
+        public get rows(): int {
+            return this._rows;
+        }
+
+        public set rows(value: int) {
+            if (this._rows === value) {
+                return;
+            }
+
+            this._rows = value;
+            this._rowThenColum = false;
 
             Tools.SetImmediate(() => {
                 this._arrangeChildren();               
             });
-        }         
+        }           
 
         /**
          * Creates new SpherePanel
@@ -78,6 +115,7 @@ module BABYLON.GUI {
             let cellWidth = 0;
             let cellHeight = 0;
             let rows = 0;
+            let columns = 0;
             let controlCount = 0;
 
             let currentInverseWorld = Matrix.Invert(this.node!.computeWorldMatrix(true));
@@ -99,11 +137,19 @@ module BABYLON.GUI {
                 cellHeight = Math.max(cellHeight, extendSize.y * 2);
             }
 
-            console.log(cellWidth + "x" + cellHeight)
+       //     cellWidth += this.margin * 2;
+         //   cellHeight += this.margin * 2;
+
             // Arrange
-            rows = Math.ceil(controlCount / this._columns);
+            if (this._rowThenColum) {
+                columns = this._columns;
+                rows = Math.ceil(controlCount / this._columns);
+            } else {
+                rows = this._rows;
+                columns = Math.ceil(controlCount / this._rows);
+            }
 
-            let startOffsetX = (this._columns * 0.5) * cellWidth;
+            let startOffsetX = (columns * 0.5) * cellWidth;
             let startOffsetY = (rows * 0.5) * cellHeight;
             let nodeGrid = [];
             let cellCounter = 0;
@@ -111,9 +157,9 @@ module BABYLON.GUI {
             if (this._rowThenColum) {
                 for (var r = 0; r < rows; r++)
                 {
-                    for (var c = 0; c < this._columns; c++)
+                    for (var c = 0; c < columns; c++)
                     {
-                        nodeGrid.push(new Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, -(r * cellHeight) - startOffsetY - cellHeight / 2, 0));
+                        nodeGrid.push(new Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, (r * cellHeight) - startOffsetY + cellHeight / 2, 0));
                         cellCounter++;
                         if (cellCounter > controlCount)
                         {
@@ -122,11 +168,11 @@ module BABYLON.GUI {
                     }
                 }
             } else {
-                for (var c = 0; c < this._columns; c++)
+                for (var c = 0; c < columns; c++)
                 {
                     for (var r = 0; r < rows; r++)
                     {
-                        nodeGrid.push(new Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, -(r * cellHeight) - startOffsetY - cellHeight / 2, 0));
+                        nodeGrid.push(new Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, (r * cellHeight) - startOffsetY + cellHeight / 2, 0));
                         cellCounter++;
                         if (cellCounter > controlCount)
                         {
@@ -143,6 +189,20 @@ module BABYLON.GUI {
                 }                
                 let newPos = this._sphericalMapping(nodeGrid[cellCounter]);
 
+                switch (this._orientation) {
+                    case Container3D.FACEORIGIN_ORIENTATION:
+                        child.mesh.lookAt(new BABYLON.Vector3(-newPos.x, 0, -newPos.z));
+                        break;
+                    case Container3D.FACEORIGINREVERSED_ORIENTATION:
+                        child.mesh.lookAt(new BABYLON.Vector3(newPos.x, 0, newPos.z));
+                        break;
+                    case Container3D.FACEFORWARD_ORIENTATION:
+                        child.mesh.lookAt(new BABYLON.Vector3(0, 0, 1));
+                        break;
+                    case Container3D.FACEFORWARDREVERSED_ORIENTATION:
+                        child.mesh.lookAt(new BABYLON.Vector3(0, 0, -1));
+                        break;
+                }
                 child.position = newPos;
 
                 cellCounter++;
@@ -158,7 +218,7 @@ module BABYLON.GUI {
 
             Matrix.RotationYawPitchRollToRef(yAngle, xAngle, 0, Tmp.Matrix[0]);
 
-            return Vector3.TransformCoordinates(newPos, Tmp.Matrix[0]);
+            return Vector3.TransformNormal(newPos, Tmp.Matrix[0]);
         }
     }
 }

+ 28 - 21
gui/src/3D/gui3DManager.ts

@@ -12,6 +12,7 @@ module BABYLON.GUI {
         private _utilityLayer: Nullable<UtilityLayerRenderer>;
         private _rootContainer: Container3D;
         private _pointerObserver: Nullable<Observer<PointerInfo>>;
+        private _pointerOutObserver: Nullable<Observer<number>>;
         /** @hidden */
         public _lastPickedControl: Control3D;
         /** @hidden */
@@ -59,14 +60,11 @@ module BABYLON.GUI {
             let utilityLayerScene = this._utilityLayer.utilityLayerScene;
             
             // Events
-            this._pointerObserver = utilityLayerScene.onPointerObservable.add((pi, state) => {
-
-                if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE
-                    && pi.type !== BABYLON.PointerEventTypes.POINTERUP
-                    && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
-                    return;
-                }
+            this._pointerOutObserver = this._utilityLayer.onPointerOutObservable.add((pointerId) => {
+                this._handlePointerOut(pointerId, true);
+            });
 
+            this._pointerObserver = utilityLayerScene.onPointerObservable.add((pi, state) => {
                 this._doPicking(pi)
             });
 
@@ -76,6 +74,23 @@ module BABYLON.GUI {
             new BABYLON.HemisphericLight("hemi", Vector3.Up(), this._utilityLayer.utilityLayerScene);
         }
 
+        private _handlePointerOut(pointerId: number, isPointerUp: boolean) {
+            var previousControlOver = this._lastControlOver[pointerId];
+            if (previousControlOver) {
+                previousControlOver._onPointerOut(previousControlOver);
+                delete this._lastControlOver[pointerId];
+            }               
+            
+            if (isPointerUp) {
+                if (this._lastControlDown[pointerId]) {
+                    this._lastControlDown[pointerId].forcePointerUp();
+                    delete this._lastControlDown[pointerId];
+                }
+            }        
+            
+            this.onPickedPointChangedObservable.notifyObservers(null);
+        }
+
         private _doPicking(pi: PointerInfo): boolean {
             if (!this._utilityLayer || !this._utilityLayer.utilityLayerScene.activeCamera) {
                 return false;                
@@ -88,20 +103,7 @@ module BABYLON.GUI {
             
             let pickingInfo = pi.pickInfo;
             if (!pickingInfo || !pickingInfo.hit) {
-                var previousControlOver = this._lastControlOver[pointerId];
-                if (previousControlOver) {
-                    previousControlOver._onPointerOut(previousControlOver);
-                    delete this._lastControlOver[pointerId];
-                }               
-                
-                if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
-                    if (this._lastControlDown[pointerEvent.pointerId]) {
-                        this._lastControlDown[pointerEvent.pointerId].forcePointerUp();
-                        delete this._lastControlDown[pointerEvent.pointerId];
-                    }
-                }        
-                
-                this.onPickedPointChangedObservable.notifyObservers(null);
+                this._handlePointerOut(pointerId, pi.type === BABYLON.PointerEventTypes.POINTERUP);
                 return false;
             }
 
@@ -183,6 +185,11 @@ module BABYLON.GUI {
 
             this._sharedMaterials = {};
 
+            if (this._pointerOutObserver && this._utilityLayer) {
+                this._utilityLayer.onPointerOutObservable.remove(this._pointerOutObserver);
+                this._pointerOutObserver = null;
+            }
+
             this.onPickedPointChangedObservable.clear();
 
             let utilityLayerScene = this._utilityLayer ? this._utilityLayer.utilityLayerScene : null;

+ 13 - 6
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -25,6 +25,11 @@ module BABYLON {
          */
         public processAllEvents = false;
 
+        /**
+         * Observable raised when the pointer move from the utility layer scene to the main scene
+         */
+        public onPointerOutObservable = new Observable<number>();
+
         private _afterRenderObserver:Nullable<Observer<Scene>>;
         private _sceneDisposeObserver:Nullable<Observer<Scene>>;
         private _originalPointerObserver:Nullable<Observer<PointerInfoPre>>;
@@ -83,9 +88,7 @@ module BABYLON {
                         if (utilityScenePick.distance === 0) {
                             if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
                                 this._pointerCaptures[pointerEvent.pointerId] = true;
-                            } else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP) {
-                                this._pointerCaptures[pointerEvent.pointerId] = false;
-                            }
+                            } 
                         }
 
                         if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)){
@@ -96,11 +99,15 @@ module BABYLON {
                             prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                         } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
                             // We need to send a last pointup to the utilityLayerScene to make sure animations can complete
-                            if (this._lastPointerEvents[pointerEvent.pointerId] !== BABYLON.PointerEventTypes.POINTERUP) {
-                                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(BABYLON.PointerEventTypes.POINTERUP, prePointerInfo.event, utilityScenePick))
-                                this._lastPointerEvents[pointerEvent.pointerId] = BABYLON.PointerEventTypes.POINTERUP;                                
+                            if (this._lastPointerEvents[pointerEvent.pointerId]) {
+                                this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete this._lastPointerEvents[pointerEvent.pointerId];
                             }
                         }
+
+                        if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+                            this._pointerCaptures[pointerEvent.pointerId] = false;
+                        }
                     }
                 }