@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
178 lines (166 loc) • 5.82 kB
text/typescript
/**
* Creates a light map
*
*
*/
// https://threejs.org/examples/?q=light#webgl_shadowmap_progressive
import {TypedCopNode} from './_Base';
import {NodeParamsConfig, ParamConfig} from '../utils/params/ParamsConfig';
import {BaseNodeType} from '../_Base';
import {Object3D, WebGLRenderer} from 'three';
import {Light} from 'three';
import {LightMapController} from './utils/LightMapController';
// import {DEFAULT_ITERATION_BLEND} from './utils/lightMap/Common';
import {Mesh} from 'three';
import {isBooleanTrue} from '../../../core/BooleanValue';
import {DataTextureController, DataTextureControllerBufferType} from './utils/DataTextureController';
import {CopRendererController} from './utils/RendererController';
import {CopType} from '../../poly/registers/nodes/types/Cop';
class LightMapCopParamConfig extends NodeParamsConfig {
/** @param click to update shadow, when mode is manual */
update = ParamConfig.BUTTON(null, {
callback: (node: BaseNodeType) => {
LightMapCopNode.PARAM_CALLBACK_updateManual(node as LightMapCopNode);
},
});
/** @param defines if the shader is rendered via the same camera used to render the scene */
useCameraRenderer = ParamConfig.BOOLEAN(1); // needs to be 1, as it does not work on firefox otherwise
/** @param shadow resolution */
lightMapRes = ParamConfig.INTEGER(1024, {range: [1, 2048], rangeLocked: [true, false]});
/** @param iterations */
iterations = ParamConfig.INTEGER(512, {range: [1, 2048], rangeLocked: [true, false]});
/** @param blendWindow */
// iterationBlend = ParamConfig.FLOAT(DEFAULT_ITERATION_BLEND, {
// range: [0, 1],
// rangeLocked: [true, true],
// });
/** @param blurEdges */
blur = ParamConfig.BOOLEAN(1);
/** @param blurAmount */
blurAmount = ParamConfig.FLOAT(1, {
visibleIf: {blur: 1},
range: [0, 1],
rangeLocked: [true, false],
});
/** @param lightPositionVariation */
lightRadius = ParamConfig.FLOAT(1, {
range: [0, 10],
});
objectsMask = ParamConfig.STRING('', {objectMask: true});
lightsMask = ParamConfig.STRING('*', {objectMask: true});
}
const ParamsConfig = new LightMapCopParamConfig();
export class LightMapCopNode extends TypedCopNode<LightMapCopParamConfig> {
override paramsConfig = ParamsConfig;
static override type(): Readonly<CopType.LIGHT_MAP> {
return CopType.LIGHT_MAP;
}
private lightMapController: LightMapController | undefined;
private _includedObjects: Mesh[] = [];
private _includedLights: Light[] = [];
private _dataTextureController: DataTextureController | undefined;
private _rendererController: CopRendererController | undefined;
override async cook() {
await this._render();
}
private async _createLightMapController() {
const renderer = await this.scene().renderersRegister.waitForRenderer();
if (!renderer) {
console.warn('no renderer found');
return;
}
if (!(renderer instanceof WebGLRenderer)) {
this.states.error.set('renderer found is not WebGLRenderer');
return;
}
const lightMapController = new LightMapController(renderer);
return lightMapController;
}
//
//
// ACTIVE
//
//
// static PARAM_CALLBACK_update_updateMode(node: LightMapCopNode) {
// // node._updateRenderHook();
// }
//
//
// UPDATE
//
//
private async _render() {
this.lightMapController = this.lightMapController || (await this._createLightMapController());
if (!this.lightMapController) {
return;
}
const mainCamera = await this.scene().mainCamera();
if (!mainCamera) {
return;
}
this._updateObjectsAndLightsList();
this.lightMapController.setState(this._includedObjects, this._includedLights);
// const camera = mainCameraNode.camera();
this.lightMapController.setParams({
resolution: this.pv.lightMapRes,
lightRadius: this.pv.lightRadius,
totalIterationsCount: this.pv.iterations,
// iterationBlend: this.pv.iterationBlend,
blur: this.pv.blur,
blurAmount: this.pv.blurAmount,
});
this.lightMapController.runUpdates(mainCamera);
this.lightMapController.restoreState();
// this.setTexture();
const renderTarget = this.lightMapController.textureRenderTarget();
if (isBooleanTrue(this.pv.useCameraRenderer)) {
this.setTexture(renderTarget.texture);
} else {
this._dataTextureController =
this._dataTextureController || new DataTextureController(DataTextureControllerBufferType.Float32Array);
this._rendererController = this._rendererController || new CopRendererController(this);
const renderer = await this._rendererController.waitForRenderer();
if (!(renderer instanceof WebGLRenderer)) {
this.states.error.set('renderer found is not WebGLRenderer');
this.cookController.endCook();
return;
}
const texture = this._dataTextureController.fromRenderTarget(renderer, renderTarget);
this.setTexture(texture);
}
}
static PARAM_CALLBACK_updateManual(node: LightMapCopNode) {
node.setDirty();
}
//
//
// UPDATE OBJECTS LIST
//
//
private _updateObjectsAndLightsList() {
let matchedObjects: Object3D[] = [];
let matchedLights: Object3D[] = [];
this._includedLights = [];
this._includedObjects = [];
const lightsByUuid: WeakSet<Object3D> = new WeakSet();
if (this.pv.lightsMask != '') {
matchedLights = this.scene().objectsByMask(this.pv.lightsMask) as Object3D[];
for (const matchedLight of matchedLights) {
if (matchedLight instanceof Light) {
this._includedLights.push(matchedLight);
lightsByUuid.add(matchedLight);
}
}
}
if (this.pv.objectsMask != '') {
matchedObjects = this.scene().objectsByMask(this.pv.objectsMask) as Object3D[];
for (const matchedObject of matchedObjects) {
if (!(matchedObject instanceof Light)) {
if (!lightsByUuid.has(matchedObject) && matchedObject instanceof Mesh) {
this._includedObjects.push(matchedObject);
}
}
}
}
}
}