David Catuhe 6 years ago
parent
commit
5482dc32d1
4 changed files with 11601 additions and 11567 deletions
  1. 15 5
      gui/src/2D/advancedDynamicTexture.ts
  2. 6671 6656
      src/Maths/math.ts
  3. 223 222
      src/Rendering/utilityLayerRenderer.ts
  4. 4692 4684
      src/scene.ts

+ 15 - 5
gui/src/2D/advancedDynamicTexture.ts

@@ -645,6 +645,9 @@ export class AdvancedDynamicTexture extends DynamicTexture {
         if (!scene) {
             return;
         }
+
+        let tempViewport = new Viewport(0, 0, 0, 0);
+
         this._pointerMoveObserver = scene.onPrePointerObservable.add((pi, state) => {
             if (scene!.isPointerCaptured((<PointerEvent>(pi.event)).pointerId)) {
                 return;
@@ -657,14 +660,21 @@ export class AdvancedDynamicTexture extends DynamicTexture {
             if (!scene) {
                 return;
             }
+
             let camera = scene.cameraToUseForPointers || scene.activeCamera;
+            let engine = scene.getEngine();
+
             if (!camera) {
-                return;
+                tempViewport.x = 0;
+                tempViewport.y = 0;
+                tempViewport.width = engine.getRenderWidth();
+                tempViewport.height = engine.getRenderHeight();
+            } else {
+                camera.viewport.toGlobalToRef(engine.getRenderWidth(), engine.getRenderHeight(), tempViewport);
             }
-            let engine = scene.getEngine();
-            let viewport = camera.viewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
-            let x = scene.pointerX / engine.getHardwareScalingLevel() - viewport.x;
-            let y = scene.pointerY / engine.getHardwareScalingLevel() - (engine.getRenderHeight() - viewport.y - viewport.height);
+
+            let x = scene.pointerX / engine.getHardwareScalingLevel() - tempViewport.x;
+            let y = scene.pointerY / engine.getHardwareScalingLevel() - (engine.getRenderHeight() - tempViewport.y - tempViewport.height);
             this._shouldBlockPointer = false;
             // Do picking modifies _shouldBlockPointer
             this._doPicking(x, y, pi.type, (pi.event as PointerEvent).pointerId || 0, pi.event.button);

File diff suppressed because it is too large
+ 6671 - 6656
src/Maths/math.ts


+ 223 - 222
src/Rendering/utilityLayerRenderer.ts

@@ -6,267 +6,268 @@ import { PickingInfo } from "../Collisions/pickingInfo";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { EngineStore } from "../Engines/engineStore";
 
+/**
+ * Renders a layer on top of an existing scene
+ */
+export class UtilityLayerRenderer implements IDisposable {
+    private _pointerCaptures: { [pointerId: number]: boolean } = {};
+    private _lastPointerEvents: { [pointerId: number]: boolean } = {};
+    private static _DefaultUtilityLayer: Nullable<UtilityLayerRenderer> = null;
+    private static _DefaultKeepDepthUtilityLayer: Nullable<UtilityLayerRenderer> = null;
+
     /**
-     * Renders a layer on top of an existing scene
+     * If the picking should be done on the utility layer prior to the actual scene (Default: true)
      */
-    export class UtilityLayerRenderer implements IDisposable {
-        private _pointerCaptures: { [pointerId: number]: boolean } = {};
-        private _lastPointerEvents: { [pointerId: number]: boolean } = {};
-        private static _DefaultUtilityLayer: Nullable<UtilityLayerRenderer> = null;
-        private static _DefaultKeepDepthUtilityLayer: Nullable<UtilityLayerRenderer> = null;
-
-        /**
-         * If the picking should be done on the utility layer prior to the actual scene (Default: true)
-         */
-        public pickUtilitySceneFirst = true;
-        /**
-         * A shared utility layer that can be used to overlay objects into a scene (Depth map of the previous scene is cleared before drawing on top of it)
-         */
-        public static get DefaultUtilityLayer(): UtilityLayerRenderer {
-            if (UtilityLayerRenderer._DefaultUtilityLayer == null) {
-                UtilityLayerRenderer._DefaultUtilityLayer = new UtilityLayerRenderer(EngineStore.LastCreatedScene!);
-                UtilityLayerRenderer._DefaultUtilityLayer.originalScene.onDisposeObservable.addOnce(() => {
-                    UtilityLayerRenderer._DefaultUtilityLayer = null;
-                });
-            }
-            return UtilityLayerRenderer._DefaultUtilityLayer;
+    public pickUtilitySceneFirst = true;
+    /**
+     * A shared utility layer that can be used to overlay objects into a scene (Depth map of the previous scene is cleared before drawing on top of it)
+     */
+    public static get DefaultUtilityLayer(): UtilityLayerRenderer {
+        if (UtilityLayerRenderer._DefaultUtilityLayer == null) {
+            UtilityLayerRenderer._DefaultUtilityLayer = new UtilityLayerRenderer(EngineStore.LastCreatedScene!);
+            UtilityLayerRenderer._DefaultUtilityLayer.originalScene.onDisposeObservable.addOnce(() => {
+                UtilityLayerRenderer._DefaultUtilityLayer = null;
+            });
         }
-        /**
-         * A shared utility layer that can be used to embed objects into a scene (Depth map of the previous scene is not cleared before drawing on top of it)
-         */
-        public static get DefaultKeepDepthUtilityLayer(): UtilityLayerRenderer {
-            if (UtilityLayerRenderer._DefaultKeepDepthUtilityLayer == null) {
-                UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = new UtilityLayerRenderer(EngineStore.LastCreatedScene!);
-                UtilityLayerRenderer._DefaultKeepDepthUtilityLayer.utilityLayerScene.autoClearDepthAndStencil = false;
-                UtilityLayerRenderer._DefaultKeepDepthUtilityLayer.originalScene.onDisposeObservable.addOnce(() => {
-                    UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = null;
-                });
-            }
-            return UtilityLayerRenderer._DefaultKeepDepthUtilityLayer;
+        return UtilityLayerRenderer._DefaultUtilityLayer;
+    }
+    /**
+     * A shared utility layer that can be used to embed objects into a scene (Depth map of the previous scene is not cleared before drawing on top of it)
+     */
+    public static get DefaultKeepDepthUtilityLayer(): UtilityLayerRenderer {
+        if (UtilityLayerRenderer._DefaultKeepDepthUtilityLayer == null) {
+            UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = new UtilityLayerRenderer(EngineStore.LastCreatedScene!);
+            UtilityLayerRenderer._DefaultKeepDepthUtilityLayer.utilityLayerScene.autoClearDepthAndStencil = false;
+            UtilityLayerRenderer._DefaultKeepDepthUtilityLayer.originalScene.onDisposeObservable.addOnce(() => {
+                UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = null;
+            });
         }
+        return UtilityLayerRenderer._DefaultKeepDepthUtilityLayer;
+    }
 
-        /**
-         * The scene that is rendered on top of the original scene
-         */
-        public utilityLayerScene: Scene;
-
-        /**
-         *  If the utility layer should automatically be rendered on top of existing scene
-        */
-        public shouldRender: boolean = true;
-        /**
-         * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
-         */
-        public onlyCheckPointerDownEvents = true;
-
-        /**
-         * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
-         */
-        public processAllEvents = false;
-
-        /**
-         * Observable raised when the pointer move from the utility layer scene to the main scene
-         */
-        public onPointerOutObservable = new Observable<number>();
-
-        /** Gets or sets a predicate that will be used to indicate utility meshes present in the main scene */
-        public mainSceneTrackerPredicate: (mesh: Nullable<AbstractMesh>) => boolean;
-
-        private _afterRenderObserver: Nullable<Observer<Scene>>;
-        private _sceneDisposeObserver: Nullable<Observer<Scene>>;
-        private _originalPointerObserver: Nullable<Observer<PointerInfoPre>>;
-        /**
-         * Instantiates a UtilityLayerRenderer
-         * @param originalScene the original scene that will be rendered on top of
-         * @param handleEvents boolean indicating if the utility layer should handle events
-         */
-        constructor(
-            /** the original scene that will be rendered on top of */
-            public originalScene: Scene,
-            handleEvents: boolean = 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 Scene(originalScene.getEngine());
-            this.utilityLayerScene.useRightHandedSystem = originalScene.useRightHandedSystem;
-            this.utilityLayerScene._allowPostProcessClearColor = false;
-            originalScene.getEngine().scenes.pop();
-
-            // Detach controls on utility scene, events will be fired by logic below to handle picking priority
-            this.utilityLayerScene.detachControl();
-
-            if (handleEvents) {
-                this._originalPointerObserver = originalScene.onPrePointerObservable.add((prePointerInfo, eventState) => {
-                    if (!this.utilityLayerScene.activeCamera) {
-                        return;
-                    }
-                    if (!this.processAllEvents) {
-                        if (prePointerInfo.type !== PointerEventTypes.POINTERMOVE
-                            && prePointerInfo.type !== PointerEventTypes.POINTERUP
-                            && prePointerInfo.type !== PointerEventTypes.POINTERDOWN) {
-                            return;
-                        }
-                    }
+    /**
+     * The scene that is rendered on top of the original scene
+     */
+    public utilityLayerScene: Scene;
 
-                    let pointerEvent = <PointerEvent>(prePointerInfo.event);
-                    if (originalScene!.isPointerCaptured(pointerEvent.pointerId)) {
-                        this._pointerCaptures[pointerEvent.pointerId] = false;
+    /**
+     *  If the utility layer should automatically be rendered on top of existing scene
+    */
+    public shouldRender: boolean = true;
+    /**
+     * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
+     */
+    public onlyCheckPointerDownEvents = true;
+
+    /**
+     * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
+     */
+    public processAllEvents = false;
+
+    /**
+     * Observable raised when the pointer move from the utility layer scene to the main scene
+     */
+    public onPointerOutObservable = new Observable<number>();
+
+    /** Gets or sets a predicate that will be used to indicate utility meshes present in the main scene */
+    public mainSceneTrackerPredicate: (mesh: Nullable<AbstractMesh>) => boolean;
+
+    private _afterRenderObserver: Nullable<Observer<Scene>>;
+    private _sceneDisposeObserver: Nullable<Observer<Scene>>;
+    private _originalPointerObserver: Nullable<Observer<PointerInfoPre>>;
+    /**
+     * Instantiates a UtilityLayerRenderer
+     * @param originalScene the original scene that will be rendered on top of
+     * @param handleEvents boolean indicating if the utility layer should handle events
+     */
+    constructor(
+        /** the original scene that will be rendered on top of */
+        public originalScene: Scene,
+        handleEvents: boolean = 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 Scene(originalScene.getEngine());
+        this.utilityLayerScene.useRightHandedSystem = originalScene.useRightHandedSystem;
+        this.utilityLayerScene._allowPostProcessClearColor = false;
+        originalScene.getEngine().scenes.pop();
+
+        // Detach controls on utility scene, events will be fired by logic below to handle picking priority
+        this.utilityLayerScene.detachControl();
+
+        if (handleEvents) {
+            this._originalPointerObserver = originalScene.onPrePointerObservable.add((prePointerInfo, eventState) => {
+                if (!this.utilityLayerScene.activeCamera) {
+                    return;
+                }
+                if (!this.processAllEvents) {
+                    if (prePointerInfo.type !== PointerEventTypes.POINTERMOVE
+                        && prePointerInfo.type !== PointerEventTypes.POINTERUP
+                        && prePointerInfo.type !== PointerEventTypes.POINTERDOWN) {
                         return;
                     }
+                }
+                this.utilityLayerScene.pointerX = originalScene.pointerX;
+                this.utilityLayerScene.pointerY = originalScene.pointerY;
+                let pointerEvent = <PointerEvent>(prePointerInfo.event);
+                if (originalScene!.isPointerCaptured(pointerEvent.pointerId)) {
+                    this._pointerCaptures[pointerEvent.pointerId] = false;
+                    return;
+                }
+
+                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);
 
-                    var utilityScenePick = prePointerInfo.ray ? this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
-                    if (!prePointerInfo.ray && utilityScenePick) {
-                        prePointerInfo.ray = utilityScenePick.ray;
+                // allow every non pointer down event to flow to the utility layer
+                if (this.onlyCheckPointerDownEvents && prePointerInfo.type != PointerEventTypes.POINTERDOWN) {
+                    if (!prePointerInfo.skipOnPointerObservable) {
+                        this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                     }
+                    if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+                        this._pointerCaptures[pointerEvent.pointerId] = false;
+                    }
+                    return;
+                }
 
-                    // always fire the prepointer oversvable
-                    this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
+                if (this.utilityLayerScene.autoClearDepthAndStencil || this.pickUtilitySceneFirst) {
+                    // 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) {
 
-                    // allow every non pointer down event to flow to the utility layer
-                    if (this.onlyCheckPointerDownEvents && prePointerInfo.type != PointerEventTypes.POINTERDOWN) {
                         if (!prePointerInfo.skipOnPointerObservable) {
                             this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
                         }
-                        if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
-                            this._pointerCaptures[pointerEvent.pointerId] = false;
-                        }
-                        return;
+                        prePointerInfo.skipOnPointerObservable = true;
                     }
+                } else {
+                    var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
+                    let pointerEvent = <PointerEvent>(prePointerInfo.event);
 
-                    if (this.utilityLayerScene.autoClearDepthAndStencil || this.pickUtilitySceneFirst) {
-                        // 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 the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
+                    if (originalScenePick && utilityScenePick) {
 
+                        // No pick in utility scene
+                        if (utilityScenePick.distance === 0 && originalScenePick.pickedMesh) {
+                            if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                // We touched an utility mesh present in the main scene
+                                this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            } else if (prePointerInfo.type === PointerEventTypes.POINTERDOWN) {
+                                this._pointerCaptures[pointerEvent.pointerId] = true;
+                            } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete this._lastPointerEvents[pointerEvent.pointerId];
+                            }
+                        } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
+                            // We pick something in utility scene or the pick in utility is closer than the one in main scene
+                            this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
+                            // If a previous utility layer set this, do not unset this
                             if (!prePointerInfo.skipOnPointerObservable) {
-                                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick));
+                                prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
                             }
-                            prePointerInfo.skipOnPointerObservable = true;
-                        }
-                    } 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) {
-
-                            // No pick in utility scene
-                            if (utilityScenePick.distance === 0 && originalScenePick.pickedMesh) {
-                                if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
-                                    // We touched an utility mesh present in the main scene
-                                    this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
-                                    prePointerInfo.skipOnPointerObservable = true;
-                                } else if (prePointerInfo.type === PointerEventTypes.POINTERDOWN) {
-                                    this._pointerCaptures[pointerEvent.pointerId] = true;
-                                } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
-                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                    this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
-                                    delete this._lastPointerEvents[pointerEvent.pointerId];
-                                }
-                            } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)) {
-                                // We pick something in utility scene or the pick in utility is closer than the one in main scene
-                                this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
-                                // If a previous utility layer set this, do not unset this
-                                if (!prePointerInfo.skipOnPointerObservable) {
-                                    prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
-                                }
-                            } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
-                                // We have a pick in both scenes but main is closer than utility
-
-                                // We touched an utility mesh present in the main scene
-                                if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
-                                    this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
-                                    prePointerInfo.skipOnPointerObservable = true;
-                                } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
-                                    // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
-                                    this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
-                                    delete this._lastPointerEvents[pointerEvent.pointerId];
-                                }
+                        } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
+                            // We have a pick in both scenes but main is closer than utility
+
+                            // We touched an utility mesh present in the main scene
+                            if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
+                                this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
+                                prePointerInfo.skipOnPointerObservable = true;
+                            } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
+                                // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
+                                this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
+                                delete this._lastPointerEvents[pointerEvent.pointerId];
                             }
+                        }
 
-                            if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
-                                this._pointerCaptures[pointerEvent.pointerId] = false;
-                            }
+                        if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
+                            this._pointerCaptures[pointerEvent.pointerId] = false;
                         }
                     }
-
-                });
-            }
-
-            // Render directly on top of existing scene without clearing
-            this.utilityLayerScene.autoClear = false;
-
-            this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(() => {
-                if (this.shouldRender) {
-                    this.render();
                 }
-            });
 
-            this._sceneDisposeObserver = this.originalScene.onDisposeObservable.add(() => {
-                this.dispose();
             });
-
-            this._updateCamera();
         }
 
-        private _notifyObservers(prePointerInfo: PointerInfoPre, pickInfo: PickingInfo, pointerEvent: PointerEvent) {
-            if (!prePointerInfo.skipOnPointerObservable) {
-                this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
-                this._lastPointerEvents[pointerEvent.pointerId] = true;
-            }
-        }
+        // Render directly on top of existing scene without clearing
+        this.utilityLayerScene.autoClear = false;
 
-        /**
-         * Renders the utility layers scene on top of the original scene
-         */
-        public render() {
-            this._updateCamera();
-            if (this.utilityLayerScene.activeCamera) {
-                // Set the camera's scene to utility layers scene
-                var oldScene = this.utilityLayerScene.activeCamera.getScene();
-                var camera = this.utilityLayerScene.activeCamera;
-                camera._scene = this.utilityLayerScene;
-                if (camera.leftCamera) {
-                    camera.leftCamera._scene = this.utilityLayerScene;
-                }
-                if (camera.rightCamera) {
-                    camera.rightCamera._scene = this.utilityLayerScene;
-                }
+        this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(() => {
+            if (this.shouldRender) {
+                this.render();
+            }
+        });
 
-                this.utilityLayerScene.render(false);
+        this._sceneDisposeObserver = this.originalScene.onDisposeObservable.add(() => {
+            this.dispose();
+        });
 
-                // Reset camera's scene back to original
-                camera._scene = oldScene;
-                if (camera.leftCamera) {
-                    camera.leftCamera._scene = oldScene;
-                }
-                if (camera.rightCamera) {
-                    camera.rightCamera._scene = oldScene;
-                }
-            }
+        this._updateCamera();
+    }
 
+    private _notifyObservers(prePointerInfo: PointerInfoPre, pickInfo: PickingInfo, pointerEvent: PointerEvent) {
+        if (!prePointerInfo.skipOnPointerObservable) {
+            this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo));
+            this._lastPointerEvents[pointerEvent.pointerId] = true;
         }
+    }
 
-        /**
-         * Disposes of the renderer
-         */
-        public dispose() {
-            this.onPointerOutObservable.clear();
-
-            if (this._afterRenderObserver) {
-                this.originalScene.onAfterRenderObservable.remove(this._afterRenderObserver);
+    /**
+     * Renders the utility layers scene on top of the original scene
+     */
+    public render() {
+        this._updateCamera();
+        if (this.utilityLayerScene.activeCamera) {
+            // Set the camera's scene to utility layers scene
+            var oldScene = this.utilityLayerScene.activeCamera.getScene();
+            var camera = this.utilityLayerScene.activeCamera;
+            camera._scene = this.utilityLayerScene;
+            if (camera.leftCamera) {
+                camera.leftCamera._scene = this.utilityLayerScene;
+            }
+            if (camera.rightCamera) {
+                camera.rightCamera._scene = this.utilityLayerScene;
             }
-            if (this._sceneDisposeObserver) {
-                this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
+
+            this.utilityLayerScene.render(false);
+
+            // Reset camera's scene back to original
+            camera._scene = oldScene;
+            if (camera.leftCamera) {
+                camera.leftCamera._scene = oldScene;
             }
-            if (this._originalPointerObserver) {
-                this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
+            if (camera.rightCamera) {
+                camera.rightCamera._scene = oldScene;
             }
-            this.utilityLayerScene.dispose();
         }
 
-        private _updateCamera() {
-            if (this.originalScene.activeCameras.length > 1) {
-                this.utilityLayerScene.activeCamera = this.originalScene.activeCameras[0];
-            }else {
-                this.utilityLayerScene.activeCamera = this.originalScene.activeCamera;
-            }
+    }
+
+    /**
+     * Disposes of the renderer
+     */
+    public dispose() {
+        this.onPointerOutObservable.clear();
+
+        if (this._afterRenderObserver) {
+            this.originalScene.onAfterRenderObservable.remove(this._afterRenderObserver);
+        }
+        if (this._sceneDisposeObserver) {
+            this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
+        }
+        if (this._originalPointerObserver) {
+            this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
+        }
+        this.utilityLayerScene.dispose();
+    }
+
+    private _updateCamera() {
+        if (this.originalScene.activeCameras.length > 1) {
+            this.utilityLayerScene.activeCamera = this.originalScene.activeCameras[0];
+        } else {
+            this.utilityLayerScene.activeCamera = this.originalScene.activeCamera;
         }
     }
+}

File diff suppressed because it is too large
+ 4692 - 4684
src/scene.ts