UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

158 lines (138 loc) 4.94 kB
import {WebGLRenderer} from 'three/src/renderers/WebGLRenderer'; import {Texture} from 'three/src/textures/Texture'; import {WebGLRenderTarget, WebGLRenderTargetOptions} from 'three/src/renderers/WebGLRenderTarget'; import {WebGLMultisampleRenderTarget} from 'three/src/renderers/WebGLMultisampleRenderTarget'; interface RendererByString { [propName: string]: WebGLRenderer; } interface TextureByString { [propName: string]: Texture; } interface POLYWebGLRenderer extends WebGLRenderer { _polygon_id: number; } const CONTEXT_OPTIONS = { // antialias: false, // leave that to the renderer node // preserveDrawingBuffer: true, // this could only be useful to capture static images }; type Callback = (value: WebGLRenderer) => void; enum WebGLContext { WEBGL = 'webgl', WEBGL2 = 'webgl2', EXPERIMENTAL_WEBGL = 'experimental-webgl', EXPERIMENTAL_WEBGL2 = 'experimental-webgl2', } export class RenderersController { _next_renderer_id: number = 0; _next_env_map_id: number = 0; _renderers: RendererByString = {}; _env_maps: TextureByString = {}; private _require_webgl2: boolean = false; private _resolves: Callback[] = []; private _webgl2_available: boolean | undefined; constructor() {} setRequireWebGL2() { if (!this._require_webgl2) { this._require_webgl2 = true; } } webgl2Available() { if (this._webgl2_available === undefined) { this._webgl2_available = this._set_webgl2_available(); } return this._webgl2_available; } private _set_webgl2_available() { const canvas = document.createElement('canvas'); return (window.WebGL2RenderingContext && canvas.getContext(WebGLContext.WEBGL2)) != null; } renderingContext(canvas: HTMLCanvasElement): WebGLRenderingContext | null { let gl: WebGLRenderingContext | null = null; if (this._require_webgl2) { gl = this._rendering_context_webgl(canvas, true); if (!gl) { console.warn('failed to create webgl2 context'); } } if (!gl) { gl = this._rendering_context_webgl(canvas, false); } // 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 _rendering_context_webgl(canvas: HTMLCanvasElement, webgl2: boolean): WebGLRenderingContext | null { let context_name: WebGLContext; if (this.webgl2Available()) { context_name = WebGLContext.WEBGL2; } else { context_name = webgl2 ? WebGLContext.WEBGL2 : WebGLContext.WEBGL; } let gl = canvas.getContext(context_name, CONTEXT_OPTIONS); if (!gl) { context_name = webgl2 ? WebGLContext.EXPERIMENTAL_WEBGL2 : WebGLContext.EXPERIMENTAL_WEBGL; gl = canvas.getContext(context_name, CONTEXT_OPTIONS); } return gl as WebGLRenderingContext | null; } registerRenderer(renderer: WebGLRenderer) { if ((renderer as POLYWebGLRenderer)._polygon_id) { throw new Error('render already registered'); } (renderer as POLYWebGLRenderer)._polygon_id = this._next_renderer_id += 1; // there is a bug where 2 renderers are created from the beginning // because the from_json of the viewer_component is called after // the camera being set for the first time // console.log("register renderer", renderer, renderer._polygon_id) // this is being tested in PostProcess // const canvas = renderer.domElement // const gl = canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) // const extension_exist = gl.getExtension('OES_standard_derivatives') // if(!extension_exist){ // console.warn("renderers controller: gl extension not available") // } this._renderers[(renderer as POLYWebGLRenderer)._polygon_id] = renderer; if (Object.keys(this._renderers).length == 1) { this.flush_callbacks_with_renderer(renderer); } } deregisterRenderer(renderer: WebGLRenderer) { delete this._renderers[(renderer as POLYWebGLRenderer)._polygon_id]; renderer.dispose(); } firstRenderer(): WebGLRenderer | null { const first_id = Object.keys(this._renderers)[0]; if (first_id) { return this._renderers[first_id]; } return null; } renderers(): WebGLRenderer[] { return Object.values(this._renderers); } private flush_callbacks_with_renderer(renderer: WebGLRenderer) { let callback: Callback | undefined; while ((callback = this._resolves.pop())) { callback(renderer); } } async waitForRenderer(): Promise<WebGLRenderer> { const renderer = this.firstRenderer(); if (renderer) { return renderer; } else { return new Promise((resolve, reject) => { this._resolves.push(resolve); }); } } renderTarget(width: number, height: number, parameters: WebGLRenderTargetOptions) { if (this.webgl2Available()) { return new WebGLMultisampleRenderTarget(width, height, parameters); } else { return new WebGLRenderTarget(width, height, parameters); } } }