babylon.utilityLayerRenderer.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. module BABYLON {
  2. /**
  3. * Renders a layer on top of an existing scene
  4. */
  5. export class UtilityLayerRenderer implements IDisposable {
  6. private _pointerCaptures: {[pointerId:number]: boolean} = {};
  7. private _lastPointerEvents: {[pointerId:number]: number} = {};
  8. /**
  9. * The scene that is rendered on top of the original scene
  10. */
  11. public utilityLayerScene:Scene;
  12. /**
  13. * If the utility layer should automatically be rendered on top of existing scene
  14. */
  15. public shouldRender:boolean = true;
  16. /**
  17. * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
  18. */
  19. public onlyCheckPointerDownEvents = true;
  20. /**
  21. * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
  22. */
  23. public processAllEvents = false;
  24. /**
  25. * Observable raised when the pointer move from the utility layer scene to the main scene
  26. */
  27. public onPointerOutObservable = new Observable<number>();
  28. /** Gets or sets a predicate that will be used to indicate utility meshes present in the main scene */
  29. public mainSceneTrackerPredicate: (mesh: Nullable<AbstractMesh>) => boolean;
  30. private _afterRenderObserver:Nullable<Observer<Scene>>;
  31. private _sceneDisposeObserver:Nullable<Observer<Scene>>;
  32. private _originalPointerObserver:Nullable<Observer<PointerInfoPre>>;
  33. /**
  34. * Instantiates a UtilityLayerRenderer
  35. * @param originalScene the original scene that will be rendered on top of
  36. */
  37. constructor(/** the original scene that will be rendered on top of */ public originalScene:Scene){
  38. // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
  39. this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
  40. originalScene.getEngine().scenes.pop();
  41. // Detach controls on utility scene, events will be fired by logic below to handle picking priority
  42. this.utilityLayerScene.detachControl();
  43. this._originalPointerObserver = originalScene.onPrePointerObservable.add((prePointerInfo, eventState) => {
  44. if (!this.processAllEvents) {
  45. if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
  46. && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
  47. && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
  48. return;
  49. }
  50. }
  51. var utilityScenePick = prePointerInfo.ray ? this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
  52. if(!prePointerInfo.ray && utilityScenePick){
  53. prePointerInfo.ray = utilityScenePick.ray;
  54. }
  55. // always fire the prepointer oversvable
  56. this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo)
  57. // allow every non pointer down event to flow to the utility layer
  58. if(this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN){
  59. if(!prePointerInfo.skipOnPointerObservable){
  60. this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
  61. }
  62. let pointerEvent = <PointerEvent>(prePointerInfo.event);
  63. if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
  64. this._pointerCaptures[pointerEvent.pointerId] = false;
  65. }
  66. return;
  67. }
  68. if(this.utilityLayerScene.autoClearDepthAndStencil){
  69. // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
  70. if(utilityScenePick && utilityScenePick.hit){
  71. if(!prePointerInfo.skipOnPointerObservable){
  72. this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
  73. }
  74. prePointerInfo.skipOnPointerObservable = true;
  75. }
  76. }else{
  77. var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
  78. let pointerEvent = <PointerEvent>(prePointerInfo.event);
  79. // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
  80. if (originalScenePick && utilityScenePick){
  81. // No pick in utility scene
  82. if (utilityScenePick.distance === 0) {
  83. if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
  84. // We touched an utility mesh present in the main scene
  85. this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
  86. prePointerInfo.skipOnPointerObservable = true;
  87. } else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
  88. this._pointerCaptures[pointerEvent.pointerId] = true;
  89. }
  90. }
  91. // We pick something in utility scene or the pick in utility is closer than the one in main scene
  92. if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)){
  93. this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
  94. prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
  95. } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
  96. // We have a pick in both scenes but main is closer than utility
  97. // We touched an utility mesh present in the main scene
  98. if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
  99. this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
  100. prePointerInfo.skipOnPointerObservable = true;
  101. } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
  102. // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
  103. this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
  104. delete this._lastPointerEvents[pointerEvent.pointerId];
  105. }
  106. }
  107. if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
  108. this._pointerCaptures[pointerEvent.pointerId] = false;
  109. }
  110. }
  111. }
  112. })
  113. // Render directly on top of existing scene without clearing
  114. this.utilityLayerScene.autoClear = false;
  115. this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(()=>{
  116. if(this.shouldRender){
  117. this.render();
  118. }
  119. });
  120. this._sceneDisposeObserver = this.originalScene.onDisposeObservable.add(()=>{
  121. this.dispose();
  122. })
  123. this._updateCamera();
  124. }
  125. private _notifyObservers(prePointerInfo: PointerInfoPre, pickInfo: PickingInfo, pointerEvent: PointerEvent) {
  126. if (!prePointerInfo.skipOnPointerObservable){
  127. this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo))
  128. this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
  129. }
  130. }
  131. /**
  132. * Renders the utility layers scene on top of the original scene
  133. */
  134. public render(){
  135. this._updateCamera();
  136. this.utilityLayerScene.render(false);
  137. }
  138. /**
  139. * Disposes of the renderer
  140. */
  141. public dispose(){
  142. this.onPointerOutObservable.clear();
  143. if(this._afterRenderObserver){
  144. this.originalScene.onAfterRenderObservable.remove(this._afterRenderObserver);
  145. }
  146. if(this._sceneDisposeObserver){
  147. this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
  148. }
  149. if(this._originalPointerObserver){
  150. this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
  151. }
  152. this.utilityLayerScene.dispose();
  153. }
  154. private _updateCamera(){
  155. this.utilityLayerScene.activeCamera = this.originalScene.activeCamera;
  156. }
  157. }
  158. }