@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
178 lines (132 loc) • 5.35 kB
JavaScript
import { BufferGeometry, Float32BufferAttribute, Line, LineBasicMaterial } from "three";
import { SurfacePoint3 } from "../../../src/core/geom/3d/SurfacePoint3.js";
import Vector3 from "../../../src/core/geom/Vector3.js";
import { max2 } from "../../../src/core/math/max2.js";
import { min2 } from "../../../src/core/math/min2.js";
import { ProcessState } from "../../../src/core/process/ProcessState.js";
import Task from "../../../src/core/process/task/Task.js";
import { TaskSignal } from "../../../src/core/process/task/TaskSignal.js";
import Entity from "../../../src/engine/ecs/Entity.js";
import { EventType } from "../../../src/engine/ecs/EventType.js";
import Renderable from "../../../src/engine/ecs/renderable/Renderable.js";
import { obtainTerrain } from "../../../src/engine/ecs/terrain/util/obtainTerrain.js";
import { Transform } from "../../../src/engine/ecs/transform/Transform.js";
import GridPosition from "../../../src/engine/grid/position/GridPosition.js";
import EditorEntity from "../../ecs/EditorEntity.js";
import { make3DSymbolicDisplay } from "./make3DSymbolicDisplay.js";
/**
*
* @param {Engine} engine
*/
export function makeGridPositionSymbolDisplay(engine) {
/**
*
* @type {EntityManager}
*/
const em = engine.entityManager;
const updateQueue = [];
const tTerrainWaiter = new Task({
name: 'terrain-waiter',
cycleFunction() {
if (updateQueue.length === 0) {
return TaskSignal.Yield;
}
const f = updateQueue.shift();
f();
return TaskSignal.Continue;
}
});
/**
*
* @param {GridPosition} gridPosition
* @param {Transform} transform
* @returns {Entity}
*/
function makeHelper(gridPosition, transform) {
const builder = new Entity();
// TODO replace with {@link VisualSymbolLine}
const lineMaterial = new LineBasicMaterial({ color: 0xFFFFFF });
lineMaterial.depthTest = false;
const lineGeometry = new BufferGeometry();
const positionAttribute = new Float32BufferAttribute(new Float32Array(6), 3);
lineGeometry.setAttribute('position', positionAttribute);
//find terrain
const terrain = obtainTerrain(em.dataset);
const line = new Line(lineGeometry, lineMaterial);
line.updateMatrixWorld();
line.frustumCulled = false;
const renderable = new Renderable(line);
renderable.matrixAutoUpdate = false;
const contact = new SurfacePoint3();
const p0 = transform.position;
const p1 = new Vector3();
/**
*
* @returns {boolean}
*/
function updateGridPosition() {
//get grid position in the world
terrain.mapPointGrid2World(gridPosition.x, gridPosition.y, p1);
return terrain.raycastFirstSync(contact, p1.x, -(terrain.heightRange + 1), p1.z, 0, 1, 0);
}
function updateGeometry() {
const c = contact.position;
positionAttribute.setXYZ(0, p0.x, p0.y, p0.z);
positionAttribute.setXYZ(1, c.x, c.y, c.z);
positionAttribute.needsUpdate = true;
}
function updateBounds() {
const c = contact.position;
const x0 = min2(p0.x, c.x),
y0 = min2(p0.y, c.y),
z0 = min2(p0.z, c.z),
x1 = max2(p0.x, c.x),
y1 = max2(p0.y, c.y),
z1 = max2(p0.z, c.z);
renderable.boundingBox.setBounds(x0, y0, z0, x1, y1, z1);
renderable.bvh.resize(x0, y0, z0, x1, y1, z1);
}
function attemptUpdate() {
if (updateGridPosition()) {
updateGeometry();
updateBounds();
} else if (updateQueue.indexOf(attemptUpdate) === -1) {
updateQueue.push(attemptUpdate);
}
}
attemptUpdate();
builder
.add(renderable)
.add(new Transform())
.add(new EditorEntity());
builder.addEventListener(EventType.EntityRemoved, () => {
p0.onChanged.remove(attemptUpdate);
gridPosition.onChanged.remove(attemptUpdate);
});
builder.on.built.add(() => {
p0.onChanged.add(attemptUpdate);
gridPosition.onChanged.add(attemptUpdate);
attemptUpdate();
});
return builder;
}
const display = make3DSymbolicDisplay({
engine,
components: [GridPosition, Transform],
factory([gridPosition, transform, entity], api) {
api.emit(makeHelper(gridPosition, transform));
}
});
display.__state.onChanged.add((s0, s1) => {
if (s0 === ProcessState.Running) {
//started
engine.executor.run(tTerrainWaiter);
} else if (s1 === ProcessState.Running) {
//stopepd
engine.executor.removeTask(tTerrainWaiter);
//purge update queue
updateQueue.splice(0, updateQueue.length);
}
});
return display;
}