123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- import { Scene } from "../scene.js";
- import { Observable } from "../Misc/observable.js";
- import { PointerInfo, PointerEventTypes } from "../Events/pointerEvents.js";
- import { PickingInfo } from "../Collisions/pickingInfo.js";
- import { EngineStore } from "../Engines/engineStore.js";
- import { HemisphericLight } from "../Lights/hemisphericLight.js";
- import { Vector3 } from "../Maths/math.vector.js";
- import { Color3 } from "../Maths/math.color.js";
- /**
- * Renders a layer on top of an existing scene
- */
- export class UtilityLayerRenderer {
- /**
- * Gets the camera that is used to render the utility layer (when not set, this will be the last active camera)
- * @param getRigParentIfPossible if the current active camera is a rig camera, should its parent camera be returned
- * @returns the camera that is used when rendering the utility layer
- */
- getRenderCamera(getRigParentIfPossible) {
- if (this._renderCamera) {
- return this._renderCamera;
- }
- else {
- let activeCam;
- if (this.originalScene.activeCameras && this.originalScene.activeCameras.length > 1) {
- activeCam = this.originalScene.activeCameras[this.originalScene.activeCameras.length - 1];
- }
- else {
- activeCam = this.originalScene.activeCamera;
- }
- if (getRigParentIfPossible && activeCam && activeCam.isRigCamera) {
- return activeCam.rigParent;
- }
- return activeCam;
- }
- }
- /**
- * Sets the camera that should be used when rendering the utility layer (If set to null the last active camera will be used)
- * @param cam the camera that should be used when rendering the utility layer
- */
- setRenderCamera(cam) {
- this._renderCamera = cam;
- }
- /**
- * @internal
- * Light which used by gizmos to get light shading
- */
- _getSharedGizmoLight() {
- if (!this._sharedGizmoLight) {
- this._sharedGizmoLight = new HemisphericLight("shared gizmo light", new Vector3(0, 1, 0), this.utilityLayerScene);
- this._sharedGizmoLight.intensity = 2;
- this._sharedGizmoLight.groundColor = Color3.Gray();
- }
- return this._sharedGizmoLight;
- }
- /**
- * 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)
- */
- static get DefaultUtilityLayer() {
- if (UtilityLayerRenderer._DefaultUtilityLayer == null) {
- return UtilityLayerRenderer._CreateDefaultUtilityLayerFromScene(EngineStore.LastCreatedScene);
- }
- return UtilityLayerRenderer._DefaultUtilityLayer;
- }
- /**
- * Creates an utility layer, and set it as a default utility layer
- * @param scene associated scene
- * @internal
- */
- static _CreateDefaultUtilityLayerFromScene(scene) {
- UtilityLayerRenderer._DefaultUtilityLayer = new UtilityLayerRenderer(scene);
- UtilityLayerRenderer._DefaultUtilityLayer.originalScene.onDisposeObservable.addOnce(() => {
- UtilityLayerRenderer._DefaultUtilityLayer = null;
- });
- 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)
- */
- static get DefaultKeepDepthUtilityLayer() {
- 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;
- }
- /**
- * 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 */
- originalScene, handleEvents = true) {
- this.originalScene = originalScene;
- this._pointerCaptures = {};
- this._lastPointerEvents = {};
- this._sharedGizmoLight = null;
- this._renderCamera = null;
- /**
- * If the picking should be done on the utility layer prior to the actual scene (Default: true)
- */
- this.pickUtilitySceneFirst = true;
- /**
- * 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;
- /**
- * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
- */
- this.processAllEvents = false;
- /**
- * Set to false to disable picking
- */
- this.pickingEnabled = true;
- /**
- * Observable raised when the pointer moves from the utility layer scene to the main scene
- */
- this.onPointerOutObservable = new Observable();
- // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
- this.utilityLayerScene = new Scene(originalScene.getEngine(), { virtual: true });
- this.utilityLayerScene.useRightHandedSystem = originalScene.useRightHandedSystem;
- this.utilityLayerScene._allowPostProcessClearColor = false;
- // Deactivate post processes
- this.utilityLayerScene.postProcessesEnabled = false;
- // 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) => {
- if (!this.utilityLayerScene.activeCamera) {
- return;
- }
- if (!this.pickingEnabled) {
- return;
- }
- if (!this.processAllEvents) {
- if (prePointerInfo.type !== PointerEventTypes.POINTERMOVE &&
- prePointerInfo.type !== PointerEventTypes.POINTERUP &&
- prePointerInfo.type !== PointerEventTypes.POINTERDOWN &&
- prePointerInfo.type !== PointerEventTypes.POINTERDOUBLETAP) {
- return;
- }
- }
- this.utilityLayerScene.pointerX = originalScene.pointerX;
- this.utilityLayerScene.pointerY = originalScene.pointerY;
- const pointerEvent = prePointerInfo.event;
- if (originalScene.isPointerCaptured(pointerEvent.pointerId)) {
- this._pointerCaptures[pointerEvent.pointerId] = false;
- return;
- }
- const getNearPickDataForScene = (scene) => {
- let scenePick = null;
- if (prePointerInfo.nearInteractionPickingInfo) {
- if (prePointerInfo.nearInteractionPickingInfo.pickedMesh.getScene() == scene) {
- scenePick = prePointerInfo.nearInteractionPickingInfo;
- }
- else {
- scenePick = new PickingInfo();
- }
- }
- else if (scene !== this.utilityLayerScene && prePointerInfo.originalPickingInfo) {
- scenePick = prePointerInfo.originalPickingInfo;
- }
- else {
- let previousActiveCamera = null;
- // If a camera is set for rendering with this layer
- // it will also be used for the ray computation
- // To preserve back compat and because scene.pick always use activeCamera
- // it's substituted temporarily and a new scenePick is forced.
- // otherwise, the ray with previously active camera is always used.
- // It's set back to previous activeCamera after operation.
- if (this._renderCamera) {
- previousActiveCamera = scene._activeCamera;
- scene._activeCamera = this._renderCamera;
- prePointerInfo.ray = null;
- }
- scenePick = prePointerInfo.ray ? scene.pickWithRay(prePointerInfo.ray) : scene.pick(originalScene.pointerX, originalScene.pointerY);
- if (previousActiveCamera) {
- scene._activeCamera = previousActiveCamera;
- }
- }
- return scenePick;
- };
- const utilityScenePick = getNearPickDataForScene(this.utilityLayerScene);
- if (!prePointerInfo.ray && utilityScenePick) {
- prePointerInfo.ray = utilityScenePick.ray;
- }
- // always fire the prepointer observable
- this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo);
- // 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), prePointerInfo.type);
- }
- if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
- this._pointerCaptures[pointerEvent.pointerId] = false;
- }
- return;
- }
- 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 (!prePointerInfo.skipOnPointerObservable) {
- this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick), prePointerInfo.type);
- }
- prePointerInfo.skipOnPointerObservable = true;
- }
- }
- else {
- const originalScenePick = getNearPickDataForScene(originalScene);
- const 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 (prePointerInfo.type === PointerEventTypes.POINTERMOVE || prePointerInfo.type === PointerEventTypes.POINTERUP) {
- 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];
- }
- this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
- }
- }
- 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 (prePointerInfo.type === PointerEventTypes.POINTERMOVE || prePointerInfo.type === PointerEventTypes.POINTERUP) {
- 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];
- }
- }
- this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
- }
- }
- if (prePointerInfo.type === PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
- this._pointerCaptures[pointerEvent.pointerId] = false;
- }
- }
- }
- });
- // As a newly added utility layer will be rendered over the screen last, it's pointer events should be processed first
- if (this._originalPointerObserver) {
- originalScene.onPrePointerObservable.makeObserverTopPriority(this._originalPointerObserver);
- }
- }
- // Render directly on top of existing scene without clearing
- this.utilityLayerScene.autoClear = false;
- this._afterRenderObserver = this.originalScene.onAfterRenderCameraObservable.add((camera) => {
- // Only render when the render camera finishes rendering
- if (this.shouldRender && camera == this.getRenderCamera()) {
- this.render();
- }
- });
- this._sceneDisposeObserver = this.originalScene.onDisposeObservable.add(() => {
- this.dispose();
- });
- this._updateCamera();
- }
- _notifyObservers(prePointerInfo, pickInfo, pointerEvent) {
- if (!prePointerInfo.skipOnPointerObservable) {
- this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo), prePointerInfo.type);
- this._lastPointerEvents[pointerEvent.pointerId] = true;
- }
- }
- /**
- * Renders the utility layers scene on top of the original scene
- */
- render() {
- this._updateCamera();
- if (this.utilityLayerScene.activeCamera) {
- // Set the camera's scene to utility layers scene
- const oldScene = this.utilityLayerScene.activeCamera.getScene();
- const 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.utilityLayerScene.render(false);
- // Reset camera's scene back to original
- camera._scene = oldScene;
- if (camera.leftCamera) {
- camera.leftCamera._scene = oldScene;
- }
- if (camera.rightCamera) {
- camera.rightCamera._scene = oldScene;
- }
- }
- }
- /**
- * Disposes of the renderer
- */
- dispose() {
- this.onPointerOutObservable.clear();
- if (this._afterRenderObserver) {
- this.originalScene.onAfterCameraRenderObservable.remove(this._afterRenderObserver);
- }
- if (this._sceneDisposeObserver) {
- this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
- }
- if (this._originalPointerObserver) {
- this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
- }
- this.utilityLayerScene.dispose();
- }
- _updateCamera() {
- this.utilityLayerScene.cameraToUseForPointers = this.getRenderCamera();
- this.utilityLayerScene.activeCamera = this.getRenderCamera();
- }
- }
- /** @internal */
- UtilityLayerRenderer._DefaultUtilityLayer = null;
- /** @internal */
- UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = null;
- //# sourceMappingURL=utilityLayerRenderer.js.map
|