Skip to content

Commit

Permalink
NavMesh: Improve pathfinding
Browse files Browse the repository at this point in the history
  • Loading branch information
Mugen87 committed Sep 1, 2018
1 parent c36b7a2 commit 4fba0f8
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 173 deletions.
211 changes: 154 additions & 57 deletions build/yuka.js
Original file line number Diff line number Diff line change
Expand Up @@ -2747,9 +2747,9 @@

this._buildRegions( sortedEdgeList );

// compute centroids of convex regions
// ensure unique node indices for all twin edges

this._buildCentrois();
this._buildNodeIndices();

// now build the navigation graph

Expand All @@ -2774,7 +2774,9 @@
let closesNodeIndex = null;
let minDistance = Infinity;

const nodes = graph.getNodes();
const nodes = [];

graph.getNodes( nodes );

for ( const node of nodes ) {

Expand Down Expand Up @@ -2803,13 +2805,17 @@

do {

const distance = point.squaredDistanceTo( edge.from() );
if ( edge.twin || edge.prev.twin ) {

if ( distance < minDistance ) {
const distance = point.squaredDistanceTo( edge.from() );

minDistance = distance;
if ( distance < minDistance ) {

minDistance = distance;

closesNodeIndex = edge.nodeIndex;
closesNodeIndex = edge.twin ? edge.nodeIndex : edge.prev.twin.nodeIndex;

}

}

Expand Down Expand Up @@ -2872,7 +2878,7 @@

const path = [];

// 1. ensure start and end point are inside of navmesh
// 1. ensure start and end point are inside the navmesh

if ( fromRegion === null || toRegion === null ) {

Expand Down Expand Up @@ -2906,6 +2912,50 @@

const shortestPath = astar.getPath();

let count = shortestPath.length;

// smoothing

for ( let i = 0, l = shortestPath.length; i < l; i ++ ) {

const index = shortestPath[ i ];
const node = graph.getNode( index );

if ( fromRegion.contains( node.position ) === false ) {

count = i;
break;

}

}

shortestPath.splice( 0, count - 1 );

//

shortestPath.reverse();

count = shortestPath.length;

for ( let i = 0, l = shortestPath.length; i < l; i ++ ) {

const index = shortestPath[ i ];
const node = graph.getNode( index );

if ( toRegion.contains( node.position ) === false ) {

count = i;
break;

}

}

shortestPath.splice( 0, count - 1 );

shortestPath.reverse();

// create final path

path.push( new Vector3().copy( from ) );
Expand Down Expand Up @@ -2994,83 +3044,106 @@

}

//

for ( const region of regions ) {

region.computeCentroid();

}

}

_buildCentrois() {
_buildNodeIndices() {

const regions = this.regions;

const indicesMap = new Map();
let nextNodeIndex = 0;

for ( const region of regions ) {

let edge = region.edge;
let count = 0;

region.centroid.set( 0, 0, 0 );

do {

region.centroid.add( edge.from() );
// only edges with a twin reference needs to be considered

if ( edge.twin !== null ) {

let nodeIndex = - 1;
const position = edge.from();

// check all existing entries

for ( const [ index, pos ] of indicesMap.entries() ) {

if ( position.equals( pos ) === true ) {

// found, use the existing index

nodeIndex = index;
break;

}

}

// if no suitable index was found, create a new one

if ( nodeIndex === - 1 ) {

nodeIndex = nextNodeIndex ++;
indicesMap.set( nodeIndex, position );

}

// assign unique node index to edge

count ++;
edge.nodeIndex = nodeIndex;

}

edge = edge.next;

} while ( edge !== region.edge );

region.centroid.divideScalar( count );

}

return this;

}

_buildGraph() {

const graph = this.graph;
const regions = this.regions;
let nextNodeIndex = 0;

// generate unique navigation nodes
// for each region, the code creates an array of directly accessible node indices

const nodeIndicesPerRegion = new Set();

for ( const region of regions ) {

const nodeIndices = new Array();
nodeIndicesPerRegion.add( nodeIndices );

let edge = region.edge;

do {

let nodeIndex = - 1;
const position = edge.from();

// check all existing nodes
if ( edge.twin !== null ) {

for ( const [ index, node ] of graph._nodes.entries() ) {
nodeIndices.push( edge.nodeIndex, edge.twin.nodeIndex );

if ( position.equals( node.position ) === true ) {
// add node to graph if necessary

// found, use the existing node
if ( graph.hasNode( edge.nodeIndex ) === false ) {

nodeIndex = index;
break;
graph.addNode( new NavNode( edge.nodeIndex, edge.from() ) );

}

}

// if no existing node was found, create a new one

if ( nodeIndex === - 1 ) {

nodeIndex = nextNodeIndex ++;

graph.addNode( new NavNode( nodeIndex, position ) );

}

// assign unique index to edge

edge.nodeIndex = nodeIndex;

edge = edge.next;

} while ( edge !== region.edge );
Expand All @@ -3079,30 +3152,30 @@

// add navigation edges

for ( const region of regions ) {
for ( const indices of nodeIndicesPerRegion ) {

let edge = region.edge;
for ( const from of indices ) {

do {
for ( const to of indices ) {

const from = edge.nodeIndex;
const to = edge.next.nodeIndex;
const cost = edge.length();
if ( from !== to ) {

graph.addEdge( new NavEdge( from, to, cost ) );
if ( graph.hasEdge( from, to ) === false ) {

// since it's a directed graph, the code creates the opponent navigation edge
// if there is no twin defined
const nodeFrom = graph.getNode( from );
const nodeTo = graph.getNode( to );

if ( edge.twin === null ) {
const cost = nodeFrom.position.distanceTo( nodeTo.position );

graph.addEdge( new NavEdge( to, from, cost ) );
graph.addEdge( new NavEdge( from, to, cost ) );

}
}

edge = edge.next;
}

} while ( edge !== region.edge );
}

}

}

Expand Down Expand Up @@ -3227,6 +3300,30 @@

}

computeCentroid() {

const centroid = this.centroid;
let edge = this.edge;
let count = 0;

centroid.set( 0, 0, 0 );

do {

centroid.add( edge.from() );

count ++;

edge = edge.next;

} while ( edge !== this.edge );

centroid.divideScalar( count );

return this;

}

convex() {

let edge = this.edge;
Expand Down
2 changes: 1 addition & 1 deletion build/yuka.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit 4fba0f8

Please sign in to comment.