diff --git a/GDJS/Runtime/pixi-renderers/runtimegame-pixi-renderer.ts b/GDJS/Runtime/pixi-renderers/runtimegame-pixi-renderer.ts index 75c970afe09f..002e2a5801e2 100644 --- a/GDJS/Runtime/pixi-renderers/runtimegame-pixi-renderer.ts +++ b/GDJS/Runtime/pixi-renderers/runtimegame-pixi-renderer.ts @@ -189,6 +189,120 @@ namespace gdjs { gameCanvas.focus(); } + useCanvas(gameCanvas: HTMLCanvasElement): void { + if (typeof THREE !== "undefined") { + this._threeRenderer = new THREE.WebGLRenderer({ + canvas: gameCanvas, + antialias: + this._game.getAntialiasingMode() !== "none" && + (this._game.isAntialisingEnabledOnMobile() || !gdjs.evtTools.common.isMobile()), + preserveDrawingBuffer: true, // Keep to true to allow screenshots. + }); + this._threeRenderer.useLegacyLights = true; + this._threeRenderer.autoClear = false; + this._threeRenderer.setSize(this._game.getGameResolutionWidth(), this._game.getGameResolutionHeight()); + + // Create a PixiJS renderer that use the same GL context as Three.js + // so that both can render to the canvas and even have PixiJS rendering + // reused in Three.js (by using a RenderTexture and the same internal WebGL texture). + this._pixiRenderer = new PIXI.Renderer({ + width: this._game.getGameResolutionWidth(), + height: this._game.getGameResolutionHeight(), + view: gameCanvas, + // @ts-ignore - reuse the context from Three.js. + context: this._threeRenderer.getContext(), + clearBeforeRender: false, + preserveDrawingBuffer: true, // Keep to true to allow screenshots. + antialias: false, + backgroundAlpha: 0, + // TODO (3D): add a setting for pixel ratio (`resolution: window.devicePixelRatio`) + }); + } else { + // Create the renderer and setup the rendering area. + // "preserveDrawingBuffer: true" is needed to avoid flickering + // and background issues on some mobile phones (see #585 #572 #566 #463). + this._pixiRenderer = PIXI.autoDetectRenderer({ + width: this._game.getGameResolutionWidth(), + height: this._game.getGameResolutionHeight(), + preserveDrawingBuffer: true, + antialias: false, + view: gameCanvas, + }) as PIXI.Renderer; + } + + // Deactivating accessibility support in PixiJS renderer, as we want to be in control of this. + // See https://github.com/pixijs/pixijs/issues/5111#issuecomment-420047824 + this._pixiRenderer.plugins.accessibility.destroy(); + delete this._pixiRenderer.plugins.accessibility; + + // Add the renderer view element to the DOM + this._gameCanvas = gameCanvas; + + gameCanvas.style.position = "absolute"; + + // Ensure that the canvas has the focus. + gameCanvas.tabIndex = 1; + + // Ensure long press can't create a selection + gameCanvas.style.userSelect = "none"; + gameCanvas.style.outline = "none"; // No selection/focus ring on the canvas. + + // Set up the container for HTML elements on top of the game canvas. + const domElementsContainer = document.createElement("div"); + domElementsContainer.style.position = "absolute"; + domElementsContainer.style.overflow = "hidden"; // Never show anything outside the container. + domElementsContainer.style.outline = "none"; // No selection/focus ring on this container. + domElementsContainer.style.pointerEvents = "none"; // Clicks go through the container. + + // The container should *never* scroll. + // Elements are put inside with the same coordinates (with a scaling factor) + // as on the game canvas. + domElementsContainer.addEventListener("scroll", (event) => { + domElementsContainer.scrollLeft = 0; + domElementsContainer.scrollTop = 0; + event.preventDefault(); + }); + + // When clicking outside an input, (or other HTML element), + // give back focus to the game canvas so that this element is blurred. + gameCanvas.addEventListener("pointerdown", () => { + gameCanvas.focus(); + }); + + // Prevent magnifying glass on iOS with a long press. + // Note that there are related bugs on iOS 15 (see https://bugs.webkit.org/show_bug.cgi?id=231161) + // but it seems not to affect us as the `domElementsContainer` has `pointerEvents` set to `none`. + domElementsContainer.style["-webkit-user-select"] = "none"; + + gameCanvas.parentNode?.appendChild(domElementsContainer); + this._domElementsContainer = domElementsContainer; + + this._resizeCanvas(); + + // Handle scale mode. + if (this._game.getScaleMode() === "nearest") { + gameCanvas.style["image-rendering"] = "-moz-crisp-edges"; + gameCanvas.style["image-rendering"] = "-webkit-optimize-contrast"; + gameCanvas.style["image-rendering"] = "-webkit-crisp-edges"; + gameCanvas.style["image-rendering"] = "pixelated"; + } + + // Handle pixels rounding. + if (this._game.getPixelsRounding()) { + PIXI.settings.ROUND_PIXELS = true; + } + + // Handle resize: immediately adjust the game canvas (and dom element container) + // and notify the game (that may want to adjust to the new size of the window). + window.addEventListener("resize", () => { + this._game.onWindowInnerSizeChanged(); + this._resizeCanvas(); + }); + + // Focus the canvas when created. + gameCanvas.focus(); + } + static getWindowInnerWidth() { return typeof window !== 'undefined' ? window.innerWidth : 800; }