Detach the GL context state to a separate module.
This commit is contained in:
@@ -1,335 +0,0 @@
|
||||
import { ResizeObserver as ResizeObserverPolyfill } from '@juggle/resize-observer';
|
||||
import { EventEmitter } from "./EventEmitter";
|
||||
|
||||
const ResizeObserver = window.ResizeObserver || ResizeObserverPolyfill;
|
||||
|
||||
interface WebGLCanvasOption {
|
||||
|
||||
/**
|
||||
* Canvas element for outputting renderer images.
|
||||
* If a string is passed in:
|
||||
* Here will try `document.getElementById` and then `document.querySelector`
|
||||
* until a valid canvas element is found.
|
||||
*/
|
||||
canvas?: HTMLCanvasElement | OffscreenCanvas | string;
|
||||
|
||||
/**
|
||||
* Initialize with WebGL context.
|
||||
*/
|
||||
context?: WebGLRenderingContext | WebGL2RenderingContext;
|
||||
|
||||
/**
|
||||
* Parent node of canvas.
|
||||
*/
|
||||
container?: HTMLElement | string;
|
||||
|
||||
/**
|
||||
* Automatically adjust width and height to parent node width and height.
|
||||
*/
|
||||
autoResize?: boolean;
|
||||
|
||||
/**
|
||||
* Width of Canvas.
|
||||
* It will become invalid when autoResize is set.
|
||||
*/
|
||||
width?: number;
|
||||
|
||||
/**
|
||||
* Height of Canvas.
|
||||
* It will become invalid when autoResize is set.
|
||||
*/
|
||||
height?: number;
|
||||
|
||||
/**
|
||||
* Whether to use offline canvas.
|
||||
*/
|
||||
isOffScreen?: boolean;
|
||||
|
||||
/**
|
||||
* WebGL context attributes.
|
||||
*/
|
||||
contextAttributes?: WebGLContextAttributes;
|
||||
}
|
||||
|
||||
type WebGLCanvasEventMap = {
|
||||
|
||||
/**
|
||||
* Emit When the canvas size changes.
|
||||
*/
|
||||
"resize": void;
|
||||
};
|
||||
|
||||
class WebGLCanvas extends EventEmitter<WebGLCanvasEventMap> {
|
||||
|
||||
public readonly element: HTMLCanvasElement | OffscreenCanvas;
|
||||
|
||||
public readonly context: WebGLRenderingContext | WebGL2RenderingContext;
|
||||
|
||||
public readonly glVersion: 0 | 1 | 2;
|
||||
|
||||
public readonly isOffScreen: boolean;
|
||||
|
||||
public constructor(userOption?: WebGLCanvasOption) {
|
||||
|
||||
super();
|
||||
|
||||
const option: WebGLCanvasOption = userOption ?? {};
|
||||
|
||||
let targetCanvas: HTMLCanvasElement | OffscreenCanvas | undefined = void 0;
|
||||
let targetContext: WebGLRenderingContext | WebGL2RenderingContext | undefined = void 0;
|
||||
let targetGLVersion: 0 | 1 | 2 = 0;
|
||||
let targetIsOffScreen: boolean = false;
|
||||
|
||||
if (option.canvas instanceof HTMLCanvasElement || option.canvas instanceof OffscreenCanvas) {
|
||||
targetCanvas = option.canvas;
|
||||
}
|
||||
|
||||
else if (typeof option.canvas === "string") {
|
||||
|
||||
let findTarget: HTMLElement | null = null;
|
||||
|
||||
findTarget = document.getElementById(option.canvas);
|
||||
|
||||
if (findTarget instanceof HTMLCanvasElement) {
|
||||
targetCanvas = findTarget;
|
||||
}
|
||||
|
||||
else {
|
||||
findTarget = document.querySelector(option.canvas);
|
||||
|
||||
if (findTarget instanceof HTMLCanvasElement) {
|
||||
targetCanvas = findTarget;
|
||||
}
|
||||
|
||||
else {
|
||||
console.warn(
|
||||
`[ray-lab Renderer] Unable to search any valid canvas through "${option.canvas}". \r\n` +
|
||||
"Created internal canvas instead..."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option.context) {
|
||||
|
||||
if (targetCanvas && targetCanvas !== option.context.canvas) {
|
||||
console.warn(
|
||||
"[ray-lab Renderer] The `canvas` element does not match the `context.canvas`: \r\n" +
|
||||
"The `canvas` option will be ignored. \r\n" +
|
||||
"Please do not use `canvas` and `context` at the same time!"
|
||||
);
|
||||
}
|
||||
|
||||
targetCanvas = option.context.canvas;
|
||||
targetContext = option.context;
|
||||
}
|
||||
|
||||
if (targetCanvas) {
|
||||
|
||||
if (targetCanvas instanceof HTMLCanvasElement) {
|
||||
|
||||
if (option.isOffScreen) {
|
||||
|
||||
if (OffscreenCanvas) {
|
||||
targetCanvas = targetCanvas.transferControlToOffscreen();
|
||||
targetIsOffScreen = true;
|
||||
}
|
||||
|
||||
else {
|
||||
targetIsOffScreen = false;
|
||||
console.warn(
|
||||
"[ray-lab Renderer] The target environment does not support OffscreenCanvas \r\n" +
|
||||
"`isOffScreen` is ignored!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
targetIsOffScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (targetCanvas instanceof OffscreenCanvas) {
|
||||
targetIsOffScreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if (option.isOffScreen) {
|
||||
|
||||
if (OffscreenCanvas) {
|
||||
targetIsOffScreen = true;
|
||||
targetCanvas = new OffscreenCanvas(option.width ?? 300, option.height ?? 150);
|
||||
}
|
||||
|
||||
else {
|
||||
targetIsOffScreen = false;
|
||||
targetCanvas = document.createElement("canvas");
|
||||
console.warn(
|
||||
"[ray-lab Renderer] The target environment does not support OffscreenCanvas \r\n" +
|
||||
"`isOffScreen` is ignored!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
targetIsOffScreen = false;
|
||||
targetCanvas = document.createElement("canvas");
|
||||
}
|
||||
}
|
||||
|
||||
if (targetContext) {
|
||||
|
||||
if (targetCanvas instanceof WebGL2RenderingContext) {
|
||||
targetGLVersion = 2;
|
||||
}
|
||||
|
||||
else if (targetCanvas instanceof WebGLRenderingContext) {
|
||||
targetGLVersion = 1;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
let getContext: OffscreenRenderingContext | RenderingContext | null;
|
||||
|
||||
getContext = targetCanvas.getContext("webgl2", option.contextAttributes);
|
||||
|
||||
if (!getContext) {
|
||||
getContext = targetCanvas.getContext("webgl", option.contextAttributes);
|
||||
}
|
||||
|
||||
if (WebGL2RenderingContext && (getContext instanceof WebGL2RenderingContext)) {
|
||||
targetContext = getContext;
|
||||
targetGLVersion = 2;
|
||||
}
|
||||
|
||||
else if (getContext instanceof WebGLRenderingContext) {
|
||||
targetContext = getContext;
|
||||
targetGLVersion = 1;
|
||||
}
|
||||
|
||||
else {
|
||||
throw new Error("[ray-lab Renderer] The target environment does not support WebGL :(");
|
||||
}
|
||||
}
|
||||
|
||||
this.element = targetCanvas;
|
||||
this.context = targetContext;
|
||||
this.glVersion = targetGLVersion;
|
||||
this.isOffScreen = targetIsOffScreen;
|
||||
|
||||
if (option.autoResize) {
|
||||
this.enableAutoResize(option.container);
|
||||
}
|
||||
|
||||
else {
|
||||
this.resize(option.width, option.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the canvas size.
|
||||
* @param width - canvas width.
|
||||
* @param height - canvas height.
|
||||
*/
|
||||
public resize(width?: number, height?: number) {
|
||||
|
||||
let hasResized = false;
|
||||
|
||||
if (width !== void 0) {
|
||||
this.element.width = width;
|
||||
hasResized = true;
|
||||
}
|
||||
|
||||
if (height !== void 0) {
|
||||
this.element.height = height;
|
||||
hasResized = true;
|
||||
}
|
||||
|
||||
if (hasResized) {
|
||||
this.emit("resize");
|
||||
}
|
||||
}
|
||||
|
||||
private resizeObserver: ResizeObserver | undefined;
|
||||
|
||||
/**
|
||||
* enable auto resize.
|
||||
* @param container - Parent node of canvas.
|
||||
*/
|
||||
public enableAutoResize(container?: HTMLElement | string) {
|
||||
let targetcontainer: HTMLElement | null = null;
|
||||
|
||||
if (container instanceof HTMLElement) {
|
||||
targetcontainer = container;
|
||||
}
|
||||
|
||||
else if (typeof container === "string") {
|
||||
|
||||
targetcontainer = document.getElementById(container);
|
||||
|
||||
if (targetcontainer === null) {
|
||||
targetcontainer = document.querySelector(container);
|
||||
|
||||
if (targetcontainer === null) {
|
||||
console.warn(
|
||||
`[ray-lab Renderer] Unable to search any valid container through "${container}".`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetcontainer === null && this.element instanceof HTMLCanvasElement) {
|
||||
targetcontainer = this.element.parentElement;
|
||||
}
|
||||
|
||||
if (targetcontainer === null) {
|
||||
console.warn(
|
||||
`[ray-lab Renderer] Unable to enable auto resize. \r\n` +
|
||||
`Because a suitable container could not be found.`
|
||||
);
|
||||
}
|
||||
|
||||
else {
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.contentRect) {
|
||||
this.resize(entry.contentRect.width, entry.contentRect.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
this.resizeObserver = resizeObserver;
|
||||
|
||||
if (this.element instanceof HTMLElement) {
|
||||
this.element.style.width = "100%";
|
||||
this.element.style.height = "100%";
|
||||
this.element.style.boxSizing = "border-box";
|
||||
this.element.style.display = "block";
|
||||
}
|
||||
|
||||
resizeObserver.observe(targetcontainer);
|
||||
this.resize(targetcontainer.offsetWidth, targetcontainer.offsetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* disable auto resize.
|
||||
*/
|
||||
public disableAutoResize() {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
this.resizeObserver = void 0;
|
||||
}
|
||||
}
|
||||
|
||||
export { WebGLCanvas };
|
||||
export type { WebGLCanvasOption };
|
||||
Reference in New Issue
Block a user