aframe-extras.pathfinding.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516
  1. (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. 'use strict';
  3. require('./src/pathfinding');
  4. },{"./src/pathfinding":8}],2:[function(require,module,exports){
  5. 'use strict';
  6. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  7. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  8. var BinaryHeap = require('./BinaryHeap');
  9. var utils = require('./utils.js');
  10. var AStar = function () {
  11. function AStar() {
  12. _classCallCheck(this, AStar);
  13. }
  14. _createClass(AStar, null, [{
  15. key: 'init',
  16. value: function init(graph) {
  17. for (var x = 0; x < graph.length; x++) {
  18. //for(var x in graph) {
  19. var node = graph[x];
  20. node.f = 0;
  21. node.g = 0;
  22. node.h = 0;
  23. node.cost = 1.0;
  24. node.visited = false;
  25. node.closed = false;
  26. node.parent = null;
  27. }
  28. }
  29. }, {
  30. key: 'cleanUp',
  31. value: function cleanUp(graph) {
  32. for (var x = 0; x < graph.length; x++) {
  33. var node = graph[x];
  34. delete node.f;
  35. delete node.g;
  36. delete node.h;
  37. delete node.cost;
  38. delete node.visited;
  39. delete node.closed;
  40. delete node.parent;
  41. }
  42. }
  43. }, {
  44. key: 'heap',
  45. value: function heap() {
  46. return new BinaryHeap(function (node) {
  47. return node.f;
  48. });
  49. }
  50. }, {
  51. key: 'search',
  52. value: function search(graph, start, end) {
  53. this.init(graph);
  54. //heuristic = heuristic || astar.manhattan;
  55. var openHeap = this.heap();
  56. openHeap.push(start);
  57. while (openHeap.size() > 0) {
  58. // Grab the lowest f(x) to process next. Heap keeps this sorted for us.
  59. var currentNode = openHeap.pop();
  60. // End case -- result has been found, return the traced path.
  61. if (currentNode === end) {
  62. var curr = currentNode;
  63. var ret = [];
  64. while (curr.parent) {
  65. ret.push(curr);
  66. curr = curr.parent;
  67. }
  68. this.cleanUp(ret);
  69. return ret.reverse();
  70. }
  71. // Normal case -- move currentNode from open to closed, process each of its neighbours.
  72. currentNode.closed = true;
  73. // Find all neighbours for the current node. Optionally find diagonal neighbours as well (false by default).
  74. var neighbours = this.neighbours(graph, currentNode);
  75. for (var i = 0, il = neighbours.length; i < il; i++) {
  76. var neighbour = neighbours[i];
  77. if (neighbour.closed) {
  78. // Not a valid node to process, skip to next neighbour.
  79. continue;
  80. }
  81. // The g score is the shortest distance from start to current node.
  82. // We need to check if the path we have arrived at this neighbour is the shortest one we have seen yet.
  83. var gScore = currentNode.g + neighbour.cost;
  84. var beenVisited = neighbour.visited;
  85. if (!beenVisited || gScore < neighbour.g) {
  86. // Found an optimal (so far) path to this node. Take score for node to see how good it is.
  87. neighbour.visited = true;
  88. neighbour.parent = currentNode;
  89. if (!neighbour.centroid || !end.centroid) throw new Error('Unexpected state');
  90. neighbour.h = neighbour.h || this.heuristic(neighbour.centroid, end.centroid);
  91. neighbour.g = gScore;
  92. neighbour.f = neighbour.g + neighbour.h;
  93. if (!beenVisited) {
  94. // Pushing to heap will put it in proper place based on the 'f' value.
  95. openHeap.push(neighbour);
  96. } else {
  97. // Already seen the node, but since it has been rescored we need to reorder it in the heap
  98. openHeap.rescoreElement(neighbour);
  99. }
  100. }
  101. }
  102. }
  103. // No result was found - empty array signifies failure to find path.
  104. return [];
  105. }
  106. }, {
  107. key: 'heuristic',
  108. value: function heuristic(pos1, pos2) {
  109. return utils.distanceToSquared(pos1, pos2);
  110. }
  111. }, {
  112. key: 'neighbours',
  113. value: function neighbours(graph, node) {
  114. var ret = [];
  115. for (var e = 0; e < node.neighbours.length; e++) {
  116. ret.push(graph[node.neighbours[e]]);
  117. }
  118. return ret;
  119. }
  120. }]);
  121. return AStar;
  122. }();
  123. module.exports = AStar;
  124. },{"./BinaryHeap":3,"./utils.js":7}],3:[function(require,module,exports){
  125. "use strict";
  126. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  127. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  128. // javascript-astar
  129. // http://github.com/bgrins/javascript-astar
  130. // Freely distributable under the MIT License.
  131. // Implements the astar search algorithm in javascript using a binary heap.
  132. var BinaryHeap = function () {
  133. function BinaryHeap(scoreFunction) {
  134. _classCallCheck(this, BinaryHeap);
  135. this.content = [];
  136. this.scoreFunction = scoreFunction;
  137. }
  138. _createClass(BinaryHeap, [{
  139. key: "push",
  140. value: function push(element) {
  141. // Add the new element to the end of the array.
  142. this.content.push(element);
  143. // Allow it to sink down.
  144. this.sinkDown(this.content.length - 1);
  145. }
  146. }, {
  147. key: "pop",
  148. value: function pop() {
  149. // Store the first element so we can return it later.
  150. var result = this.content[0];
  151. // Get the element at the end of the array.
  152. var end = this.content.pop();
  153. // If there are any elements left, put the end element at the
  154. // start, and let it bubble up.
  155. if (this.content.length > 0) {
  156. this.content[0] = end;
  157. this.bubbleUp(0);
  158. }
  159. return result;
  160. }
  161. }, {
  162. key: "remove",
  163. value: function remove(node) {
  164. var i = this.content.indexOf(node);
  165. // When it is found, the process seen in 'pop' is repeated
  166. // to fill up the hole.
  167. var end = this.content.pop();
  168. if (i !== this.content.length - 1) {
  169. this.content[i] = end;
  170. if (this.scoreFunction(end) < this.scoreFunction(node)) {
  171. this.sinkDown(i);
  172. } else {
  173. this.bubbleUp(i);
  174. }
  175. }
  176. }
  177. }, {
  178. key: "size",
  179. value: function size() {
  180. return this.content.length;
  181. }
  182. }, {
  183. key: "rescoreElement",
  184. value: function rescoreElement(node) {
  185. this.sinkDown(this.content.indexOf(node));
  186. }
  187. }, {
  188. key: "sinkDown",
  189. value: function sinkDown(n) {
  190. // Fetch the element that has to be sunk.
  191. var element = this.content[n];
  192. // When at 0, an element can not sink any further.
  193. while (n > 0) {
  194. // Compute the parent element's index, and fetch it.
  195. var parentN = (n + 1 >> 1) - 1;
  196. var parent = this.content[parentN];
  197. if (this.scoreFunction(element) < this.scoreFunction(parent)) {
  198. // Swap the elements if the parent is greater.
  199. this.content[parentN] = element;
  200. this.content[n] = parent;
  201. // Update 'n' to continue at the new position.
  202. n = parentN;
  203. } else {
  204. // Found a parent that is less, no need to sink any further.
  205. break;
  206. }
  207. }
  208. }
  209. }, {
  210. key: "bubbleUp",
  211. value: function bubbleUp(n) {
  212. // Look up the target element and its score.
  213. var length = this.content.length,
  214. element = this.content[n],
  215. elemScore = this.scoreFunction(element);
  216. while (true) {
  217. // Compute the indices of the child elements.
  218. var child2N = n + 1 << 1,
  219. child1N = child2N - 1;
  220. // This is used to store the new position of the element,
  221. // if any.
  222. var swap = null;
  223. var child1Score = void 0;
  224. // If the first child exists (is inside the array)...
  225. if (child1N < length) {
  226. // Look it up and compute its score.
  227. var child1 = this.content[child1N];
  228. child1Score = this.scoreFunction(child1);
  229. // If the score is less than our element's, we need to swap.
  230. if (child1Score < elemScore) {
  231. swap = child1N;
  232. }
  233. }
  234. // Do the same checks for the other child.
  235. if (child2N < length) {
  236. var child2 = this.content[child2N],
  237. child2Score = this.scoreFunction(child2);
  238. if (child2Score < (swap === null ? elemScore : child1Score)) {
  239. swap = child2N;
  240. }
  241. }
  242. // If the element needs to be moved, swap it, and continue.
  243. if (swap !== null) {
  244. this.content[n] = this.content[swap];
  245. this.content[swap] = element;
  246. n = swap;
  247. }
  248. // Otherwise, we are done.
  249. else {
  250. break;
  251. }
  252. }
  253. }
  254. }]);
  255. return BinaryHeap;
  256. }();
  257. module.exports = BinaryHeap;
  258. },{}],4:[function(require,module,exports){
  259. 'use strict';
  260. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  261. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  262. var utils = require('./utils');
  263. var polygonId = 1;
  264. var Builder = function () {
  265. function Builder() {
  266. _classCallCheck(this, Builder);
  267. }
  268. _createClass(Builder, null, [{
  269. key: 'buildZone',
  270. /**
  271. * Constructs groups from the given navigation mesh.
  272. * @param {THREE.Geometry} geometry
  273. * @return {Zone}
  274. */
  275. value: function buildZone(geometry) {
  276. var _this = this;
  277. var navMesh = this._buildNavigationMesh(geometry);
  278. var zone = {};
  279. navMesh.vertices.forEach(function (v) {
  280. v.x = utils.roundNumber(v.x, 2);
  281. v.y = utils.roundNumber(v.y, 2);
  282. v.z = utils.roundNumber(v.z, 2);
  283. });
  284. zone.vertices = navMesh.vertices;
  285. var groups = this._buildPolygonGroups(navMesh);
  286. zone.groups = [];
  287. var findPolygonIndex = function findPolygonIndex(group, p) {
  288. for (var i = 0; i < group.length; i++) {
  289. if (p === group[i]) return i;
  290. }
  291. };
  292. groups.forEach(function (group) {
  293. var newGroup = [];
  294. group.forEach(function (p) {
  295. var neighbours = p.neighbours.map(function (n) {
  296. return findPolygonIndex(group, n);
  297. });
  298. // Build a portal list to each neighbour
  299. var portals = p.neighbours.map(function (n) {
  300. return _this._getSharedVerticesInOrder(p, n);
  301. });
  302. p.centroid.x = utils.roundNumber(p.centroid.x, 2);
  303. p.centroid.y = utils.roundNumber(p.centroid.y, 2);
  304. p.centroid.z = utils.roundNumber(p.centroid.z, 2);
  305. newGroup.push({
  306. id: findPolygonIndex(group, p),
  307. neighbours: neighbours,
  308. vertexIds: p.vertexIds,
  309. centroid: p.centroid,
  310. portals: portals
  311. });
  312. });
  313. zone.groups.push(newGroup);
  314. });
  315. return zone;
  316. }
  317. /**
  318. * Constructs a navigation mesh from the given geometry.
  319. * @param {THREE.Geometry} geometry
  320. * @return {Object}
  321. */
  322. }, {
  323. key: '_buildNavigationMesh',
  324. value: function _buildNavigationMesh(geometry) {
  325. utils.computeCentroids(geometry);
  326. geometry.mergeVertices();
  327. return this._buildPolygonsFromGeometry(geometry);
  328. }
  329. }, {
  330. key: '_buildPolygonGroups',
  331. value: function _buildPolygonGroups(navigationMesh) {
  332. var polygons = navigationMesh.polygons;
  333. var polygonGroups = [];
  334. var groupCount = 0;
  335. var spreadGroupId = function spreadGroupId(polygon) {
  336. polygon.neighbours.forEach(function (neighbour) {
  337. if (neighbour.group === undefined) {
  338. neighbour.group = polygon.group;
  339. spreadGroupId(neighbour);
  340. }
  341. });
  342. };
  343. polygons.forEach(function (polygon) {
  344. if (polygon.group === undefined) {
  345. polygon.group = groupCount++;
  346. // Spread it
  347. spreadGroupId(polygon);
  348. }
  349. if (!polygonGroups[polygon.group]) polygonGroups[polygon.group] = [];
  350. polygonGroups[polygon.group].push(polygon);
  351. });
  352. return polygonGroups;
  353. }
  354. }, {
  355. key: '_buildPolygonNeighbours',
  356. value: function _buildPolygonNeighbours(polygon, navigationMesh) {
  357. polygon.neighbours = [];
  358. // All other nodes that contain at least two of our vertices are our neighbours
  359. for (var i = 0, len = navigationMesh.polygons.length; i < len; i++) {
  360. if (polygon === navigationMesh.polygons[i]) continue;
  361. // Don't check polygons that are too far, since the intersection tests take a long time
  362. if (polygon.centroid.distanceToSquared(navigationMesh.polygons[i].centroid) > 100 * 100) continue;
  363. var matches = utils.array_intersect(polygon.vertexIds, navigationMesh.polygons[i].vertexIds);
  364. if (matches.length >= 2) {
  365. polygon.neighbours.push(navigationMesh.polygons[i]);
  366. }
  367. }
  368. }
  369. }, {
  370. key: '_buildPolygonsFromGeometry',
  371. value: function _buildPolygonsFromGeometry(geometry) {
  372. var _this2 = this;
  373. var polygons = [];
  374. var vertices = geometry.vertices;
  375. var faceVertexUvs = geometry.faceVertexUvs;
  376. // Convert the faces into a custom format that supports more than 3 vertices
  377. geometry.faces.forEach(function (face) {
  378. polygons.push({
  379. id: polygonId++,
  380. vertexIds: [face.a, face.b, face.c],
  381. centroid: face.centroid,
  382. normal: face.normal,
  383. neighbours: []
  384. });
  385. });
  386. var navigationMesh = {
  387. polygons: polygons,
  388. vertices: vertices,
  389. faceVertexUvs: faceVertexUvs
  390. };
  391. // Build a list of adjacent polygons
  392. polygons.forEach(function (polygon) {
  393. _this2._buildPolygonNeighbours(polygon, navigationMesh);
  394. });
  395. return navigationMesh;
  396. }
  397. }, {
  398. key: '_getSharedVerticesInOrder',
  399. value: function _getSharedVerticesInOrder(a, b) {
  400. var aList = a.vertexIds;
  401. var bList = b.vertexIds;
  402. var sharedVertices = [];
  403. aList.forEach(function (vId) {
  404. if (bList.includes(vId)) {
  405. sharedVertices.push(vId);
  406. }
  407. });
  408. if (sharedVertices.length < 2) return [];
  409. if (sharedVertices.includes(aList[0]) && sharedVertices.includes(aList[aList.length - 1])) {
  410. // Vertices on both edges are bad, so shift them once to the left
  411. aList.push(aList.shift());
  412. }
  413. if (sharedVertices.includes(bList[0]) && sharedVertices.includes(bList[bList.length - 1])) {
  414. // Vertices on both edges are bad, so shift them once to the left
  415. bList.push(bList.shift());
  416. }
  417. // Again!
  418. sharedVertices.length = 0;
  419. aList.forEach(function (vId) {
  420. if (bList.includes(vId)) {
  421. sharedVertices.push(vId);
  422. }
  423. });
  424. return sharedVertices;
  425. }
  426. }]);
  427. return Builder;
  428. }();
  429. module.exports = Builder;
  430. },{"./utils":7}],5:[function(require,module,exports){
  431. 'use strict';
  432. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  433. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  434. var utils = require('./utils');
  435. var Channel = function () {
  436. function Channel() {
  437. _classCallCheck(this, Channel);
  438. this.portals = [];
  439. }
  440. _createClass(Channel, [{
  441. key: 'push',
  442. value: function push(p1, p2) {
  443. if (p2 === undefined) p2 = p1;
  444. this.portals.push({
  445. left: p1,
  446. right: p2
  447. });
  448. }
  449. }, {
  450. key: 'stringPull',
  451. value: function stringPull() {
  452. var portals = this.portals;
  453. var pts = [];
  454. // Init scan state
  455. var portalApex = void 0,
  456. portalLeft = void 0,
  457. portalRight = void 0;
  458. var apexIndex = 0,
  459. leftIndex = 0,
  460. rightIndex = 0;
  461. portalApex = portals[0].left;
  462. portalLeft = portals[0].left;
  463. portalRight = portals[0].right;
  464. // Add start point.
  465. pts.push(portalApex);
  466. for (var i = 1; i < portals.length; i++) {
  467. var left = portals[i].left;
  468. var right = portals[i].right;
  469. // Update right vertex.
  470. if (utils.triarea2(portalApex, portalRight, right) <= 0.0) {
  471. if (utils.vequal(portalApex, portalRight) || utils.triarea2(portalApex, portalLeft, right) > 0.0) {
  472. // Tighten the funnel.
  473. portalRight = right;
  474. rightIndex = i;
  475. } else {
  476. // Right over left, insert left to path and restart scan from portal left point.
  477. pts.push(portalLeft);
  478. // Make current left the new apex.
  479. portalApex = portalLeft;
  480. apexIndex = leftIndex;
  481. // Reset portal
  482. portalLeft = portalApex;
  483. portalRight = portalApex;
  484. leftIndex = apexIndex;
  485. rightIndex = apexIndex;
  486. // Restart scan
  487. i = apexIndex;
  488. continue;
  489. }
  490. }
  491. // Update left vertex.
  492. if (utils.triarea2(portalApex, portalLeft, left) >= 0.0) {
  493. if (utils.vequal(portalApex, portalLeft) || utils.triarea2(portalApex, portalRight, left) < 0.0) {
  494. // Tighten the funnel.
  495. portalLeft = left;
  496. leftIndex = i;
  497. } else {
  498. // Left over right, insert right to path and restart scan from portal right point.
  499. pts.push(portalRight);
  500. // Make current right the new apex.
  501. portalApex = portalRight;
  502. apexIndex = rightIndex;
  503. // Reset portal
  504. portalLeft = portalApex;
  505. portalRight = portalApex;
  506. leftIndex = apexIndex;
  507. rightIndex = apexIndex;
  508. // Restart scan
  509. i = apexIndex;
  510. continue;
  511. }
  512. }
  513. }
  514. if (pts.length === 0 || !utils.vequal(pts[pts.length - 1], portals[portals.length - 1].left)) {
  515. // Append last point to path.
  516. pts.push(portals[portals.length - 1].left);
  517. }
  518. this.path = pts;
  519. return pts;
  520. }
  521. }]);
  522. return Channel;
  523. }();
  524. module.exports = Channel;
  525. },{"./utils":7}],6:[function(require,module,exports){
  526. 'use strict';
  527. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  528. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  529. /* global THREE */
  530. var utils = require('./utils');
  531. var AStar = require('./AStar');
  532. var Builder = require('./Builder');
  533. var Channel = require('./Channel');
  534. /**
  535. * Defines an instance of the pathfinding module, with one or more zones.
  536. */
  537. var Path = function () {
  538. function Path() {
  539. _classCallCheck(this, Path);
  540. this.zones = {};
  541. }
  542. /**
  543. * (Static) Builds a zone/node set from navigation mesh geometry.
  544. * @param {THREE.Geometry} geometry
  545. * @return {Zone}
  546. */
  547. _createClass(Path, [{
  548. key: 'setZoneData',
  549. /**
  550. * Sets data for the given zone.
  551. * @param {string} zoneID
  552. * @param {Zone} zone
  553. */
  554. value: function setZoneData(zoneID, zone) {
  555. this.zones[zoneID] = zone;
  556. }
  557. /**
  558. * Returns closest node group ID for given position.
  559. * @param {string} zoneID
  560. * @param {THREE.Vector3} position
  561. * @return {number}
  562. */
  563. }, {
  564. key: 'getGroup',
  565. value: function getGroup(zoneID, position) {
  566. if (!this.zones[zoneID]) return null;
  567. var closestNodeGroup = null;
  568. var distance = Math.pow(50, 2);
  569. this.zones[zoneID].groups.forEach(function (group, index) {
  570. group.forEach(function (node) {
  571. var measuredDistance = utils.distanceToSquared(node.centroid, position);
  572. if (measuredDistance < distance) {
  573. closestNodeGroup = index;
  574. distance = measuredDistance;
  575. }
  576. });
  577. });
  578. return closestNodeGroup;
  579. }
  580. /**
  581. * Returns a random node within a given range of a given position.
  582. * @param {string} zoneID
  583. * @param {number} groupID
  584. * @param {THREE.Vector3} nearPosition
  585. * @param {number} nearRange
  586. * @return {Node}
  587. */
  588. }, {
  589. key: 'getRandomNode',
  590. value: function getRandomNode(zoneID, groupID, nearPosition, nearRange) {
  591. if (!this.zones[zoneID]) return new THREE.Vector3();
  592. nearPosition = nearPosition || null;
  593. nearRange = nearRange || 0;
  594. var candidates = [];
  595. var polygons = this.zones[zoneID].groups[groupID];
  596. polygons.forEach(function (p) {
  597. if (nearPosition && nearRange) {
  598. if (utils.distanceToSquared(nearPosition, p.centroid) < nearRange * nearRange) {
  599. candidates.push(p.centroid);
  600. }
  601. } else {
  602. candidates.push(p.centroid);
  603. }
  604. });
  605. return utils.sample(candidates) || new THREE.Vector3();
  606. }
  607. /**
  608. * Returns the closest node to the target position.
  609. * @param {THREE.Vector3} position
  610. * @param {string} zoneID
  611. * @param {number} groupID
  612. * @param {boolean} checkPolygon
  613. * @return {Node}
  614. */
  615. }, {
  616. key: 'getClosestNode',
  617. value: function getClosestNode(position, zoneID, groupID) {
  618. var checkPolygon = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
  619. var nodes = this.zones[zoneID].groups[groupID];
  620. var vertices = this.zones[zoneID].vertices;
  621. var closestNode = null;
  622. var closestDistance = Infinity;
  623. nodes.forEach(function (node) {
  624. var distance = utils.distanceToSquared(node.centroid, position);
  625. if (distance < closestDistance && (!checkPolygon || utils.isVectorInPolygon(position, node, vertices))) {
  626. closestNode = node;
  627. closestDistance = distance;
  628. }
  629. });
  630. return closestNode;
  631. }
  632. /**
  633. * Returns a path between given start and end points. If a complete path
  634. * cannot be found, will return the nearest endpoint available.
  635. *
  636. * @param {THREE.Vector3} startPosition Start position.
  637. * @param {THREE.Vector3} targetPosition Destination.
  638. * @param {string} zoneID ID of current zone.
  639. * @param {number} groupID Current group ID.
  640. * @return {Array<THREE.Vector3>} Array of points defining the path.
  641. */
  642. }, {
  643. key: 'findPath',
  644. value: function findPath(startPosition, targetPosition, zoneID, groupID) {
  645. var nodes = this.zones[zoneID].groups[groupID];
  646. var vertices = this.zones[zoneID].vertices;
  647. var closestNode = this.getClosestNode(startPosition, zoneID, groupID);
  648. var farthestNode = this.getClosestNode(targetPosition, zoneID, groupID, true);
  649. // If we can't find any node, just go straight to the target
  650. if (!closestNode || !farthestNode) {
  651. return null;
  652. }
  653. var paths = AStar.search(nodes, closestNode, farthestNode);
  654. var getPortalFromTo = function getPortalFromTo(a, b) {
  655. for (var i = 0; i < a.neighbours.length; i++) {
  656. if (a.neighbours[i] === b.id) {
  657. return a.portals[i];
  658. }
  659. }
  660. };
  661. // We have the corridor, now pull the rope.
  662. var channel = new Channel();
  663. channel.push(startPosition);
  664. for (var i = 0; i < paths.length; i++) {
  665. var polygon = paths[i];
  666. var nextPolygon = paths[i + 1];
  667. if (nextPolygon) {
  668. var portals = getPortalFromTo(polygon, nextPolygon);
  669. channel.push(vertices[portals[0]], vertices[portals[1]]);
  670. }
  671. }
  672. channel.push(targetPosition);
  673. channel.stringPull();
  674. // Return the path, omitting first position (which is already known).
  675. var path = channel.path.map(function (c) {
  676. return new THREE.Vector3(c.x, c.y, c.z);
  677. });
  678. path.shift();
  679. return path;
  680. }
  681. }], [{
  682. key: 'createZone',
  683. value: function createZone(geometry) {
  684. return Builder.buildZone(geometry);
  685. }
  686. }]);
  687. return Path;
  688. }();
  689. /**
  690. * Clamps a step along the navmesh, given start and desired endpoint. May be
  691. * used to constrain first-person / WASD controls.
  692. *
  693. * @param {THREE.Vector3} start
  694. * @param {THREE.Vector3} end Desired endpoint.
  695. * @param {Node} node
  696. * @param {string} zoneID
  697. * @param {number} groupID
  698. * @param {THREE.Vector3} endTarget Updated endpoint.
  699. * @return {Node} Updated node.
  700. */
  701. Path.prototype.clampStep = function () {
  702. var point = new THREE.Vector3();
  703. var plane = new THREE.Plane();
  704. var triangle = new THREE.Triangle();
  705. var closestNode = void 0;
  706. var closestPoint = new THREE.Vector3();
  707. var closestDistance = void 0;
  708. return function (start, end, node, zoneID, groupID, endTarget) {
  709. var vertices = this.zones[zoneID].vertices;
  710. var nodes = this.zones[zoneID].groups[groupID];
  711. var nodeQueue = [node];
  712. var nodeDepth = {};
  713. nodeDepth[node.id] = 0;
  714. closestNode = undefined;
  715. closestPoint.set(0, 0, 0);
  716. closestDistance = Infinity;
  717. // Project the step along the current node.
  718. plane.setFromCoplanarPoints(vertices[node.vertexIds[0]], vertices[node.vertexIds[1]], vertices[node.vertexIds[2]]);
  719. plane.projectPoint(end, point);
  720. end.copy(point);
  721. for (var currentNode = nodeQueue.pop(); currentNode; currentNode = nodeQueue.pop()) {
  722. triangle.set(vertices[currentNode.vertexIds[0]], vertices[currentNode.vertexIds[1]], vertices[currentNode.vertexIds[2]]);
  723. triangle.closestPointToPoint(end, point);
  724. if (point.distanceToSquared(end) < closestDistance) {
  725. closestNode = currentNode;
  726. closestPoint.copy(point);
  727. closestDistance = point.distanceToSquared(end);
  728. }
  729. var depth = nodeDepth[currentNode];
  730. if (depth > 2) continue;
  731. for (var i = 0; i < currentNode.neighbours.length; i++) {
  732. var neighbour = nodes[currentNode.neighbours[i]];
  733. if (neighbour.id in nodeDepth) continue;
  734. nodeQueue.push(neighbour);
  735. nodeDepth[neighbour.id] = depth + 1;
  736. }
  737. }
  738. endTarget.copy(closestPoint);
  739. return closestNode;
  740. };
  741. }();
  742. /**
  743. * Defines a zone of interconnected groups on a navigation mesh.
  744. *
  745. * @type {Object}
  746. * @property {Array<Group>} groups
  747. * @property {Array<THREE.Vector3} vertices
  748. */
  749. var Zone = {}; // jshint ignore:line
  750. /**
  751. * Defines a group within a navigation mesh.
  752. *
  753. * @type {Object}
  754. */
  755. var Group = {}; // jshint ignore:line
  756. /**
  757. * Defines a node (or polygon) within a group.
  758. *
  759. * @type {Object}
  760. * @property {number} id
  761. * @property {Array<number>} neighbours IDs of neighboring nodes.
  762. * @property {Array<number} vertexIds
  763. * @property {THREE.Vector3} centroid
  764. * @property {Array<Array<number>>} portals Array of portals, each defined by two vertex IDs.
  765. * @property {boolean} closed
  766. * @property {number} cost
  767. */
  768. var Node = {}; // jshint ignore:line
  769. module.exports = Path;
  770. },{"./AStar":2,"./Builder":4,"./Channel":5,"./utils":7}],7:[function(require,module,exports){
  771. 'use strict';
  772. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  773. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  774. var Utils = function () {
  775. function Utils() {
  776. _classCallCheck(this, Utils);
  777. }
  778. _createClass(Utils, null, [{
  779. key: 'computeCentroids',
  780. value: function computeCentroids(geometry) {
  781. var f, fl, face;
  782. for (f = 0, fl = geometry.faces.length; f < fl; f++) {
  783. face = geometry.faces[f];
  784. face.centroid = new THREE.Vector3(0, 0, 0);
  785. face.centroid.add(geometry.vertices[face.a]);
  786. face.centroid.add(geometry.vertices[face.b]);
  787. face.centroid.add(geometry.vertices[face.c]);
  788. face.centroid.divideScalar(3);
  789. }
  790. }
  791. }, {
  792. key: 'roundNumber',
  793. value: function roundNumber(number, decimals) {
  794. var newnumber = Number(number + '').toFixed(parseInt(decimals));
  795. return parseFloat(newnumber);
  796. }
  797. }, {
  798. key: 'sample',
  799. value: function sample(list) {
  800. return list[Math.floor(Math.random() * list.length)];
  801. }
  802. }, {
  803. key: 'mergeVertexIds',
  804. value: function mergeVertexIds(aList, bList) {
  805. var sharedVertices = [];
  806. aList.forEach(function (vID) {
  807. if (bList.indexOf(vID) >= 0) {
  808. sharedVertices.push(vID);
  809. }
  810. });
  811. if (sharedVertices.length < 2) return [];
  812. if (sharedVertices.includes(aList[0]) && sharedVertices.includes(aList[aList.length - 1])) {
  813. // Vertices on both edges are bad, so shift them once to the left
  814. aList.push(aList.shift());
  815. }
  816. if (sharedVertices.includes(bList[0]) && sharedVertices.includes(bList[bList.length - 1])) {
  817. // Vertices on both edges are bad, so shift them once to the left
  818. bList.push(bList.shift());
  819. }
  820. // Again!
  821. sharedVertices = [];
  822. aList.forEach(function (vId) {
  823. if (bList.includes(vId)) {
  824. sharedVertices.push(vId);
  825. }
  826. });
  827. var clockwiseMostSharedVertex = sharedVertices[1];
  828. var counterClockwiseMostSharedVertex = sharedVertices[0];
  829. var cList = aList.slice();
  830. while (cList[0] !== clockwiseMostSharedVertex) {
  831. cList.push(cList.shift());
  832. }
  833. var c = 0;
  834. var temp = bList.slice();
  835. while (temp[0] !== counterClockwiseMostSharedVertex) {
  836. temp.push(temp.shift());
  837. if (c++ > 10) throw new Error('Unexpected state');
  838. }
  839. // Shave
  840. temp.shift();
  841. temp.pop();
  842. cList = cList.concat(temp);
  843. return cList;
  844. }
  845. }, {
  846. key: 'setPolygonCentroid',
  847. value: function setPolygonCentroid(polygon, navigationMesh) {
  848. var sum = new THREE.Vector3();
  849. var vertices = navigationMesh.vertices;
  850. polygon.vertexIds.forEach(function (vId) {
  851. sum.add(vertices[vId]);
  852. });
  853. sum.divideScalar(polygon.vertexIds.length);
  854. polygon.centroid.copy(sum);
  855. }
  856. }, {
  857. key: 'cleanPolygon',
  858. value: function cleanPolygon(polygon, navigationMesh) {
  859. var newVertexIds = [];
  860. var vertices = navigationMesh.vertices;
  861. for (var i = 0; i < polygon.vertexIds.length; i++) {
  862. var vertex = vertices[polygon.vertexIds[i]];
  863. var nextVertexId, previousVertexId;
  864. var nextVertex, previousVertex;
  865. if (i === 0) {
  866. nextVertexId = polygon.vertexIds[1];
  867. previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 1];
  868. } else if (i === polygon.vertexIds.length - 1) {
  869. nextVertexId = polygon.vertexIds[0];
  870. previousVertexId = polygon.vertexIds[polygon.vertexIds.length - 2];
  871. } else {
  872. nextVertexId = polygon.vertexIds[i + 1];
  873. previousVertexId = polygon.vertexIds[i - 1];
  874. }
  875. nextVertex = vertices[nextVertexId];
  876. previousVertex = vertices[previousVertexId];
  877. var a = nextVertex.clone().sub(vertex);
  878. var b = previousVertex.clone().sub(vertex);
  879. var angle = a.angleTo(b);
  880. if (angle > Math.PI - 0.01 && angle < Math.PI + 0.01) {
  881. // Remove the neighbours who had this vertex
  882. var goodNeighbours = [];
  883. polygon.neighbours.forEach(function (neighbour) {
  884. if (!neighbour.vertexIds.includes(polygon.vertexIds[i])) {
  885. goodNeighbours.push(neighbour);
  886. }
  887. });
  888. polygon.neighbours = goodNeighbours;
  889. // TODO cleanup the list of vertices and rebuild vertexIds for all polygons
  890. } else {
  891. newVertexIds.push(polygon.vertexIds[i]);
  892. }
  893. }
  894. polygon.vertexIds = newVertexIds;
  895. this.setPolygonCentroid(polygon, navigationMesh);
  896. }
  897. }, {
  898. key: 'isConvex',
  899. value: function isConvex(polygon, navigationMesh) {
  900. var vertices = navigationMesh.vertices;
  901. if (polygon.vertexIds.length < 3) return false;
  902. var convex = true;
  903. var total = 0;
  904. var results = [];
  905. for (var i = 0; i < polygon.vertexIds.length; i++) {
  906. var vertex = vertices[polygon.vertexIds[i]];
  907. var nextVertex, previousVertex;
  908. if (i === 0) {
  909. nextVertex = vertices[polygon.vertexIds[1]];
  910. previousVertex = vertices[polygon.vertexIds[polygon.vertexIds.length - 1]];
  911. } else if (i === polygon.vertexIds.length - 1) {
  912. nextVertex = vertices[polygon.vertexIds[0]];
  913. previousVertex = vertices[polygon.vertexIds[polygon.vertexIds.length - 2]];
  914. } else {
  915. nextVertex = vertices[polygon.vertexIds[i + 1]];
  916. previousVertex = vertices[polygon.vertexIds[i - 1]];
  917. }
  918. var a = nextVertex.clone().sub(vertex);
  919. var b = previousVertex.clone().sub(vertex);
  920. var angle = a.angleTo(b);
  921. total += angle;
  922. if (angle === Math.PI || angle === 0) return false;
  923. var r = a.cross(b).y;
  924. results.push(r);
  925. }
  926. // if ( total > (polygon.vertexIds.length-2)*Math.PI ) return false;
  927. results.forEach(function (r) {
  928. if (r === 0) convex = false;
  929. });
  930. if (results[0] > 0) {
  931. results.forEach(function (r) {
  932. if (r < 0) convex = false;
  933. });
  934. } else {
  935. results.forEach(function (r) {
  936. if (r > 0) convex = false;
  937. });
  938. }
  939. return convex;
  940. }
  941. }, {
  942. key: 'distanceToSquared',
  943. value: function distanceToSquared(a, b) {
  944. var dx = a.x - b.x;
  945. var dy = a.y - b.y;
  946. var dz = a.z - b.z;
  947. return dx * dx + dy * dy + dz * dz;
  948. }
  949. //+ Jonas Raoni Soares Silva
  950. //@ http://jsfromhell.com/math/is-point-in-poly [rev. #0]
  951. }, {
  952. key: 'isPointInPoly',
  953. value: function isPointInPoly(poly, pt) {
  954. for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) {
  955. (poly[i].z <= pt.z && pt.z < poly[j].z || poly[j].z <= pt.z && pt.z < poly[i].z) && pt.x < (poly[j].x - poly[i].x) * (pt.z - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x && (c = !c);
  956. }return c;
  957. }
  958. }, {
  959. key: 'isVectorInPolygon',
  960. value: function isVectorInPolygon(vector, polygon, vertices) {
  961. // reference point will be the centroid of the polygon
  962. // We need to rotate the vector as well as all the points which the polygon uses
  963. var lowestPoint = 100000;
  964. var highestPoint = -100000;
  965. var polygonVertices = [];
  966. polygon.vertexIds.forEach(function (vId) {
  967. lowestPoint = Math.min(vertices[vId].y, lowestPoint);
  968. highestPoint = Math.max(vertices[vId].y, highestPoint);
  969. polygonVertices.push(vertices[vId]);
  970. });
  971. if (vector.y < highestPoint + 0.5 && vector.y > lowestPoint - 0.5 && this.isPointInPoly(polygonVertices, vector)) {
  972. return true;
  973. }
  974. return false;
  975. }
  976. }, {
  977. key: 'triarea2',
  978. value: function triarea2(a, b, c) {
  979. var ax = b.x - a.x;
  980. var az = b.z - a.z;
  981. var bx = c.x - a.x;
  982. var bz = c.z - a.z;
  983. return bx * az - ax * bz;
  984. }
  985. }, {
  986. key: 'vequal',
  987. value: function vequal(a, b) {
  988. return this.distanceToSquared(a, b) < 0.00001;
  989. }
  990. }, {
  991. key: 'array_intersect',
  992. value: function array_intersect() {
  993. var i = void 0,
  994. shortest = void 0,
  995. nShortest = void 0,
  996. n = void 0,
  997. len = void 0,
  998. ret = [],
  999. obj = {},
  1000. nOthers = void 0;
  1001. nOthers = arguments.length - 1;
  1002. nShortest = arguments[0].length;
  1003. shortest = 0;
  1004. for (i = 0; i <= nOthers; i++) {
  1005. n = arguments[i].length;
  1006. if (n < nShortest) {
  1007. shortest = i;
  1008. nShortest = n;
  1009. }
  1010. }
  1011. for (i = 0; i <= nOthers; i++) {
  1012. n = i === shortest ? 0 : i || shortest; //Read the shortest array first. Read the first array instead of the shortest
  1013. len = arguments[n].length;
  1014. for (var j = 0; j < len; j++) {
  1015. var elem = arguments[n][j];
  1016. if (obj[elem] === i - 1) {
  1017. if (i === nOthers) {
  1018. ret.push(elem);
  1019. obj[elem] = 0;
  1020. } else {
  1021. obj[elem] = i;
  1022. }
  1023. } else if (i === 0) {
  1024. obj[elem] = 0;
  1025. }
  1026. }
  1027. }
  1028. return ret;
  1029. }
  1030. }]);
  1031. return Utils;
  1032. }();
  1033. module.exports = Utils;
  1034. },{}],8:[function(require,module,exports){
  1035. 'use strict';
  1036. require('./nav-mesh');
  1037. require('./nav-agent');
  1038. require('./system');
  1039. },{"./nav-agent":9,"./nav-mesh":10,"./system":11}],9:[function(require,module,exports){
  1040. 'use strict';
  1041. module.exports = AFRAME.registerComponent('nav-agent', {
  1042. schema: {
  1043. destination: { type: 'vec3' },
  1044. active: { default: false },
  1045. speed: { default: 2 }
  1046. },
  1047. init: function init() {
  1048. this.system = this.el.sceneEl.systems.nav;
  1049. this.system.addAgent(this);
  1050. this.group = null;
  1051. this.path = [];
  1052. this.raycaster = new THREE.Raycaster();
  1053. },
  1054. remove: function remove() {
  1055. this.system.removeAgent(this);
  1056. },
  1057. update: function update() {
  1058. this.path.length = 0;
  1059. },
  1060. tick: function () {
  1061. var vDest = new THREE.Vector3();
  1062. var vDelta = new THREE.Vector3();
  1063. var vNext = new THREE.Vector3();
  1064. return function (t, dt) {
  1065. var el = this.el;
  1066. var data = this.data;
  1067. var raycaster = this.raycaster;
  1068. var speed = data.speed * dt / 1000;
  1069. if (!data.active) return;
  1070. // Use PatrolJS pathfinding system to get shortest path to target.
  1071. if (!this.path.length) {
  1072. var position = this.el.object3D.position;
  1073. this.group = this.group || this.system.getGroup(position);
  1074. this.path = this.system.getPath(position, vDest.copy(data.destination), this.group);
  1075. this.path = this.path || [];
  1076. el.emit('nav-start');
  1077. }
  1078. // If no path is found, exit.
  1079. if (!this.path.length) {
  1080. console.warn('[nav] Unable to find path to %o.', data.destination);
  1081. this.el.setAttribute('nav-agent', { active: false });
  1082. el.emit('nav-end');
  1083. return;
  1084. }
  1085. // Current segment is a vector from current position to next waypoint.
  1086. var vCurrent = el.object3D.position;
  1087. var vWaypoint = this.path[0];
  1088. vDelta.subVectors(vWaypoint, vCurrent);
  1089. var distance = vDelta.length();
  1090. var gazeTarget = void 0;
  1091. if (distance < speed) {
  1092. // If <1 step from current waypoint, discard it and move toward next.
  1093. this.path.shift();
  1094. // After discarding the last waypoint, exit pathfinding.
  1095. if (!this.path.length) {
  1096. this.el.setAttribute('nav-agent', { active: false });
  1097. el.emit('nav-end');
  1098. return;
  1099. } else {
  1100. gazeTarget = this.path[0];
  1101. }
  1102. } else {
  1103. // If still far away from next waypoint, find next position for
  1104. // the current frame.
  1105. vNext.copy(vDelta.setLength(speed)).add(vCurrent);
  1106. gazeTarget = vWaypoint;
  1107. }
  1108. // Look at the next waypoint.
  1109. gazeTarget.y = vCurrent.y;
  1110. el.object3D.lookAt(gazeTarget);
  1111. // Raycast against the nav mesh, to keep the agent moving along the
  1112. // ground, not traveling in a straight line from higher to lower waypoints.
  1113. raycaster.ray.origin.copy(vNext);
  1114. raycaster.ray.origin.y += 1.5;
  1115. raycaster.ray.direction.y = -1;
  1116. var intersections = raycaster.intersectObject(this.system.getNavMesh());
  1117. if (!intersections.length) {
  1118. // Raycasting failed. Step toward the waypoint and hope for the best.
  1119. vCurrent.copy(vNext);
  1120. } else {
  1121. // Re-project next position onto nav mesh.
  1122. vDelta.subVectors(intersections[0].point, vCurrent);
  1123. vCurrent.add(vDelta.setLength(speed));
  1124. }
  1125. };
  1126. }()
  1127. });
  1128. },{}],10:[function(require,module,exports){
  1129. 'use strict';
  1130. /**
  1131. * nav-mesh
  1132. *
  1133. * Waits for a mesh to be loaded on the current entity, then sets it as the
  1134. * nav mesh in the pathfinding system.
  1135. */
  1136. module.exports = AFRAME.registerComponent('nav-mesh', {
  1137. init: function init() {
  1138. this.system = this.el.sceneEl.systems.nav;
  1139. this.loadNavMesh();
  1140. this.el.addEventListener('model-loaded', this.loadNavMesh.bind(this));
  1141. },
  1142. loadNavMesh: function loadNavMesh() {
  1143. var object = this.el.getObject3D('mesh');
  1144. if (!object) return;
  1145. var navMesh = void 0;
  1146. object.traverse(function (node) {
  1147. if (node.isMesh) navMesh = node;
  1148. });
  1149. if (!navMesh) return;
  1150. this.system.setNavMesh(navMesh);
  1151. }
  1152. });
  1153. },{}],11:[function(require,module,exports){
  1154. 'use strict';
  1155. var Path = require('three-pathfinding');
  1156. var pathfinder = new Path();
  1157. var ZONE = 'level';
  1158. /**
  1159. * nav
  1160. *
  1161. * Pathfinding system, using PatrolJS.
  1162. */
  1163. module.exports = AFRAME.registerSystem('nav', {
  1164. init: function init() {
  1165. this.navMesh = null;
  1166. this.agents = new Set();
  1167. },
  1168. /**
  1169. * @param {THREE.Mesh} mesh
  1170. */
  1171. setNavMesh: function setNavMesh(mesh) {
  1172. var geometry = mesh.geometry.isBufferGeometry ? new THREE.Geometry().fromBufferGeometry(mesh.geometry) : mesh.geometry;
  1173. this.navMesh = new THREE.Mesh(geometry);
  1174. pathfinder.setZoneData(ZONE, Path.createZone(this.navMesh.geometry));
  1175. },
  1176. /**
  1177. * @return {THREE.Mesh}
  1178. */
  1179. getNavMesh: function getNavMesh() {
  1180. return this.navMesh;
  1181. },
  1182. /**
  1183. * @param {NavAgent} ctrl
  1184. */
  1185. addAgent: function addAgent(ctrl) {
  1186. this.agents.add(ctrl);
  1187. },
  1188. /**
  1189. * @param {NavAgent} ctrl
  1190. */
  1191. removeAgent: function removeAgent(ctrl) {
  1192. this.agents.remove(ctrl);
  1193. },
  1194. /**
  1195. * @param {THREE.Vector3} start
  1196. * @param {THREE.Vector3} end
  1197. * @param {number} groupID
  1198. * @return {Array<THREE.Vector3>}
  1199. */
  1200. getPath: function getPath(start, end, groupID) {
  1201. return pathfinder.findPath(start, end, ZONE, groupID);
  1202. },
  1203. /**
  1204. * @param {THREE.Vector3} position
  1205. * @return {number}
  1206. */
  1207. getGroup: function getGroup(position) {
  1208. return pathfinder.getGroup(ZONE, position);
  1209. },
  1210. /**
  1211. * @param {THREE.Vector3} position
  1212. * @param {number} groupID
  1213. * @return {Node}
  1214. */
  1215. getNode: function getNode(position, groupID) {
  1216. return pathfinder.getClosestNode(position, ZONE, groupID, true);
  1217. },
  1218. /**
  1219. * @param {THREE.Vector3} start Starting position.
  1220. * @param {THREE.Vector3} end Desired ending position.
  1221. * @param {number} groupID
  1222. * @param {Node} node
  1223. * @param {THREE.Vector3} endTarget (Output) Adjusted step end position.
  1224. * @return {Node} Current node, after step is taken.
  1225. */
  1226. clampStep: function clampStep(start, end, groupID, node, endTarget) {
  1227. if (!this.navMesh || !node) {
  1228. endTarget.copy(end);
  1229. return this.navMesh ? this.getNode(end, groupID) : null;
  1230. }
  1231. return pathfinder.clampStep(start, end, node, ZONE, groupID, endTarget);
  1232. }
  1233. });
  1234. },{"three-pathfinding":6}]},{},[1]);