UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

160 lines (147 loc) 5.93 kB
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; } }