@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
147 lines (132 loc) • 4.77 kB
text/typescript
/**
* Creates a plane visible by a camera.
*
*/
import {Plane, PlaneGeometry, Vector2, Vector3, Camera} 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 {BaseNodeType} from '../_Base';
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 {
/** @param axis perpendicular to the plane */
direction = ParamConfig.VECTOR3(DEFAULT.direction);
/** @param plane offset */
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 */
useSegmentsCount = ParamConfig.BOOLEAN(true);
/** @param step size */
stepSize = ParamConfig.FLOAT(1, {
range: [0.001, 1],
rangeLocked: [false, false],
visibleIf: {useSegmentsCount: 0},
});
/** @param segments count */
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 */
sizeMult = ParamConfig.FLOAT(1, {
range: [0, 2],
rangeLocked: [true, false],
});
/** @param update on window resize */
updateOnWindowResize = ParamConfig.BOOLEAN(1);
/** @param update */
update = ParamConfig.BUTTON(null, {
callback: (node: BaseNodeType) => {
CameraPlaneSopNode.PARAM_CALLBACK_update(node as CameraPlaneSopNode);
},
});
}
const ParamsConfig = new CameraPlaneSopParamsConfig();
export class CameraPlaneSopNode extends TypedSopNode<CameraPlaneSopParamsConfig> {
override paramsConfig = ParamsConfig;
static override type() {
return 'cameraPlane';
}
private _raycaster = createRaycaster();
private _coreTransform = new CoreTransform();
override 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');
}
}
private _updateWindowControllerDependency() {
if (isBooleanTrue(this.pv.updateOnWindowResize)) {
this.addGraphInput(this.scene().viewersRegister.graphNode());
} else {
this.removeGraphInput(this.scene().viewersRegister.graphNode());
}
}
private _computePlaneParams(camera: 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);
}
private _createPlane(size: Vector2) {
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: CameraPlaneSopNode) {
node._paramCallbackUpdate();
}
private _paramCallbackUpdate() {
this.setDirty();
}
}