UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

278 lines (194 loc) • 6.74 kB
import { BackSide, PlaneBufferGeometry } from 'three'; import { BVH } from "../../../../core/bvh2/bvh3/BVH.js"; import { System } from '../../../ecs/System.js'; import { obtainTerrain } from "../../../ecs/terrain/util/obtainTerrain.js"; import { make_bvh_visibility_builder } from "../../render/make_bvh_visibility_builder.js"; import { StandardFrameBuffers } from "../../StandardFrameBuffers.js"; import ThreeFactory from "../../three/ThreeFactory.js"; import { makeAlexWaterMaterial } from "../water2/shader/AlexWaterShader.js"; import Water from './Water.js'; import { WATER_SIZE } from "./WATER_SIZE.js"; class WaterSystem extends System { dependencies = [Water]; /** * @readonly * @type {BVH} */ bvh = new BVH(); cleaup = []; /** * * @type {Water[]} */ updateQueue = []; /** * * @type {number} * @private */ __time_delta = 0; /** * * @param {GraphicsEngine} graphics * @constructor */ constructor(graphics) { super(); this.graphicsEngine = graphics; /** * * @type {RenderLayer|null} */ this.renderLayer = null; } async shutdown(entityManager) { this.graphicsEngine.size.onChanged.remove(this.setViewportSize); this.graphicsEngine.layers.remove(this.renderLayer); } async startup(entityManager) { this.entityManager = entityManager; const graphicsEngine = this.graphicsEngine; this.renderLayer = graphicsEngine.layers.create(WaterSystem.RENDER_LAYER_NAME); const viewport_size = graphicsEngine.viewport.size; this.renderLayer.buildVisibleSet = make_bvh_visibility_builder( this.entityManager, this.bvh, (destination, offset, entity, ecd) => { const component = ecd.getComponent(entity, Water); const mesh = component.__threeObject; if (mesh === null) { return 0 } destination[offset] = mesh; return 1; } ) const self = this; /** * * @param {WebGLRenderer} renderer * @param camera * @param scene */ function preRenderHook(renderer, camera, scene) { const em = self.entityManager; const dataset = em.dataset; if (dataset === null) { return; } const pixelRatio = graphicsEngine.computeTotalPixelRatio(); dataset.traverseComponents(Water, function (component, entity) { const shader = component.__shader; shader.uniforms.fCameraNear.value = camera.near; shader.uniforms.fCameraFar.value = camera.far; shader.uniforms.vScreenResolution.value.set(viewport_size.x * pixelRatio, viewport_size.y * pixelRatio); }); } graphicsEngine.on.preRender.add(preRenderHook); } /** * * @param {Water} component * @param {number} entity */ link(component, entity) { const frameBuffer = this.graphicsEngine.frameBuffers.getById(StandardFrameBuffers.ColorAndDepth); /** * @type {ShaderMaterial} */ let water; if (component.__shader === null) { water = makeAlexWaterMaterial(); water.uniforms.tDepthTexture.value = frameBuffer.renderTarget.depthTexture; component.__shader = water; component.writeShaderUniforms(); } else { water = component.__shader; } // water.level.value = component.level.getValue(); const width = WATER_SIZE; const height = WATER_SIZE; // water.textureRepeat.value.set(width / 6, height / 6); water.side = BackSide; if (component.__threeObject === null) { const geometry = new PlaneBufferGeometry(width, height, 1, 1); const mesh = ThreeFactory.createMesh( geometry, water ); component.__threeObject = mesh; } component.updateTransform(); component.level.onChanged.add(component.updateTransform, component); component.bvh.link(this.bvh, entity); this.cleaup[entity] = function () { component.level.onChanged.remove(component.updateTransform, component); }; this.updateQueue.push(component); } /** * * @param {Water} component * @param {number} entity */ unlink(component, entity) { component.bvh.unlink(); const cleaup = this.cleaup[entity]; cleaup(); delete this.cleaup[entity]; //remove from update queue const i = this.updateQueue.indexOf(component); if (i !== -1) { this.updateQueue.splice(i, 1); } } processUpdateQueue() { //do updates const updateQueue = this.updateQueue; let l = updateQueue.length; if (l === 0) { return; } const dataset = this.entityManager.dataset; if (dataset === null) { return; } const terrain = obtainTerrain(dataset); if (terrain === null) { return; } for (let i = 0; i < l; i++) { const water = updateQueue[i]; water.updateShaderForTerrain(terrain, WATER_SIZE); updateQueue.splice(i, 1); i--; l--; } } /** * * @param {Water} component * @param {number} entity * @private */ __visit_component(component, entity) { const material = component.__shader; if (material === undefined) { console.error('Material undefined on WaterComponent', entity, component); //skip return; } material.uniforms.time.value += this.__time_delta; } update(timeDelta) { this.__time_delta = timeDelta; const em = this.entityManager; const dataset = em.dataset; if (dataset === null) { return; } this.processUpdateQueue(); dataset.traverseComponents(Water, this.__visit_component, this); } } WaterSystem.RENDER_LAYER_NAME = 'water-system'; export default WaterSystem;