main.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
  2. import {BufferGeometryUtils} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/utils/BufferGeometryUtils.js';
  3. import {Sky} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/objects/Sky.js';
  4. import {agent} from './agent.js';
  5. import {astar} from './astar.js';
  6. import {game} from './game.js';
  7. import {math} from './math.js';
  8. import {mazegen} from './mazegen.js';
  9. const _BOID_SPEED = 0.25;
  10. const _BOID_ACCELERATION = _BOID_SPEED / 2.5;
  11. const _BOID_FORCE_MAX = _BOID_ACCELERATION / 5.0;
  12. const _TILES_X = 500;
  13. const _TILES_Y = 20;
  14. const _TILES_S = 50;
  15. let _APP = null;
  16. function _Key(x, y) {
  17. return x + '.' + y;
  18. }
  19. function _ManhattanDistance(n1, n2) {
  20. const p1 = n1.metadata.position;
  21. const p2 = n2.metadata.position;
  22. const dx = Math.abs(p2.x - p1.x);
  23. const dy = Math.abs(p2.y - p1.y);
  24. return (dx + dy);
  25. }
  26. function _Distance(n1, n2) {
  27. const p1 = n1.metadata.position;
  28. const p2 = n2.metadata.position;
  29. return p1.distanceTo(p2);
  30. }
  31. class Graph {
  32. constructor() {
  33. this._nodes = {};
  34. }
  35. get Nodes() {
  36. return this._nodes;
  37. }
  38. AddNode(k, e, m) {
  39. this._nodes[k] = {
  40. edges: [...e],
  41. potentialEdges: [...e],
  42. metadata: m
  43. };
  44. }
  45. }
  46. function NodesToMesh(scene, nodes) {
  47. const material = new THREE.MeshStandardMaterial({color: 0x71b5ef});
  48. const material2 = new THREE.MeshStandardMaterial({color: 0xFFFFFF});
  49. const edges = {};
  50. const geometries = [];
  51. for (const k in nodes) {
  52. const curNode = nodes[k];
  53. const x = curNode.metadata.position.x;
  54. const y = curNode.metadata.position.y;
  55. const w = 1;
  56. const h = 1;
  57. const wallWidth = 0.25;
  58. const wallHeight = 0.5;
  59. const neighbours = [[0, 1], [1, 0], [0, -1], [-1, 0]];
  60. if (!curNode.metadata.render.visible) {
  61. continue;
  62. }
  63. for (let ni = 0; ni < neighbours.length; ni++) {
  64. const n = neighbours[ni];
  65. const ki = _Key(x + n[0], y + n[1]);
  66. if (curNode.edges.indexOf(_Key(x, y + 1)) < 0) {
  67. // this._gfx.moveTo(w * (x + 0.0), h * (y + 1.0));
  68. // this._gfx.lineTo(w * (x + 1.0), h * (y + 1.0));
  69. const x1 = w * (x + 0.0);
  70. const y1 = h * (y + 1.0);
  71. const x2 = w * (x + 1.0);
  72. const y2 = h * (y + 1.0);
  73. const sq = new THREE.BoxBufferGeometry(x2 - x1, wallHeight, wallWidth);
  74. const m = new THREE.Matrix4();
  75. m.makeTranslation(x1 + 0.5, wallHeight * 0.5, y1);
  76. sq.applyMatrix(m);
  77. geometries.push(sq);
  78. }
  79. if (curNode.edges.indexOf(_Key(x + 1, y + 0)) < 0) {
  80. // this._gfx.moveTo(w * (x + 1.0), h * (y + 0.0));
  81. // this._gfx.lineTo(w * (x + 1.0), h * (y + 1.0));
  82. const x1 = w * (x + 1.0);
  83. const y1 = h * (y + 0.0);
  84. const x2 = w * (x + 1.0);
  85. const y2 = h * (y + 1.0);
  86. const sq = new THREE.BoxBufferGeometry(wallWidth, wallHeight, y2 - y1);
  87. const m = new THREE.Matrix4();
  88. m.makeTranslation(x1, wallHeight * 0.5, y1 + 0.5);
  89. sq.applyMatrix(m);
  90. geometries.push(sq);
  91. }
  92. if (curNode.edges.indexOf(_Key(x, y - 1)) < 0) {
  93. // this._gfx.moveTo(w * (x + 0.0), h * (y + 0.0));
  94. // this._gfx.lineTo(w * (x + 1.0), h * (y + 0.0));
  95. const x1 = w * (x + 0.0);
  96. const y1 = h * (y + 0.0);
  97. const x2 = w * (x + 1.0);
  98. const y2 = h * (y + 0.0);
  99. const sq = new THREE.BoxBufferGeometry(x2 - x1, wallHeight, wallWidth);
  100. const m = new THREE.Matrix4();
  101. m.makeTranslation(x1 + 0.5, wallHeight * 0.5, y1);
  102. sq.applyMatrix(m);
  103. geometries.push(sq);
  104. }
  105. if (curNode.edges.indexOf(_Key(x - 1, y)) < 0) {
  106. // this._gfx.moveTo(w * (x + 0.0), h * (y + 0.0));
  107. // this._gfx.lineTo(w * (x + 0.0), h * (y + 1.0));
  108. const x1 = w * (x + 0.0);
  109. const y1 = h * (y + 0.0);
  110. const x2 = w * (x + 0.0);
  111. const y2 = h * (y + 1.0);
  112. const sq = new THREE.BoxBufferGeometry(wallWidth, wallHeight, y2 - y1);
  113. const m = new THREE.Matrix4();
  114. m.makeTranslation(x1, wallHeight * 0.5, y1 + 0.5);
  115. sq.applyMatrix(m);
  116. geometries.push(sq);
  117. }
  118. }
  119. }
  120. for (const k in nodes) {
  121. const curNode = nodes[k];
  122. curNode.edges = [...new Set(curNode.edges)];
  123. }
  124. const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(
  125. geometries, false);
  126. const mesh = new THREE.Mesh(mergedGeometry, material2);
  127. mesh.castShadow = true;
  128. mesh.receiveShadow = true;
  129. scene.add(mesh);
  130. const plane = new THREE.Mesh(new THREE.PlaneGeometry(5000, 5000, 1, 1), material);
  131. plane.position.set(0, 0, 0);
  132. plane.castShadow = false;
  133. plane.receiveShadow = true;
  134. plane.rotation.x = -Math.PI / 2;
  135. scene.add(plane);
  136. }
  137. class Demo extends game.Game {
  138. constructor() {
  139. super();
  140. }
  141. _OnInitialize() {
  142. this._entities = [];
  143. this._controls.panningMode = 1;
  144. this._CreateMaze();
  145. this._LoadBackground();
  146. }
  147. _LoadBackground() {
  148. this._sky = new Sky();
  149. this._sky.scale.setScalar(10000);
  150. this._graphics.Scene.add(this._sky);
  151. const sky = {
  152. turbidity: 10.0,
  153. rayleigh: 2,
  154. mieCoefficient: 0.005,
  155. mieDirectionalG: 0.8,
  156. luminance: 1,
  157. };
  158. const sun = {
  159. inclination: 0.31,
  160. azimuth: 0.25,
  161. };
  162. for (let k in sky) {
  163. this._sky.material.uniforms[k].value = sky[k];
  164. }
  165. const theta = Math.PI * (sun.inclination - 0.5);
  166. const phi = 2 * Math.PI * (sun.azimuth - 0.5);
  167. const sunPosition = new THREE.Vector3();
  168. sunPosition.x = Math.cos(phi);
  169. sunPosition.y = Math.sin(phi) * Math.sin(theta);
  170. sunPosition.z = Math.sin(phi) * Math.cos(theta);
  171. this._sky.material.uniforms['sunPosition'].value.copy(sunPosition);
  172. }
  173. _CreateMaze() {
  174. this._graph = new Graph();
  175. for (let x = 0; x < _TILES_X; x++) {
  176. for (let y = 0; y < _TILES_Y; y++) {
  177. const k = _Key(x, y);
  178. this._graph.AddNode(
  179. k, [],
  180. {
  181. position: new THREE.Vector2(x, y),
  182. weight: 0,
  183. render: {
  184. visited: false,
  185. visible: true,
  186. }
  187. });
  188. }
  189. }
  190. for (let x = 0; x < _TILES_X; x++) {
  191. for (let y = 0; y < _TILES_Y; y++) {
  192. const k = _Key(x, y);
  193. for (let xi = -1; xi <= 1; xi++) {
  194. for (let yi = -1; yi <= 1; yi++) {
  195. if (xi == 0 && yi == 0 || (Math.abs(xi) + Math.abs(yi) != 1)) {
  196. continue;
  197. }
  198. const ki = _Key(x + xi, y + yi);
  199. if (ki in this._graph.Nodes) {
  200. this._graph.Nodes[k].potentialEdges.push(ki);
  201. }
  202. }
  203. }
  204. }
  205. }
  206. const start = _Key(0, 0);
  207. const end = _Key(4, 0);
  208. this._mazeGenerator = new mazegen.MazeGenerator(this._graph.Nodes);
  209. this._mazeIterator = this._mazeGenerator.GenerateIteratively(start);
  210. this._mazeDone = () => {
  211. const nodes = [];
  212. for (let x = 0; x < _TILES_X; x++) {
  213. for (let y = 0; y > -_TILES_S; y--) {
  214. const k = _Key(x, y);
  215. if (k in this._graph.Nodes) {
  216. continue;
  217. }
  218. this._graph.AddNode(
  219. k, [],
  220. {
  221. position: new THREE.Vector2(x, y),
  222. weight: 0,
  223. render: {
  224. visited: false,
  225. visible: false,
  226. }
  227. });
  228. nodes.push(k);
  229. }
  230. }
  231. for (let x = 0; x < _TILES_X; x++) {
  232. for (let y = _TILES_Y - 1; y < _TILES_Y + _TILES_S; y++) {
  233. const k = _Key(x, y);
  234. if (k in this._graph.Nodes) {
  235. continue;
  236. }
  237. this._graph.AddNode(
  238. k, [],
  239. {
  240. position: new THREE.Vector2(x, y),
  241. weight: 0,
  242. render: {
  243. visited: false,
  244. visible: false,
  245. }
  246. });
  247. nodes.push(k);
  248. }
  249. }
  250. for (let k of nodes) {
  251. const n = this._graph.Nodes[k];
  252. const x = n.metadata.position.x;
  253. const y = n.metadata.position.y;
  254. for (let xi = -1; xi <= 1; xi++) {
  255. for (let yi = -1; yi <= 1; yi++) {
  256. if (xi == 0 && yi == 0 || (Math.abs(xi) + Math.abs(yi) != 1)) {
  257. continue;
  258. }
  259. const ki = _Key(x + xi, y + yi);
  260. if (ki in this._graph.Nodes) {
  261. this._graph.Nodes[k].potentialEdges.push(ki);
  262. }
  263. for (let pk of this._graph.Nodes[k].potentialEdges) {
  264. this._graph.Nodes[k].edges.push(pk);
  265. this._graph.Nodes[pk].edges.push(k);
  266. }
  267. }
  268. }
  269. }
  270. this._CreateEntities();
  271. };
  272. }
  273. _CreateEntities() {
  274. const geometries = {
  275. cone: new THREE.ConeGeometry(1, 2, 32)
  276. };
  277. const material = new THREE.MeshStandardMaterial({color: 0xFF0000});
  278. const numInstances = _TILES_X * _TILES_S / 2;
  279. const mesh = new THREE.InstancedMesh(
  280. geometries.cone, material, numInstances);
  281. mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
  282. mesh.castShadow = true;
  283. mesh.receiveShadow = true;
  284. mesh.frustumCulled = false;
  285. let index = 0;
  286. const nodes = this._graph.Nodes;
  287. function _ManhattanDistance(n1, n2) {
  288. const p1 = n1.metadata.position;
  289. const p2 = n2.metadata.position;
  290. const dx = Math.abs(p2.x - p1.x);
  291. const dy = Math.abs(p2.y - p1.y);
  292. return (dx + dy);
  293. }
  294. function _Distance(n1, n2) {
  295. const p1 = n1.metadata.position;
  296. const p2 = n2.metadata.position;
  297. return p1.distanceTo(p2);
  298. }
  299. const heuristicFunction = (s, e) => {
  300. return 2 * _ManhattanDistance(nodes[s], nodes[e]);
  301. };
  302. const weightFunction = (s, e) => {
  303. return _ManhattanDistance(nodes[s], nodes[e]);
  304. };
  305. const mgr = new astar.AStarManager(
  306. this._graph.Nodes,
  307. heuristicFunction,
  308. weightFunction);
  309. this._entities.push(mgr);
  310. for (let j = 0; j < _TILES_S / 2; j++) {
  311. for (let i = 0; i < _TILES_X; i++) {
  312. const xe = math.clamp(math.rand_int(i - 20, i + 20), 0, _TILES_X - 1);
  313. const start = _Key(i, -j - 1);
  314. const end = _Key(xe, _TILES_Y + 5);
  315. let params = {
  316. geometry: geometries.cone,
  317. material: material,
  318. mesh: mesh,
  319. index: index++,
  320. speed: _BOID_SPEED,
  321. maxSteeringForce: _BOID_FORCE_MAX,
  322. acceleration: _BOID_ACCELERATION,
  323. position: new THREE.Vector3(i, 0.25, -j - 1),
  324. astar: mgr.CreateClient(start, end),
  325. };
  326. const e = new agent.Agent_Instanced(this, params);
  327. this._entities.push(e);
  328. }
  329. }
  330. this._graphics._camera.position.set(_TILES_X / 2, 7, 12);
  331. this._controls.target.set(_TILES_X / 2, 0, -5);
  332. this._controls.update();
  333. console.log('AGENTS: ' + this._entities.length)
  334. this._graphics.Scene.add( mesh );
  335. }
  336. _OnStep(timeInSeconds) {
  337. timeInSeconds = Math.min(timeInSeconds, 1 / 10.0);
  338. this._StepMazeGeneration();
  339. this._StepEntities(timeInSeconds);
  340. }
  341. _StepMazeGeneration() {
  342. for (let i = 0; i < 100; i++) {
  343. if (this._mazeIterator) {
  344. const r = this._mazeIterator.next();
  345. if (r.done) {
  346. console.log('DONE');
  347. this._mazeGenerator.Randomize();
  348. this._mazeDone();
  349. NodesToMesh(this._graphics.Scene, this._graph.Nodes);
  350. this._graphics._shadowLight.position.set(_TILES_X * 0.5, 10, _TILES_Y * 0.5);
  351. this._graphics._shadowLight.target.position.set(_TILES_X * 0.5 - 5, 0, _TILES_Y * 0.5 - 5);
  352. this._graphics._shadowLight.target.updateWorldMatrix();
  353. this._mazeIterator = null;
  354. }
  355. }
  356. }
  357. }
  358. _StepEntities(timeInSeconds) {
  359. for (let e of this._entities) {
  360. e.Step(timeInSeconds);
  361. }
  362. }
  363. }
  364. function _Main() {
  365. _APP = new Demo();
  366. }
  367. _Main();