UNPKG

@polygonjs/polygonjs

Version:

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

182 lines (165 loc) 5.58 kB
import {RaycastEventNode} from '../../Raycast'; import {NodeContext} from '../../../../poly/NodeContext'; import {BaseMatNodeType} from '../../../mat/_Base'; import { Scene, Color, Texture, WebGLRenderer, WebGLRenderTarget, Material, LinearFilter, NearestFilter, RGBAFormat, FloatType, NoToneMapping, NoColorSpace, ToneMapping, ColorSpace, } from 'three'; import {Number2, Number3} from '../../../../../types/GlobalTypes'; import {isBooleanTrue} from '../../../../../core/Type'; import {BaseRaycastController} from './BaseRaycastController'; import { EventContext } from '../../../../../core/event/EventContextType'; interface SceneRestoreContext { overrideMaterial: Material | null; background: Color | Texture | null; } interface RendererRestoreContext { toneMapping: ToneMapping; outputColorSpace: ColorSpace; } interface RestoreContext { scene: SceneRestoreContext; renderer: RendererRestoreContext; } export class RaycastGPUController extends BaseRaycastController { private _resolvedMaterial: Material | null = null; private _restoreContext: RestoreContext = { scene: { overrideMaterial: null, background: null, }, renderer: { toneMapping: NoToneMapping, outputColorSpace: NoColorSpace, }, }; // private _mouse: Vector2 = new Vector2(); private _cursorArray: Number2 = [0, 0]; private _renderTarget: WebGLRenderTarget | undefined; private _read = new Float32Array(4); private _paramColor: Number3 = [0, 0, 0]; private _paramAlpha: number = 0; constructor(private _node: RaycastEventNode) { super(); } updateMouse(eventContext: EventContext<MouseEvent | DragEvent | PointerEvent>) { this._cursorHelper.setCursorForGPU(eventContext, this._cursor); if (isBooleanTrue(this._node.pv.tmouse)) { this._cursor.toArray(this._cursorArray); this._node.p.mouse.set(this._cursorArray); } } processEvent(context: EventContext<MouseEvent>) { const canvas = context.viewer?.canvas(); const camera = context.viewer?.camera(); if (!(canvas && camera)) { return; } // const camera_node = context.cameraNode; // const renderer_controller = (camera_node as BaseThreejsCameraObjNodeType).renderController(); // if (renderer_controller) { this._renderTarget = this._renderTarget || new WebGLRenderTarget(1, 1, { 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.resolvedScene() || camera_node.scene().threejsScene(); const scene = this._node.scene().threejsScene(); const renderer = this._node.scene().renderersRegister.lastRegisteredRenderer(); if (!renderer) { return; } if (!(renderer instanceof WebGLRenderer)) { console.log('renderer found is not WebGLRenderer'); return; } this._modifySceneAndRenderer(scene, renderer); (camera as any).setViewOffset( canvas.offsetWidth, canvas.offsetHeight, this._cursor.x * canvas.offsetWidth, (1 - this._cursor.y) * canvas.offsetHeight, 1, 1 ); renderer.setRenderTarget(this._renderTarget); renderer.clear(); renderer.render(scene, camera); renderer.setRenderTarget(null); (camera as any).clearViewOffset(); this._restoreSceneAndRenderer(scene, renderer); // read result renderer.readRenderTargetPixels(this._renderTarget, 0, 0, 1, 1, this._read); this._paramColor[0] = this._read[0]; this._paramColor[1] = this._read[1]; this._paramColor[2] = this._read[2]; this._paramAlpha = this._read[3]; this._node.scene().batchUpdates(() => { this._node.p.pixelColor.set(this._paramColor); this._node.p.pixelAlpha.set(this._paramAlpha); }); if (this._node.pv.pixelColor.r > this._node.pv.hitThreshold) { this._node.triggerHit(context); } else { this._node.triggerMiss(context); } } private _modifySceneAndRenderer(scene: Scene, renderer: WebGLRenderer) { this._restoreContext.scene.overrideMaterial = scene.overrideMaterial; this._restoreContext.scene.background = scene.background; this._restoreContext.renderer.outputColorSpace = renderer.outputColorSpace; this._restoreContext.renderer.toneMapping = renderer.toneMapping; if (isBooleanTrue(this._node.pv.overrideMaterial)) { if (this._resolvedMaterial == null) { this._updateMaterial(); } scene.overrideMaterial = this._resolvedMaterial; } scene.background = null; renderer.toneMapping = NoToneMapping; renderer.outputColorSpace = NoColorSpace; } private _restoreSceneAndRenderer(scene: Scene, renderer: WebGLRenderer) { scene.overrideMaterial = this._restoreContext.scene.overrideMaterial; scene.background = this._restoreContext.scene.background; renderer.outputColorSpace = this._restoreContext.renderer.outputColorSpace; renderer.toneMapping = this._restoreContext.renderer.toneMapping; } private async _updateMaterial() { const node = this._node.pv.material.nodeWithContext(NodeContext.MAT, this._node.states.error); if (node) { if (node.context() == NodeContext.MAT) { this._resolvedMaterial = (await (node as BaseMatNodeType).material()) || null; } else { this._node.states.error.set('material is not a mat node'); } } else { this._node.states.error.set('no override material found'); } } static PARAM_CALLBACK_updateMaterial(node: RaycastEventNode) { node.gpuController._updateMaterial(); } }