@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
135 lines (134 loc) • 4.91 kB
JavaScript
"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();
}
}