UNPKG

polygonjs-engine

Version:

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

148 lines (133 loc) 4.94 kB
import {EventContext} from '../../../../scene/utils/events/_BaseEventsController'; import {RaycastEventNode} from '../../Raycast'; import {BaseThreejsCameraObjNodeType} from '../../../obj/_BaseCamera'; import {WebGLRenderTarget} from 'three/src/renderers/WebGLRenderTarget'; import {Material} from 'three/src/materials/Material'; import {Vector2} from 'three/src/math/Vector2'; import {LinearFilter, NearestFilter, RGBAFormat, FloatType, NoToneMapping, LinearEncoding} from 'three/src/constants'; import {NodeContext} from '../../../../poly/NodeContext'; import {BaseMatNodeType} from '../../../mat/_Base'; import {Scene} from 'three/src/scenes/Scene'; import {WebGLRenderer} from 'three/src/renderers/WebGLRenderer'; import {Number2, Number4} from '../../../../../types/GlobalTypes'; interface SceneRestoreContext { overrideMaterial: Material | null; } interface RendererRestoreContext { toneMapping: number; outputEncoding: number; } interface RestoreContext { scene: SceneRestoreContext; renderer: RendererRestoreContext; } export class RaycastGPUController { private _resolved_material: Material | null = null; private _restore_context: RestoreContext = { scene: { overrideMaterial: null, }, renderer: { toneMapping: -1, outputEncoding: -1, }, }; private _mouse: Vector2 = new Vector2(); private _mouse_array: Number2 = [0, 0]; private _render_target: WebGLRenderTarget | undefined; private _read = new Float32Array(4); private _param_read: Number4 = [0, 0, 0, 0]; constructor(private _node: RaycastEventNode) {} update_mouse(context: EventContext<MouseEvent>) { const canvas = context.viewer?.canvas(); if (!(canvas && context.event)) { return; } if (context.event instanceof MouseEvent) { this._mouse.x = context.event.offsetX / canvas.offsetWidth; this._mouse.y = 1 - context.event.offsetY / canvas.offsetHeight; this._mouse.toArray(this._mouse_array); this._node.p.mouse.set(this._mouse_array); } } process_event(context: EventContext<MouseEvent>) { const canvas = context.viewer?.canvas(); if (!(canvas && context.cameraNode)) { return; } const camera_node = context.cameraNode; const renderer_controller = (camera_node as BaseThreejsCameraObjNodeType).renderController; if (renderer_controller) { this._render_target = this._render_target || new WebGLRenderTarget(canvas.offsetWidth, canvas.offsetHeight, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat, type: FloatType, }); if (!this._resolved_material) { this.update_material(); console.warn('no material found'); return; } // find renderer and use it const threejs_camera = camera_node as BaseThreejsCameraObjNodeType; const scene = renderer_controller.resolved_scene || camera_node.scene().threejsScene(); const renderer = renderer_controller.renderer(canvas); this._modify_scene_and_renderer(scene, renderer); renderer.setRenderTarget(this._render_target); renderer.clear(); renderer.render(scene, threejs_camera.object); renderer.setRenderTarget(null); this._restore_scene_and_renderer(scene, renderer); // read result renderer.readRenderTargetPixels( this._render_target, Math.round(this._mouse.x * canvas.offsetWidth), Math.round(this._mouse.y * canvas.offsetHeight), 1, 1, this._read ); this._param_read[0] = this._read[0]; this._param_read[1] = this._read[1]; this._param_read[2] = this._read[2]; this._param_read[3] = this._read[3]; this._node.p.pixelValue.set(this._param_read); if (this._node.pv.pixelValue.x > this._node.pv.hitThreshold) { this._node.trigger_hit(context); } else { this._node.trigger_miss(context); } } } private _modify_scene_and_renderer(scene: Scene, renderer: WebGLRenderer) { this._restore_context.scene.overrideMaterial = scene.overrideMaterial; this._restore_context.renderer.outputEncoding = renderer.outputEncoding; this._restore_context.renderer.toneMapping = renderer.toneMapping; scene.overrideMaterial = this._resolved_material; renderer.toneMapping = NoToneMapping; renderer.outputEncoding = LinearEncoding; } private _restore_scene_and_renderer(scene: Scene, renderer: WebGLRenderer) { scene.overrideMaterial = this._restore_context.scene.overrideMaterial; renderer.outputEncoding = this._restore_context.renderer.outputEncoding; renderer.toneMapping = this._restore_context.renderer.toneMapping; } update_material() { const node = this._node.p.material.found_node(); if (node) { if (node.nodeContext() == NodeContext.MAT) { this._resolved_material = (node as BaseMatNodeType).material; } else { this._node.states.error.set('target is not an obj'); } } else { this._node.states.error.set('no target found'); } } static PARAM_CALLBACK_update_material(node: RaycastEventNode) { node.gpu_controller.update_material(); } }