UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

350 lines (267 loc) • 9.87 kB
import { BinaryDataType } from "../../src/core/binary/type/BinaryDataType.js"; import { compute_typed_array_constructor_from_data_type } from "../../src/core/binary/type/DataType2TypedArrayConstructorMapping.js"; import { Cache } from "../../src/core/cache/Cache.js"; import Signal from "../../src/core/events/signal/Signal.js"; import { noop } from "../../src/core/function/noop.js"; import { strictEquals } from "../../src/core/function/strictEquals.js"; import { computeStringHash } from "../../src/core/primitives/strings/computeStringHash.js"; import { ArrayBufferLoader } from "../../src/engine/asset/loaders/ArrayBufferLoader.js"; import { TextureAssetLoader } from "../../src/engine/asset/loaders/texture/TextureAssetLoader.js"; import { TerrainLayer } from "../../src/engine/ecs/terrain/ecs/layers/TerrainLayer.js"; import { TerrainFlags } from "../../src/engine/ecs/terrain/ecs/TerrainFlags.js"; import { obtainTerrain } from "../../src/engine/ecs/terrain/util/obtainTerrain.js"; import { EngineConfiguration } from "../../src/engine/EngineConfiguration.js"; import { EngineHarness } from "../../src/engine/EngineHarness.js"; import { load_and_set_cubemap_v0 } from "../../src/engine/graphics/load_and_set_cubemap_v0.js"; import CheckersTextureURI from "../../src/engine/graphics/texture/CheckersTextureURI.js"; import { sampler2d_scale } from "../../src/engine/graphics/texture/sampler/resize/sampler2d_scale.js"; import { Sampler2D } from "../../src/engine/graphics/texture/sampler/Sampler2D.js"; import sampler2d_to_html_canvas from "../../src/engine/graphics/texture/sampler/sampler2d_to_html_canvas.js"; import { sampler2d_transfer_data } from "../../src/engine/graphics/texture/sampler/sampler2d_transfer_data.js"; import { NativeListController } from "../../src/view/controller/controls/NativeListController.js"; import { CanvasView } from "../../src/view/elements/CanvasView.js"; import EmptyView from "../../src/view/elements/EmptyView.js"; const engineHarness = new EngineHarness(); /** * * @param {Engine} engine */ function makeConfig(engine) { const config = new EngineConfiguration(); config.addLoader('arraybuffer', new ArrayBufferLoader()); config.addLoader('texture', new TextureAssetLoader()); return config; } class EditorTextureSlot { constructor() { this.onChanged = new Signal(); this.sampler = null; this.__read = noop; this.__write = noop; /** * * @type {number} */ this.itemSize = 1; } read() { this.__read(this.sampler); this.onChanged.send0(); } write() { this.__write(this.sampler); } /** * * @param {(Sampler2D)=>any} read * @param {(Sampler2D)=>any} write * @param {number} [itemSize] * @param {BinaryDataType} [dataType] * @return {EditorTextureSlot} */ static from({ read, write, itemSize = 1, dataType = BinaryDataType.Uint8 }) { const r = new EditorTextureSlot(); r.__read = read; r.__write = write; const TA = compute_typed_array_constructor_from_data_type(dataType); r.sampler = new Sampler2D(new TA(itemSize), itemSize, 1, 1); return r; } } class TextureLayerEditor { constructor() { this.index = 0; /** * * @type {Terrain|null} * @private */ this.__terrain = null; this.__splat = EditorTextureSlot.from({ read: (target) => { const terrain = this.__terrain; target.resize(terrain.splat.size.x, terrain.splat.size.y) terrain.splat.readLayerToSampler(target, this.index, 0); }, write: (source) => { this.__terrain.splat.writeLayerFromSampler(source, this.index, 0); }, itemSize: 1 }); this.__diffuse = EditorTextureSlot.from({ read: (target) => { const layers = this.__terrain.layers; const layer = layers.get(this.index); target.resize(layer.diffuse.width, layer.diffuse.height); sampler2d_transfer_data(layer.diffuse, target); }, write: (source) => { const layer = this.__terrain.layers.get(this.index); sampler2d_transfer_data(source, layer.diffuse); this.__terrain.layers.writeLayerDataIntoTexture(this.index); }, itemSize: 3 }); } /** * @param {Terrain} t */ set terrain(t) { this.__terrain = t; } init() { this.__splat.read(); this.__diffuse.read(); } } /** * * @param {EditorTextureSlot} slot */ function makeTextureSlotView({ slot }) { const preview = new CanvasView(); preview.size.set(64, 64); function update() { sampler2d_to_html_canvas(slot.sampler, 1, 0, preview.el); } preview.on.linked.add(update); preview.bindSignal(slot.onChanged, update); return preview; } /** * * @param {View} root * @param {Terrain} terrain * @param {Engine} engine */ function makeTerrainEditor(root, terrain, engine) { /** * * @type {Cache<string, Sampler2D>} */ const PREVIEW_CACHE = new Cache({ keyHashFunction: computeStringHash, keyEqualityFunction: strictEquals }); const PREVIEW_SIZE = 64; /** * * @param {string} url * @return {Promise<Sampler2D>} */ async function getPreviewFromURL(url) { if (PREVIEW_CACHE.contains(url)) { return PREVIEW_CACHE.get(url); } const asset = await engine.assetManager.promise(url, 'image'); // check cache again, might have been loaded in the meanwhile if (PREVIEW_CACHE.contains(url)) { return PREVIEW_CACHE.get(url); } const source = asset.create(); const preview = Sampler2D.uint8(4, PREVIEW_SIZE, PREVIEW_SIZE); sampler2d_scale(source, preview); PREVIEW_CACHE.put(url, preview); return preview; } /** * * @type {EditorTextureSlot|null} */ let active_slot = null; function rebuild() { // rebuild terrain terrain.clearFlag(TerrainFlags.Built); terrain.build(engine.assetManager); } function build_splat_editor() { /** * * @type {Map<TerrainLayer, TextureLayerEditor>} */ const editors = new Map(); return new NativeListController({ model: terrain.layers.layers, elementFactory() { return TerrainLayer.from(CheckersTextureURI, 1, 1); }, /** * * @param {TerrainLayer} layer */ elementViewFactory(layer) { const e = new TextureLayerEditor(); editors.set(layer, e); const layer_index = terrain.layers.layers.indexOf(layer); e.terrain = terrain; e.index = layer_index; e.init(); const view = new EmptyView(); view.addChild(makeTextureSlotView({ slot: e.__diffuse })); view.addChild(makeTextureSlotView({ slot: e.__splat })); view.bindSignal(layer.onChanged, e.__diffuse.read, e.__diffuse); return view; }, operationAdd(list, el) { terrain.layers.addLayer(el); terrain.splat.addWeightLayer(); }, operationRemove(list, el) { const index = terrain.layers.layers.indexOf(el); terrain.layers.removeLayer(el); terrain.splat.removeWeightLayer(index); editors.delete(el); // re-shuffle remaining editors for (const [layer, editor] of editors) { if (editor.index > index) { editor.index--; editor.init(); } } } }); } root.addChild(build_splat_editor()); } /** * * @param {Engine} engine * @return {string} */ async function main(engine) { //disable drag and drop on game canvas engine.graphics.domElement.addEventListener('dragover', (e) => e.preventDefault()); engine.graphics.domElement.addEventListener('drop', (e) => e.preventDefault()); EngineHarness.buildLights({ engine }); const camera = EngineHarness.buildCamera({ engine }); EngineHarness.buildOrbitalCameraController({ engine, cameraEntity: camera.id }) await EngineHarness.buildTerrain({ engine }); const vMain = new EmptyView({ classList: ['ui-texture-testing-tool-view'] }); vMain.css({ position: "absolute", left: "0", top: "0", pointerEvents: "initial" }); makeTerrainEditor(vMain, obtainTerrain(engine.entityManager.dataset), engine); document.body.appendChild(vMain.el); vMain.link(); } /** * * @param {EngineHarness} harness */ async function init(harness) { const engine = harness.engine; await makeConfig(engine).apply(engine); await load_and_set_cubemap_v0(engine.graphics, 'data/textures/cubemaps/hip_miramar/32/', '.png'); await harness.initialize(); main(engine); } init(engineHarness);