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