@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
160 lines (147 loc) • 5.93 kB
JavaScript
import {
CubeCamera,
LinearEncoding,
LinearFilter,
MeshStandardMaterial,
RGBAFormat,
Scene,
VSMShadowMap,
WebGLCubeRenderTarget
} from "three";
import { assert } from "../../../../core/assert.js";
import { array_copy } from "../../../../core/collection/array/array_copy.js";
import { Transform } from "../../../ecs/transform/Transform.js";
import { three_set_shadow_resolution } from "../../ecs/light/binding/three/ThreeLightBinding.js";
import { threeMakeLight } from "../../ecs/light/binding/three/threeMakeLight.js";
import { Light } from "../../ecs/light/Light.js";
import { ThreeLightCache } from "../../ecs/light/three/ThreeLightCache.js";
import { build_three_object } from "../../ecs/mesh-v2/build_three_object.js";
import { ShadedGeometry } from "../../ecs/mesh-v2/ShadedGeometry.js";
import { applyTransformToThreeObject } from "../../ecs/mesh/applyTransformToThreeObject.js";
import { WebGLRendererPool } from "../../render/RendererPool.js";
import { fromCubeRenderTarget } from "../fromCubeRenderTarget.js";
import { ProbeRenderer } from "./ProbeRenderer.js";
export class WebGLCubeProbeRenderer extends ProbeRenderer {
constructor(resolution = 32) {
super();
assert.isNonNegativeInteger(resolution, 'resolution');
/**
*
* @type {THREE.WebGLRenderer|null}
* @private
*/
this.__renderer = null;
this.render_target = new WebGLCubeRenderTarget(resolution, {
format: RGBAFormat,
minFilter: LinearFilter,
magFilter: LinearFilter,
depthBuffer: true,
scissorTest: false,
stencilBuffer: false,
generateMipmaps: false,
encoding: LinearEncoding
});
/**
* Used to read out GPU memory for each face
* @type {Uint8Array}
* @private
*/
this.__cube_face_raw = new Uint8Array(this.render_target.width * this.render_target.width * 4);
this.__cube_camera = new CubeCamera(0.1, 100, this.render_target);
/**
*
* @type {Scene}
* @private
*/
this.__scene = new Scene();
}
/**
*
* @param {EntityComponentDataset} ecd
*/
build_scene(ecd) {
this.__scene.children.splice(0, this.__scene.children.length);
ecd.traverseEntities([ShadedGeometry, Transform], (sg, t, entity) => {
const object3D = build_three_object(sg);
const source_material = sg.material;
//
if (source_material.isMeshStandardMaterial === true) {
object3D.material = new MeshStandardMaterial({
map: source_material.map,
});
object3D.material.color.copy(source_material.color);
} else {
object3D.material = source_material.clone();
}
applyTransformToThreeObject(object3D, t);
array_copy(t.matrix, 0, object3D.matrixWorld.elements, 0, 16);
this.__scene.add(object3D);
});
const lightCache = new ThreeLightCache();
ecd.traverseEntities([Light, Transform], (light, t) => {
/**
*
* @type {DirectionalLight|SpotLight|PointLight|AmbientLight}
*/
const object = threeMakeLight(light, lightCache);
applyTransformToThreeObject(object, t);
const shadow = object.shadow;
if (shadow !== undefined) {
shadow.radius = 1;
shadow.blurSamples = 8;
three_set_shadow_resolution(shadow, 2048);
}
this.__scene.add(object);
});
// this.__scene.add(this.__cube_camera);
}
bake_start() {
const renderer = WebGLRendererPool.global.get();
this.__renderer = renderer;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = VSMShadowMap;
// this.__renderer = this.ge.getRenderer();
}
bake_end() {
WebGLRendererPool.global.release(this.__renderer);
this.__renderer = null;
}
/**
*
* @param {number[]} position
* @param {number} position_offset
* @param {number[]} output
* @param {number} output_offset
*/
bake(position, position_offset, output, output_offset) {
// console.warn(position_offset, output_offset);
// const ge = this.ge;
const renderer = this.__renderer;
const _rt = renderer.getRenderTarget();
const _rt_active_face = renderer.getActiveCubeFace();
const _acc = renderer.autoClearColor;
const _acd = renderer.autoClearDepth;
const _ac = renderer.autoClear;
const _outputEncoding = renderer.outputEncoding;
renderer.outputEncoding = LinearEncoding;
renderer.autoClearColor = true;
renderer.autoClearDepth = true;
renderer.autoClear = true;
this.__cube_camera.position.fromArray(position, position_offset);
this.__cube_camera.children.forEach(c => {
c.updateProjectionMatrix();
c.updateMatrix();
c.updateMatrixWorld(true);
});
this.__cube_camera.update(renderer, this.__scene);
// compute coefficients from renderer
const probe_coefficients = fromCubeRenderTarget(this.__cube_face_raw, renderer, this.render_target);
array_copy(probe_coefficients, 0, output, output_offset, 27);
// restore state
renderer.setRenderTarget(_rt, _rt_active_face);
renderer.autoClearColor = _acc;
renderer.autoClearDepth = _acd;
renderer.autoClear = _ac;
renderer.outputEncoding = _outputEncoding;
}
}