@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
532 lines (488 loc) • 17.9 kB
text/typescript
// /**
// * Creates smooth shadows
// *
// *
// */
// // https://github.com/mrdoob/three.js/blob/master/examples/webgl_shadow_contact.html
// import {
// WebGLRenderer,
// Object3D,
// Vector2,
// Group,
// Scene,
// Camera,
// BufferGeometry,
// Material,
// Mesh,
// MeshBasicMaterial,
// MeshDepthMaterial,
// OrthographicCamera,
// PlaneGeometry,
// WebGLRenderTarget,
// BufferAttribute,
// } from 'three';
// import {TypedObjNode} from './_Base';
// import {FlagsControllerD} from '../utils/FlagsController';
// import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
// import {BaseNodeType} from '../_Base';
// import {HierarchyController} from './utils/HierarchyController';
// import {RenderHook} from '../../../core/geometry/Material';
// import {TypeAssert} from '../../poly/Assert';
// import {CameraHelper} from '../../../core/helpers/CameraHelper';
// import {isBooleanTrue} from '../../../core/BooleanValue';
// import {TransformController, TransformedParamConfig} from './utils/TransformController';
// import {CoreRenderBlur} from '../../../core/render/Blur';
// enum ContactShadowUpdateMode {
// ON_RENDER = 'On Every Render',
// MANUAL = 'Manual',
// }
// const UPDATE_MODES: ContactShadowUpdateMode[] = [ContactShadowUpdateMode.ON_RENDER, ContactShadowUpdateMode.MANUAL];
// class ContactShadowObjParamConfig extends TransformedParamConfig(NodeParamsConfig) {
// shadow = ParamConfig.FOLDER();
// /** @param distance from the ground up to which shadows are visible */
// dist = ParamConfig.FLOAT(1, {
// range: [0, 10],
// rangeLocked: [true, false],
// });
// /** @param size of the plane on which shadows are rendered */
// planeSize = ParamConfig.VECTOR2([1, 1]);
// /** @param shadow resolution */
// shadowRes = ParamConfig.VECTOR2([256, 256]);
// /** @param blur amount */
// blur = ParamConfig.FLOAT(1, {
// range: [0, 10],
// rangeLocked: [true, false],
// });
// /** @param toggle on to add a secondary blur, which may be useful to get rid of artefacts */
// tblur2 = ParamConfig.BOOLEAN(1);
// /** @param secondary blur amount */
// blur2 = ParamConfig.FLOAT(1, {
// range: [0, 10],
// rangeLocked: [true, false],
// visibleIf: {tblur2: 1},
// });
// /** @param shadow darkness */
// darkness = ParamConfig.FLOAT(1);
// /** @param shadow opacity */
// opacity = ParamConfig.FLOAT(1);
// /** @param show helper */
// showHelper = ParamConfig.BOOLEAN(0);
// /** @param set update mode, which can be to update on every frame, or manually only */
// updateMode = ParamConfig.INTEGER(UPDATE_MODES.indexOf(ContactShadowUpdateMode.ON_RENDER), {
// callback: (node: BaseNodeType) => {
// ContactShadowObjNode.PARAM_CALLBACK_update_updateMode(node as ContactShadowObjNode);
// },
// menu: {
// entries: UPDATE_MODES.map((name, value) => {
// return {name, value};
// }),
// },
// });
// /** @param click to update shadow, when mode is manual */
// update = ParamConfig.BUTTON(null, {
// callback: (node: BaseNodeType) => {
// ContactShadowObjNode.PARAM_CALLBACK_updateManual(node as ContactShadowObjNode);
// },
// visibleIf: {updateMode: UPDATE_MODES.indexOf(ContactShadowUpdateMode.MANUAL)},
// });
// scene = ParamConfig.FOLDER();
// renderAllObjects = ParamConfig.BOOLEAN(true);
// include = ParamConfig.STRING('', {
// visibleIf: {renderAllObjects: 0},
// objectMask: true,
// });
// exclude = ParamConfig.STRING('', {
// visibleIf: {renderAllObjects: 0},
// objectMask: true,
// });
// updateObjectsList = ParamConfig.BUTTON(null, {
// visibleIf: {renderAllObjects: 0},
// callback: (node: BaseNodeType) => {
// ContactShadowObjNode.PARAM_CALLBACK_updateObjectsList(node as ContactShadowObjNode);
// },
// });
// printResolveObjectsList = ParamConfig.BUTTON(null, {
// visibleIf: {renderAllObjects: 0},
// callback: (node: BaseNodeType) => {
// ContactShadowObjNode.PARAM_CALLBACK_printResolveObjectsList(node as ContactShadowObjNode);
// },
// });
// }
// const ParamsConfig = new ContactShadowObjParamConfig();
// const PLANE_WIDTH = 1;
// const PLANE_HEIGHT = 1;
// const CAMERA_HEIGHT = 1;
// const DARKNESS = 1;
// const DEFAULT_RENDER_TARGET_RES = new Vector2(256, 256);
// export class ContactShadowObjNode extends TypedObjNode<Group, ContactShadowObjParamConfig> {
// override paramsConfig = ParamsConfig;
// static override type(): Readonly<'contactShadow'> {
// return 'contactShadow';
// }
// override readonly hierarchyController: HierarchyController = new HierarchyController(this);
// public override readonly flags: FlagsControllerD = new FlagsControllerD(this);
// private _helper: CameraHelper | undefined;
// private _renderTarget = this._createRenderTarget(DEFAULT_RENDER_TARGET_RES);
// private _coreRenderBlur: CoreRenderBlur = this._createCoreRenderBlur(DEFAULT_RENDER_TARGET_RES);
// private _createRenderTarget(res: Vector2) {
// const renderTarget = new WebGLRenderTarget(res.x, res.y);
// renderTarget.texture.generateMipmaps = false;
// return renderTarget;
// }
// private _createCoreRenderBlur(res: Vector2) {
// return new CoreRenderBlur(res);
// }
// private _shadowGroup: Group | undefined;
// private _plane: Mesh | undefined;
// private _planeMaterial: MeshBasicMaterial | undefined;
// private _includedObjects: Object3D[] = [];
// private _includedAncestors: Object3D[] = [];
// private _excludedObjects: Object3D[] = [];
// override createObject() {
// const group = new Group();
// this._shadowGroup = new Group();
// group.add(this._shadowGroup);
// this._shadowGroup.name = 'shadowGroup';
// const planeGeometry = new PlaneGeometry(PLANE_WIDTH, PLANE_HEIGHT).rotateX(-Math.PI / 2);
// // update uvs
// const uvArray = (planeGeometry.getAttribute('uv') as BufferAttribute).array as number[];
// for (let index of [1, 3, 5, 7]) {
// uvArray[index] = 1 - uvArray[index];
// }
// this._planeMaterial = new MeshBasicMaterial({
// map: this._renderTarget.texture,
// opacity: 1,
// transparent: true,
// depthWrite: false,
// });
// // this._renderTarget.texture.flipY = true;
// this._plane = new Mesh(planeGeometry, this._planeMaterial);
// // make sure it's rendered after the fillPlane
// this._plane.renderOrder = 1;
// // the y from the texture is flipped!
// // this._plane.scale.y = -1;
// this._plane.matrixAutoUpdate = false;
// this._shadowGroup.add(this._plane);
// // the plane with the color of the ground
// // this._fillPlaneMaterial = new MeshBasicMaterial({
// // color: 0xffffff,
// // opacity: OPACITY,
// // transparent: true,
// // depthWrite: false,
// // });
// // const fillPlane = new Mesh(planeGeometry, this._fillPlaneMaterial);
// // fillPlane.rotateX(Math.PI);
// // this._shadowGroup.add(fillPlane);
// this._createDepthCamera(this._shadowGroup);
// this._createMaterials();
// return group;
// }
// override readonly transformController: TransformController = new TransformController(this);
// override initializeNode() {
// this.hierarchyController.initializeNode();
// this.transformController.initializeNode();
// this._updateShadowGroupVisibility();
// this._updateHelperVisibility();
// this.flags.display.onUpdate(() => {
// this._updateShadowGroupVisibility();
// this._updateHelperVisibility();
// });
// }
// override async cook() {
// this.transformController.update();
// this._updateRenderHook();
// this._updateHelperVisibility();
// this._updateObjectsList();
// if (this._planeMaterial) {
// this._planeMaterial.opacity = this.pv.opacity;
// }
// this._darknessUniform.value = this.pv.darkness;
// // update planes size
// if (this._plane && this._shadowCamera && this._helper) {
// this._plane.scale.x = this.pv.planeSize.x;
// this._plane.scale.z = this.pv.planeSize.y;
// this._plane.updateMatrix();
// this._shadowCamera.left = -this.pv.planeSize.x / 2;
// this._shadowCamera.right = this.pv.planeSize.x / 2;
// this._shadowCamera.bottom = -this.pv.planeSize.y / 2;
// this._shadowCamera.top = this.pv.planeSize.y / 2;
// this._shadowCamera.far = this.pv.dist;
// this._shadowCamera.updateProjectionMatrix();
// this._helper.update();
// }
// // update renderTargets if needed
// if (this._renderTarget.width != this.pv.shadowRes.x || this._renderTarget.height != this.pv.shadowRes.y) {
// if (this._planeMaterial) {
// this._renderTarget = this._createRenderTarget(this.pv.shadowRes);
// this._coreRenderBlur = this._createCoreRenderBlur(this.pv.shadowRes);
// this._planeMaterial.map = this._renderTarget.texture;
// }
// }
// this.cookController.endCook();
// }
// private _shadowCamera: OrthographicCamera | undefined;
// private _createDepthCamera(group: Group) {
// // the camera to render the depth material from
// this._shadowCamera = new OrthographicCamera(
// -PLANE_WIDTH / 2,
// PLANE_WIDTH / 2,
// PLANE_HEIGHT / 2,
// -PLANE_HEIGHT / 2,
// 0,
// CAMERA_HEIGHT
// );
// // this._shadowCamera.rotation.z = Math.PI / 2;
// this._shadowCamera.rotation.x = Math.PI / 2; // get the camera to look up
// // this._shadowCamera.position.y = 0.1;
// group.add(this._shadowCamera);
// this._helper = new CameraHelper(this._shadowCamera);
// this._helper.visible = false;
// this._shadowCamera.add(this._helper);
// }
// private _depthMaterial: MeshDepthMaterial | undefined;
// private _darknessUniform = {value: DARKNESS};
// private _createMaterials() {
// // like MeshDepthMaterial, but goes from black to transparent
// this._depthMaterial = new MeshDepthMaterial();
// this._depthMaterial.onBeforeCompile = (shader) => {
// shader.uniforms.darkness = this._darknessUniform;
// shader.fragmentShader = /* glsl */ `
// uniform float darkness;
// ${shader.fragmentShader.replace(
// 'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );',
// 'gl_FragColor = vec4( vec3( 0.0 ), ( 1.0 - fragCoordZ ) * darkness );'
// )}
// `;
// };
// this._depthMaterial.depthTest = false;
// this._depthMaterial.depthWrite = false;
// }
// private _emptyOnBeforeRender = () => {};
// private _renderShadow(renderer: WebGLRenderer, scene: Scene) {
// if (!(this._helper && this._depthMaterial && this._shadowCamera && this._helper && this._plane)) {
// return;
// }
// // save current state
// const previousOnBeforeRender = this._plane.onBeforeRender;
// const initialBackground = scene.background;
// const helperVisible = this._helper.visible;
// // prepare scene
// scene.background = null;
// this._plane.onBeforeRender = this._emptyOnBeforeRender;
// this._helper.visible = false;
// scene.overrideMaterial = this._depthMaterial;
// this._initVisibility(scene);
// // set renderer clear alpha
// const initialClearAlpha = renderer.getClearAlpha();
// renderer.setClearAlpha(0);
// // render to the render target to get the depths
// renderer.setRenderTarget(this._renderTarget);
// renderer.render(scene, this._shadowCamera);
// this._coreRenderBlur.applyBlur(this._renderTarget, renderer, this.pv.blur, this.pv.blur);
// if (isBooleanTrue(this.pv.tblur2)) {
// this._coreRenderBlur.applyBlur(this._renderTarget, renderer, this.pv.blur2, this.pv.blur2);
// }
// // reset and render the normal scene
// this._restoreVisibility(scene);
// scene.overrideMaterial = null;
// this._helper.visible = helperVisible;
// renderer.setRenderTarget(null);
// renderer.setClearAlpha(initialClearAlpha);
// scene.background = initialBackground;
// this._plane.onBeforeRender = previousOnBeforeRender;
// }
// private _updateShadowGroupVisibility() {
// if (!this._shadowGroup) {
// return;
// }
// if (this.flags.display.active()) {
// this._shadowGroup.visible = true;
// } else {
// this._shadowGroup.visible = false;
// }
// }
// private _updateHelperVisibility() {
// if (!this._helper) {
// return;
// }
// if (this.flags.display.active() && isBooleanTrue(this.pv.showHelper)) {
// this._helper.visible = true;
// } else {
// this._helper.visible = false;
// }
// }
// private _updateRenderHook() {
// const mode = UPDATE_MODES[this.pv.updateMode];
// switch (mode) {
// case ContactShadowUpdateMode.ON_RENDER: {
// return this._addRenderHook();
// }
// case ContactShadowUpdateMode.MANUAL: {
// return this._removeRenderHook();
// }
// }
// TypeAssert.unreachable(mode);
// }
// private _addRenderHook() {
// if (!this._plane) {
// return;
// }
// if (this._plane.onBeforeRender != this._on_object_before_render_bound) {
// this._plane.onBeforeRender = this._on_object_before_render_bound;
// }
// }
// private _emptyRenderHook = () => {};
// private _removeRenderHook() {
// if (!this._plane) {
// return;
// }
// if (this._plane.onBeforeRender != this._emptyRenderHook) {
// this._plane.onBeforeRender = this._emptyRenderHook;
// }
// }
// private _on_object_before_render_bound: RenderHook = this._update.bind(this);
// // private _previous_on_before_render: RenderHook | undefined;
// private _update(
// renderer?: WebGLRenderer,
// scene?: Scene,
// camera?: Camera,
// geometry?: BufferGeometry,
// material?: Material,
// group?: Group | null
// ) {
// if (!renderer || !scene) {
// console.log('no renderer or scene');
// return;
// }
// this._renderShadow(renderer, scene);
// }
// private _updateManual() {
// const renderer = this.scene().renderersRegister.lastRegisteredRenderer();
// if (!renderer) {
// console.log('no renderer found');
// return;
// }
// if (!(renderer instanceof WebGLRenderer)) {
// console.log('renderer found is not WebGLRenderer');
// return;
// }
// const scene = this.scene().threejsScene();
// this._renderShadow(renderer, scene);
// }
// //
// //
// // ACTIVE
// //
// //
// static PARAM_CALLBACK_update_updateMode(node: ContactShadowObjNode) {
// node._updateRenderHook();
// }
// //
// //
// // UPDATE
// //
// //
// static PARAM_CALLBACK_updateManual(node: ContactShadowObjNode) {
// node._updateManual();
// }
// //
// //
// // UPDATE OBJECTS LIST
// //
// //
// static PARAM_CALLBACK_updateObjectsList(node: ContactShadowObjNode) {
// node._updateObjectsList();
// }
// private _updateObjectsList() {
// if (this.pv.include != '') {
// this._includedObjects = this.scene().objectsByMask(this.pv.include) as Object3D[];
// } else {
// this._includedObjects = [];
// }
// // we also need to add the parents of this._includedObjects,
// // as otherwise those will be hidden, indirectly hidding this._includedObjects
// const parentsMap: Map<string, Object3D> = new Map();
// for (let object of this._includedObjects) {
// object.traverseAncestors((parent) => {
// parentsMap.set(parent.uuid, parent);
// });
// }
// this._includedAncestors = [];
// parentsMap.forEach((parent, uuid) => {
// this._includedAncestors.push(parent);
// });
// // add excluded
// if (this.pv.exclude != '') {
// this._excludedObjects = this.scene().objectsByMask(this.pv.exclude) as Object3D[];
// } else {
// this._excludedObjects = [];
// }
// }
// static PARAM_CALLBACK_printResolveObjectsList(node: ContactShadowObjNode) {
// node._printResolveObjectsList();
// }
// private _printResolveObjectsList() {
// console.log('included objects:');
// console.log(this._includedObjects);
// console.log('included parents:');
// console.log(this._includedAncestors);
// console.log('excluded objects:');
// console.log(this._excludedObjects);
// }
// private _initialVisibilityState: WeakMap<Object3D, boolean> = new WeakMap();
// private _initVisibility(scene: Scene) {
// if (isBooleanTrue(this.pv.renderAllObjects)) {
// return;
// }
// // if at least one object is included,
// // this means those which are not are to be hidden.
// // Therefore, we then have to traverse the whole scene.
// // If none is specified in included, we do not need to traverse
// if (this._includedObjects.length > 0) {
// scene.traverse((object) => {
// this._initialVisibilityState.set(object, object.visible);
// object.visible = false;
// });
// } else {
// this._storeObjectsVisibility(this._includedObjects);
// this._storeObjectsVisibility(this._includedAncestors);
// this._storeObjectsVisibility(this._excludedObjects);
// }
// this._setObjectsVisibility(this._includedObjects, true);
// this._setObjectsVisibility(this._includedAncestors, true);
// this._setObjectsVisibility(this._excludedObjects, false);
// }
// private _storeObjectsVisibility(objects: Object3D[]) {
// for (let object of objects) {
// this._initialVisibilityState.set(object, object.visible);
// }
// }
// private _setObjectsVisibility(objects: Object3D[], visible: boolean) {
// for (let object of objects) {
// object.visible = visible;
// }
// }
// private _restoreVisibility(scene: Scene) {
// if (isBooleanTrue(this.pv.renderAllObjects)) {
// return;
// }
// if (this._includedObjects.length > 0) {
// scene.traverse((object) => {
// const state = this._initialVisibilityState.get(object);
// if (state != null) object.visible = state;
// });
// } else {
// this._restoreObjectsVisibility(this._includedObjects);
// this._restoreObjectsVisibility(this._includedAncestors);
// this._restoreObjectsVisibility(this._excludedObjects);
// }
// }
// private _restoreObjectsVisibility(objects: Object3D[]) {
// for (let object of objects) {
// const state = this._initialVisibilityState.get(object);
// if (state) {
// object.visible = state;
// }
// }
// }
// }