浏览代码

Update UtilityLayer to capture pointers

David Catuhe 7 年之前
父节点
当前提交
71283c76e8
共有 38 个文件被更改,包括 14934 次插入14069 次删除
  1. 2 1
      Tools/Gulp/config.json
  2. 13065 13026
      dist/preview release/babylon.d.ts
  3. 41 41
      dist/preview release/babylon.js
  4. 343 204
      dist/preview release/babylon.max.js
  5. 343 204
      dist/preview release/babylon.no-module.max.js
  6. 41 41
      dist/preview release/babylon.worker.js
  7. 345 206
      dist/preview release/es6.js
  8. 26 0
      dist/preview release/gui/babylon.gui.d.ts
  9. 123 1
      dist/preview release/gui/babylon.gui.js
  10. 1 1
      dist/preview release/gui/babylon.gui.min.js
  11. 26 0
      dist/preview release/gui/babylon.gui.module.d.ts
  12. 1 1
      dist/preview release/inspector/babylon.inspector.d.ts
  13. 9 9
      dist/preview release/inspector/babylon.inspector.js
  14. 2 2
      dist/preview release/inspector/babylon.inspector.min.js
  15. 4 8
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  16. 2 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  17. 4 9
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  18. 2 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  19. 4 9
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  20. 2 1
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 4 9
      dist/preview release/loaders/babylonjs.loaders.d.ts
  22. 2 1
      dist/preview release/loaders/babylonjs.loaders.js
  23. 4 9
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  24. 1 1
      dist/preview release/materialsLibrary/babylon.mixMaterial.js
  25. 1 1
      dist/preview release/materialsLibrary/babylon.mixMaterial.min.js
  26. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.js
  27. 1 1
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  28. 0 4
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  29. 0 4
      dist/preview release/serializers/babylon.glTF2Serializer.js
  30. 0 4
      dist/preview release/serializers/babylonjs.serializers.d.ts
  31. 0 4
      dist/preview release/serializers/babylonjs.serializers.js
  32. 0 4
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  33. 24 24
      dist/preview release/viewer/babylon.viewer.js
  34. 343 204
      dist/preview release/viewer/babylon.viewer.max.js
  35. 128 0
      gui/src/3D/controls/spherePanel.ts
  36. 3 1
      gui/src/3D/controls/stackPanel3D.ts
  37. 22 29
      gui/src/3D/gui3DManager.ts
  38. 14 3
      src/Rendering/babylon.utilityLayerRenderer.ts

+ 2 - 1
Tools/Gulp/config.json

@@ -1712,7 +1712,8 @@
                     "../../gui/src/3D/controls/container3D.ts",
                     "../../gui/src/3D/controls/button3D.ts",
                     "../../gui/src/3D/controls/holographicButton.ts",
-                    "../../gui/src/3D/controls/stackPanel3D.ts"
+                    "../../gui/src/3D/controls/stackPanel3D.ts",
+                    "../../gui/src/3D/controls/spherePanel.ts"
                 ],
                 "shaderFiles": [
                     "../../gui/src/3D/materials/shaders/fluent.vertex.fx",

文件差异内容过多而无法显示
+ 13065 - 13026
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 41 - 41
dist/preview release/babylon.js


+ 343 - 204
dist/preview release/babylon.max.js

@@ -24709,7 +24709,7 @@ var BABYLON;
         });
         Object.defineProperty(Scene.prototype, "debugLayer", {
             /**
-             * Gets the debug layer associated with the scene
+             * Gets the debug layer (aka Inspector) associated with the scene
              * @see http://doc.babylonjs.com/features/playground_debuglayer
              */
             get: function () {
@@ -28610,10 +28610,14 @@ var BABYLON;
             if (!BABYLON.PickingInfo) {
                 return null;
             }
-            return this._internalPick(function (world) {
+            var result = this._internalPick(function (world) {
                 _this.createPickingRayToRef(x, y, world, _this._tempPickingRay, camera || null);
                 return _this._tempPickingRay;
             }, predicate, fastCheck);
+            if (result) {
+                result.ray = this.createPickingRay(x, y, BABYLON.Matrix.Identity(), camera || null);
+            }
+            return result;
         };
         /** Launch a ray to try to pick a sprite in the scene
          * @param x position on screen
@@ -86920,9 +86924,51 @@ var BABYLON;
              *  If the utility layer should automatically be rendered on top of existing scene
             */
             this.shouldRender = true;
+            /**
+             * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
+             */
+            this.onlyCheckPointerDownEvents = true;
             // 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();
+            // Detach controls on utility scene, events will be fired by logic below to handle picking priority
+            this.utilityLayerScene.detachControl();
+            this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
+                var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                if (!prePointerInfo.ray && utilityScenePick) {
+                    prePointerInfo.ray = utilityScenePick.ray;
+                }
+                // always fire the prepointer oversvable
+                _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                // allow every non pointer down event to flow to the utility layer
+                if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!prePointerInfo.skipOnPointerObservable) {
+                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                    }
+                    return;
+                }
+                if (_this.utilityLayerScene.autoClearDepthAndStencil) {
+                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                    if (utilityScenePick && utilityScenePick.hit) {
+                        if (!prePointerInfo.skipOnPointerObservable) {
+                            _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                        }
+                        prePointerInfo.skipOnPointerObservable = true;
+                    }
+                }
+                else {
+                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
+                    if (originalScenePick && utilityScenePick) {
+                        if (utilityScenePick.distance < originalScenePick.distance) {
+                            if (!prePointerInfo.skipOnPointerObservable) {
+                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                            }
+                            prePointerInfo.skipOnPointerObservable = true;
+                        }
+                    }
+                }
+            });
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {
@@ -86952,6 +86998,9 @@ var BABYLON;
             if (this._sceneDisposeObserver) {
                 this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
             }
+            if (this._originalPointerObserver) {
+                this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
+            }
             this.utilityLayerScene.dispose();
         };
         UtilityLayerRenderer.prototype._updateCamera = function () {
@@ -86967,6 +87016,190 @@ var BABYLON;
 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 across.
+         */
+        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;
+            /**
+             *  If the drag behavior will react to drag events
+             */
+            this.enabled = true;
+            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);
+            var pickPredicate = function (m) {
+                return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
+            };
+            this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
+                if (!_this.enabled) {
+                    return;
+                }
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(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;
+(function (BABYLON) {
+    /**
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      */
     var Gizmo = /** @class */ (function () {
@@ -87056,7 +87289,7 @@ var BABYLON;
             arrowTail.position.z += 0.2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(dragAxis));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
@@ -87121,7 +87354,7 @@ var BABYLON;
             arrowTail.position.z += 0.2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(dragAxis));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
@@ -87180,7 +87413,7 @@ var BABYLON;
             rotationMesh.rotation.x = Math.PI / 2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(planeNormal));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragPlaneNormal: planeNormal, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragPlaneNormal: planeNormal });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
@@ -87389,6 +87622,91 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     /**
+     * Helps setup gizmo's in the scene to rotate/scale/position meshes
+     */
+    var GizmoManager = /** @class */ (function () {
+        /**
+         * Instatiates a gizmo manager
+         * @param scene the scene to overlay the gizmos on top of
+         * @param options If only a single gizmo should exist at one time
+         */
+        function GizmoManager(scene, options) {
+            var _this = this;
+            this.scene = scene;
+            // Set of gizmos that are currently in the scene for each mesh
+            this._gizmoSet = {};
+            this._pointerObserver = null;
+            this._gizmoLayer = new BABYLON.UtilityLayerRenderer(scene);
+            // Options parsing
+            if (!options) {
+                options = {};
+            }
+            if (options.singleGizmo === undefined) {
+                options.singleGizmo = true;
+            }
+            // Instatiate/dispose gizmos based on pointer actions
+            this._pointerObserver = scene.onPointerObservable.add(function (pointerInfo, state) {
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh) {
+                        if (!_this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId]) {
+                            if (options.singleGizmo) {
+                                _this._clearGizmos();
+                            }
+                            // Enable gizmo when mesh is selected
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId] = { positionGizmo: new BABYLON.PositionGizmo(_this._gizmoLayer), rotationGizmo: new BABYLON.RotationGizmo(_this._gizmoLayer) };
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                        }
+                        else {
+                            if (!options.singleGizmo) {
+                                // Disable gizmo when clicked again
+                                _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.dispose();
+                                _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.dispose();
+                                delete _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId];
+                            }
+                        }
+                    }
+                    else {
+                        if (options.singleGizmo) {
+                            // Disable gizmo when clicked away
+                            if (pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                                var gizmoPick = _this._gizmoLayer.utilityLayerScene.pickWithRay(pointerInfo.pickInfo.ray);
+                                if (gizmoPick && !gizmoPick.hit) {
+                                    _this._clearGizmos();
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+        }
+        /**
+         * Disposes of the gizmo manager
+         */
+        GizmoManager.prototype.dispose = function () {
+            this.scene.onPointerObservable.remove(this._pointerObserver);
+            this._clearGizmos();
+            this._gizmoLayer.dispose();
+        };
+        GizmoManager.prototype._clearGizmos = function () {
+            for (var key in this._gizmoSet) {
+                if (this._gizmoSet.hasOwnProperty(key)) {
+                    this._gizmoSet[key].positionGizmo.dispose();
+                    this._gizmoSet[key].rotationGizmo.dispose();
+                    delete this._gizmoSet[key];
+                }
+            }
+        };
+        return GizmoManager;
+    }());
+    BABYLON.GizmoManager = GizmoManager;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.gizmoManager.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
      * Defines a target to use with MorphTargetManager
      * @see http://doc.babylonjs.com/how_to/how_to_use_morphtargets
      */
@@ -94734,6 +95052,13 @@ var BABYLON;
         AbstractAssetTask.prototype.runTask = function (scene, onSuccess, onError) {
             throw new Error("runTask is not implemented");
         };
+        /**
+         * Reset will set the task state back to INIT, so the next load call of the assets manager will execute this task again.
+         * This can be used with failed tasks that have the reason for failure fixed.
+         */
+        AbstractAssetTask.prototype.reset = function () {
+            this._taskState = AssetTaskState.INIT;
+        };
         AbstractAssetTask.prototype.onErrorCallback = function (onError, message, exception) {
             this._taskState = AssetTaskState.ERROR;
             this._errorObject = {
@@ -95299,6 +95624,16 @@ var BABYLON;
             this._tasks.push(task);
             return task;
         };
+        /**
+         * Remove a task from the assets manager.
+         * @param task the task to remove
+         */
+        AssetsManager.prototype.removeTask = function (task) {
+            var index = this._tasks.indexOf(task);
+            if (index > -1) {
+                this._tasks.splice(index, 1);
+            }
+        };
         AssetsManager.prototype._decreaseWaitingTasksCount = function (task) {
             this._waitingTasksCount--;
             try {
@@ -95394,7 +95729,9 @@ var BABYLON;
             }
             for (var index = 0; index < this._tasks.length; index++) {
                 var task = this._tasks[index];
-                this._runTask(task);
+                if (task.taskState === AssetTaskState.INIT) {
+                    this._runTask(task);
+                }
             }
             return this;
         };
@@ -96898,204 +97235,6 @@ var BABYLON;
 
 //# 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 across. pointerObservableScene can be used to listen to drag events from another scene(eg. if the attached mesh is in an overlay scene).
-         */
-        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;
-            /**
-             *  If the drag behavior will react to drag events
-             */
-            this.enabled = true;
-            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();
-            if (!this.options.pointerObservableScene) {
-                this.options.pointerObservableScene = this._scene;
-            }
-            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);
-            var pickPredicate = function (m) {
-                return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
-            };
-            this._pointerObserver = this.options.pointerObservableScene.onPrePointerObservable.add(function (pointerInfoPre, eventState) {
-                if (!_this.enabled) {
-                    return;
-                }
-                // Check if attached mesh is picked
-                var pickInfo = pointerInfoPre.ray ? _this._scene.pickWithRay(pointerInfoPre.ray, pickPredicate) : _this._scene.pick(_this._scene.pointerX, _this._scene.pointerY, pickPredicate);
-                if (pickInfo) {
-                    pickInfo.ray = pointerInfoPre.ray;
-                    if (!pickInfo.ray) {
-                        pickInfo.ray = _this.options.pointerObservableScene.createPickingRay(_this._scene.pointerX, _this._scene.pointerY, BABYLON.Matrix.Identity(), _this._scene.activeCamera);
-                    }
-                    if (pickInfo.hit && pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                        pointerInfoPre.skipOnPointerObservable = true;
-                    }
-                }
-                if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!dragging && pickInfo && pickInfo.hit && pickInfo.pickedMesh && pickInfo.ray) {
-                        _this._updateDragPlanePosition(pickInfo.ray);
-                        var pickedPoint = _this._pickWithRayOnDragPlane(pickInfo.ray);
-                        if (pickedPoint) {
-                            dragging = true;
-                            _this._draggingID = pointerInfoPre.event.pointerId;
-                            lastPosition.copyFrom(pickedPoint);
-                            _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint });
-                        }
-                    }
-                }
-                else if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERUP) {
-                    if (_this._draggingID == pointerInfoPre.event.pointerId) {
-                        dragging = false;
-                        _this._draggingID = -1;
-                        _this.onDragEndObservable.notifyObservers({ dragPlanePoint: lastPosition });
-                    }
-                }
-                else if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERMOVE) {
-                    if (_this._draggingID == pointerInfoPre.event.pointerId && dragging && pickInfo && pickInfo.ray) {
-                        var pickedPoint = _this._pickWithRayOnDragPlane(pickInfo.ray);
-                        _this._updateDragPlanePosition(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.onPrePointerObservable.remove(this._pointerObserver);
-            }
-        };
-        return PointerDragBehavior;
-    }());
-    BABYLON.PointerDragBehavior = PointerDragBehavior;
-})(BABYLON || (BABYLON = {}));
-
-//# sourceMappingURL=babylon.pointerDragBehavior.js.map
-
 
 var BABYLON;
 (function (BABYLON) {

+ 343 - 204
dist/preview release/babylon.no-module.max.js

@@ -24676,7 +24676,7 @@ var BABYLON;
         });
         Object.defineProperty(Scene.prototype, "debugLayer", {
             /**
-             * Gets the debug layer associated with the scene
+             * Gets the debug layer (aka Inspector) associated with the scene
              * @see http://doc.babylonjs.com/features/playground_debuglayer
              */
             get: function () {
@@ -28577,10 +28577,14 @@ var BABYLON;
             if (!BABYLON.PickingInfo) {
                 return null;
             }
-            return this._internalPick(function (world) {
+            var result = this._internalPick(function (world) {
                 _this.createPickingRayToRef(x, y, world, _this._tempPickingRay, camera || null);
                 return _this._tempPickingRay;
             }, predicate, fastCheck);
+            if (result) {
+                result.ray = this.createPickingRay(x, y, BABYLON.Matrix.Identity(), camera || null);
+            }
+            return result;
         };
         /** Launch a ray to try to pick a sprite in the scene
          * @param x position on screen
@@ -86887,9 +86891,51 @@ var BABYLON;
              *  If the utility layer should automatically be rendered on top of existing scene
             */
             this.shouldRender = true;
+            /**
+             * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
+             */
+            this.onlyCheckPointerDownEvents = true;
             // 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();
+            // Detach controls on utility scene, events will be fired by logic below to handle picking priority
+            this.utilityLayerScene.detachControl();
+            this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
+                var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                if (!prePointerInfo.ray && utilityScenePick) {
+                    prePointerInfo.ray = utilityScenePick.ray;
+                }
+                // always fire the prepointer oversvable
+                _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                // allow every non pointer down event to flow to the utility layer
+                if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!prePointerInfo.skipOnPointerObservable) {
+                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                    }
+                    return;
+                }
+                if (_this.utilityLayerScene.autoClearDepthAndStencil) {
+                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                    if (utilityScenePick && utilityScenePick.hit) {
+                        if (!prePointerInfo.skipOnPointerObservable) {
+                            _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                        }
+                        prePointerInfo.skipOnPointerObservable = true;
+                    }
+                }
+                else {
+                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
+                    if (originalScenePick && utilityScenePick) {
+                        if (utilityScenePick.distance < originalScenePick.distance) {
+                            if (!prePointerInfo.skipOnPointerObservable) {
+                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                            }
+                            prePointerInfo.skipOnPointerObservable = true;
+                        }
+                    }
+                }
+            });
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {
@@ -86919,6 +86965,9 @@ var BABYLON;
             if (this._sceneDisposeObserver) {
                 this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
             }
+            if (this._originalPointerObserver) {
+                this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
+            }
             this.utilityLayerScene.dispose();
         };
         UtilityLayerRenderer.prototype._updateCamera = function () {
@@ -86934,6 +86983,190 @@ var BABYLON;
 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 across.
+         */
+        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;
+            /**
+             *  If the drag behavior will react to drag events
+             */
+            this.enabled = true;
+            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);
+            var pickPredicate = function (m) {
+                return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
+            };
+            this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
+                if (!_this.enabled) {
+                    return;
+                }
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(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;
+(function (BABYLON) {
+    /**
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      */
     var Gizmo = /** @class */ (function () {
@@ -87023,7 +87256,7 @@ var BABYLON;
             arrowTail.position.z += 0.2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(dragAxis));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
@@ -87088,7 +87321,7 @@ var BABYLON;
             arrowTail.position.z += 0.2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(dragAxis));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
@@ -87147,7 +87380,7 @@ var BABYLON;
             rotationMesh.rotation.x = Math.PI / 2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(planeNormal));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragPlaneNormal: planeNormal, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragPlaneNormal: planeNormal });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
@@ -87356,6 +87589,91 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     /**
+     * Helps setup gizmo's in the scene to rotate/scale/position meshes
+     */
+    var GizmoManager = /** @class */ (function () {
+        /**
+         * Instatiates a gizmo manager
+         * @param scene the scene to overlay the gizmos on top of
+         * @param options If only a single gizmo should exist at one time
+         */
+        function GizmoManager(scene, options) {
+            var _this = this;
+            this.scene = scene;
+            // Set of gizmos that are currently in the scene for each mesh
+            this._gizmoSet = {};
+            this._pointerObserver = null;
+            this._gizmoLayer = new BABYLON.UtilityLayerRenderer(scene);
+            // Options parsing
+            if (!options) {
+                options = {};
+            }
+            if (options.singleGizmo === undefined) {
+                options.singleGizmo = true;
+            }
+            // Instatiate/dispose gizmos based on pointer actions
+            this._pointerObserver = scene.onPointerObservable.add(function (pointerInfo, state) {
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh) {
+                        if (!_this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId]) {
+                            if (options.singleGizmo) {
+                                _this._clearGizmos();
+                            }
+                            // Enable gizmo when mesh is selected
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId] = { positionGizmo: new BABYLON.PositionGizmo(_this._gizmoLayer), rotationGizmo: new BABYLON.RotationGizmo(_this._gizmoLayer) };
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                        }
+                        else {
+                            if (!options.singleGizmo) {
+                                // Disable gizmo when clicked again
+                                _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.dispose();
+                                _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.dispose();
+                                delete _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId];
+                            }
+                        }
+                    }
+                    else {
+                        if (options.singleGizmo) {
+                            // Disable gizmo when clicked away
+                            if (pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                                var gizmoPick = _this._gizmoLayer.utilityLayerScene.pickWithRay(pointerInfo.pickInfo.ray);
+                                if (gizmoPick && !gizmoPick.hit) {
+                                    _this._clearGizmos();
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+        }
+        /**
+         * Disposes of the gizmo manager
+         */
+        GizmoManager.prototype.dispose = function () {
+            this.scene.onPointerObservable.remove(this._pointerObserver);
+            this._clearGizmos();
+            this._gizmoLayer.dispose();
+        };
+        GizmoManager.prototype._clearGizmos = function () {
+            for (var key in this._gizmoSet) {
+                if (this._gizmoSet.hasOwnProperty(key)) {
+                    this._gizmoSet[key].positionGizmo.dispose();
+                    this._gizmoSet[key].rotationGizmo.dispose();
+                    delete this._gizmoSet[key];
+                }
+            }
+        };
+        return GizmoManager;
+    }());
+    BABYLON.GizmoManager = GizmoManager;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.gizmoManager.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
      * Defines a target to use with MorphTargetManager
      * @see http://doc.babylonjs.com/how_to/how_to_use_morphtargets
      */
@@ -94701,6 +95019,13 @@ var BABYLON;
         AbstractAssetTask.prototype.runTask = function (scene, onSuccess, onError) {
             throw new Error("runTask is not implemented");
         };
+        /**
+         * Reset will set the task state back to INIT, so the next load call of the assets manager will execute this task again.
+         * This can be used with failed tasks that have the reason for failure fixed.
+         */
+        AbstractAssetTask.prototype.reset = function () {
+            this._taskState = AssetTaskState.INIT;
+        };
         AbstractAssetTask.prototype.onErrorCallback = function (onError, message, exception) {
             this._taskState = AssetTaskState.ERROR;
             this._errorObject = {
@@ -95266,6 +95591,16 @@ var BABYLON;
             this._tasks.push(task);
             return task;
         };
+        /**
+         * Remove a task from the assets manager.
+         * @param task the task to remove
+         */
+        AssetsManager.prototype.removeTask = function (task) {
+            var index = this._tasks.indexOf(task);
+            if (index > -1) {
+                this._tasks.splice(index, 1);
+            }
+        };
         AssetsManager.prototype._decreaseWaitingTasksCount = function (task) {
             this._waitingTasksCount--;
             try {
@@ -95361,7 +95696,9 @@ var BABYLON;
             }
             for (var index = 0; index < this._tasks.length; index++) {
                 var task = this._tasks[index];
-                this._runTask(task);
+                if (task.taskState === AssetTaskState.INIT) {
+                    this._runTask(task);
+                }
             }
             return this;
         };
@@ -96865,204 +97202,6 @@ var BABYLON;
 
 //# 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 across. pointerObservableScene can be used to listen to drag events from another scene(eg. if the attached mesh is in an overlay scene).
-         */
-        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;
-            /**
-             *  If the drag behavior will react to drag events
-             */
-            this.enabled = true;
-            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();
-            if (!this.options.pointerObservableScene) {
-                this.options.pointerObservableScene = this._scene;
-            }
-            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);
-            var pickPredicate = function (m) {
-                return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
-            };
-            this._pointerObserver = this.options.pointerObservableScene.onPrePointerObservable.add(function (pointerInfoPre, eventState) {
-                if (!_this.enabled) {
-                    return;
-                }
-                // Check if attached mesh is picked
-                var pickInfo = pointerInfoPre.ray ? _this._scene.pickWithRay(pointerInfoPre.ray, pickPredicate) : _this._scene.pick(_this._scene.pointerX, _this._scene.pointerY, pickPredicate);
-                if (pickInfo) {
-                    pickInfo.ray = pointerInfoPre.ray;
-                    if (!pickInfo.ray) {
-                        pickInfo.ray = _this.options.pointerObservableScene.createPickingRay(_this._scene.pointerX, _this._scene.pointerY, BABYLON.Matrix.Identity(), _this._scene.activeCamera);
-                    }
-                    if (pickInfo.hit && pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                        pointerInfoPre.skipOnPointerObservable = true;
-                    }
-                }
-                if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!dragging && pickInfo && pickInfo.hit && pickInfo.pickedMesh && pickInfo.ray) {
-                        _this._updateDragPlanePosition(pickInfo.ray);
-                        var pickedPoint = _this._pickWithRayOnDragPlane(pickInfo.ray);
-                        if (pickedPoint) {
-                            dragging = true;
-                            _this._draggingID = pointerInfoPre.event.pointerId;
-                            lastPosition.copyFrom(pickedPoint);
-                            _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint });
-                        }
-                    }
-                }
-                else if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERUP) {
-                    if (_this._draggingID == pointerInfoPre.event.pointerId) {
-                        dragging = false;
-                        _this._draggingID = -1;
-                        _this.onDragEndObservable.notifyObservers({ dragPlanePoint: lastPosition });
-                    }
-                }
-                else if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERMOVE) {
-                    if (_this._draggingID == pointerInfoPre.event.pointerId && dragging && pickInfo && pickInfo.ray) {
-                        var pickedPoint = _this._pickWithRayOnDragPlane(pickInfo.ray);
-                        _this._updateDragPlanePosition(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.onPrePointerObservable.remove(this._pointerObserver);
-            }
-        };
-        return PointerDragBehavior;
-    }());
-    BABYLON.PointerDragBehavior = PointerDragBehavior;
-})(BABYLON || (BABYLON = {}));
-
-//# sourceMappingURL=babylon.pointerDragBehavior.js.map
-
 
 var BABYLON;
 (function (BABYLON) {

文件差异内容过多而无法显示
+ 41 - 41
dist/preview release/babylon.worker.js


文件差异内容过多而无法显示
+ 345 - 206
dist/preview release/es6.js


+ 26 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -2353,7 +2353,33 @@ declare module BABYLON.GUI {
          * Creates new StackPanel
          * @param isVertical
          */
+        constructor(isVertical?: boolean);
+        protected _arrangeChildren(): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
+     * Class used to create a conainter panel deployed on the surface of a sphere
+     */
+    class SpherePanel extends Container3D {
+        private _radius;
+        private _columns;
+        /**
+         * Gets or sets a 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
+         */
+        columns: int;
+        /**
+         * Creates new SpherePanel
+         */
         constructor();
         protected _arrangeChildren(): void;
+        private _sphericalMapping(source);
     }
 }

+ 123 - 1
dist/preview release/gui/babylon.gui.js

@@ -7809,13 +7809,15 @@ var BABYLON;
              * Creates new StackPanel
              * @param isVertical
              */
-            function StackPanel3D() {
+            function StackPanel3D(isVertical) {
+                if (isVertical === void 0) { isVertical = false; }
                 var _this = _super.call(this) || this;
                 _this._isVertical = false;
                 /**
                  * Gets or sets the distance between elements
                  */
                 _this.margin = 0.1;
+                _this._isVertical = isVertical;
                 return _this;
             }
             Object.defineProperty(StackPanel3D.prototype, "isVertical", {
@@ -7904,6 +7906,126 @@ var BABYLON;
     })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
 })(BABYLON || (BABYLON = {}));
 
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+var BABYLON;
+(function (BABYLON) {
+    var GUI;
+    (function (GUI) {
+        /**
+         * Class used to create a conainter panel deployed on the surface of a sphere
+         */
+        var SpherePanel = /** @class */ (function (_super) {
+            __extends(SpherePanel, _super);
+            /**
+             * Creates new SpherePanel
+             */
+            function SpherePanel() {
+                var _this = _super.call(this) || this;
+                _this._radius = 5.0;
+                _this._columns = 10;
+                return _this;
+            }
+            Object.defineProperty(SpherePanel.prototype, "radius", {
+                /**
+                 * Gets or sets a the radius of the sphere where to project controls (5 by default)
+                 */
+                get: function () {
+                    return this._radius;
+                },
+                set: function (value) {
+                    var _this = this;
+                    if (this._radius === value) {
+                        return;
+                    }
+                    this._radius = value;
+                    BABYLON.Tools.SetImmediate(function () {
+                        _this._arrangeChildren();
+                    });
+                },
+                enumerable: true,
+                configurable: true
+            });
+            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
+                 */
+                get: function () {
+                    return this._columns;
+                },
+                set: function (value) {
+                    var _this = this;
+                    if (this._columns === value) {
+                        return;
+                    }
+                    this._columns = value;
+                    BABYLON.Tools.SetImmediate(function () {
+                        _this._arrangeChildren();
+                    });
+                },
+                enumerable: true,
+                configurable: true
+            });
+            SpherePanel.prototype._arrangeChildren = function () {
+                var cellWidth = 0;
+                var cellHeight = 0;
+                var rows = 0;
+                var controlCount = 0;
+                var currentInverseWorld = BABYLON.Matrix.Invert(this.node.computeWorldMatrix(true));
+                // Measure
+                for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
+                    var child = _a[_i];
+                    if (!child.mesh) {
+                        continue;
+                    }
+                    controlCount++;
+                    child.mesh.computeWorldMatrix(true);
+                    child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, BABYLON.Tmp.Matrix[0]);
+                    var boundingBox = child.mesh.getBoundingInfo().boundingBox;
+                    var extendSize = BABYLON.Vector3.TransformNormal(boundingBox.extendSize, BABYLON.Tmp.Matrix[0]);
+                    cellWidth = Math.max(cellWidth, extendSize.x * 2);
+                    cellHeight = Math.max(cellHeight, extendSize.y * 2);
+                }
+                // Arrange
+                rows = Math.ceil(controlCount / this._columns);
+                var startOffsetX = (this._columns * 0.5) * cellWidth;
+                var startOffsetY = (rows * 0.5) * cellHeight;
+                var nodeGrid = [];
+                var cellCounter = 0;
+                for (var c = 0; c < this._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));
+                        cellCounter++;
+                        if (cellCounter > controlCount) {
+                            break;
+                        }
+                    }
+                }
+                cellCounter = 0;
+                for (var _b = 0, _c = this._children; _b < _c.length; _b++) {
+                    var child = _c[_b];
+                    if (!child.mesh) {
+                        continue;
+                    }
+                    var newPos = this._sphericalMapping(nodeGrid[cellCounter]);
+                    child.position = newPos;
+                    cellCounter++;
+                }
+            };
+            SpherePanel.prototype._sphericalMapping = function (source) {
+                var newPos = new BABYLON.Vector3(0, 0, this._radius);
+                var xAngle = (source.x / this._radius);
+                var yAngle = -(source.y / this._radius);
+                BABYLON.Matrix.RotationYawPitchRollToRef(yAngle, xAngle, 0, BABYLON.Tmp.Matrix[0]);
+                return GUI.Vector3WithInfo.TransformCoordinates(newPos, BABYLON.Tmp.Matrix[0]);
+            };
+            return SpherePanel;
+        }(GUI.Container3D));
+        GUI.SpherePanel = SpherePanel;
+    })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
+})(BABYLON || (BABYLON = {}));
+
     
 
     return BABYLON.GUI;

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


+ 26 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -2358,7 +2358,33 @@ declare module BABYLON.GUI {
          * Creates new StackPanel
          * @param isVertical
          */
+        constructor(isVertical?: boolean);
+        protected _arrangeChildren(): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
+     * Class used to create a conainter panel deployed on the surface of a sphere
+     */
+    class SpherePanel extends Container3D {
+        private _radius;
+        private _columns;
+        /**
+         * Gets or sets a 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
+         */
+        columns: int;
+        /**
+         * Creates new SpherePanel
+         */
         constructor();
         protected _arrangeChildren(): void;
+        private _sphericalMapping(source);
     }
 }

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

@@ -1105,7 +1105,7 @@ declare module INSPECTOR {
     abstract class AbstractTool {
         private _elem;
         protected _inspector: Inspector;
-        constructor(icon: string, parent: HTMLElement, inspector: Inspector, tooltip: string);
+        constructor(iconSet: string, icon: string, parent: HTMLElement, inspector: Inspector, tooltip: string);
         toHtml(): HTMLElement;
         /**
          * Returns the total width in pixel of this tool, 0 by default

+ 9 - 9
dist/preview release/inspector/babylon.inspector.js

@@ -4468,11 +4468,11 @@ var INSPECTOR;
 var INSPECTOR;
 (function (INSPECTOR) {
     var AbstractTool = /** @class */ (function () {
-        function AbstractTool(icon, parent, inspector, tooltip) {
+        function AbstractTool(iconSet, icon, parent, inspector, tooltip) {
             var _this = this;
             this._inspector = inspector;
             this._elem = INSPECTOR.Inspector.DOCUMENT.createElement('i');
-            this._elem.className = "tool fa " + icon;
+            this._elem.className = "tool " + iconSet + " " + icon;
             parent.appendChild(this._elem);
             this._elem.addEventListener('click', function (e) {
                 _this.action();
@@ -4520,7 +4520,7 @@ var INSPECTOR;
     var PauseScheduleTool = /** @class */ (function (_super) {
         __extends(PauseScheduleTool, _super);
         function PauseScheduleTool(parent, inspector) {
-            var _this = _super.call(this, 'fa-pause', parent, inspector, 'Pause the automatic update of properties') || this;
+            var _this = _super.call(this, 'fa', 'fa-pause', parent, inspector, 'Pause the automatic update of properties') || this;
             _this._isPause = false;
             return _this;
         }
@@ -4556,7 +4556,7 @@ var INSPECTOR;
     var PickTool = /** @class */ (function (_super) {
         __extends(PickTool, _super);
         function PickTool(parent, inspector) {
-            var _this = _super.call(this, 'fa-mouse-pointer', parent, inspector, 'Select a mesh in the scene') || this;
+            var _this = _super.call(this, 'fa', 'fa-mouse-pointer', parent, inspector, 'Select a mesh in the scene') || this;
             _this._isActive = false;
             // Create handler
             _this._pickHandler = _this._pickMesh.bind(_this);
@@ -4619,7 +4619,7 @@ var INSPECTOR;
     var PopupTool = /** @class */ (function (_super) {
         __extends(PopupTool, _super);
         function PopupTool(parent, inspector) {
-            return _super.call(this, 'fa-external-link-alt', parent, inspector, 'Open the inspector in a popup') || this;
+            return _super.call(this, 'fas', 'fa-external-link-alt', parent, inspector, 'Open the inspector in a popup') || this;
         }
         // Action : refresh the whole panel
         PopupTool.prototype.action = function () {
@@ -4645,7 +4645,7 @@ var INSPECTOR;
     var RefreshTool = /** @class */ (function (_super) {
         __extends(RefreshTool, _super);
         function RefreshTool(parent, inspector) {
-            return _super.call(this, 'fa-sync', parent, inspector, 'Refresh the current tab') || this;
+            return _super.call(this, 'fa', 'fa-sync', parent, inspector, 'Refresh the current tab') || this;
         }
         // Action : refresh the whole panel
         RefreshTool.prototype.action = function () {
@@ -4671,7 +4671,7 @@ var INSPECTOR;
     var LabelTool = /** @class */ (function (_super) {
         __extends(LabelTool, _super);
         function LabelTool(parent, inspector) {
-            var _this = _super.call(this, 'fa-tags', parent, inspector, 'Display mesh names on the canvas') || this;
+            var _this = _super.call(this, 'fa', 'fa-tags', parent, inspector, 'Display mesh names on the canvas') || this;
             /** True if label are displayed, false otherwise */
             _this._isDisplayed = false;
             _this._advancedTexture = null;
@@ -4860,7 +4860,7 @@ var INSPECTOR;
     var DisposeTool = /** @class */ (function (_super) {
         __extends(DisposeTool, _super);
         function DisposeTool(parent, inspector) {
-            return _super.call(this, 'fa-times', parent, inspector, 'Close the inspector panel') || this;
+            return _super.call(this, 'fa', 'fa-times', parent, inspector, 'Close the inspector panel') || this;
         }
         // Action : refresh the whole panel
         DisposeTool.prototype.action = function () {
@@ -4886,7 +4886,7 @@ var INSPECTOR;
     var FullscreenTool = /** @class */ (function (_super) {
         __extends(FullscreenTool, _super);
         function FullscreenTool(parent, inspector) {
-            return _super.call(this, 'fa-expand', parent, inspector, 'Open the scene in fullscreen, press Esc to exit') || this;
+            return _super.call(this, 'fa', 'fa-expand', parent, inspector, 'Open the scene in fullscreen, press Esc to exit') || this;
         }
         // Action : refresh the whole panel
         FullscreenTool.prototype.action = function () {

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/inspector/babylon.inspector.min.js


+ 4 - 8
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -162,15 +162,9 @@ declare module BABYLON {
      * File loader for loading glTF files into a scene.
      */
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        /**
-         * Factory function that creates a glTF 1.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV1: () => IGLTFLoader;
-        /**
-         * Factory function that creates a glTF 2.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
          * Raised when the asset has been parsed
@@ -185,11 +179,13 @@ declare module BABYLON {
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         static IncrementalLoading: boolean;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         static HomogeneousCoordinates: boolean;
         /**

+ 2 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -546,11 +546,13 @@ var BABYLON;
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         GLTFFileLoader.IncrementalLoading = true;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;

+ 4 - 9
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -162,15 +162,9 @@ declare module BABYLON {
      * File loader for loading glTF files into a scene.
      */
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        /**
-         * Factory function that creates a glTF 1.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV1: () => IGLTFLoader;
-        /**
-         * Factory function that creates a glTF 2.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
          * Raised when the asset has been parsed
@@ -185,11 +179,13 @@ declare module BABYLON {
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         static IncrementalLoading: boolean;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         static HomogeneousCoordinates: boolean;
         /**
@@ -865,7 +861,6 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        /** @hidden */
         protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         private _loadSpecularGlossinessPropertiesAsync(context, material, properties, babylonMaterial);
     }

+ 2 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -546,11 +546,13 @@ var BABYLON;
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         GLTFFileLoader.IncrementalLoading = true;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
@@ -2772,7 +2774,6 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                /** @hidden */
                 KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (extensionContext, extension) {

+ 4 - 9
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -162,15 +162,9 @@ declare module BABYLON {
      * File loader for loading glTF files into a scene.
      */
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        /**
-         * Factory function that creates a glTF 1.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV1: () => IGLTFLoader;
-        /**
-         * Factory function that creates a glTF 2.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
          * Raised when the asset has been parsed
@@ -185,11 +179,13 @@ declare module BABYLON {
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         static IncrementalLoading: boolean;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         static HomogeneousCoordinates: boolean;
         /**
@@ -1442,7 +1438,6 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        /** @hidden */
         protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         private _loadSpecularGlossinessPropertiesAsync(context, material, properties, babylonMaterial);
     }

+ 2 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -546,11 +546,13 @@ var BABYLON;
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         GLTFFileLoader.IncrementalLoading = true;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
@@ -4988,7 +4990,6 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                /** @hidden */
                 KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (extensionContext, extension) {

+ 4 - 9
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -258,15 +258,9 @@ declare module BABYLON {
      * File loader for loading glTF files into a scene.
      */
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        /**
-         * Factory function that creates a glTF 1.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV1: () => IGLTFLoader;
-        /**
-         * Factory function that creates a glTF 2.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
          * Raised when the asset has been parsed
@@ -281,11 +275,13 @@ declare module BABYLON {
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         static IncrementalLoading: boolean;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         static HomogeneousCoordinates: boolean;
         /**
@@ -1538,7 +1534,6 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        /** @hidden */
         protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         private _loadSpecularGlossinessPropertiesAsync(context, material, properties, babylonMaterial);
     }

+ 2 - 1
dist/preview release/loaders/babylonjs.loaders.js

@@ -1546,11 +1546,13 @@ var BABYLON;
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         GLTFFileLoader.IncrementalLoading = true;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         GLTFFileLoader.HomogeneousCoordinates = false;
         return GLTFFileLoader;
@@ -5925,7 +5927,6 @@ var BABYLON;
                     _this.name = NAME;
                     return _this;
                 }
-                /** @hidden */
                 KHR_materials_pbrSpecularGlossiness.prototype._loadMaterialAsync = function (context, material, mesh, babylonMesh, babylonDrawMode, assign) {
                     var _this = this;
                     return this._loadExtensionAsync(context, material, function (extensionContext, extension) {

+ 4 - 9
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -265,15 +265,9 @@ declare module BABYLON {
      * File loader for loading glTF files into a scene.
      */
     class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
-        /**
-         * Factory function that creates a glTF 1.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV1: () => IGLTFLoader;
-        /**
-         * Factory function that creates a glTF 2.0 loader
-         * @hidden
-         */
+        /** @hidden */
         static _CreateGLTFLoaderV2: () => IGLTFLoader;
         /**
          * Raised when the asset has been parsed
@@ -288,11 +282,13 @@ declare module BABYLON {
          * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders.
          * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled.
          * Defaults to true.
+         * @hidden
          */
         static IncrementalLoading: boolean;
         /**
          * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters.
          * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates.
+         * @hidden
          */
         static HomogeneousCoordinates: boolean;
         /**
@@ -1545,7 +1541,6 @@ declare module BABYLON.GLTF2.Extensions {
      */
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
-        /** @hidden */
         protected _loadMaterialAsync(context: string, material: _ILoaderMaterial, mesh: _ILoaderMesh, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>>;
         private _loadSpecularGlossinessPropertiesAsync(context, material, properties, babylonMaterial);
     }

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylon.mixMaterial.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylon.mixMaterial.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 0 - 4
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -40,10 +40,6 @@ declare module BABYLON {
 }
 
 
-/**
- * Module for the Babylon glTF 2.0 exporter.  Should ONLY be used internally
- * @hidden
- */
 declare module BABYLON.GLTF2 {
     /**
      * Converts Babylon Scene into glTF 2.0.

+ 0 - 4
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -47,10 +47,6 @@ var BABYLON;
 //# sourceMappingURL=babylon.glTFSerializer.js.map
 
 /// <reference path="../../../../dist/preview release/gltf2Interface/babylon.glTF2Interface.d.ts"/>
-/**
- * Module for the Babylon glTF 2.0 exporter.  Should ONLY be used internally
- * @hidden
- */
 var BABYLON;
 (function (BABYLON) {
     var GLTF2;

+ 0 - 4
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -48,10 +48,6 @@ declare module BABYLON {
 }
 
 
-/**
- * Module for the Babylon glTF 2.0 exporter.  Should ONLY be used internally
- * @hidden
- */
 declare module BABYLON.GLTF2 {
     /**
      * Converts Babylon Scene into glTF 2.0.

+ 0 - 4
dist/preview release/serializers/babylonjs.serializers.js

@@ -197,10 +197,6 @@ var BABYLON;
 //# sourceMappingURL=babylon.glTFSerializer.js.map
 
 
-/**
- * Module for the Babylon glTF 2.0 exporter.  Should ONLY be used internally
- * @hidden
- */
 var BABYLON;
 (function (BABYLON) {
     var GLTF2;

+ 0 - 4
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -55,10 +55,6 @@ declare module BABYLON {
 }
 
 
-/**
- * Module for the Babylon glTF 2.0 exporter.  Should ONLY be used internally
- * @hidden
- */
 declare module BABYLON.GLTF2 {
     /**
      * Converts Babylon Scene into glTF 2.0.

文件差异内容过多而无法显示
+ 24 - 24
dist/preview release/viewer/babylon.viewer.js


+ 343 - 204
dist/preview release/viewer/babylon.viewer.max.js

@@ -24797,7 +24797,7 @@ var BABYLON;
         });
         Object.defineProperty(Scene.prototype, "debugLayer", {
             /**
-             * Gets the debug layer associated with the scene
+             * Gets the debug layer (aka Inspector) associated with the scene
              * @see http://doc.babylonjs.com/features/playground_debuglayer
              */
             get: function () {
@@ -28698,10 +28698,14 @@ var BABYLON;
             if (!BABYLON.PickingInfo) {
                 return null;
             }
-            return this._internalPick(function (world) {
+            var result = this._internalPick(function (world) {
                 _this.createPickingRayToRef(x, y, world, _this._tempPickingRay, camera || null);
                 return _this._tempPickingRay;
             }, predicate, fastCheck);
+            if (result) {
+                result.ray = this.createPickingRay(x, y, BABYLON.Matrix.Identity(), camera || null);
+            }
+            return result;
         };
         /** Launch a ray to try to pick a sprite in the scene
          * @param x position on screen
@@ -87008,9 +87012,51 @@ var BABYLON;
              *  If the utility layer should automatically be rendered on top of existing scene
             */
             this.shouldRender = true;
+            /**
+             * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
+             */
+            this.onlyCheckPointerDownEvents = true;
             // 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();
+            // Detach controls on utility scene, events will be fired by logic below to handle picking priority
+            this.utilityLayerScene.detachControl();
+            this._originalPointerObserver = originalScene.onPrePointerObservable.add(function (prePointerInfo, eventState) {
+                var utilityScenePick = prePointerInfo.ray ? _this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : _this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
+                if (!prePointerInfo.ray && utilityScenePick) {
+                    prePointerInfo.ray = utilityScenePick.ray;
+                }
+                // always fire the prepointer oversvable
+                _this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                // allow every non pointer down event to flow to the utility layer
+                if (_this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!prePointerInfo.skipOnPointerObservable) {
+                        _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                    }
+                    return;
+                }
+                if (_this.utilityLayerScene.autoClearDepthAndStencil) {
+                    // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
+                    if (utilityScenePick && utilityScenePick.hit) {
+                        if (!prePointerInfo.skipOnPointerObservable) {
+                            _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                        }
+                        prePointerInfo.skipOnPointerObservable = true;
+                    }
+                }
+                else {
+                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
+                    if (originalScenePick && utilityScenePick) {
+                        if (utilityScenePick.distance < originalScenePick.distance) {
+                            if (!prePointerInfo.skipOnPointerObservable) {
+                                _this.utilityLayerScene.onPointerObservable.notifyObservers(new BABYLON.PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                            }
+                            prePointerInfo.skipOnPointerObservable = true;
+                        }
+                    }
+                }
+            });
             // Render directly on top of existing scene without clearing
             this.utilityLayerScene.autoClear = false;
             this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(function () {
@@ -87040,6 +87086,9 @@ var BABYLON;
             if (this._sceneDisposeObserver) {
                 this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
             }
+            if (this._originalPointerObserver) {
+                this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
+            }
             this.utilityLayerScene.dispose();
         };
         UtilityLayerRenderer.prototype._updateCamera = function () {
@@ -87055,6 +87104,190 @@ var BABYLON;
 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 across.
+         */
+        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;
+            /**
+             *  If the drag behavior will react to drag events
+             */
+            this.enabled = true;
+            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);
+            var pickPredicate = function (m) {
+                return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
+            };
+            this._pointerObserver = this._scene.onPointerObservable.add(function (pointerInfo, eventState) {
+                if (!_this.enabled) {
+                    return;
+                }
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (!dragging && pointerInfo.pickInfo && pointerInfo.pickInfo.hit && pointerInfo.pickInfo.pickedMesh && pointerInfo.pickInfo.ray && pickPredicate(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;
+(function (BABYLON) {
+    /**
      * Renders gizmos on top of an existing scene which provide controls for position, rotation, etc.
      */
     var Gizmo = /** @class */ (function () {
@@ -87144,7 +87377,7 @@ var BABYLON;
             arrowTail.position.z += 0.2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(dragAxis));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
@@ -87209,7 +87442,7 @@ var BABYLON;
             arrowTail.position.z += 0.2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(dragAxis));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragAxis: dragAxis });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             _this._dragBehavior.onDragObservable.add(function (event) {
@@ -87268,7 +87501,7 @@ var BABYLON;
             rotationMesh.rotation.x = Math.PI / 2;
             _this._rootMesh.lookAt(_this._rootMesh.position.subtract(planeNormal));
             // Add drag behavior to handle events when the gizmo is dragged
-            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragPlaneNormal: planeNormal, pointerObservableScene: gizmoLayer.originalScene });
+            _this._dragBehavior = new BABYLON.PointerDragBehavior({ dragPlaneNormal: planeNormal });
             _this._dragBehavior.moveAttached = false;
             _this._rootMesh.addBehavior(_this._dragBehavior);
             var lastDragPosition = null;
@@ -87477,6 +87710,91 @@ var BABYLON;
 var BABYLON;
 (function (BABYLON) {
     /**
+     * Helps setup gizmo's in the scene to rotate/scale/position meshes
+     */
+    var GizmoManager = /** @class */ (function () {
+        /**
+         * Instatiates a gizmo manager
+         * @param scene the scene to overlay the gizmos on top of
+         * @param options If only a single gizmo should exist at one time
+         */
+        function GizmoManager(scene, options) {
+            var _this = this;
+            this.scene = scene;
+            // Set of gizmos that are currently in the scene for each mesh
+            this._gizmoSet = {};
+            this._pointerObserver = null;
+            this._gizmoLayer = new BABYLON.UtilityLayerRenderer(scene);
+            // Options parsing
+            if (!options) {
+                options = {};
+            }
+            if (options.singleGizmo === undefined) {
+                options.singleGizmo = true;
+            }
+            // Instatiate/dispose gizmos based on pointer actions
+            this._pointerObserver = scene.onPointerObservable.add(function (pointerInfo, state) {
+                if (pointerInfo.type == BABYLON.PointerEventTypes.POINTERDOWN) {
+                    if (pointerInfo.pickInfo && pointerInfo.pickInfo.pickedMesh) {
+                        if (!_this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId]) {
+                            if (options.singleGizmo) {
+                                _this._clearGizmos();
+                            }
+                            // Enable gizmo when mesh is selected
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId] = { positionGizmo: new BABYLON.PositionGizmo(_this._gizmoLayer), rotationGizmo: new BABYLON.RotationGizmo(_this._gizmoLayer) };
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                            _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.attachedMesh = pointerInfo.pickInfo.pickedMesh;
+                        }
+                        else {
+                            if (!options.singleGizmo) {
+                                // Disable gizmo when clicked again
+                                _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].positionGizmo.dispose();
+                                _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId].rotationGizmo.dispose();
+                                delete _this._gizmoSet[pointerInfo.pickInfo.pickedMesh.uniqueId];
+                            }
+                        }
+                    }
+                    else {
+                        if (options.singleGizmo) {
+                            // Disable gizmo when clicked away
+                            if (pointerInfo.pickInfo && pointerInfo.pickInfo.ray) {
+                                var gizmoPick = _this._gizmoLayer.utilityLayerScene.pickWithRay(pointerInfo.pickInfo.ray);
+                                if (gizmoPick && !gizmoPick.hit) {
+                                    _this._clearGizmos();
+                                }
+                            }
+                        }
+                    }
+                }
+            });
+        }
+        /**
+         * Disposes of the gizmo manager
+         */
+        GizmoManager.prototype.dispose = function () {
+            this.scene.onPointerObservable.remove(this._pointerObserver);
+            this._clearGizmos();
+            this._gizmoLayer.dispose();
+        };
+        GizmoManager.prototype._clearGizmos = function () {
+            for (var key in this._gizmoSet) {
+                if (this._gizmoSet.hasOwnProperty(key)) {
+                    this._gizmoSet[key].positionGizmo.dispose();
+                    this._gizmoSet[key].rotationGizmo.dispose();
+                    delete this._gizmoSet[key];
+                }
+            }
+        };
+        return GizmoManager;
+    }());
+    BABYLON.GizmoManager = GizmoManager;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.gizmoManager.js.map
+
+var BABYLON;
+(function (BABYLON) {
+    /**
      * Defines a target to use with MorphTargetManager
      * @see http://doc.babylonjs.com/how_to/how_to_use_morphtargets
      */
@@ -94822,6 +95140,13 @@ var BABYLON;
         AbstractAssetTask.prototype.runTask = function (scene, onSuccess, onError) {
             throw new Error("runTask is not implemented");
         };
+        /**
+         * Reset will set the task state back to INIT, so the next load call of the assets manager will execute this task again.
+         * This can be used with failed tasks that have the reason for failure fixed.
+         */
+        AbstractAssetTask.prototype.reset = function () {
+            this._taskState = AssetTaskState.INIT;
+        };
         AbstractAssetTask.prototype.onErrorCallback = function (onError, message, exception) {
             this._taskState = AssetTaskState.ERROR;
             this._errorObject = {
@@ -95387,6 +95712,16 @@ var BABYLON;
             this._tasks.push(task);
             return task;
         };
+        /**
+         * Remove a task from the assets manager.
+         * @param task the task to remove
+         */
+        AssetsManager.prototype.removeTask = function (task) {
+            var index = this._tasks.indexOf(task);
+            if (index > -1) {
+                this._tasks.splice(index, 1);
+            }
+        };
         AssetsManager.prototype._decreaseWaitingTasksCount = function (task) {
             this._waitingTasksCount--;
             try {
@@ -95482,7 +95817,9 @@ var BABYLON;
             }
             for (var index = 0; index < this._tasks.length; index++) {
                 var task = this._tasks[index];
-                this._runTask(task);
+                if (task.taskState === AssetTaskState.INIT) {
+                    this._runTask(task);
+                }
             }
             return this;
         };
@@ -96986,204 +97323,6 @@ var BABYLON;
 
 //# 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 across. pointerObservableScene can be used to listen to drag events from another scene(eg. if the attached mesh is in an overlay scene).
-         */
-        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;
-            /**
-             *  If the drag behavior will react to drag events
-             */
-            this.enabled = true;
-            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();
-            if (!this.options.pointerObservableScene) {
-                this.options.pointerObservableScene = this._scene;
-            }
-            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);
-            var pickPredicate = function (m) {
-                return _this._attachedNode == m || m.isDescendantOf(_this._attachedNode);
-            };
-            this._pointerObserver = this.options.pointerObservableScene.onPrePointerObservable.add(function (pointerInfoPre, eventState) {
-                if (!_this.enabled) {
-                    return;
-                }
-                // Check if attached mesh is picked
-                var pickInfo = pointerInfoPre.ray ? _this._scene.pickWithRay(pointerInfoPre.ray, pickPredicate) : _this._scene.pick(_this._scene.pointerX, _this._scene.pointerY, pickPredicate);
-                if (pickInfo) {
-                    pickInfo.ray = pointerInfoPre.ray;
-                    if (!pickInfo.ray) {
-                        pickInfo.ray = _this.options.pointerObservableScene.createPickingRay(_this._scene.pointerX, _this._scene.pointerY, BABYLON.Matrix.Identity(), _this._scene.activeCamera);
-                    }
-                    if (pickInfo.hit && pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                        pointerInfoPre.skipOnPointerObservable = true;
-                    }
-                }
-                if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERDOWN) {
-                    if (!dragging && pickInfo && pickInfo.hit && pickInfo.pickedMesh && pickInfo.ray) {
-                        _this._updateDragPlanePosition(pickInfo.ray);
-                        var pickedPoint = _this._pickWithRayOnDragPlane(pickInfo.ray);
-                        if (pickedPoint) {
-                            dragging = true;
-                            _this._draggingID = pointerInfoPre.event.pointerId;
-                            lastPosition.copyFrom(pickedPoint);
-                            _this.onDragStartObservable.notifyObservers({ dragPlanePoint: pickedPoint });
-                        }
-                    }
-                }
-                else if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERUP) {
-                    if (_this._draggingID == pointerInfoPre.event.pointerId) {
-                        dragging = false;
-                        _this._draggingID = -1;
-                        _this.onDragEndObservable.notifyObservers({ dragPlanePoint: lastPosition });
-                    }
-                }
-                else if (pointerInfoPre.type == BABYLON.PointerEventTypes.POINTERMOVE) {
-                    if (_this._draggingID == pointerInfoPre.event.pointerId && dragging && pickInfo && pickInfo.ray) {
-                        var pickedPoint = _this._pickWithRayOnDragPlane(pickInfo.ray);
-                        _this._updateDragPlanePosition(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.onPrePointerObservable.remove(this._pointerObserver);
-            }
-        };
-        return PointerDragBehavior;
-    }());
-    BABYLON.PointerDragBehavior = PointerDragBehavior;
-})(BABYLON || (BABYLON = {}));
-
-//# sourceMappingURL=babylon.pointerDragBehavior.js.map
-
 
 var BABYLON;
 (function (BABYLON) {

+ 128 - 0
gui/src/3D/controls/spherePanel.ts

@@ -0,0 +1,128 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GUI {
+    /**
+     * Class used to create a conainter panel deployed on the surface of a sphere
+     */
+    export class SpherePanel extends Container3D {
+        private _radius = 5.0;
+        private _columns = 10;
+
+        /**
+         * Gets or sets a the radius of the sphere where to project controls (5 by default)
+         */
+        public get radius(): float {
+            return this._radius;
+        }
+
+        public set radius(value: float) {
+            if (this._radius === value) {
+                return;
+            }
+
+            this._radius = value;
+
+            Tools.SetImmediate(() => {
+                this._arrangeChildren();               
+            });
+        }        
+
+        /**
+         * 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 
+         */
+        public get columns(): int {
+            return this._columns;
+        }
+
+        public set columns(value: int) {
+            if (this._columns === value) {
+                return;
+            }
+
+            this._columns = value;
+
+            Tools.SetImmediate(() => {
+                this._arrangeChildren();               
+            });
+        }         
+
+        /**
+         * Creates new SpherePanel
+         */
+        public constructor() {
+            super();
+        }        
+
+        protected _arrangeChildren() {
+            let cellWidth = 0;
+            let cellHeight = 0;
+            let rows = 0;
+            let controlCount = 0;
+
+            let currentInverseWorld = Matrix.Invert(this.node!.computeWorldMatrix(true));
+
+            // Measure
+            for (var child of this._children) {
+                if (!child.mesh) {
+                    continue;
+                }
+
+                controlCount++;
+                child.mesh.computeWorldMatrix(true);
+                child.mesh.getWorldMatrix().multiplyToRef(currentInverseWorld, Tmp.Matrix[0]);
+
+                let boundingBox = child.mesh.getBoundingInfo().boundingBox;
+                let extendSize = Vector3.TransformNormal(boundingBox.extendSize, Tmp.Matrix[0]);
+
+                cellWidth = Math.max(cellWidth, extendSize.x * 2);
+                cellHeight = Math.max(cellHeight, extendSize.y * 2);
+            }
+
+            // Arrange
+            rows = Math.ceil(controlCount / this._columns);
+
+            let startOffsetX = (this._columns * 0.5) * cellWidth;
+            let startOffsetY = (rows * 0.5) * cellHeight;
+            let nodeGrid = [];
+            let cellCounter = 0;
+
+            for (var c = 0; c < this._columns; c++)
+            {
+                for (var r = 0; r < rows; r++)
+                {
+                    nodeGrid.push(new Vector3((c * cellWidth) - startOffsetX + cellWidth / 2, -(r * cellHeight) + startOffsetY - cellHeight / 2, 0));
+                    cellCounter++;
+                    if (cellCounter > controlCount)
+                    {
+                        break;
+                    }
+                }
+            }
+
+            cellCounter = 0;
+            for (var child of this._children) {
+                if (!child.mesh) {
+                    continue;
+                }                
+                let newPos = this._sphericalMapping(nodeGrid[cellCounter]);
+
+                child.position = newPos;
+
+                cellCounter++;
+            }
+        }
+
+        private _sphericalMapping(source: Vector3)
+        {
+            let newPos = new Vector3(0, 0, this._radius);
+
+            let xAngle = (source.x / this._radius) ;
+            let yAngle = -(source.y / this._radius);
+
+            Matrix.RotationYawPitchRollToRef(yAngle, xAngle, 0, Tmp.Matrix[0]);
+
+            return Vector3WithInfo.TransformCoordinates(newPos, Tmp.Matrix[0]);
+        }
+    }
+}

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

@@ -35,8 +35,10 @@ module BABYLON.GUI {
          * Creates new StackPanel
          * @param isVertical 
          */
-        public constructor() {
+        public constructor(isVertical = false) {
             super();
+
+            this._isVertical = isVertical;
         }
 
         protected _arrangeChildren() {

+ 22 - 29
gui/src/3D/gui3DManager.ts

@@ -1,5 +1,6 @@
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
 
+
 module BABYLON.GUI {
     /**
      * Class used to manage 3D user interface
@@ -10,7 +11,7 @@ module BABYLON.GUI {
         private _sceneDisposeObserver: Nullable<Observer<Scene>>;
         private _utilityLayer: Nullable<UtilityLayerRenderer>;
         private _rootContainer: Container3D;
-        private _pointerObserver: Nullable<Observer<PointerInfoPre>>;
+        private _pointerObserver: Nullable<Observer<PointerInfo>>;
         /** @hidden */
         public _lastPickedControl: Control3D;
         /** @hidden */
@@ -50,22 +51,15 @@ module BABYLON.GUI {
             })
 
             this._utilityLayer = new UtilityLayerRenderer(this._scene);
+            this._utilityLayer.onlyCheckPointerDownEvents = false;
 
             // Root
             this._rootContainer = new Container3D("RootContainer");
             this._rootContainer._host = this;
+            let utilityLayerScene = this._utilityLayer.utilityLayerScene;
             
             // Events
-            this._pointerObserver = this._scene.onPrePointerObservable.add((pi, state) => {
-
-                if (pi.skipOnPointerObservable) {
-                    return;
-                }
-
-                let pointerEvent = <PointerEvent>(pi.event);
-                if (this._scene.isPointerCaptured(pointerEvent.pointerId)) {
-                    return;
-                }
+            this._pointerObserver = utilityLayerScene.onPointerObservable.add((pi, state) => {
 
                 if (pi.type !== BABYLON.PointerEventTypes.POINTERMOVE
                     && pi.type !== BABYLON.PointerEventTypes.POINTERUP
@@ -73,13 +67,7 @@ module BABYLON.GUI {
                     return;
                 }
 
-                let camera = this._scene.cameraToUseForPointers || this._scene.activeCamera;
-
-                if (!camera) {
-                    return;
-                }
-
-                pi.skipOnPointerObservable = this._doPicking(pi.type, pointerEvent, pi.ray)
+                this._doPicking(pi)
             });
 
             // Scene
@@ -88,16 +76,17 @@ module BABYLON.GUI {
             new BABYLON.HemisphericLight("hemi", Vector3.Up(), this._utilityLayer.utilityLayerScene);
         }
 
-        private _doPicking(type: number, pointerEvent: PointerEvent, ray?:Nullable<Ray>): boolean {
+        private _doPicking(pi: PointerInfo): boolean {
             if (!this._utilityLayer || !this._utilityLayer.utilityLayerScene.activeCamera) {
                 return false;                
             }
 
+            let pointerEvent = <PointerEvent>(pi.event);
+
             let pointerId = pointerEvent.pointerId || 0;
             let buttonIndex = pointerEvent.button;
-            var utilityScene = this._utilityLayer.utilityLayerScene;
-
-            let pickingInfo = ray ? utilityScene.pickWithRay(ray): utilityScene.pick(this._scene.pointerX, this._scene.pointerY);
+            
+            let pickingInfo = pi.pickInfo;
             if (!pickingInfo || !pickingInfo.hit) {
                 var previousControlOver = this._lastControlOver[pointerId];
                 if (previousControlOver) {
@@ -105,7 +94,7 @@ module BABYLON.GUI {
                     delete this._lastControlOver[pointerId];
                 }               
                 
-                if (type === BABYLON.PointerEventTypes.POINTERUP) {
+                if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
                     if (this._lastControlDown[pointerEvent.pointerId]) {
                         this._lastControlDown[pointerEvent.pointerId].forcePointerUp();
                         delete this._lastControlDown[pointerEvent.pointerId];
@@ -121,9 +110,9 @@ module BABYLON.GUI {
                 this.onPickedPointChangedObservable.notifyObservers(pickingInfo.pickedPoint);
             }
 
-            if (!control._processObservables(type, pickingInfo.pickedPoint!, pointerId, buttonIndex)) {
+            if (!control._processObservables(pi.type, pickingInfo.pickedPoint!, pointerId, buttonIndex)) {
 
-                if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
+                if (pi.type === BABYLON.PointerEventTypes.POINTERMOVE) {
                     if (this._lastControlOver[pointerId]) {
                         this._lastControlOver[pointerId]._onPointerOut(this._lastControlOver[pointerId]);
                     }
@@ -132,7 +121,7 @@ module BABYLON.GUI {
                 }
             }
 
-            if (type === BABYLON.PointerEventTypes.POINTERUP) {
+            if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
                 if (this._lastControlDown[pointerEvent.pointerId]) {
                     this._lastControlDown[pointerEvent.pointerId].forcePointerUp();
                     delete this._lastControlDown[pointerEvent.pointerId];
@@ -196,12 +185,16 @@ module BABYLON.GUI {
 
             this.onPickedPointChangedObservable.clear();
 
-            if (this._scene) {
+            let utilityLayerScene = this._utilityLayer ? this._utilityLayer.utilityLayerScene : null;
+
+            if (utilityLayerScene) {
                 if (this._pointerObserver) {
-                    this._scene.onPrePointerObservable.remove(this._pointerObserver);
+                    utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
                     this._pointerObserver = null;
                 }
-                if (this._sceneDisposeObserver) {
+            }
+            if (this._scene) {
+                 if (this._sceneDisposeObserver) {
                     this._scene.onDisposeObservable.remove(this._sceneDisposeObserver);
                     this._sceneDisposeObserver = null;
                 }                

+ 14 - 3
src/Rendering/babylon.utilityLayerRenderer.ts

@@ -3,6 +3,8 @@ module BABYLON {
      * Renders a layer on top of an existing scene
      */
     export class UtilityLayerRenderer implements IDisposable {
+        private _pointerCaptures: {[pointerId:number]: boolean} = {};
+
         /** 
          * The scene that is rendered on top of the original scene
          */ 
@@ -58,15 +60,24 @@ module BABYLON {
                     }
                 }else{
                     var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    let pointerEvent = <PointerEvent>(prePointerInfo.event);
 
                     // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
                     if(originalScenePick && utilityScenePick){
-                        if(utilityScenePick.distance < originalScenePick.distance){
+                        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)){
                             if(!prePointerInfo.skipOnPointerObservable){
                                 this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
                             }
-                            prePointerInfo.skipOnPointerObservable = true;
-                        }
+                            prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
+                        } 
                     }
                 }