UNPKG

@polygonjs/polygonjs

Version:

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

411 lines (410 loc) 14.3 kB
"use strict"; import { TypedRopNode } from "./_Base"; import { RopType } from "../../poly/registers/nodes/types/Rop"; import { SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace, NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping, BasicShadowMap, PCFShadowMap, PCFSoftShadowMap, VSMShadowMap } from "three"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { isArray } from "../../../core/Type"; import { Poly } from "../../Poly"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { defaultPixelRatio } from "../../../core/render/defaultPixelRatio"; import { PowerPreference, POWER_PREFERENCES, RENDERER_PRECISIONS, RendererPrecision, WEBGL_RENDERER_DEFAULT_PARAMS } from "../../../core/render/Common"; import { COLOR_SPACE_NAME_BY_COLOR_SPACE } from "../../../core/cop/ColorSpace"; const COLOR_SPACES = [ // NoColorSpace, // crashes the renderer SRGBColorSpace, LinearSRGBColorSpace, DisplayP3ColorSpace, LinearDisplayP3ColorSpace ]; export const DEFAULT_OUTPUT_COLOR_SPACE = SRGBColorSpace; var ToneMappingName = /* @__PURE__ */ ((ToneMappingName2) => { ToneMappingName2["No"] = "No"; ToneMappingName2["Linear"] = "Linear"; ToneMappingName2["Reinhard"] = "Reinhard"; ToneMappingName2["Cineon"] = "Cineon"; ToneMappingName2["ACESFilmic"] = "ACESFilmic"; return ToneMappingName2; })(ToneMappingName || {}); var ToneMappingValue = /* @__PURE__ */ ((ToneMappingValue2) => { ToneMappingValue2[ToneMappingValue2["No"] = NoToneMapping] = "No"; ToneMappingValue2[ToneMappingValue2["Linear"] = LinearToneMapping] = "Linear"; ToneMappingValue2[ToneMappingValue2["Reinhard"] = ReinhardToneMapping] = "Reinhard"; ToneMappingValue2[ToneMappingValue2["Cineon"] = CineonToneMapping] = "Cineon"; ToneMappingValue2[ToneMappingValue2["ACESFilmic"] = ACESFilmicToneMapping] = "ACESFilmic"; return ToneMappingValue2; })(ToneMappingValue || {}); const TONE_MAPPING_NAMES = [ "No" /* No */, "Linear" /* Linear */, "Reinhard" /* Reinhard */, "Cineon" /* Cineon */, "ACESFilmic" /* ACESFilmic */ ]; const TONE_MAPPING_VALUES = [ ToneMappingValue.No, ToneMappingValue.Linear, ToneMappingValue.Reinhard, ToneMappingValue.Cineon, ToneMappingValue.ACESFilmic ]; export const DEFAULT_TONE_MAPPING = ToneMappingValue.ACESFilmic; const TONE_MAPPING_MENU_ENTRIES = TONE_MAPPING_NAMES.map((name, i) => { return { name, value: TONE_MAPPING_VALUES[i] }; }); const TONE_MAPPING_EXPOSURE_VISIBLE_OPTIONS = []; for (const value of TONE_MAPPING_VALUES) { if (value != ToneMappingValue.No) { TONE_MAPPING_EXPOSURE_VISIBLE_OPTIONS.push(value); } } var ShadowMapTypeName = /* @__PURE__ */ ((ShadowMapTypeName2) => { ShadowMapTypeName2["Basic"] = "Basic"; ShadowMapTypeName2["PCF"] = "PCF"; ShadowMapTypeName2["PCFSoft"] = "PCFSoft"; ShadowMapTypeName2["VSM"] = "VSM"; return ShadowMapTypeName2; })(ShadowMapTypeName || {}); var ShadowMapTypeValue = /* @__PURE__ */ ((ShadowMapTypeValue2) => { ShadowMapTypeValue2[ShadowMapTypeValue2["Basic"] = BasicShadowMap] = "Basic"; ShadowMapTypeValue2[ShadowMapTypeValue2["PCF"] = PCFShadowMap] = "PCF"; ShadowMapTypeValue2[ShadowMapTypeValue2["PCFSoft"] = PCFSoftShadowMap] = "PCFSoft"; ShadowMapTypeValue2[ShadowMapTypeValue2["VSM"] = VSMShadowMap] = "VSM"; return ShadowMapTypeValue2; })(ShadowMapTypeValue || {}); const SHADOW_MAP_TYPE_NAMES = [ "Basic" /* Basic */, "PCF" /* PCF */, "PCFSoft" /* PCFSoft */, "VSM" /* VSM */ ]; const SHADOW_MAP_TYPE_VALUES = [ ShadowMapTypeValue.Basic, ShadowMapTypeValue.PCF, ShadowMapTypeValue.PCFSoft, ShadowMapTypeValue.VSM ]; export const SHADOW_MAP_TYPES = [BasicShadowMap, PCFShadowMap, PCFSoftShadowMap, VSMShadowMap]; export const DEFAULT_SHADOW_MAP_TYPE = ShadowMapTypeValue.PCFSoft; class WebGLRendererRopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); // // // // // this.common = ParamConfig.FOLDER(); /** @param tone mapping */ this.toneMapping = ParamConfig.INTEGER(DEFAULT_TONE_MAPPING, { menu: { entries: TONE_MAPPING_MENU_ENTRIES }, cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateToneMapping(node); } }); /** @param tone mapping exposure */ this.toneMappingExposure = ParamConfig.FLOAT(1, { range: [0, 2], cook: false, visibleIf: TONE_MAPPING_EXPOSURE_VISIBLE_OPTIONS.map((value) => ({ toneMapping: value })), callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateToneMappingExposure(node); } }); /** @param output color space */ this.outputColorSpace = ParamConfig.STRING(DEFAULT_OUTPUT_COLOR_SPACE, { menuString: { entries: COLOR_SPACES.map((colorSpace) => ({ name: COLOR_SPACE_NAME_BY_COLOR_SPACE[colorSpace], value: colorSpace })) }, cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateOutputColorSpace(node); } }); /** @param sort objects, which can be necessary when rendering transparent objects */ this.sortObjects = ParamConfig.BOOLEAN(1, { cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateSortObjects(node); } }); /** @param toggle to override the default pixel ratio, which is 1 for mobile devices, and Math.max(2, globalThis.devicePixelRatio) for other devices */ this.tpixelRatio = ParamConfig.BOOLEAN(0, { cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updatePixelRatio(node); } }); /** @param higher pixelRatio improves render sharpness but reduces performance */ this.pixelRatio = ParamConfig.FLOAT(2, { visibleIf: { tpixelRatio: true }, range: [0.1, 4], rangeLocked: [true, false], cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updatePixelRatio(node); } }); // // // // // this.shadow = ParamConfig.FOLDER(); /** @param toggle on to have shadow maps */ this.tshadowMap = ParamConfig.BOOLEAN(1, { cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateShadow(node); } }); /** @param toggle on to recompute the shadow maps on every frame. If all objects are static, you may want to turn this off */ this.shadowMapAutoUpdate = ParamConfig.BOOLEAN(1, { visibleIf: { tshadowMap: 1 }, cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateShadow(node); } }); /** @param toggle on to trigger shadows update */ this.shadowMapNeedsUpdate = ParamConfig.BOOLEAN(0, { visibleIf: { tshadowMap: 1 }, cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateShadow(node); } }); /** @param shadows type */ this.shadowMapType = ParamConfig.INTEGER(DEFAULT_SHADOW_MAP_TYPE, { visibleIf: { tshadowMap: 1 }, menu: { entries: SHADOW_MAP_TYPE_NAMES.map((name, i) => { return { name, value: SHADOW_MAP_TYPE_VALUES[i] }; }) }, cook: false, callback: (node) => { WebGLRendererRopNode.PARAM_CALLBACK_updateShadow(node); } }); // // // // // this.advanced = ParamConfig.FOLDER(); /** @param toggle on to have alpha on (change requires page reload) */ this.alpha = ParamConfig.BOOLEAN(1); /** @param toggle on to have antialias on (change requires page reload) */ this.antialias = ParamConfig.BOOLEAN(1); /** @param premultipliedAlpha */ this.premultipliedAlpha = ParamConfig.BOOLEAN(1); /** @param stencil */ this.stencil = ParamConfig.BOOLEAN(1); /** @param depth */ this.depth = ParamConfig.BOOLEAN(1); /** @param localClippingEnabled */ this.localClippingEnabled = ParamConfig.BOOLEAN(0); /** @param logarithmicDepthBuffer */ this.logarithmicDepthBuffer = ParamConfig.BOOLEAN(0); /** @param preserveDrawingBuffer */ this.preserveDrawingBuffer = ParamConfig.BOOLEAN(0); /** @param toggle on to set the precision */ this.tprecision = ParamConfig.BOOLEAN(0); /** @param set the precision */ this.precision = ParamConfig.INTEGER(RENDERER_PRECISIONS.indexOf(RendererPrecision.HIGH), { visibleIf: { tprecision: 1 }, menu: { entries: RENDERER_PRECISIONS.map((name, value) => { return { value, name }; }) } }); /** @param toggle on to set the power preferenc */ this.tpowerPreference = ParamConfig.BOOLEAN(0); /** @param set the precision */ this.powerPreference = ParamConfig.INTEGER(POWER_PREFERENCES.indexOf(PowerPreference.HIGH), { visibleIf: { tpowerPreference: 1 }, menu: { entries: POWER_PREFERENCES.map((name, value) => { return { value, name }; }) } }); } // preserve_drawing_buffer = ParamConfig.BOOLEAN(0); } const ParamsConfig = new WebGLRendererRopParamsConfig(); export class WebGLRendererRopNode extends TypedRopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._rendererByCanvas = /* @__PURE__ */ new Map(); } static type() { return RopType.WEBGL; } // private _renderersbyCamera: Map<Camera, WebGLRenderer> = new Map(); createRenderer(canvas, gl) { const params = {}; const keys = Object.keys(WEBGL_RENDERER_DEFAULT_PARAMS); let k; for (k of keys) { params[k] = WEBGL_RENDERER_DEFAULT_PARAMS[k]; } if (isBooleanTrue(this.pv.tprecision)) { const precision = RENDERER_PRECISIONS[this.pv.precision]; params.precision = precision; } if (isBooleanTrue(this.pv.tpowerPreference)) { const powerPreference = POWER_PREFERENCES[this.pv.powerPreference]; params.powerPreference = powerPreference; } params.antialias = isBooleanTrue(this.pv.antialias); params.alpha = isBooleanTrue(this.pv.alpha); params.premultipliedAlpha = isBooleanTrue(this.pv.premultipliedAlpha); params.depth = isBooleanTrue(this.pv.depth); params.stencil = isBooleanTrue(this.pv.stencil); params.logarithmicDepthBuffer = isBooleanTrue(this.pv.logarithmicDepthBuffer); params.canvas = canvas; params.context = gl; params.preserveDrawingBuffer = this.pv.preserveDrawingBuffer; const renderer = Poly.renderersController.createWebGLRenderer(params); renderer.localClippingEnabled = isBooleanTrue(this.pv.localClippingEnabled); this._rendererByCanvas.set(canvas, renderer); if (Poly.renderersController.printDebug()) { Poly.renderersController.printDebugMessage(`create renderer from node '${this.path()}'`); Poly.renderersController.printDebugMessage({ params }); } this._updateRenderer(renderer); return renderer; } cook() { this._traverseSceneAndUpdateMaterials(); this.cookController.endCook(); } _updateRenderer(renderer) { this._updateRendererOutputColorSpace(renderer); this._updateRendererToneMapping(renderer); this._updateRendererToneMappingExposure(renderer); this._updateRendererShadow(renderer); this._updateRendererSortObjects(renderer); this._updateRendererPixelRatio(renderer); } _traverseSceneAndUpdateMaterials() { this.scene().threejsScene().traverse((object) => { const material = object.material; if (material) { if (isArray(material)) { for (const mat of material) { mat.needsUpdate = true; } } else { material.needsUpdate = true; } } }); } // // static PARAM_CALLBACK_updateToneMapping(node) { node._rendererByCanvas.forEach((renderer, canvas) => { node._updateRendererToneMapping(renderer); }); } static PARAM_CALLBACK_updateToneMappingExposure(node) { node._rendererByCanvas.forEach((renderer, canvas) => { node._updateRendererToneMappingExposure(renderer); }); } static PARAM_CALLBACK_updateOutputColorSpace(node) { node._rendererByCanvas.forEach((renderer, canvas) => { node._updateRendererOutputColorSpace(renderer); }); } static PARAM_CALLBACK_updateShadow(node) { node._rendererByCanvas.forEach((renderer, canvas) => { node._updateRendererShadow(renderer); }); } static PARAM_CALLBACK_updateSortObjects(node) { node._rendererByCanvas.forEach((renderer, canvas) => { node._updateRendererSortObjects(renderer); }); } static PARAM_CALLBACK_updatePixelRatio(node) { node._rendererByCanvas.forEach((renderer, canvas) => { node._updateRendererPixelRatio(renderer); }); globalThis.dispatchEvent(new Event("resize")); } // // _updateRendererToneMapping(renderer) { renderer.toneMapping = this.pv.toneMapping; } _updateRendererToneMappingExposure(renderer) { renderer.toneMappingExposure = this.pv.toneMappingExposure; } _updateRendererOutputColorSpace(renderer) { const newOutputColorSpace = this.pv.outputColorSpace; if (newOutputColorSpace != renderer.outputColorSpace) { renderer.outputColorSpace = newOutputColorSpace; } } _updateRendererShadow(renderer) { renderer.shadowMap.enabled = this.pv.tshadowMap; renderer.shadowMap.autoUpdate = this.pv.shadowMapAutoUpdate; renderer.shadowMap.needsUpdate = this.pv.shadowMapNeedsUpdate; renderer.shadowMap.type = this.pv.shadowMapType; } _updateRendererSortObjects(renderer) { renderer.sortObjects = this.pv.sortObjects; } _updateRendererPixelRatio(renderer) { const pixelRatio = this.pv.tpixelRatio ? this.pv.pixelRatio : defaultPixelRatio(); if (Poly.renderersController.printDebug()) { Poly.renderersController.printDebugMessage(`set renderer pixelRatio from '${this.path()}'`); Poly.renderersController.printDebugMessage({ pixelRatio }); } renderer.setPixelRatio(pixelRatio); } setToneMapping(toneMapping) { this.p.toneMapping.set(toneMapping); } }