@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
238 lines (237 loc) • 8.02 kB
JavaScript
"use strict";
import {
Quaternion,
Vector3,
WebGLRenderTarget,
Scene,
BackSide,
FrontSide,
DoubleSide
} from "three";
import {
RENDER_TARGET_DEFAULT_SIZE,
renderTargetFormat,
renderTargetType
} from "./lightMap/Common";
import { setBlurMaterial } from "./lightMap/BlurMaterial";
import { createBlurPlane } from "./lightMap/BlurPlane";
import { setRenderTargetsCombineMaterial } from "./lightMap/RenderTargetsCombineMaterial";
import { createRenderTargetsCombinePlane } from "./lightMap/RenderTargetsCombinePlane";
import { createLightMapMaterial, setLightMapMaterial } from "./lightMap/LightMapMaterial";
import { invertNormals } from "./lightMap/LightMapUtils";
import { RenderTargetPair } from "./lightMap/RenderTargetPair";
export class LightMapController {
constructor(renderer) {
this.renderer = renderer;
this.objectTargets = [];
this.lights = [];
this._scene = new Scene();
this.renderFlippedUvs = false;
this.nonFlippedRenderTargetPair = new RenderTargetPair();
this.flippedRenderTargetPair = new RenderTargetPair();
this.finalRenderTarget = new WebGLRenderTarget(RENDER_TARGET_DEFAULT_SIZE, RENDER_TARGET_DEFAULT_SIZE, {
type: renderTargetType,
format: renderTargetFormat
});
this._params = {
resolution: 1,
lightRadius: 1,
totalIterationsCount: 1,
blur: false,
blurAmount: 0
};
this._objectStateByObject = /* @__PURE__ */ new WeakMap();
this._previousRenderTarget = null;
this._lightHierarchyStateByLight = /* @__PURE__ */ new WeakMap();
this._lightMatrixStateByLight = /* @__PURE__ */ new WeakMap();
this._t = new Vector3();
this._q = new Quaternion();
this._s = new Vector3();
this.lightMapMaterial = createLightMapMaterial();
const blurPlaneData = createBlurPlane();
this.blurPlane = blurPlaneData.plane;
this.blurMaterial = blurPlaneData.mat;
const renderTargetsCombinePlaneData = createRenderTargetsCombinePlane();
this.renderTargetsCombinePlane = renderTargetsCombinePlaneData.plane;
this.renderTargetsCombineMaterial = renderTargetsCombinePlaneData.mat;
}
setSize(w, h) {
this.nonFlippedRenderTargetPair.setSize(w, h);
this.flippedRenderTargetPair.setSize(w, h);
this.finalRenderTarget.setSize(w, h);
}
renderTargetPair() {
return this.renderFlippedUvs ? this.flippedRenderTargetPair : this.nonFlippedRenderTargetPair;
}
textureRenderTarget() {
return this.finalRenderTarget;
}
setParams(params) {
this._params.resolution = params.resolution;
this._params.lightRadius = params.lightRadius;
this._params.totalIterationsCount = params.totalIterationsCount;
this._params.blur = params.blur;
this._params.blurAmount = params.blurAmount;
this.setSize(params.resolution, params.resolution);
}
setState(objects, lights) {
this._clearScene();
this._scene.add(this.blurPlane);
this._previousRenderTarget = this.renderer.getRenderTarget();
this._setObjects(objects);
this._setLights(lights);
}
_clearScene() {
let child;
while (child = this._scene.children[0]) {
this._scene.remove(child);
}
}
_setObjects(objects) {
this.objectTargets = [...objects];
this._saveObjectsState();
}
_setLights(lights) {
this.lights = lights;
for (const light of lights) {
this._saveLightHierarchyState(light);
this._scene.attach(light);
this._saveLightMatrixState(light);
}
}
_saveLightHierarchyState(light) {
this._lightHierarchyStateByLight.set(light, {
parent: light.parent,
matrixAutoUpdate: light.matrixAutoUpdate
});
light.matrixAutoUpdate = true;
}
_saveLightMatrixState(light) {
light.updateMatrix();
light.matrix.decompose(this._t, this._q, this._s);
this._lightMatrixStateByLight.set(light, {
matrix: light.matrix.clone(),
position: this._t.clone()
});
}
_saveObjectsState() {
let i = 0;
for (const object of this.objectTargets) {
this._objectStateByObject.set(object, {
frustumCulled: object.frustumCulled,
material: object.material,
parent: object.parent,
renderOrder: object.renderOrder
});
object.material = this.lightMapMaterial;
object.frustumCulled = false;
object.renderOrder = 1e3 + i;
this._scene.attach(object);
i++;
}
}
_moveLights() {
const lightRadius = this._params.lightRadius;
for (const light of this.lights) {
const state = this._lightMatrixStateByLight.get(light);
if (state) {
const position = state.position;
light.position.x = position.x + lightRadius * (Math.random() - 0.5);
light.position.y = position.y + lightRadius * (Math.random() - 0.5);
light.position.z = position.z + lightRadius * (Math.random() - 0.5);
}
}
}
restoreState() {
this._restoreObjectsState();
this._restoreLightsState();
this.renderer.setRenderTarget(this._previousRenderTarget);
}
_invertObjects() {
for (const object of this.objectTargets) {
invertNormals(object);
}
}
_restoreObjectsState() {
for (const object of this.objectTargets) {
const state = this._objectStateByObject.get(object);
if (state) {
object.frustumCulled = state.frustumCulled;
object.renderOrder = state.renderOrder;
object.material = state.material;
const parent = state.parent;
if (parent) {
parent.add(object);
}
}
}
}
_restoreLightsState() {
var _a;
for (const light of this.lights) {
const stateH = this._lightHierarchyStateByLight.get(light);
const stateM = this._lightMatrixStateByLight.get(light);
if (stateH && stateM) {
light.matrixAutoUpdate = stateH.matrixAutoUpdate;
light.matrix.copy(stateM.matrix);
light.matrix.decompose(light.position, light.quaternion, light.scale);
light.updateMatrix();
(_a = stateH.parent) == null ? void 0 : _a.attach(light);
}
}
}
runUpdates(camera) {
const totalIterationsCount = this._params.totalIterationsCount;
this.blurMaterial.uniforms.pixelOffset.value = this._params.blurAmount / this._params.resolution;
this.blurPlane.visible = this._params.blur;
this.lightMapMaterial.uniforms.lightMapMult.value = 1 / totalIterationsCount;
this.lightMapMaterial.side = FrontSide;
this.lightMapMaterial.shadowSide = null;
this.renderFlippedUvs = false;
this._clear(camera);
for (let i = 0; i < totalIterationsCount; i++) {
this._moveLights();
this._update(camera);
}
this.renderFlippedUvs = true;
this._clear(camera);
this._invertObjects();
this.lightMapMaterial.side = [DoubleSide, BackSide][1];
this.lightMapMaterial.shadowSide = BackSide;
for (let i = 0; i < totalIterationsCount; i++) {
this._moveLights();
this._update(camera);
}
this._invertObjects();
this._clearScene();
this._scene.add(this.renderTargetsCombinePlane);
setRenderTargetsCombineMaterial(this.renderTargetsCombineMaterial, {
rt1: this.flippedRenderTargetPair.current(),
rt2: this.nonFlippedRenderTargetPair.current()
});
this.renderer.setRenderTarget(this.finalRenderTarget);
this.renderer.render(this._scene, camera);
}
_clear(camera) {
this._scene.visible = false;
this._update(camera);
this._update(camera);
this._scene.visible = true;
}
_update(camera) {
const rtPair = this.renderTargetPair();
const activeMap = rtPair.current();
const inactiveMap = rtPair.previous();
this.renderer.setRenderTarget(activeMap);
setLightMapMaterial(this.lightMapMaterial, {
lightMap: inactiveMap
// lightMapMult:1 / totalIterationsCount,
});
setBlurMaterial(this.blurMaterial, {
res: this._params.resolution,
lightMap: inactiveMap
});
rtPair.toggle();
this.renderer.render(this._scene, camera);
}
}