UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

267 lines (199 loc) • 8.07 kB
import { Frustum } from "three"; import Vector3 from "../../../core/geom/Vector3.js"; import Vector4 from "../../../core/geom/Vector4.js"; import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js"; import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js"; import { CompositingStages } from "../../graphics/composit/CompositingStages.js"; import { CompositLayer } from "../../graphics/composit/CompositLayer.js"; import { Camera } from "../../graphics/ecs/camera/Camera.js"; import { compute_perspective_camera_focal_position } from "../../graphics/ecs/camera/compute_perspective_camera_focal_position.js"; import { frustum_from_camera } from "../../graphics/ecs/camera/frustum_from_camera.js"; import WaterSystem from "../../graphics/ecs/water/WaterSystem.js"; import { VisibilityFilter } from "../../graphics/render/visibility/VisibilityFilter.js"; import { StandardFrameBuffers } from "../../graphics/StandardFrameBuffers.js"; import { BlendingType } from "../../graphics/texture/sampler/BlendingType.js"; import { System } from "../System.js"; import Terrain from "../terrain/ecs/Terrain.js"; import { FogOfWar } from "./FogOfWar.js"; import { FogOfWarRenderer } from "./shader/FogOfWarRenderer.js"; const frustum = new Frustum(); export class FogOfWarSystem extends System { /** * * @param {GraphicsEngine} graphics */ constructor(graphics) { super(); /** * * @type {GraphicsEngine} */ this.graphics = graphics; this.dependencies = [FogOfWar, Terrain]; this.components_used = [ ResourceAccessSpecification.from(FogOfWar, ResourceAccessKind.Read | ResourceAccessKind.Write) ]; this.visibilityFilter = this.buildVisibilityFilter(); //turn off by default this.visibilityFilter.enabled = false; /** * * @type {CompositLayer} */ this.compositLayer = null; this.componentCount = 0; } /** * * @returns {VisibilityFilter} */ buildVisibilityFilter() { /** * * @type {FogOfWar|null} */ let fog = null; const projectionPlane = new Vector4(); const cameraFocalPoint = new Vector3(); const result = new VisibilityFilter({ name: 'fog of war', layerPredicate: (layer) => { return (layer !== this.renderLayer) && (layer.name !== WaterSystem.RENDER_LAYER_NAME); }, objectPredicateInitialize: (camera) => { compute_perspective_camera_focal_position(camera, cameraFocalPoint); fog = null; const dataset = this.entityManager.dataset; if (dataset !== null) { dataset.traverseComponents(FogOfWar, (fow) => { fog = fow; // stop traversal return false; }); //find current projection plane dataset.traverseComponents(Camera, c => { if (c.active) { frustum_from_camera(c.object, frustum); const nearPlane = frustum.planes[4]; const ppN = nearPlane.normal; projectionPlane.set(ppN.x, ppN.y, ppN.z, nearPlane.constant); return false; } }); } }, objectPredicateFinalize: () => { fog = null; }, /** * * @param {AABB3} aabb * @returns {boolean} */ objectPredicateExecute: function (aabb) { return fog.computeAABBVisibility(aabb, cameraFocalPoint, 1); } }); return result; } /** * * @param {EntityManager} entityManager */ async startup(entityManager) { this.entityManager = entityManager; const graphics = this.graphics; const compositerLayer = graphics.layerComposer.createLayer({}, BlendingType.Normal); compositerLayer.name = 'Fog of War'; compositerLayer.stage = CompositingStages.POST_TRANSPARENT; // Fog of war should be drawn last typically, over everything else, so we set order to a large number compositerLayer.order = 1000; compositerLayer.enabled = false; this.compositLayer = compositerLayer; const renderTarget = compositerLayer.renderTarget; this.renderTarget = renderTarget; const fogOfWarRenderer = new FogOfWarRenderer(); // renderer.visibilitySet.addFilter(this.visibilityFilter); let renderingFog = false; graphics.on.preRender.add(function (renderer, camera, scene) { const dataset = entityManager.dataset; const depthFrameBuffer = graphics.frameBuffers.getById(StandardFrameBuffers.ColorAndDepth); renderingFog = false; /** * * @param {FogOfWar} fow */ function drawFow(fow) { renderingFog = true; fogOfWarRenderer.setDepthBuffer(depthFrameBuffer.renderTarget.depthTexture); fogOfWarRenderer.setFogBuffer(fow.texture); fogOfWarRenderer.setResolution(fow.size.x, fow.size.y); fogOfWarRenderer.setUvTransformFromFog(fow); fogOfWarRenderer.setFogColor(fow.color); //stop traversal return false; } if (dataset !== null) { dataset.traverseComponents(FogOfWar, drawFow); if (!renderingFog) { compositerLayer.disable(); } else { compositerLayer.enable(); depthFrameBuffer.referenceCount++; } } }); graphics.on.buffersRendered.add(function (renderer, camera, scene) { if (renderingFog) { fogOfWarRenderer.render(renderer, camera, scene, compositerLayer.renderTarget); } }); graphics.on.postRender.add(function (renderer, camera, scene) { if (renderingFog) { const depthFrameBuffer = graphics.frameBuffers.getById(StandardFrameBuffers.ColorAndDepth); depthFrameBuffer.referenceCount--; } }); } async shutdown(entityManager) { this.graphics.layerComposer.remove(this.compositLayer); } /** * * @param {FogOfWar} fow * @param {Terrain} terrain * @param {number} entity */ link(fow, terrain, entity) { /** * * @type {Vector2} */ const terrainSize = terrain.size; fow.resize(terrainSize.x, terrainSize.y, terrain.gridScale); this.componentCount++; if (!this.visibilityFilter.enabled) { this.visibilityFilter.enabled = true; } this.compositLayer.enabled = true; } unlink(fow, terrain, entity) { this.componentCount--; if (this.componentCount <= 0) { //disable visibility filter this.visibilityFilter.enabled = false; this.compositLayer.enabled = false; } } update(timeDelta) { const entityManager = this.entityManager; const dataset = entityManager.dataset; if (dataset !== null) { dataset.traverseComponents(FogOfWar, function (fow) { fow.update(timeDelta); }); } } }