babylon.utilityLayerRenderer.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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. private static _DefaultUtilityLayer:Nullable<UtilityLayerRenderer> = null;
  9. private static _DefaultKeepDepthUtilityLayer:Nullable<UtilityLayerRenderer> = null;
  10. /**
  11. * 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)
  12. */
  13. public static get DefaultUtilityLayer():UtilityLayerRenderer{
  14. if(UtilityLayerRenderer._DefaultUtilityLayer == null){
  15. UtilityLayerRenderer._DefaultUtilityLayer = new UtilityLayerRenderer(BABYLON.Engine.LastCreatedScene!);
  16. UtilityLayerRenderer._DefaultUtilityLayer.originalScene.onDisposeObservable.addOnce(()=>{
  17. UtilityLayerRenderer._DefaultUtilityLayer = null;
  18. });
  19. }
  20. return UtilityLayerRenderer._DefaultUtilityLayer;
  21. }
  22. /**
  23. * 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)
  24. */
  25. public static get DefaultKeepDepthUtilityLayer():UtilityLayerRenderer{
  26. if(UtilityLayerRenderer._DefaultKeepDepthUtilityLayer == null){
  27. UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = new UtilityLayerRenderer(BABYLON.Engine.LastCreatedScene!);
  28. UtilityLayerRenderer._DefaultKeepDepthUtilityLayer.utilityLayerScene.autoClearDepthAndStencil = false;
  29. UtilityLayerRenderer._DefaultKeepDepthUtilityLayer.originalScene.onDisposeObservable.addOnce(()=>{
  30. UtilityLayerRenderer._DefaultKeepDepthUtilityLayer = null;
  31. });
  32. }
  33. return UtilityLayerRenderer._DefaultKeepDepthUtilityLayer;
  34. }
  35. /**
  36. * The scene that is rendered on top of the original scene
  37. */
  38. public utilityLayerScene:Scene;
  39. /**
  40. * If the utility layer should automatically be rendered on top of existing scene
  41. */
  42. public shouldRender:boolean = true;
  43. /**
  44. * If set to true, only pointer down onPointerObservable events will be blocked when picking is occluded by original scene
  45. */
  46. public onlyCheckPointerDownEvents = true;
  47. /**
  48. * If set to false, only pointerUp, pointerDown and pointerMove will be sent to the utilityLayerScene (false by default)
  49. */
  50. public processAllEvents = false;
  51. /**
  52. * Observable raised when the pointer move from the utility layer scene to the main scene
  53. */
  54. public onPointerOutObservable = new Observable<number>();
  55. /** Gets or sets a predicate that will be used to indicate utility meshes present in the main scene */
  56. public mainSceneTrackerPredicate: (mesh: Nullable<AbstractMesh>) => boolean;
  57. private _afterRenderObserver:Nullable<Observer<Scene>>;
  58. private _sceneDisposeObserver:Nullable<Observer<Scene>>;
  59. private _originalPointerObserver:Nullable<Observer<PointerInfoPre>>;
  60. /**
  61. * Instantiates a UtilityLayerRenderer
  62. * @param originalScene the original scene that will be rendered on top of
  63. */
  64. constructor(/** the original scene that will be rendered on top of */ public originalScene:Scene){
  65. // Create scene which will be rendered in the foreground and remove it from being referenced by engine to avoid interfering with existing app
  66. this.utilityLayerScene = new BABYLON.Scene(originalScene.getEngine());
  67. this.utilityLayerScene._allowPostProcessClear = false;
  68. originalScene.getEngine().scenes.pop();
  69. // Detach controls on utility scene, events will be fired by logic below to handle picking priority
  70. this.utilityLayerScene.detachControl();
  71. this._originalPointerObserver = originalScene.onPrePointerObservable.add((prePointerInfo, eventState) => {
  72. if (!this.processAllEvents) {
  73. if (prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERMOVE
  74. && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERUP
  75. && prePointerInfo.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
  76. return;
  77. }
  78. }
  79. let pointerEvent = <PointerEvent>(prePointerInfo.event);
  80. if (originalScene!.isPointerCaptured(pointerEvent.pointerId)) {
  81. this._pointerCaptures[pointerEvent.pointerId] = false;
  82. return;
  83. }
  84. var utilityScenePick = prePointerInfo.ray ? this.utilityLayerScene.pickWithRay(prePointerInfo.ray) : this.utilityLayerScene.pick(originalScene.pointerX, originalScene.pointerY);
  85. if(!prePointerInfo.ray && utilityScenePick){
  86. prePointerInfo.ray = utilityScenePick.ray;
  87. }
  88. // always fire the prepointer oversvable
  89. this.utilityLayerScene.onPrePointerObservable.notifyObservers(prePointerInfo)
  90. // allow every non pointer down event to flow to the utility layer
  91. if(this.onlyCheckPointerDownEvents && prePointerInfo.type != BABYLON.PointerEventTypes.POINTERDOWN){
  92. if(!prePointerInfo.skipOnPointerObservable){
  93. this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
  94. }
  95. if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
  96. this._pointerCaptures[pointerEvent.pointerId] = false;
  97. }
  98. return;
  99. }
  100. if(this.utilityLayerScene.autoClearDepthAndStencil){
  101. // If this layer is an overlay, check if this layer was hit and if so, skip pointer events for the main scene
  102. if(utilityScenePick && utilityScenePick.hit){
  103. if(!prePointerInfo.skipOnPointerObservable){
  104. this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, utilityScenePick))
  105. }
  106. prePointerInfo.skipOnPointerObservable = true;
  107. }
  108. }else{
  109. var originalScenePick = prePointerInfo.ray ? originalScene.pickWithRay(prePointerInfo.ray) : originalScene.pick(originalScene.pointerX, originalScene.pointerY);
  110. let pointerEvent = <PointerEvent>(prePointerInfo.event);
  111. // If the layer can be occluded by the original scene, only fire pointer events to the first layer that hit they ray
  112. if (originalScenePick && utilityScenePick){
  113. // No pick in utility scene
  114. if (utilityScenePick.distance === 0 && originalScenePick.pickedMesh) {
  115. if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
  116. // We touched an utility mesh present in the main scene
  117. this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
  118. prePointerInfo.skipOnPointerObservable = true;
  119. } else if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERDOWN) {
  120. this._pointerCaptures[pointerEvent.pointerId] = true;
  121. }
  122. } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance < originalScenePick.distance || originalScenePick.distance === 0)){
  123. // We pick something in utility scene or the pick in utility is closer than the one in main scene
  124. this._notifyObservers(prePointerInfo, utilityScenePick, pointerEvent);
  125. // If a previous utility layer set this, do not unset this
  126. if(!prePointerInfo.skipOnPointerObservable){
  127. prePointerInfo.skipOnPointerObservable = utilityScenePick.distance > 0;
  128. }
  129. } else if (!this._pointerCaptures[pointerEvent.pointerId] && (utilityScenePick.distance > originalScenePick.distance)) {
  130. // We have a pick in both scenes but main is closer than utility
  131. // We touched an utility mesh present in the main scene
  132. if (this.mainSceneTrackerPredicate && this.mainSceneTrackerPredicate(originalScenePick.pickedMesh)) {
  133. this._notifyObservers(prePointerInfo, originalScenePick, pointerEvent);
  134. prePointerInfo.skipOnPointerObservable = true;
  135. } else if (this._lastPointerEvents[pointerEvent.pointerId]) {
  136. // We need to send a last pointerup to the utilityLayerScene to make sure animations can complete
  137. this.onPointerOutObservable.notifyObservers(pointerEvent.pointerId);
  138. delete this._lastPointerEvents[pointerEvent.pointerId];
  139. }
  140. }
  141. if (prePointerInfo.type === BABYLON.PointerEventTypes.POINTERUP && this._pointerCaptures[pointerEvent.pointerId]) {
  142. this._pointerCaptures[pointerEvent.pointerId] = false;
  143. }
  144. }
  145. }
  146. })
  147. // Render directly on top of existing scene without clearing
  148. this.utilityLayerScene.autoClear = false;
  149. this._afterRenderObserver = this.originalScene.onAfterRenderObservable.add(()=>{
  150. if(this.shouldRender){
  151. this.render();
  152. }
  153. });
  154. this._sceneDisposeObserver = this.originalScene.onDisposeObservable.add(()=>{
  155. this.dispose();
  156. })
  157. this._updateCamera();
  158. }
  159. private _notifyObservers(prePointerInfo: PointerInfoPre, pickInfo: PickingInfo, pointerEvent: PointerEvent) {
  160. if (!prePointerInfo.skipOnPointerObservable){
  161. this.utilityLayerScene.onPointerObservable.notifyObservers(new PointerInfo(prePointerInfo.type, prePointerInfo.event, pickInfo))
  162. this._lastPointerEvents[pointerEvent.pointerId] = pointerEvent.pointerType;
  163. }
  164. }
  165. /**
  166. * Renders the utility layers scene on top of the original scene
  167. */
  168. public render(){
  169. this._updateCamera();
  170. if(this.utilityLayerScene.activeCamera){
  171. // Set the camera's scene to utility layers scene
  172. var oldScene = this.utilityLayerScene.activeCamera.getScene();
  173. var camera = this.utilityLayerScene.activeCamera;
  174. camera._scene = this.utilityLayerScene;
  175. if(camera.leftCamera){
  176. camera.leftCamera._scene = this.utilityLayerScene;
  177. }
  178. if(camera.rightCamera){
  179. camera.rightCamera._scene = this.utilityLayerScene;
  180. }
  181. this.utilityLayerScene.render(false);
  182. // Reset camera's scene back to original
  183. camera._scene = oldScene;
  184. if(camera.leftCamera){
  185. camera.leftCamera._scene = oldScene;
  186. }
  187. if(camera.rightCamera){
  188. camera.rightCamera._scene = oldScene;
  189. }
  190. }
  191. }
  192. /**
  193. * Disposes of the renderer
  194. */
  195. public dispose(){
  196. this.onPointerOutObservable.clear();
  197. if(this._afterRenderObserver){
  198. this.originalScene.onAfterRenderObservable.remove(this._afterRenderObserver);
  199. }
  200. if(this._sceneDisposeObserver){
  201. this.originalScene.onDisposeObservable.remove(this._sceneDisposeObserver);
  202. }
  203. if(this._originalPointerObserver){
  204. this.originalScene.onPrePointerObservable.remove(this._originalPointerObserver);
  205. }
  206. this.utilityLayerScene.dispose();
  207. }
  208. private _updateCamera(){
  209. this.utilityLayerScene.activeCamera = this.originalScene.activeCamera;
  210. }
  211. }
  212. }