@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
236 lines (212 loc) • 7.36 kB
text/typescript
import {
WebGLRenderer,
WebGLRendererParameters,
WebGLRenderTarget,
RenderTargetOptions,
NoColorSpace,
NoToneMapping,
} from 'three';
import type {AbstractRenderer} from '../viewers/Common';
import {WEBGL_RENDERER_DEFAULT_PARAMS} from '../../core/render/Common';
import {PolyEngine} from '../Poly';
export interface WithPolyId {
_polygonId?: number;
}
export interface WithContextId {
_polygonjsContextId?: number;
}
export interface POLYAbstractRenderer extends AbstractRenderer, WithPolyId {}
export interface CanvasContext extends WebGLRenderingContext, WithContextId {}
const CONTEXT_OPTIONS: WebGLContextAttributes = {
// powerPreference: 'high-performance', // attempt to fix issues in safari
// antialias: false, // leave that to the renderer node
// preserveDrawingBuffer: true, // this could only be useful to capture static images
};
enum WebGLContext {
WEBGL = 'webgl',
WEBGL2 = 'webgl2',
EXPERIMENTAL_WEBGL = 'experimental-webgl',
EXPERIMENTAL_WEBGL2 = 'experimental-webgl2',
}
let nextRendererId: number = 0;
export class RenderersController {
private static _nextGlContextId = 0;
// private _firstRenderer: WebGLRenderer | null = null;
// private _lastRenderer: WebGLRenderer | null = null;
private _printDebug = false;
private _requireWebGL2: boolean = false;
// private _resolves: Callback[] = [];
private _webgl2_available: boolean | undefined;
// private _env_maps: TextureByString = {};
// private _next_env_map_id: number = 0;
private _webGLContextByCanvas: Map<HTMLCanvasElement, WebGLRenderingContext> = new Map();
private _defaultRendererByCanvas = new Map<HTMLCanvasElement, WebGLRenderer>();
constructor(public readonly poly: PolyEngine) {}
dispose() {
this._webGLContextByCanvas.clear();
this._defaultRendererByCanvas.forEach((renderer) => {
renderer.dispose();
});
this._defaultRendererByCanvas.clear();
}
setPrintDebug(state: boolean = true) {
this._printDebug = state;
}
printDebug() {
return this._printDebug;
}
printDebugMessage(message: any) {
if (!this._printDebug) {
return;
}
console.warn('[Poly debug]', message);
}
setRequireWebGL2() {
if (!this._requireWebGL2) {
this._requireWebGL2 = true;
}
}
webGL2Available(canvas?: HTMLCanvasElement) {
if (this._webgl2_available === undefined) {
this._webgl2_available = this._getWebGL2Available(canvas);
}
return this._webgl2_available;
}
private _getWebGL2Available(canvas?: HTMLCanvasElement) {
canvas = canvas || this.poly.canvasRegister.dummyCanvas();
return (globalThis.WebGL2RenderingContext && canvas.getContext(WebGLContext.WEBGL2)) != null;
}
defaultWebGLRendererForCanvas(canvas: HTMLCanvasElement) {
let renderer = this._defaultRendererByCanvas.get(canvas);
if (!renderer) {
const context = this.getRenderingContext(canvas)!;
renderer = this.createWebGLRenderer({...WEBGL_RENDERER_DEFAULT_PARAMS, canvas, context});
this._defaultRendererByCanvas.set(canvas, renderer);
}
return renderer;
}
// disposeWebGLRendererForCanvas(canvas: HTMLCanvasElement) {
// const renderer = this._defaultRendererByCanvas.get(canvas);
// if (renderer) {
// renderer.dispose();
// this._defaultRendererByCanvas.delete(canvas);
// }
// }
createWebGLRenderer(params: WebGLRendererParameters) {
const renderer = new WebGLRenderer(params);
if (params.canvas != null && params.canvas instanceof HTMLCanvasElement) {
this.poly.canvasRegister.registerCanvas(params.canvas, renderer);
} else {
console.warn('canvas not registerable');
console.log(params.canvas);
}
// renderer.debug.checkShaderErrors = true;
// renderer.debug.onShaderError = (gl, program, glVertexShader, glFragmentShader) => {
// console.log('onShaderError', {gl, program, glVertexShader, glFragmentShader});
// console.log(gl.getShaderSource(glVertexShader));
// console.log(gl.getShaderSource(glFragmentShader));
// };
this.assignIdToRenderer(renderer);
this.printDebugMessage([`create renderer:`, params]);
return renderer;
}
assignIdToRenderer(renderer: AbstractRenderer) {
if ((renderer as POLYAbstractRenderer)._polygonId != null) {
// we do not re-assign the id if there is already one
return;
}
const nextId = (nextRendererId += 1);
(renderer as POLYAbstractRenderer)._polygonId = nextId;
}
rendererId(renderer: AbstractRenderer) {
const id = (renderer as POLYAbstractRenderer)._polygonId;
if (id == null) {
console.error('renderer has no _polygonId');
return;
}
return id;
}
getRenderingContext(canvas: HTMLCanvasElement): WebGLRenderingContext | null {
let gl: WebGLRenderingContext | undefined = this._webGLContextByCanvas.get(canvas);
if (gl) {
return gl;
}
// if (this._require_webgl2) {
gl = this._getRenderingContextWebgl(canvas, true);
if (!gl) {
console.warn('failed to create webgl2 context');
}
// }
if (!gl) {
gl = this._getRenderingContextWebgl(canvas, false);
}
if (!gl) {
console.error('failed to create webgl context');
return null;
}
if ((gl as CanvasContext)._polygonjsContextId == null) {
(gl as CanvasContext)._polygonjsContextId = RenderersController._nextGlContextId++;
}
this._webGLContextByCanvas.set(canvas, gl);
// gl.getExtension('OES_standard_derivatives') // for derivative normals, but it cannot work at the moment (see node Gl/DerivativeNormals)
// to test data texture
// gl.getExtension('OES_texture_float')
// gl.getExtension('OES_texture_float_linear')
return gl;
}
private _getRenderingContextWebgl(canvas: HTMLCanvasElement, webgl2: boolean): WebGLRenderingContext | undefined {
let contextName: WebGLContext;
if (this.webGL2Available(canvas)) {
contextName = WebGLContext.WEBGL2;
} else {
contextName = webgl2 ? WebGLContext.WEBGL2 : WebGLContext.WEBGL;
}
let gl = canvas.getContext(contextName, CONTEXT_OPTIONS);
if (gl) {
this.printDebugMessage(`create gl context: ${contextName}.`);
} else {
contextName = webgl2 ? WebGLContext.EXPERIMENTAL_WEBGL2 : WebGLContext.EXPERIMENTAL_WEBGL;
this.printDebugMessage(`create gl context: ${contextName}.`);
gl = canvas.getContext(contextName, CONTEXT_OPTIONS);
}
return gl as WebGLRenderingContext | undefined;
}
createRenderTarget(width: number, height: number, parameters: RenderTargetOptions) {
if (this.webGL2Available()) {
const multiSampleRenderTarget = new WebGLRenderTarget(width, height, parameters);
multiSampleRenderTarget.samples = 2;
return multiSampleRenderTarget;
} else {
return new WebGLRenderTarget(width, height, parameters);
}
}
/*
*
* Linear renderer, used for cop/builder, cop/render
*
*/
private _linearRenderer: WebGLRenderer | undefined;
linearRenderer() {
return (this._linearRenderer = this._linearRenderer || this._createLinearRenderer());
}
private _createLinearRenderer() {
const canvas = this.poly.canvasRegister.findOrCreateCanvas();
const gl = this.getRenderingContext(canvas);
if (!gl) {
return;
}
const renderer = this.createWebGLRenderer({
// antialias: true,
// stencil: true,
// depth: false,
alpha: true,
premultipliedAlpha: true,
canvas,
context: gl,
});
this.poly.canvasRegister.registerCanvas(canvas, renderer);
renderer.outputColorSpace = NoColorSpace;
renderer.toneMapping = NoToneMapping;
return renderer;
}
}