123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
- import {BufferGeometryUtils} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/utils/BufferGeometryUtils.js';
- import {Sky} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/objects/Sky.js';
- import {agent} from './agent.js';
- import {astar} from './astar.js';
- import {game} from './game.js';
- import {math} from './math.js';
- import {mazegen} from './mazegen.js';
- const _BOID_SPEED = 0.25;
- const _BOID_ACCELERATION = _BOID_SPEED / 2.5;
- const _BOID_FORCE_MAX = _BOID_ACCELERATION / 5.0;
- const _TILES_X = 500;
- const _TILES_Y = 20;
- const _TILES_S = 50;
- let _APP = null;
- function _Key(x, y) {
- return x + '.' + y;
- }
- function _ManhattanDistance(n1, n2) {
- const p1 = n1.metadata.position;
- const p2 = n2.metadata.position;
- const dx = Math.abs(p2.x - p1.x);
- const dy = Math.abs(p2.y - p1.y);
- return (dx + dy);
- }
- function _Distance(n1, n2) {
- const p1 = n1.metadata.position;
- const p2 = n2.metadata.position;
- return p1.distanceTo(p2);
- }
- class Graph {
- constructor() {
- this._nodes = {};
- }
- get Nodes() {
- return this._nodes;
- }
- AddNode(k, e, m) {
- this._nodes[k] = {
- edges: [...e],
- potentialEdges: [...e],
- metadata: m
- };
- }
- }
- function NodesToMesh(scene, nodes) {
- const material = new THREE.MeshStandardMaterial({color: 0x71b5ef});
- const material2 = new THREE.MeshStandardMaterial({color: 0xFFFFFF});
- const edges = {};
- const geometries = [];
- for (const k in nodes) {
- const curNode = nodes[k];
- const x = curNode.metadata.position.x;
- const y = curNode.metadata.position.y;
- const w = 1;
- const h = 1;
- const wallWidth = 0.25;
- const wallHeight = 0.5;
- const neighbours = [[0, 1], [1, 0], [0, -1], [-1, 0]];
- if (!curNode.metadata.render.visible) {
- continue;
- }
- for (let ni = 0; ni < neighbours.length; ni++) {
- const n = neighbours[ni];
- const ki = _Key(x + n[0], y + n[1]);
- if (curNode.edges.indexOf(_Key(x, y + 1)) < 0) {
- // this._gfx.moveTo(w * (x + 0.0), h * (y + 1.0));
- // this._gfx.lineTo(w * (x + 1.0), h * (y + 1.0));
- const x1 = w * (x + 0.0);
- const y1 = h * (y + 1.0);
- const x2 = w * (x + 1.0);
- const y2 = h * (y + 1.0);
- const sq = new THREE.BoxBufferGeometry(x2 - x1, wallHeight, wallWidth);
- const m = new THREE.Matrix4();
- m.makeTranslation(x1 + 0.5, wallHeight * 0.5, y1);
- sq.applyMatrix(m);
- geometries.push(sq);
- }
- if (curNode.edges.indexOf(_Key(x + 1, y + 0)) < 0) {
- // this._gfx.moveTo(w * (x + 1.0), h * (y + 0.0));
- // this._gfx.lineTo(w * (x + 1.0), h * (y + 1.0));
- const x1 = w * (x + 1.0);
- const y1 = h * (y + 0.0);
- const x2 = w * (x + 1.0);
- const y2 = h * (y + 1.0);
- const sq = new THREE.BoxBufferGeometry(wallWidth, wallHeight, y2 - y1);
- const m = new THREE.Matrix4();
- m.makeTranslation(x1, wallHeight * 0.5, y1 + 0.5);
- sq.applyMatrix(m);
- geometries.push(sq);
- }
- if (curNode.edges.indexOf(_Key(x, y - 1)) < 0) {
- // this._gfx.moveTo(w * (x + 0.0), h * (y + 0.0));
- // this._gfx.lineTo(w * (x + 1.0), h * (y + 0.0));
- const x1 = w * (x + 0.0);
- const y1 = h * (y + 0.0);
- const x2 = w * (x + 1.0);
- const y2 = h * (y + 0.0);
- const sq = new THREE.BoxBufferGeometry(x2 - x1, wallHeight, wallWidth);
- const m = new THREE.Matrix4();
- m.makeTranslation(x1 + 0.5, wallHeight * 0.5, y1);
- sq.applyMatrix(m);
- geometries.push(sq);
- }
- if (curNode.edges.indexOf(_Key(x - 1, y)) < 0) {
- // this._gfx.moveTo(w * (x + 0.0), h * (y + 0.0));
- // this._gfx.lineTo(w * (x + 0.0), h * (y + 1.0));
- const x1 = w * (x + 0.0);
- const y1 = h * (y + 0.0);
- const x2 = w * (x + 0.0);
- const y2 = h * (y + 1.0);
- const sq = new THREE.BoxBufferGeometry(wallWidth, wallHeight, y2 - y1);
- const m = new THREE.Matrix4();
- m.makeTranslation(x1, wallHeight * 0.5, y1 + 0.5);
- sq.applyMatrix(m);
- geometries.push(sq);
- }
- }
- }
- for (const k in nodes) {
- const curNode = nodes[k];
- curNode.edges = [...new Set(curNode.edges)];
- }
- const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(
- geometries, false);
- const mesh = new THREE.Mesh(mergedGeometry, material2);
- mesh.castShadow = true;
- mesh.receiveShadow = true;
- scene.add(mesh);
- const plane = new THREE.Mesh(new THREE.PlaneGeometry(5000, 5000, 1, 1), material);
- plane.position.set(0, 0, 0);
- plane.castShadow = false;
- plane.receiveShadow = true;
- plane.rotation.x = -Math.PI / 2;
- scene.add(plane);
- }
- class Demo extends game.Game {
- constructor() {
- super();
- }
- _OnInitialize() {
- this._entities = [];
- this._controls.panningMode = 1;
- this._CreateMaze();
- this._LoadBackground();
- }
- _LoadBackground() {
- this._sky = new Sky();
- this._sky.scale.setScalar(10000);
- this._graphics.Scene.add(this._sky);
- const sky = {
- turbidity: 10.0,
- rayleigh: 2,
- mieCoefficient: 0.005,
- mieDirectionalG: 0.8,
- luminance: 1,
- };
- const sun = {
- inclination: 0.31,
- azimuth: 0.25,
- };
- for (let k in sky) {
- this._sky.material.uniforms[k].value = sky[k];
- }
- const theta = Math.PI * (sun.inclination - 0.5);
- const phi = 2 * Math.PI * (sun.azimuth - 0.5);
- const sunPosition = new THREE.Vector3();
- sunPosition.x = Math.cos(phi);
- sunPosition.y = Math.sin(phi) * Math.sin(theta);
- sunPosition.z = Math.sin(phi) * Math.cos(theta);
- this._sky.material.uniforms['sunPosition'].value.copy(sunPosition);
- }
- _CreateMaze() {
- this._graph = new Graph();
- for (let x = 0; x < _TILES_X; x++) {
- for (let y = 0; y < _TILES_Y; y++) {
- const k = _Key(x, y);
- this._graph.AddNode(
- k, [],
- {
- position: new THREE.Vector2(x, y),
- weight: 0,
- render: {
- visited: false,
- visible: true,
- }
- });
- }
- }
- for (let x = 0; x < _TILES_X; x++) {
- for (let y = 0; y < _TILES_Y; y++) {
- const k = _Key(x, y);
- for (let xi = -1; xi <= 1; xi++) {
- for (let yi = -1; yi <= 1; yi++) {
- if (xi == 0 && yi == 0 || (Math.abs(xi) + Math.abs(yi) != 1)) {
- continue;
- }
- const ki = _Key(x + xi, y + yi);
- if (ki in this._graph.Nodes) {
- this._graph.Nodes[k].potentialEdges.push(ki);
- }
- }
- }
- }
- }
- const start = _Key(0, 0);
- const end = _Key(4, 0);
- this._mazeGenerator = new mazegen.MazeGenerator(this._graph.Nodes);
- this._mazeIterator = this._mazeGenerator.GenerateIteratively(start);
- this._mazeDone = () => {
- const nodes = [];
- for (let x = 0; x < _TILES_X; x++) {
- for (let y = 0; y > -_TILES_S; y--) {
- const k = _Key(x, y);
- if (k in this._graph.Nodes) {
- continue;
- }
- this._graph.AddNode(
- k, [],
- {
- position: new THREE.Vector2(x, y),
- weight: 0,
- render: {
- visited: false,
- visible: false,
- }
- });
- nodes.push(k);
- }
- }
- for (let x = 0; x < _TILES_X; x++) {
- for (let y = _TILES_Y - 1; y < _TILES_Y + _TILES_S; y++) {
- const k = _Key(x, y);
- if (k in this._graph.Nodes) {
- continue;
- }
- this._graph.AddNode(
- k, [],
- {
- position: new THREE.Vector2(x, y),
- weight: 0,
- render: {
- visited: false,
- visible: false,
- }
- });
- nodes.push(k);
- }
- }
- for (let k of nodes) {
- const n = this._graph.Nodes[k];
- const x = n.metadata.position.x;
- const y = n.metadata.position.y;
- for (let xi = -1; xi <= 1; xi++) {
- for (let yi = -1; yi <= 1; yi++) {
- if (xi == 0 && yi == 0 || (Math.abs(xi) + Math.abs(yi) != 1)) {
- continue;
- }
- const ki = _Key(x + xi, y + yi);
- if (ki in this._graph.Nodes) {
- this._graph.Nodes[k].potentialEdges.push(ki);
- }
- for (let pk of this._graph.Nodes[k].potentialEdges) {
- this._graph.Nodes[k].edges.push(pk);
- this._graph.Nodes[pk].edges.push(k);
- }
- }
- }
- }
- this._CreateEntities();
- };
- }
- _CreateEntities() {
- const geometries = {
- cone: new THREE.ConeGeometry(1, 2, 32)
- };
- const material = new THREE.MeshStandardMaterial({color: 0xFF0000});
- const numInstances = _TILES_X * _TILES_S / 2;
- const mesh = new THREE.InstancedMesh(
- geometries.cone, material, numInstances);
- mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
- mesh.castShadow = true;
- mesh.receiveShadow = true;
- mesh.frustumCulled = false;
- let index = 0;
- const nodes = this._graph.Nodes;
- function _ManhattanDistance(n1, n2) {
- const p1 = n1.metadata.position;
- const p2 = n2.metadata.position;
- const dx = Math.abs(p2.x - p1.x);
- const dy = Math.abs(p2.y - p1.y);
- return (dx + dy);
- }
-
- function _Distance(n1, n2) {
- const p1 = n1.metadata.position;
- const p2 = n2.metadata.position;
- return p1.distanceTo(p2);
- }
- const heuristicFunction = (s, e) => {
- return 2 * _ManhattanDistance(nodes[s], nodes[e]);
- };
- const weightFunction = (s, e) => {
- return _ManhattanDistance(nodes[s], nodes[e]);
- };
- const mgr = new astar.AStarManager(
- this._graph.Nodes,
- heuristicFunction,
- weightFunction);
- this._entities.push(mgr);
- for (let j = 0; j < _TILES_S / 2; j++) {
- for (let i = 0; i < _TILES_X; i++) {
- const xe = math.clamp(math.rand_int(i - 20, i + 20), 0, _TILES_X - 1);
- const start = _Key(i, -j - 1);
- const end = _Key(xe, _TILES_Y + 5);
-
- let params = {
- geometry: geometries.cone,
- material: material,
- mesh: mesh,
- index: index++,
- speed: _BOID_SPEED,
- maxSteeringForce: _BOID_FORCE_MAX,
- acceleration: _BOID_ACCELERATION,
- position: new THREE.Vector3(i, 0.25, -j - 1),
- astar: mgr.CreateClient(start, end),
- };
- const e = new agent.Agent_Instanced(this, params);
- this._entities.push(e);
- }
- }
- this._graphics._camera.position.set(_TILES_X / 2, 7, 12);
- this._controls.target.set(_TILES_X / 2, 0, -5);
- this._controls.update();
- console.log('AGENTS: ' + this._entities.length)
- this._graphics.Scene.add( mesh );
- }
- _OnStep(timeInSeconds) {
- timeInSeconds = Math.min(timeInSeconds, 1 / 10.0);
- this._StepMazeGeneration();
- this._StepEntities(timeInSeconds);
- }
- _StepMazeGeneration() {
- for (let i = 0; i < 100; i++) {
- if (this._mazeIterator) {
- const r = this._mazeIterator.next();
- if (r.done) {
- console.log('DONE');
- this._mazeGenerator.Randomize();
- this._mazeDone();
- NodesToMesh(this._graphics.Scene, this._graph.Nodes);
- this._graphics._shadowLight.position.set(_TILES_X * 0.5, 10, _TILES_Y * 0.5);
- this._graphics._shadowLight.target.position.set(_TILES_X * 0.5 - 5, 0, _TILES_Y * 0.5 - 5);
- this._graphics._shadowLight.target.updateWorldMatrix();
- this._mazeIterator = null;
- }
- }
- }
- }
- _StepEntities(timeInSeconds) {
- for (let e of this._entities) {
- e.Step(timeInSeconds);
- }
- }
- }
- function _Main() {
- _APP = new Demo();
- }
- _Main();
|