UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

295 lines (240 loc) • 10.1 kB
import { EngineConfiguration } from "../../src/engine/EngineConfiguration.js"; import { EngineHarness } from "../../src/engine/EngineHarness.js"; import { GameAssetType } from "../../src/engine/asset/GameAssetType.js"; import { TextureAssetLoader } from "../../src/engine/asset/loaders/texture/TextureAssetLoader.js"; import Vector3 from "../../src/core/geom/Vector3.js"; import Entity from "../../src/engine/ecs/Entity.js"; import Terrain from "../../src/engine/ecs/terrain/ecs/Terrain.js"; import { ImageRGBADataLoader } from "../../src/engine/asset/loaders/image/ImageRGBADataLoader.js"; import TerrainSystem from "../../src/engine/ecs/terrain/ecs/TerrainSystem.js"; import WaterSystem from "../../src/engine/graphics/ecs/water/WaterSystem.js"; import Water from "../../src/engine/graphics/ecs/water/Water.js"; import { ThemeEngine } from "../../src/generation/theme/ThemeEngine.js"; import { Theme } from "../../src/generation/theme/Theme.js"; import { TerrainTheme } from "../../src/generation/theme/TerrainTheme.js"; import { AreaTheme } from "../../src/generation/theme/AreaTheme.js"; import { GridData } from "../../src/generation/grid/GridData.js"; import { TerrainLayerRule } from "../../src/generation/theme/TerrainLayerRule.js"; import { TerrainLayerDescription } from "../../src/generation/theme/TerrainLayerDescription.js"; import Vector2 from "../../src/core/geom/Vector2.js"; import { CellFilterLiteralFloat } from "../../src/generation/filtering/numeric/CellFilterLiteralFloat.js"; import { GridDataLayer } from "../../src/generation/grid/layers/GridDataLayer.js"; import { CellFilterSampleLayerLinear } from "../../src/generation/filtering/numeric/sampling/CellFilterSampleLayerLinear.js"; import { CellFilterSmoothStep } from "../../src/generation/filtering/numeric/math/CellFilterSmoothStep.js"; import { CellFilterOneMinus } from "../../src/generation/filtering/numeric/math/CellFilterOneMinus.js"; import { CellFilterMultiply } from "../../src/generation/filtering/numeric/math/algebra/CellFilterMultiply.js"; import { CellFilterCurvature } from "../../src/generation/filtering/numeric/complex/CellFilterCurvature.js"; import { CellFilterMax2 } from "../../src/generation/filtering/numeric/math/CellFilterMax2.js"; import { CellFilterMin2 } from "../../src/generation/filtering/numeric/math/CellFilterMin2.js"; import { AmbientOcclusionPostProcessEffect } from "../../src/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js"; import { CellFilterAngleToNormal } from "../../src/generation/filtering/numeric/complex/CellFilterAngleToNormal.js"; import { CellFilterCache } from "../../src/generation/filtering/numeric/CellFilterCache.js"; import { TaskLoadingScreen } from "../../src/view/task/TaskLoadingScreen.js"; const HEIGHT_RANGE = 100; const eh = new EngineHarness(); /** * * @param {Engine} engine * @return {EngineConfiguration} */ function makeConfig(engine) { const r = new EngineConfiguration(); // configure engine here, add systems, loaders etc. r.addLoader(GameAssetType.Texture, new TextureAssetLoader()); r.addLoader(GameAssetType.Image, new ImageRGBADataLoader()); r.addSystem(new TerrainSystem(engine.graphics, engine.assetManager)); r.addSystem(new WaterSystem(engine.graphics)); r.addPlugin(AmbientOcclusionPostProcessEffect); return r; } function makeTheme() { const theme = new Theme(); const terrainTheme = new TerrainTheme(); const height = CellFilterSampleLayerLinear.from('height'); const curvature = CellFilterCurvature.from(height); const slope = CellFilterAngleToNormal.from(height, Vector3.forward); const filterNotVerySharpSlope = CellFilterCache.from( CellFilterOneMinus.from( CellFilterSmoothStep.from( CellFilterLiteralFloat.from(Math.PI / 9), CellFilterLiteralFloat.from(Math.PI / 5), slope ) ) ); const main_dress_weights = CellFilterMin2.from( CellFilterSmoothStep.from( CellFilterLiteralFloat.from(0.125 * HEIGHT_RANGE), CellFilterLiteralFloat.from(0.175 * HEIGHT_RANGE), height ), CellFilterOneMinus.from(curvature) ); terrainTheme.rules.push( // RIVER TerrainLayerRule.from( CellFilterOneMinus.from( CellFilterSmoothStep.from( CellFilterLiteralFloat.from(0.115 * HEIGHT_RANGE), CellFilterLiteralFloat.from(0.125 * HEIGHT_RANGE), height ) ), TerrainLayerDescription.from({ diffuse: "data/textures/materials/water-pool-texture-seamless-and-free.jpg", size: new Vector2(100, 100) }) ), // WET AREAS TerrainLayerRule.from( CellFilterMax2.from( CellFilterMultiply.from( CellFilterSmoothStep.from( CellFilterLiteralFloat.from(0.125 * HEIGHT_RANGE), CellFilterLiteralFloat.from(0.15 * HEIGHT_RANGE), height ), CellFilterOneMinus.from( CellFilterSmoothStep.from( CellFilterLiteralFloat.from(0.175 * HEIGHT_RANGE), CellFilterLiteralFloat.from(0.2 * HEIGHT_RANGE), height ) ) ), CellFilterSmoothStep.from( CellFilterLiteralFloat.from(0.2), CellFilterLiteralFloat.from(0.5), curvature ) ), TerrainLayerDescription.from({ diffuse: "data/textures/materials/terrain_township_set/512/Ground_1_dark.png", size: new Vector2(40, 40) }) ), // TOP SOIL TerrainLayerRule.from( CellFilterMultiply.from( main_dress_weights, filterNotVerySharpSlope ), TerrainLayerDescription.from({ diffuse: "data/textures/materials/terrain_township_set/512/Grass_1.png", size: new Vector2(40, 40) }) ), // SLOPE SOIL TerrainLayerRule.from( CellFilterMultiply.from( main_dress_weights, CellFilterOneMinus.from(filterNotVerySharpSlope) ), TerrainLayerDescription.from({ diffuse: "data/textures/materials/terrain_township_set/512/Grass_3.png", size: new Vector2(40, 40) }) ) ); theme.terrain = terrainTheme; return theme; } /** * * @param {Engine} engine */ async function main(engine) { const TERRAIN_SIZE = 256; await EngineHarness.buildBasics({ engine, enableWater: false, enableTerrain: false, focus: new Vector3(250, 0, 250), yaw: -Math.PI, pitch: 0.91, distance: 770 }); /** * * @type {EntityComponentDataset} */ const ecd = engine.entityManager.dataset; const terrain = new Terrain(); terrain.size.set(TERRAIN_SIZE, TERRAIN_SIZE); terrain.resolution = 2; terrain.gridScale = 2; terrain.splat.resize(TERRAIN_SIZE, TERRAIN_SIZE, 1); terrain.splat.fillLayerWeights(0, 255); // load height map const asset = await engine.assetManager.promise('moicon/heightmap.png', 'image'); /** * * @type {Sampler2D} */ const sampler = asset.create(); terrain.samplerHeight.resize(sampler.width, sampler.height); const source_height_data = sampler.data; const sampler_pixel_count = source_height_data.length; const terrain_height_data = terrain.samplerHeight.data; let divisor; if (source_height_data instanceof Uint8Array) { divisor = 255; } else if (source_height_data instanceof Uint16Array) { divisor = 65536 - 1; } for (let i = 0; i < sampler_pixel_count; i++) { const source_v = source_height_data[i]; const b0 = source_v & 0xFF; const b1 = (source_v >> 8) & 0xFF; let rotated_v; rotated_v = b1 | (b0 << 8); if (rotated_v <= 7000) { rotated_v = 0; } terrain_height_data[i] = (rotated_v / divisor) * HEIGHT_RANGE } const grid = new GridData(); grid.resize(TERRAIN_SIZE, TERRAIN_SIZE); //inject height map into grid layer const layer = new GridDataLayer(); layer.id = 'height'; grid.addLayer(layer); layer.sampler = terrain.samplerHeight; const theme_engine = new ThemeEngine(); const areaTheme = new AreaTheme(); areaTheme.theme = makeTheme(); areaTheme.mask.resize(grid.width, grid.height); areaTheme.mask.mask.data.fill(255); areaTheme.mask.distanceField.data.fill(0); areaTheme.mask.bounds.set(0, 0, grid.width, grid.height); theme_engine.add(areaTheme); terrain.build(engine.assetManager); const water = new Water(); water.level.set(5); water.shoreDepthTransition.set(0.1, 0.3); water.scattering.set(3); new Entity() .add(terrain) // .add(water) .build(ecd); const task = theme_engine.apply(grid, ecd); const promise_load = TaskLoadingScreen.load(engine, task); engine.executor.runGroup(task); await promise_load; await terrain.buildLightMap(1); } /** * * @param {EngineHarness} harness */ async function init(harness) { const engine = eh.engine; await makeConfig(engine).apply(engine); await eh.initialize(); main(engine); } init(eh);