UNPKG

@polygonjs/polygonjs

Version:

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

135 lines (134 loc) 4.91 kB
"use strict"; import { Plane, PlaneGeometry, Vector2, Vector3 } from "three"; import { NodeParamsConfig, ParamConfig } from "../utils/params/ParamsConfig"; import { TypedSopNode } from "./_Base"; import { isBooleanTrue } from "../../../core/BooleanValue"; import { CoreTransform, rotateGeometry } from "../../../core/Transform"; import { createRaycaster } from "../../../core/RaycastHelper"; const DEFAULT = { direction: new Vector3(0, 1, 0) }; const SCREEN_CORNERS = [new Vector2(-1, -1), new Vector2(-1, 1), new Vector2(1, 1), new Vector2(1, -1)]; const DEFAULT_UP = new Vector3(0, 0, 1); const segmentsCount = new Vector2(1, 1); const planeSize = new Vector2(); const _plane = new Plane(); const _planeCorners = [new Vector3(), new Vector3(), new Vector3(), new Vector3()]; const _planeCenter = new Vector3(); class CameraPlaneSopParamsConfig extends NodeParamsConfig { constructor() { super(...arguments); /** @param axis perpendicular to the plane */ this.direction = ParamConfig.VECTOR3(DEFAULT.direction); /** @param plane offset */ this.offset = ParamConfig.FLOAT(0, { range: [-10, 10], rangeLocked: [false, false] }); /** @param defines if the plane resolution is sets via the number of segments or via the step size */ this.useSegmentsCount = ParamConfig.BOOLEAN(true); /** @param step size */ this.stepSize = ParamConfig.FLOAT(1, { range: [1e-3, 1], rangeLocked: [false, false], visibleIf: { useSegmentsCount: 0 } }); /** @param segments count */ this.segments = ParamConfig.VECTOR2([10, 10], { visibleIf: { useSegmentsCount: 1 } }); /** @param multiplies the size of the plane. This can be useful to scale down the plane. While it would cover a smaller part of the view, it would be faster to create */ this.sizeMult = ParamConfig.FLOAT(1, { range: [0, 2], rangeLocked: [true, false] }); /** @param update on window resize */ this.updateOnWindowResize = ParamConfig.BOOLEAN(1); /** @param update */ this.update = ParamConfig.BUTTON(null, { callback: (node) => { CameraPlaneSopNode.PARAM_CALLBACK_update(node); } }); } } const ParamsConfig = new CameraPlaneSopParamsConfig(); export class CameraPlaneSopNode extends TypedSopNode { constructor() { super(...arguments); this.paramsConfig = ParamsConfig; this._raycaster = createRaycaster(); this._coreTransform = new CoreTransform(); } static type() { return "cameraPlane"; } async cook() { this._updateWindowControllerDependency(); const camera = await this.scene().camerasController.mainCamera(); if (camera) { this._computePlaneParams(camera); } else { this.states.error.set("no main camera found"); } } _updateWindowControllerDependency() { if (isBooleanTrue(this.pv.updateOnWindowResize)) { this.addGraphInput(this.scene().viewersRegister.graphNode()); } else { this.removeGraphInput(this.scene().viewersRegister.graphNode()); } } _computePlaneParams(camera) { _plane.normal.copy(this.pv.direction); _plane.constant = this.pv.offset; let i = 0; _planeCenter.set(0, 0, 0); for (const corner of SCREEN_CORNERS) { this._raycaster.setFromCamera(corner, camera); const targetCorner = _planeCorners[i]; this._raycaster.ray.intersectPlane(_plane, targetCorner); _planeCenter.add(targetCorner); i++; } _planeCenter.multiplyScalar(0.25); const w0 = _planeCorners[1].distanceTo(_planeCorners[2]); const w1 = _planeCorners[0].distanceTo(_planeCorners[3]); const h0 = _planeCorners[0].distanceTo(_planeCorners[1]); const h1 = _planeCorners[2].distanceTo(_planeCorners[3]); const width = Math.max(w0, w1) * this.pv.sizeMult; const height = Math.max(h0, h1) * this.pv.sizeMult; planeSize.set(width, height); const geometry = this._createPlane(planeSize); rotateGeometry(geometry, DEFAULT_UP, this.pv.direction); const matrix = this._coreTransform.translationMatrix(_planeCenter); geometry.applyMatrix4(matrix); this.setGeometry(geometry); } _createPlane(size) { size = size.clone(); if (isBooleanTrue(this.pv.useSegmentsCount)) { segmentsCount.x = Math.floor(this.pv.segments.x); segmentsCount.y = Math.floor(this.pv.segments.y); } else { if (this.pv.stepSize > 0) { segmentsCount.x = Math.floor(size.x / this.pv.stepSize); segmentsCount.y = Math.floor(size.y / this.pv.stepSize); size.x = segmentsCount.x * this.pv.stepSize; size.y = segmentsCount.y * this.pv.stepSize; } } return new PlaneGeometry(size.x, size.y, segmentsCount.x, segmentsCount.y); } // // // CALLBACK // // static PARAM_CALLBACK_update(node) { node._paramCallbackUpdate(); } _paramCallbackUpdate() { this.setDirty(); } }