webXRManagedOutputCanvas.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import { Nullable } from "../types";
  2. import { ThinEngine } from "../Engines/thinEngine";
  3. import { WebXRRenderTarget } from "./webXRTypes";
  4. import { WebXRSessionManager } from "./webXRSessionManager";
  5. import { Observable } from "../Misc/observable";
  6. /**
  7. * COnfiguration object for WebXR output canvas
  8. */
  9. export class WebXRManagedOutputCanvasOptions {
  10. /**
  11. * An optional canvas in case you wish to create it yourself and provide it here.
  12. * If not provided, a new canvas will be created
  13. */
  14. public canvasElement?: HTMLCanvasElement;
  15. /**
  16. * Options for this XR Layer output
  17. */
  18. public canvasOptions?: XRWebGLLayerInit;
  19. /**
  20. * CSS styling for a newly created canvas (if not provided)
  21. */
  22. public newCanvasCssStyle?: string;
  23. /**
  24. * Get the default values of the configuration object
  25. * @param engine defines the engine to use (can be null)
  26. * @returns default values of this configuration object
  27. */
  28. public static GetDefaults(engine?: ThinEngine): WebXRManagedOutputCanvasOptions {
  29. const defaults = new WebXRManagedOutputCanvasOptions();
  30. defaults.canvasOptions = {
  31. antialias: true,
  32. depth: true,
  33. stencil: engine ? engine.isStencilEnable : true,
  34. alpha: true,
  35. multiview: false,
  36. framebufferScaleFactor: 1,
  37. };
  38. defaults.newCanvasCssStyle = "position:absolute; bottom:0px;right:0px;z-index:10;width:90%;height:100%;background-color: #000000;";
  39. return defaults;
  40. }
  41. }
  42. /**
  43. * Creates a canvas that is added/removed from the webpage when entering/exiting XR
  44. */
  45. export class WebXRManagedOutputCanvas implements WebXRRenderTarget {
  46. private _canvas: Nullable<HTMLCanvasElement> = null;
  47. private _engine: ThinEngine;
  48. private _originalCanvasSize: {
  49. width: number;
  50. height: number;
  51. };
  52. /**
  53. * Rendering context of the canvas which can be used to display/mirror xr content
  54. */
  55. public canvasContext: WebGLRenderingContext;
  56. /**
  57. * xr layer for the canvas
  58. */
  59. public xrLayer: Nullable<XRWebGLLayer> = null;
  60. /**
  61. * Obseervers registered here will be triggered when the xr layer was initialized
  62. */
  63. public onXRLayerInitObservable: Observable<XRWebGLLayer> = new Observable();
  64. /**
  65. * Initializes the canvas to be added/removed upon entering/exiting xr
  66. * @param _xrSessionManager The XR Session manager
  67. * @param _options optional configuration for this canvas output. defaults will be used if not provided
  68. */
  69. constructor(_xrSessionManager: WebXRSessionManager, private _options: WebXRManagedOutputCanvasOptions = WebXRManagedOutputCanvasOptions.GetDefaults()) {
  70. this._engine = _xrSessionManager.scene.getEngine();
  71. if (!_options.canvasElement) {
  72. const canvas = document.createElement("canvas");
  73. canvas.style.cssText = this._options.newCanvasCssStyle || "position:absolute; bottom:0px;right:0px;";
  74. this._setManagedOutputCanvas(canvas);
  75. } else {
  76. this._setManagedOutputCanvas(_options.canvasElement);
  77. }
  78. _xrSessionManager.onXRSessionInit.add(() => {
  79. this._addCanvas();
  80. });
  81. _xrSessionManager.onXRSessionEnded.add(() => {
  82. this._removeCanvas();
  83. });
  84. }
  85. /**
  86. * Disposes of the object
  87. */
  88. public dispose() {
  89. this._removeCanvas();
  90. this._setManagedOutputCanvas(null);
  91. }
  92. /**
  93. * Initializes the xr layer for the session
  94. * @param xrSession xr session
  95. * @returns a promise that will resolve once the XR Layer has been created
  96. */
  97. public initializeXRLayerAsync(xrSession: XRSession): Promise<XRWebGLLayer> {
  98. const createLayer = () => {
  99. const layer = new XRWebGLLayer(xrSession, this.canvasContext, this._options.canvasOptions);
  100. this.onXRLayerInitObservable.notifyObservers(layer);
  101. return layer;
  102. };
  103. // support canvases without makeXRCompatible
  104. if (!(this.canvasContext as any).makeXRCompatible) {
  105. this.xrLayer = createLayer();
  106. return Promise.resolve(this.xrLayer);
  107. }
  108. return (this.canvasContext as any).makeXRCompatible().then(() => {
  109. this.xrLayer = createLayer();
  110. return this.xrLayer;
  111. });
  112. }
  113. private _addCanvas() {
  114. if (this._canvas && this._canvas !== this._engine.getRenderingCanvas()) {
  115. document.body.appendChild(this._canvas);
  116. }
  117. if (this.xrLayer) {
  118. this._setCanvasSize(true);
  119. } else {
  120. this.onXRLayerInitObservable.addOnce((layer) => {
  121. this._setCanvasSize(true, layer);
  122. });
  123. }
  124. }
  125. private _removeCanvas() {
  126. if (this._canvas && document.body.contains(this._canvas) && this._canvas !== this._engine.getRenderingCanvas()) {
  127. document.body.removeChild(this._canvas);
  128. }
  129. this._setCanvasSize(false);
  130. }
  131. private _setCanvasSize(init: boolean = true, xrLayer = this.xrLayer) {
  132. if (!this._canvas) {
  133. return;
  134. }
  135. if (init) {
  136. if (xrLayer) {
  137. if (this._canvas !== this._engine.getRenderingCanvas()) {
  138. this._canvas.style.width = xrLayer.framebufferWidth + "px";
  139. this._canvas.style.height = xrLayer.framebufferHeight + "px";
  140. } else {
  141. this._engine.setSize(xrLayer.framebufferWidth, xrLayer.framebufferHeight);
  142. }
  143. }
  144. } else {
  145. if (this._originalCanvasSize) {
  146. if (this._canvas !== this._engine.getRenderingCanvas()) {
  147. this._canvas.style.width = this._originalCanvasSize.width + "px";
  148. this._canvas.style.height = this._originalCanvasSize.height + "px";
  149. } else {
  150. this._engine.setSize(this._originalCanvasSize.width, this._originalCanvasSize.height);
  151. }
  152. }
  153. }
  154. }
  155. private _setManagedOutputCanvas(canvas: Nullable<HTMLCanvasElement>) {
  156. this._removeCanvas();
  157. if (!canvas) {
  158. this._canvas = null;
  159. (this.canvasContext as any) = null;
  160. } else {
  161. this._originalCanvasSize = {
  162. width: canvas.offsetWidth,
  163. height: canvas.offsetHeight,
  164. };
  165. this._canvas = canvas;
  166. this.canvasContext = <any>this._canvas.getContext("webgl2");
  167. if (!this.canvasContext) {
  168. this.canvasContext = <any>this._canvas.getContext("webgl");
  169. }
  170. }
  171. }
  172. }