UNPKG

polygonjs-engine

Version:

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

232 lines (231 loc) 8.47 kB
import {WebGLRenderTarget as WebGLRenderTarget2} from "three/src/renderers/WebGLRenderTarget"; import {ShaderMaterial as ShaderMaterial2} from "three/src/materials/ShaderMaterial"; import {Scene as Scene2} from "three/src/scenes/Scene"; import { FloatType, HalfFloatType, RGBAFormat, NearestFilter, LinearFilter, ClampToEdgeWrapping } from "three/src/constants"; import {PlaneBufferGeometry as PlaneBufferGeometry2} from "three/src/geometries/PlaneBufferGeometry"; import {Mesh as Mesh2} from "three/src/objects/Mesh"; import {Camera as Camera2} from "three/src/cameras/Camera"; import {TypedCopNode} from "./_Base"; import {GlobalsGeometryHandler} from "../gl/code/globals/Geometry"; import {GlNodeFinder} from "../gl/code/utils/NodeFinder"; import {NodeContext as NodeContext2} from "../../poly/NodeContext"; const VERTEX_SHADER = ` void main() { gl_Position = vec4( position, 1.0 ); } `; const RESOLUTION_DEFAULT = [256, 256]; import {NodeParamsConfig, ParamConfig} from "../utils/params/ParamsConfig"; import {DataTextureController as DataTextureController2, DataTextureControllerBufferType} from "./utils/DataTextureController"; import {CopRendererController} from "./utils/RendererController"; import {AssemblerName} from "../../poly/registers/assemblers/_BaseRegister"; import {Poly as Poly2} from "../../Poly"; import {TexturePersistedConfig} from "../gl/code/assemblers/textures/PersistedConfig"; class BuilderCopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); this.resolution = ParamConfig.VECTOR2(RESOLUTION_DEFAULT); this.useCameraRenderer = ParamConfig.BOOLEAN(0); } } const ParamsConfig2 = new BuilderCopParamsConfig(); export class BuilderCopNode extends TypedCopNode { constructor() { super(...arguments); this.params_config = ParamsConfig2; this.persisted_config = new TexturePersistedConfig(this); this._assembler_controller = this._create_assembler_controller(); this._texture_mesh = new Mesh2(new PlaneBufferGeometry2(2, 2)); this.texture_material = new ShaderMaterial2({ uniforms: {}, vertexShader: VERTEX_SHADER, fragmentShader: "" }); this._texture_scene = new Scene2(); this._texture_camera = new Camera2(); this._children_controller_context = NodeContext2.GL; this._cook_main_without_inputs_when_dirty_bound = this._cook_main_without_inputs_when_dirty.bind(this); } static type() { return "builder"; } usedAssembler() { return AssemblerName.GL_TEXTURE; } _create_assembler_controller() { const assembler_controller = Poly2.assemblersRegister.assembler(this, this.usedAssembler()); if (assembler_controller) { const globals_handler = new GlobalsGeometryHandler(); assembler_controller.set_assembler_globals_handler(globals_handler); return assembler_controller; } } get assemblerController() { return this._assembler_controller; } initializeNode() { this._texture_mesh.material = this.texture_material; this._texture_mesh.scale.multiplyScalar(0.25); this._texture_scene.add(this._texture_mesh); this._texture_camera.position.z = 1; this.addPostDirtyHook("_cook_main_without_inputs_when_dirty", () => { setTimeout(this._cook_main_without_inputs_when_dirty_bound, 0); }); } createNode(node_class, params_init_value_overrides) { return super.createNode(node_class, params_init_value_overrides); } children() { return super.children(); } nodesByType(type) { return super.nodesByType(type); } childrenAllowed() { if (this.assemblerController) { return super.childrenAllowed(); } this.scene().markAsReadOnly(this); return false; } async _cook_main_without_inputs_when_dirty() { await this.cookController.cook_main_without_inputs(); } async cook() { this.compile_if_required(); this.render_on_target(); } shaders_by_name() { return { fragment: this._fragment_shader }; } compile_if_required() { if (this.assemblerController?.compile_required()) { this.compile(); } } compile() { const assemblerController = this.assemblerController; if (!assemblerController) { return; } const output_nodes = GlNodeFinder.find_output_nodes(this); if (output_nodes.length > 1) { this.states.error.set("only one output node allowed"); return; } const output_node = output_nodes[0]; if (output_node) { const root_nodes = output_nodes; assemblerController.assembler.set_root_nodes(root_nodes); assemblerController.assembler.update_fragment_shader(); const fragment_shader = assemblerController.assembler.fragment_shader(); const uniforms = assemblerController.assembler.uniforms(); if (fragment_shader && uniforms) { this._fragment_shader = fragment_shader; this._uniforms = uniforms; } BuilderCopNode.handle_dependencies(this, assemblerController.assembler.uniforms_time_dependent()); } if (this._fragment_shader && this._uniforms) { this.texture_material.fragmentShader = this._fragment_shader; this.texture_material.uniforms = this._uniforms; this.texture_material.needsUpdate = true; this.texture_material.uniforms.resolution = { value: this.pv.resolution }; } assemblerController.post_compile(); } static handle_dependencies(node, time_dependent, uniforms) { const scene = node.scene(); const id = node.graphNodeId(); const id_s = `${id}`; if (time_dependent) { node.states.time_dependent.force_time_dependent(); if (uniforms) { scene.uniforms_controller.add_time_dependent_uniform_owner(id_s, uniforms); } } else { node.states.time_dependent.unforce_time_dependent(); scene.uniforms_controller.remove_time_dependent_uniform_owner(id_s); } } async render_on_target() { this.create_render_target_if_required(); if (!this._render_target) { return; } this._renderer_controller = this._renderer_controller || new CopRendererController(this); const renderer = await this._renderer_controller.renderer(); const prev_target = renderer.getRenderTarget(); renderer.setRenderTarget(this._render_target); renderer.clear(); renderer.render(this._texture_scene, this._texture_camera); renderer.setRenderTarget(prev_target); if (this._render_target.texture) { if (this.pv.useCameraRenderer) { this.set_texture(this._render_target.texture); } else { this._data_texture_controller = this._data_texture_controller || new DataTextureController2(DataTextureControllerBufferType.Float32Array); const data_texture = this._data_texture_controller.from_render_target(renderer, this._render_target); this.set_texture(data_texture); } } else { this.cookController.end_cook(); } } render_target() { return this._render_target = this._render_target || this._create_render_target(this.pv.resolution.x, this.pv.resolution.y); } create_render_target_if_required() { if (!this._render_target || !this._render_target_resolution_valid()) { this._render_target = this._create_render_target(this.pv.resolution.x, this.pv.resolution.y); this._data_texture_controller?.reset(); } } _render_target_resolution_valid() { if (this._render_target) { const image = this._render_target.texture.image; if (image.width != this.pv.resolution.x || image.height != this.pv.resolution.y) { return false; } else { return true; } } else { return false; } } _create_render_target(width, height) { if (this._render_target) { const image = this._render_target.texture.image; if (image.width == width && image.height == height) { return this._render_target; } } const wrapS = ClampToEdgeWrapping; const wrapT = ClampToEdgeWrapping; const minFilter = LinearFilter; const magFilter = NearestFilter; var renderTarget = new WebGLRenderTarget2(width, height, { wrapS, wrapT, minFilter, magFilter, format: RGBAFormat, type: /(iPad|iPhone|iPod)/g.test(navigator.userAgent) ? HalfFloatType : FloatType, stencilBuffer: false, depthBuffer: false }); Poly2.warn("created render target", this.fullPath(), width, height); return renderTarget; } }