UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

188 lines (140 loc) 5.14 kB
import { MinimapWorldLayer } from "./MinimapWorldLayer.js"; import { FogOfWar } from "../../../engine/ecs/fow/FogOfWar.js"; import { Mesh, PlaneBufferGeometry } from "three"; import { buildScreenSpaceFogOfWarShader } from "../../../engine/ecs/fow/shader/screenSpaceFogOfWarShader.js"; import { computeUvTransformFromFogOfWar } from "../../../engine/ecs/fow/shader/FogOfWarRenderer.js"; import { SignalBinding } from "../../../core/events/signal/SignalBinding.js"; import AABB2 from "../../../core/geom/2d/aabb/AABB2.js"; /** * * @param {number} viewportWidth * @param {number} viewportHeight * @param {AABB2} aabb2 */ function enforceAspectRatio(viewportWidth, viewportHeight, aabb2) { const viewportAspectRatio = viewportHeight / viewportWidth; const aabbHeight = aabb2.getHeight(); const aabbWidth = aabb2.getWidth(); if (aabbWidth !== 0) { const aabbAspectRatio = aabbHeight / aabbWidth; const aspectRatioDelta = viewportAspectRatio - aabbAspectRatio; if (aspectRatioDelta > 0) { const d = aabbWidth * aspectRatioDelta; aabb2.y0 -= d / 2; aabb2.y1 += d / 2; } else { const d = -aabbWidth * aspectRatioDelta; aabb2.x0 -= d / 2; aabb2.x1 += d / 2; } } } export class MinimapFogOfWar extends MinimapWorldLayer { /** * * @param {EntityComponentDataset} ecd * @param {Rectangle} focusArea */ constructor(ecd, focusArea) { super(); /** * * @type {EntityComponentDataset} */ this.dataset = ecd; /** * * @type {FogOfWar|null} */ this.fow = null; this.material = buildScreenSpaceFogOfWarShader(); this.object = new Mesh(new PlaneBufferGeometry(1, 1, 1, 1), this.material); this.object.frustumCulled = false; /** * * @type {Rectangle} */ this.focusArea = focusArea; this.controlFocusArea = true; this.focusAreaNeedsUpdate = true; this.signalBindings = []; } updateFocusArea() { const bounds = new AABB2(); if (this.fow === null) { //no Fog of War return; } this.fow.computeRevealedGridBoundingRectangle(bounds); const PADDING = 2; bounds.grow(PADDING); bounds.move(-0.5, +0.5); const scale = this.fow.scale.getValue(); const fowSizeX = this.fow.size.x; const fowSizeY = this.fow.size.y; const scaleX = scale * (fowSizeX - 1) / fowSizeX; const scaleY = scale * (fowSizeY - 1) / fowSizeY; bounds.x0 *= scaleX; bounds.x1 *= scaleX; bounds.y0 *= scaleY; bounds.y1 *= scaleY; //we need to square the bounds enforceAspectRatio(this.viewportSize.x, this.viewportSize.y, bounds); this.focusArea.set(bounds.x0, bounds.y0, bounds.getWidth(), bounds.getHeight()); this.focusAreaNeedsUpdate = false; } startup() { const self = this; /** * * @param {FogOfWar} fow */ function visitFoW(fow) { self.fow = fow; return false; } this.dataset.traverseComponents(FogOfWar, visitFoW); const fow = this.fow; if (fow !== null) { this.signalBindings.push(new SignalBinding(fow.on.textureChanged, () => { this.material.uniforms.tFog.value = fow.texture; this.material.uniforms.uResolution.value.set(fow.size.x, fow.size.y); this.needsRender = true; this.focusAreaNeedsUpdate = true; })); } this.signalBindings.forEach(b => b.link()); this.needsRender = true; } shutdown() { this.fow = null; this.signalBindings.forEach(b => b.unlink()); this.signalBindings = []; } /** * * @param {OrthographicCamera} camera */ update(camera) { if (this.fow === null) { //no Fog of War return; } if (this.controlFocusArea && this.focusAreaNeedsUpdate) { this.updateFocusArea(); } const material = this.material; const fow = this.fow; if (fow !== null) { const uniforms = material.uniforms; uniforms.tFog.value = fow.texture; uniforms.uResolution.value.set(fow.size.x, fow.size.y); uniforms.uProjectionInverse.value.copy(camera.projectionMatrixInverse); uniforms.uViewInverse.value.copy(camera.matrixWorld); uniforms.uColor.value.copy(fow.color); computeUvTransformFromFogOfWar(fow, function (offsetX, offsetY, scaleX, scaleY) { uniforms.uFogUvTransform.value.set(offsetX, offsetY, scaleX, scaleY); }); } } }