@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
278 lines (194 loc) • 6.74 kB
JavaScript
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;