@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
182 lines (165 loc) • 5.58 kB
text/typescript
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();
}
}