@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
197 lines (140 loc) • 5.63 kB
JavaScript
import {
BufferGeometry,
Float32BufferAttribute,
Group,
Line,
LineBasicMaterial,
Mesh as ThreeMesh,
MeshLambertMaterial,
TetrahedronBufferGeometry
} from "three";
import Vector3 from "../../../core/geom/Vector3.js";
import Entity from "../../../engine/ecs/Entity.js";
import GUIElement from "../../../engine/ecs/gui/GUIElement.js";
import HeadsUpDisplay from "../../../engine/ecs/gui/hud/HeadsUpDisplay.js";
import ViewportPosition from "../../../engine/ecs/gui/position/ViewportPosition.js";
import Renderable from "../../../engine/ecs/renderable/Renderable.js";
import { obtainTerrain } from "../../../engine/ecs/terrain/util/obtainTerrain.js";
import { Transform } from "../../../engine/ecs/transform/Transform.js";
import LabelView from "../../../view/common/LabelView.js";
import { MarkerNodeMatcherAny } from "../matcher/MarkerNodeMatcherAny.js";
function formatValue(v) {
if (typeof v === "number" && v % 1 !== 0) {
return v.toPrecision(3);
}
return v;
}
function makeCircleGeometry(radius, arc) {
const geometry = new BufferGeometry();
const vertices = [];
for (let i = 0; i <= 64 * arc; ++i) {
vertices.push(Math.cos(i / 32 * Math.PI) * radius, 0, Math.sin(i / 32 * Math.PI) * radius);
}
geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3));
return geometry;
}
/**
*
* @param {GridData} grid
* @param {EntityComponentDataset} ecd
* @param {MarkerNodeMatcher} marker_filter
* @param {boolean} [display_data]
*/
export function visualizeMarkers({
grid,
ecd,
marker_filter = MarkerNodeMatcherAny.INSTANCE,
display_data = true
}) {
/**
*
* @type {MarkerNode[]}
*/
const markers = [];
grid.markers.getRawData(markers);
const n = markers.length;
const m0 = new MeshLambertMaterial({ color: 0xFFFFFF });
const m1 = new MeshLambertMaterial({ color: 0xFF0000, transparent: true, opacity: 0.5 });
const m3 = new LineBasicMaterial({ color: 0xFF0000, transparent: true, opacity: 0.3, depthTest: false });
const m_line = new LineBasicMaterial({
color: 0xffffff,
linewidth: 1,
transparent: true,
depthTest: false,
opacity: 0.5,
linecap: 'round', //ignored by WebGLRenderer
linejoin: 'round' //ignored by WebGLRenderer
});
/**
*
* @type {Terrain}
*/
const terrain = obtainTerrain(ecd);
const marker_radius = 0.05;
const geometry_0 = new TetrahedronBufferGeometry(marker_radius, 2);
const geometry_cylinder = makeCircleGeometry(1, 1);
const v3 = new Vector3();
for (let i = 0; i < n; i++) {
const markerNode = markers[i];
if (!marker_filter.match(markerNode)) {
// reject marker
continue;
}
const g = new Group();
const mark_0 = new ThreeMesh(geometry_0, m0);
const mark_1 = new ThreeMesh(geometry_0, m1);
terrain.mapPointGrid2World(markerNode.position.x, markerNode.position.y, v3);
mark_1.position.copy(markerNode.transform.position);
mark_1.position.sub(v3);
const mark_size = new Line(geometry_cylinder, m3);
mark_size.scale.set(markerNode.size, markerNode.size, markerNode.size);
g.add(mark_0);
if (mark_1.position.distanceToSquared(mark_0.position) > marker_radius * marker_radius * 0.2) {
// only draw second marker if they are sufficiently far apart
const line_geometry = new BufferGeometry();
line_geometry.setFromPoints([
mark_0.position,
mark_1.position
]);
const mark_line = new Line(line_geometry, m_line);
g.add(mark_1);
g.add(mark_line);
}
g.add(mark_size);
const entityBuilder = new Entity();
const renderable = new Renderable(g);
renderable.computeBoundsFromObject();
if (display_data) {
const props = [];
props.push(`TYPE: ${markerNode.type}`);
for (const propertyKey in markerNode.properties) {
let propValue = formatValue(markerNode.properties[propertyKey]);
props.push(`${propertyKey}:${propValue}`);
}
props.push(`# [x: ${formatValue(markerNode.position.x)}, y: ${formatValue(markerNode.position.y)}]`);
props.push(`# [size: ${formatValue(markerNode.size)}]`);
for (let j = 0; j < markerNode.tags.length; j++) {
const tag = markerNode.tags[j];
props.push(`# T: ${tag}`);
}
const v = new LabelView(props.join('\n'), { classList: ['__debug-plaque'] });
v.css({
position: 'absolute',
whiteSpace: 'pre',
left: 0,
top: 0
})
entityBuilder
.add(new HeadsUpDisplay())
.add(new ViewportPosition())
.add(GUIElement.fromView(v))
;
}
const t = new Transform();
t.position.copy(v3);
entityBuilder
.add(renderable)
.add(t)
.build(ecd);
}
}