diff --git a/.gitmodules b/.gitmodules index d42c10d6..18405cfe 100755 --- a/.gitmodules +++ b/.gitmodules @@ -20,5 +20,5 @@ path = include/TF2_NavFile_Reader url = https://github.com/nullworks/TF2_NavFile_Reader.git [submodule "external/MicroPather"] - path = src/MicroPather - url = https://github.com/leethomason/MicroPather.git + path = external/MicroPather + url = https://github.com/leethomason/MicroPather diff --git a/external/MicroPather b/external/MicroPather new file mode 160000 index 00000000..33a3b840 --- /dev/null +++ b/external/MicroPather @@ -0,0 +1 @@ +Subproject commit 33a3b8403f1bc3937c9d364fe6c3977169bee3b5 diff --git a/external/MicroPather/MakefileSpeed b/external/MicroPather/MakefileSpeed deleted file mode 100755 index a0ffd4a4..00000000 --- a/external/MicroPather/MakefileSpeed +++ /dev/null @@ -1,113 +0,0 @@ -#**************************************************************************** -# -# Makefile for Micropather test. -# Lee Thomason -# www.grinninglizard.com -# -# This is a GNU make (gmake) makefile -#**************************************************************************** - -# DEBUG can be set to YES to include debugging info, or NO otherwise -DEBUG := NO - -# PROFILE can be set to YES to include profiling info, or NO otherwise -PROFILE := NO - -#**************************************************************************** - -CC := gcc -CXX := g++ -LD := g++ -AR := ar rc -RANLIB := ranlib - -DEBUG_CFLAGS := -Wall -Wno-format -g -DDEBUG -std=c++11 -RELEASE_CFLAGS := -Wall -Wno-unknown-pragmas -Wno-format -O3 -std=c++11 - -LIBS := - -DEBUG_CXXFLAGS := ${DEBUG_CFLAGS} -RELEASE_CXXFLAGS := ${RELEASE_CFLAGS} - -DEBUG_LDFLAGS := -g -RELEASE_LDFLAGS := - -ifeq (YES, ${DEBUG}) - CFLAGS := ${DEBUG_CFLAGS} - CXXFLAGS := ${DEBUG_CXXFLAGS} - LDFLAGS := ${DEBUG_LDFLAGS} -else - CFLAGS := ${RELEASE_CFLAGS} - CXXFLAGS := ${RELEASE_CXXFLAGS} - LDFLAGS := ${RELEASE_LDFLAGS} -endif - -ifeq (YES, ${PROFILE}) - CFLAGS := ${CFLAGS} -pg -O3 - CXXFLAGS := ${CXXFLAGS} -pg -O3 - LDFLAGS := ${LDFLAGS} -pg -endif - -#**************************************************************************** -# Preprocessor directives -#**************************************************************************** - - -#**************************************************************************** -# Include paths -#**************************************************************************** - -#INCS := -I/usr/include/g++-2 -I/usr/local/include -INCS := - - -#**************************************************************************** -# Makefile code common to all platforms -#**************************************************************************** - -CFLAGS := ${CFLAGS} ${DEFS} -CXXFLAGS := ${CXXFLAGS} ${DEFS} - -#**************************************************************************** -# Targets of the build -#**************************************************************************** - -OUTPUT := speed - -all: ${OUTPUT} - - -#**************************************************************************** -# Source files -#**************************************************************************** - -SRCS := micropather.cpp speed.cpp - -# Add on the sources for libraries -SRCS := ${SRCS} - -OBJS := $(addsuffix .o,$(basename ${SRCS})) - -#**************************************************************************** -# Output -#**************************************************************************** - -${OUTPUT}: ${OBJS} - ${LD} -o $@ ${LDFLAGS} ${OBJS} ${LIBS} ${EXTRA_LIBS} - -#**************************************************************************** -# common rules -#**************************************************************************** - -# Rules for compiling source files to object files -%.o : %.cpp - ${CXX} -c ${CXXFLAGS} ${INCS} $< -o $@ - -%.o : %.c - ${CC} -c ${CFLAGS} ${INCS} $< -o $@ - -clean: - -rm -f core ${OBJS} ${OUTPUT} - -micropather.o: micropather.h -speed.o: micropather.h diff --git a/external/MicroPather/demo0.gif b/external/MicroPather/demo0.gif deleted file mode 100755 index 74bd087e..00000000 Binary files a/external/MicroPather/demo0.gif and /dev/null differ diff --git a/external/MicroPather/demo0.png b/external/MicroPather/demo0.png deleted file mode 100755 index 79e6e48e..00000000 Binary files a/external/MicroPather/demo0.png and /dev/null differ diff --git a/external/MicroPather/dungeon.cpp b/external/MicroPather/dungeon.cpp deleted file mode 100755 index 53f59d3a..00000000 --- a/external/MicroPather/dungeon.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* -Copyright (c) 2000-2012 Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - - -#define USE_PATHER - -#include -#include -#include -#include - -#include -#include - -#ifdef USE_PATHER - -#include "micropather.h" -using namespace micropather; -#endif - - -const int MAPX = 30; -const int MAPY = 10; -const char gMap[MAPX*MAPY+1] = - //"012345678901234567890123456789" - " | | |" - " | |----+ | +" - "---+ +---DD-+ +--+--+ " - " | +-- +" - " +----+ +---+ " - "---+ + D D | " - " | | +----+ +----+ +--+" - " D | | | " - " | +-------+ +-+ |--+ " - "---+ | +"; - -class Dungeon -#ifdef USE_PATHER - : public Graph -#endif -{ - private: - Dungeon( const Dungeon& ); - void operator=( const Dungeon& ); - - int playerX, playerY; - MPVector path; - bool doorsOpen; - bool showConsidered; - - MicroPather* pather; - - public: - Dungeon() : playerX( 0 ), playerY( 0 ), doorsOpen( false ), showConsidered( false ), pather( 0 ) - { - pather = new MicroPather( this, 20 ); // Use a very small memory block to stress the pather - } - - virtual ~Dungeon() { - delete pather; - } - - int X() { return playerX; } - int Y() { return playerY; } - - void ClearPath() - { - #ifdef USE_PATHER - path.resize( 0 ); - #endif - } - - void ToggleTouched() { showConsidered = !showConsidered; - pather->Reset(); - } - - void ToggleDoor() - { - doorsOpen = !doorsOpen; - - #ifdef USE_PATHER - pather->Reset(); - - #endif - } - - int Passable( int nx, int ny ) - { - if ( nx >= 0 && nx < MAPX - && ny >= 0 && ny < MAPY ) - { - int index = ny*MAPX+nx; - char c = gMap[ index ]; - if ( c == ' ' ) - return 1; - else if ( c == 'D' ) - return 2; - } - return 0; - } - - int SetPos( int nx, int ny ) - { - int result = 0; - if ( Passable( nx, ny ) == 1 ) - { - #ifdef USE_PATHER - float totalCost; - if ( showConsidered ) - pather->Reset(); - - result = pather->Solve( XYToNode( playerX, playerY ), XYToNode( nx, ny ), &path, &totalCost ); - - if ( result == MicroPather::SOLVED ) { - playerX = nx; - playerY = ny; - } - printf( "Pather returned %d\n", result ); - - #else - playerX = nx; - playerY = ny; - #endif - } - return result; - } - - void Print() - { - char buf[ MAPX+1 ]; - - MPVector< void* > stateVec; - - if ( showConsidered ) - pather->StatesInPool( &stateVec ); - printf( " doors %s\n", doorsOpen ? "open" : "closed" ); - printf( " 0 10 20\n" ); - printf( " 012345678901234567890123456789\n" ); - for( int j=0; j *neighbors ) - { - int x, y; - const int dx[8] = { 1, 1, 0, -1, -1, -1, 0, 1 }; - const int dy[8] = { 0, 1, 1, 1, 0, -1, -1, -1 }; - const float cost[8] = { 1.0f, 1.41f, 1.0f, 1.41f, 1.0f, 1.41f, 1.0f, 1.41f }; - - NodeToXY( node, &x, &y ); - - for( int i=0; i<8; ++i ) { - int nx = x + dx[i]; - int ny = y + dy[i]; - - int pass = Passable( nx, ny ); - if ( pass > 0 ) { - if ( pass == 1 || doorsOpen ) - { - // Normal floor - StateCost nodeCost = { XYToNode( nx, ny ), cost[i] }; - neighbors->push_back( nodeCost ); - } - else - { - // Normal floor - StateCost nodeCost = { XYToNode( nx, ny ), FLT_MAX }; - neighbors->push_back( nodeCost ); - } - } - } - } - - virtual void PrintStateInfo( void* node ) - { - int x, y; - NodeToXY( node, &x, &y ); - printf( "(%d,%d)", x, y ); - } - -#endif -}; - -int main( int /*argc*/, const char** /*argv*/ ) -{ - Dungeon dungeon; - bool done = false; - char buf[ 256 ]; - - while ( !done ) { - dungeon.Print(); - printf( "\n# # to move, q to quit, r to redraw, d to toggle doors, t for touched\n" ); - //gets( buf ); - //printf( "\n" ); - - std::cin.getline( buf, 256 ); - - if ( *buf ) - { - if ( buf[0] == 'q' ) { - done = true; - } - else if ( buf[0] == 'd' ) { - dungeon.ToggleDoor(); - dungeon.ClearPath(); - } - else if ( buf[0] == 't' ) { - dungeon.ToggleTouched(); - } - else if ( buf[0] == 'r' ) { - dungeon.ClearPath(); - } - else if ( isdigit( buf[0] ) ) { - int x, y; - sscanf( buf, "%d %d", &x, &y ); // sleazy, I know - dungeon.SetPos( x, y ); - } - } - else - { - dungeon.ClearPath(); - } - } - return 0; -} diff --git a/external/MicroPather/micropather.cpp b/external/MicroPather/micropather.cpp deleted file mode 100755 index dbc1db8b..00000000 --- a/external/MicroPather/micropather.cpp +++ /dev/null @@ -1,1078 +0,0 @@ -/* -Copyright (c) 2000-2009 Lee Thomason (www.grinninglizard.com) - -Grinning Lizard Utilities. - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#ifdef _MSC_VER -#pragma warning( disable : 4786 ) // Debugger truncating names. -#pragma warning( disable : 4530 ) // Exception handler isn't used -#endif - -#include -#include - -//#define DEBUG_PATH -//#define DEBUG_PATH_DEEP -//#define TRACK_COLLISION -//#define DEBUG_CACHING - -#ifdef DEBUG_CACHING -#include "../grinliz/gldebug.h" -#endif - -#include "micropather.h" - -using namespace micropather; - -class OpenQueue -{ - public: - OpenQueue( Graph* _graph ) - { - graph = _graph; - sentinel = (PathNode*) sentinelMem; - sentinel->InitSentinel(); - #ifdef DEBUG - sentinel->CheckList(); - #endif - } - ~OpenQueue() {} - - void Push( PathNode* pNode ); - PathNode* Pop(); - void Update( PathNode* pNode ); - - bool Empty() { return sentinel->next == sentinel; } - - private: - OpenQueue( const OpenQueue& ); // undefined and unsupported - void operator=( const OpenQueue& ); - - PathNode* sentinel; - int sentinelMem[ ( sizeof( PathNode ) + sizeof( int ) ) / sizeof( int ) ]; - Graph* graph; // for debugging -}; - - -void OpenQueue::Push( PathNode* pNode ) -{ - - MPASSERT( pNode->inOpen == 0 ); - MPASSERT( pNode->inClosed == 0 ); - -#ifdef DEBUG_PATH_DEEP - printf( "Open Push: " ); - graph->PrintStateInfo( pNode->state ); - printf( " total=%.1f\n", pNode->totalCost ); -#endif - - // Add sorted. Lowest to highest cost path. Note that the sentinel has - // a value of FLT_MAX, so it should always be sorted in. - MPASSERT( pNode->totalCost < FLT_MAX ); - PathNode* iter = sentinel->next; - while ( true ) - { - if ( pNode->totalCost < iter->totalCost ) { - iter->AddBefore( pNode ); - pNode->inOpen = 1; - break; - } - iter = iter->next; - } - MPASSERT( pNode->inOpen ); // make sure this was actually added. -#ifdef DEBUG - sentinel->CheckList(); -#endif -} - -PathNode* OpenQueue::Pop() -{ - MPASSERT( sentinel->next != sentinel ); - PathNode* pNode = sentinel->next; - pNode->Unlink(); -#ifdef DEBUG - sentinel->CheckList(); -#endif - - MPASSERT( pNode->inClosed == 0 ); - MPASSERT( pNode->inOpen == 1 ); - pNode->inOpen = 0; - -#ifdef DEBUG_PATH_DEEP - printf( "Open Pop: " ); - graph->PrintStateInfo( pNode->state ); - printf( " total=%.1f\n", pNode->totalCost ); -#endif - - return pNode; -} - -void OpenQueue::Update( PathNode* pNode ) -{ -#ifdef DEBUG_PATH_DEEP - printf( "Open Update: " ); - graph->PrintStateInfo( pNode->state ); - printf( " total=%.1f\n", pNode->totalCost ); -#endif - - MPASSERT( pNode->inOpen ); - - // If the node now cost less than the one before it, - // move it to the front of the list. - if ( pNode->prev != sentinel && pNode->totalCost < pNode->prev->totalCost ) { - pNode->Unlink(); - sentinel->next->AddBefore( pNode ); - } - - // If the node is too high, move to the right. - if ( pNode->totalCost > pNode->next->totalCost ) { - PathNode* it = pNode->next; - pNode->Unlink(); - - while ( pNode->totalCost > it->totalCost ) - it = it->next; - - it->AddBefore( pNode ); -#ifdef DEBUG - sentinel->CheckList(); -#endif - } -} - - -class ClosedSet -{ - public: - ClosedSet( Graph* _graph ) { this->graph = _graph; } - ~ClosedSet() {} - - void Add( PathNode* pNode ) - { - #ifdef DEBUG_PATH_DEEP - printf( "Closed add: " ); - graph->PrintStateInfo( pNode->state ); - printf( " total=%.1f\n", pNode->totalCost ); - #endif - #ifdef DEBUG - MPASSERT( pNode->inClosed == 0 ); - MPASSERT( pNode->inOpen == 0 ); - #endif - pNode->inClosed = 1; - } - - void Remove( PathNode* pNode ) - { - #ifdef DEBUG_PATH_DEEP - printf( "Closed remove: " ); - graph->PrintStateInfo( pNode->state ); - printf( " total=%.1f\n", pNode->totalCost ); - #endif - MPASSERT( pNode->inClosed == 1 ); - MPASSERT( pNode->inOpen == 0 ); - - pNode->inClosed = 0; - } - - private: - ClosedSet( const ClosedSet& ); - void operator=( const ClosedSet& ); - Graph* graph; -}; - - -PathNodePool::PathNodePool( unsigned _allocate, unsigned _typicalAdjacent ) - : firstBlock( 0 ), - blocks( 0 ), -#if defined( MICROPATHER_STRESS ) - allocate( 32 ), -#else - allocate( _allocate ), -#endif - nAllocated( 0 ), - nAvailable( 0 ) -{ - freeMemSentinel.InitSentinel(); - - cacheCap = allocate * _typicalAdjacent; - cacheSize = 0; - cache = (NodeCost*)malloc(cacheCap * sizeof(NodeCost)); - - // Want the behavior that if the actual number of states is specified, the cache - // will be at least that big. - hashShift = 3; // 8 (only useful for stress testing) -#if !defined( MICROPATHER_STRESS ) - while( HashSize() < allocate ) - ++hashShift; -#endif - hashTable = (PathNode**)calloc( HashSize(), sizeof(PathNode*) ); - - blocks = firstBlock = NewBlock(); -// printf( "HashSize=%d allocate=%d\n", HashSize(), allocate ); - totalCollide = 0; -} - - -PathNodePool::~PathNodePool() -{ - Clear(); - free( firstBlock ); - free( cache ); - free( hashTable ); -#ifdef TRACK_COLLISION - printf( "Total collide=%d HashSize=%d HashShift=%d\n", totalCollide, HashSize(), hashShift ); -#endif -} - - -bool PathNodePool::PushCache( const NodeCost* nodes, int nNodes, int* start ) { - *start = -1; - if ( nNodes+cacheSize <= cacheCap ) { - for( int i=0; i= 0 && start < cacheCap ); - MPASSERT( nNodes > 0 ); - MPASSERT( start + nNodes <= cacheCap ); - memcpy( nodes, &cache[start], sizeof(NodeCost)*nNodes ); -} - - -void PathNodePool::Clear() -{ -#ifdef TRACK_COLLISION - // Collision tracking code. - int collide=0; - for( unsigned i=0; ichild[0] || hashTable[i]->child[1]) ) - ++collide; - } - //printf( "PathNodePool %d/%d collision=%d %.1f%%\n", nAllocated, HashSize(), collide, 100.0f*(float)collide/(float)HashSize() ); - totalCollide += collide; -#endif - - Block* b = blocks; - while( b ) { - Block* temp = b->nextBlock; - if ( b != firstBlock ) { - free( b ); - } - b = temp; - } - blocks = firstBlock; // Don't delete the first block (we always need at least that much memory.) - - // Set up for new allocations (but don't do work we don't need to. Reset/Clear can be called frequently.) - if ( nAllocated > 0 ) { - freeMemSentinel.next = &freeMemSentinel; - freeMemSentinel.prev = &freeMemSentinel; - - memset( hashTable, 0, sizeof(PathNode*)*HashSize() ); - for( unsigned i=0; ipathNode[i] ); - } - } - nAvailable = allocate; - nAllocated = 0; - cacheSize = 0; -} - - -PathNodePool::Block* PathNodePool::NewBlock() -{ - Block* block = (Block*) calloc( 1, sizeof(Block) + sizeof(PathNode)*(allocate-1) ); - block->nextBlock = 0; - - nAvailable += allocate; - for( unsigned i=0; ipathNode[i] ); - } - return block; -} - - -unsigned PathNodePool::Hash( void* voidval ) -{ - /* - Spent quite some time on this, and the result isn't quite satifactory. The - input set is the size of a void*, and is generally (x,y) pairs or memory pointers. - - FNV resulting in about 45k collisions in a (large) test and some other approaches - about the same. - - Simple folding reduces collisions to about 38k - big improvement. However, that may - be an artifact of the (x,y) pairs being well distributed. And for either the x,y case - or the pointer case, there are probably very poor hash table sizes that cause "overlaps" - and grouping. (An x,y encoding with a hashShift of 8 is begging for trouble.) - - The best tested results are simple folding, but that seems to beg for a pathelogical case. - FNV-1a was the next best choice, without obvious pathelogical holes. - - Finally settled on h%HashMask(). Simple, but doesn't have the obvious collision cases of folding. - */ - /* - // Time: 567 - // FNV-1a - // http://isthe.com/chongo/tech/comp/fnv/ - // public domain. - MP_UPTR val = (MP_UPTR)(voidval); - const unsigned char *p = (unsigned char *)(&val); - unsigned int h = 2166136261; - - for( size_t i=0; i>hashShift) ^ (h>>(hashShift*2)) ^ (h>>(hashShift*3)) ) & HashMask(); - */ - /* - // Time: 526 - MP_UPTR h = (MP_UPTR)(voidval); - return ( h ^ (h>>hashShift) ^ (h>>(hashShift*2)) ^ (h>>(hashShift*3)) ) & HashMask(); - */ - - // Time: 512 - // The HashMask() is used as the divisor. h%1024 has lots of common - // repetitions, but h%1023 will move things out more. - MP_UPTR h = (MP_UPTR)(voidval); - return h % HashMask(); -} - - - -PathNode* PathNodePool::Alloc() -{ - if ( freeMemSentinel.next == &freeMemSentinel ) { - MPASSERT( nAvailable == 0 ); - - Block* b = NewBlock(); - b->nextBlock = blocks; - blocks = b; - MPASSERT( freeMemSentinel.next != &freeMemSentinel ); - } - PathNode* pathNode = freeMemSentinel.next; - pathNode->Unlink(); - - ++nAllocated; - MPASSERT( nAvailable > 0 ); - --nAvailable; - return pathNode; -} - - -void PathNodePool::AddPathNode( unsigned key, PathNode* root ) -{ - if ( hashTable[key] ) { - PathNode* p = hashTable[key]; - while( true ) { - int dir = (root->state < p->state) ? 0 : 1; - if ( p->child[dir] ) { - p = p->child[dir]; - } - else { - p->child[dir] = root; - break; - } - } - } - else { - hashTable[key] = root; - } -} - - -PathNode* PathNodePool::FetchPathNode( void* state ) -{ - unsigned key = Hash( state ); - - PathNode* root = hashTable[key]; - while( root ) { - if ( root->state == state ) { - break; - } - root = ( state < root->state ) ? root->child[0] : root->child[1]; - } - MPASSERT( root ); - return root; -} - - -PathNode* PathNodePool::GetPathNode( unsigned frame, void* _state, float _costFromStart, float _estToGoal, PathNode* _parent ) -{ - unsigned key = Hash( _state ); - - PathNode* root = hashTable[key]; - while( root ) { - if ( root->state == _state ) { - if ( root->frame == frame ) // This is the correct state and correct frame. - break; - // Correct state, wrong frame. - root->Init( frame, _state, _costFromStart, _estToGoal, _parent ); - break; - } - root = ( _state < root->state ) ? root->child[0] : root->child[1]; - } - if ( !root ) { - // allocate new one - root = Alloc(); - root->Clear(); - root->Init( frame, _state, _costFromStart, _estToGoal, _parent ); - AddPathNode( key, root ); - } - return root; -} - - -void PathNode::Init( unsigned _frame, - void* _state, - float _costFromStart, - float _estToGoal, - PathNode* _parent ) -{ - state = _state; - costFromStart = _costFromStart; - estToGoal = _estToGoal; - CalcTotalCost(); - parent = _parent; - frame = _frame; - inOpen = 0; - inClosed = 0; -} - - -void PathNode::Clear() -{ - memset( this, 0, sizeof( PathNode ) ); - numAdjacent = -1; - cacheIndex = -1; -} - -MicroPather::MicroPather( Graph* _graph, unsigned allocate, unsigned typicalAdjacent, bool cache ) - : pathNodePool( allocate, typicalAdjacent ), - graph( _graph ), - frame( 0 ) -{ - MPASSERT( allocate ); - MPASSERT( typicalAdjacent ); - pathCache = 0; - if ( cache ) { - pathCache = new PathCache( allocate*4 ); // untuned arbitrary constant - } -} - - -MicroPather::~MicroPather() -{ - delete pathCache; -} - - -void MicroPather::Reset() -{ - pathNodePool.Clear(); - if ( pathCache ) { - pathCache->Reset(); - } - frame = 0; -} - - -void MicroPather::GoalReached( PathNode* node, void* start, void* end, MP_VECTOR< void* > *_path ) -{ - MP_VECTOR< void* >& path = *_path; - path.clear(); - - // We have reached the goal. - // How long is the path? Used to allocate the vector which is returned. - int count = 1; - PathNode* it = node; - while( it->parent ) - { - ++count; - it = it->parent; - } - - // Now that the path has a known length, allocate - // and fill the vector that will be returned. - if ( count < 3 ) - { - // Handle the short, special case. - path.resize(2); - path[0] = start; - path[1] = end; - } - else - { - path.resize(count); - - path[0] = start; - path[count-1] = end; - count-=2; - it = node->parent; - - while ( it->parent ) - { - path[count] = it->state; - it = it->parent; - --count; - } - } - - if ( pathCache ) { - costVec.clear(); - - PathNode* pn0 = pathNodePool.FetchPathNode( path[0] ); - PathNode* pn1 = 0; - for( unsigned i=0; iAdd( path, costVec ); - } - - #ifdef DEBUG_PATH - printf( "Path: " ); - int counter=0; - #endif - for ( unsigned k=0; kPrintStateInfo( path[k] ); - printf( " " ); - ++counter; - if ( counter == 8 ) - { - printf( "\n" ); - counter = 0; - } - #endif - } - #ifdef DEBUG_PATH - printf( "Cost=%.1f Checksum %d\n", node->costFromStart, checksum ); - #endif -} - - -void MicroPather::GetNodeNeighbors( PathNode* node, MP_VECTOR< NodeCost >* pNodeCost ) -{ - if ( node->numAdjacent == 0 ) { - // it has no neighbors. - pNodeCost->resize( 0 ); - } - else if ( node->cacheIndex < 0 ) - { - // Not in the cache. Either the first time or just didn't fit. We don't know - // the number of neighbors and need to call back to the client. - stateCostVec.resize( 0 ); - graph->AdjacentCost( node->state, &stateCostVec ); - - #ifdef DEBUG - { - // If this assert fires, you have passed a state - // as its own neighbor state. This is impossible -- - // bad things will happen. - for ( unsigned i=0; istate ); - } - #endif - - pNodeCost->resize( stateCostVec.size() ); - node->numAdjacent = stateCostVec.size(); - - if ( node->numAdjacent > 0 ) { - // Now convert to pathNodes. - // Note that the microsoft std library is actually pretty slow. - // Move things to temp vars to help. - const unsigned stateCostVecSize = stateCostVec.size(); - const StateCost* stateCostVecPtr = &stateCostVec[0]; - NodeCost* pNodeCostPtr = &(*pNodeCost)[0]; - - for( unsigned i=0; isize() > 0 && pathNodePool.PushCache( pNodeCostPtr, pNodeCost->size(), &start ) ) { - node->cacheIndex = start; - } - } - } - else { - // In the cache! - pNodeCost->resize( node->numAdjacent ); - NodeCost* pNodeCostPtr = &(*pNodeCost)[0]; - pathNodePool.GetCache( node->cacheIndex, node->numAdjacent, pNodeCostPtr ); - - // A node is uninitialized (even if memory is allocated) if it is from a previous frame. - // Check for that, and Init() as necessary. - for( int i=0; inumAdjacent; ++i ) { - PathNode* pNode = pNodeCostPtr[i].node; - if ( pNode->frame != frame ) { - pNode->Init( frame, pNode->state, FLT_MAX, FLT_MAX, 0 ); - } - } - } -} - - -#ifdef DEBUG -/* -void MicroPather::DumpStats() -{ - int hashTableEntries = 0; - for( int i=0; i* stateVec ) -{ - stateVec->clear(); - pathNodePool.AllStates( frame, stateVec ); -} - - -void PathNodePool::AllStates( unsigned frame, MP_VECTOR< void* >* stateVec ) -{ - for ( Block* b=blocks; b; b=b->nextBlock ) - { - for( unsigned i=0; ipathNode[i].frame == frame ) - stateVec->push_back( b->pathNode[i].state ); - } - } -} - - -PathCache::PathCache( int _allocated ) -{ - mem = new Item[_allocated]; - memset( mem, 0, sizeof(*mem)*_allocated ); - allocated = _allocated; - nItems = 0; - hit = 0; - miss = 0; -} - - -PathCache::~PathCache() -{ - delete [] mem; -} - - -void PathCache::Reset() -{ - if ( nItems ) { - memset( mem, 0, sizeof(*mem)*allocated ); - nItems = 0; - hit = 0; - miss = 0; - } -} - - -void PathCache::Add( const MP_VECTOR< void* >& path, const MP_VECTOR< float >& cost ) -{ - if ( nItems + (int)path.size() > allocated*3/4 ) { - return; - } - - for( unsigned i=0; ib->c->d - // Huge memory saving to only store 3 paths to 'd' - // Can put more in cache with also adding path to b, c, & d - // But uses much more memory. Experiment with this commented - // in and out and how to set. - - void* end = path[path.size()-1]; - Item item = { path[i], end, path[i+1], cost[i] }; - AddItem( item ); - } -} - - -void PathCache::AddNoSolution( void* end, void* states[], int count ) -{ - if ( count + nItems > allocated*3/4 ) { - return; - } - - for( int i=0; i* path, float* totalCost ) -{ - const Item* item = Find( start, end ); - if ( item ) { - if ( item->cost == FLT_MAX ) { - ++hit; - return MicroPather::NO_SOLUTION; - } - - path->clear(); - path->push_back( start ); - *totalCost = 0; - - for ( ;start != end; start=item->next, item=Find(start, end) ) { - MPASSERT( item ); - *totalCost += item->cost; - path->push_back( item->next ); - } - ++hit; - return MicroPather::SOLVED; - } - ++miss; - return MicroPather::NOT_CACHED; -} - - -void PathCache::AddItem( const Item& item ) -{ - MPASSERT( allocated ); - unsigned index = item.Hash() % allocated; - while( true ) { - if ( mem[index].Empty() ) { - mem[index] = item; - ++nItems; -#ifdef DEBUG_CACHING - GLOUTPUT(( "Add: start=%x next=%x end=%x\n", item.start, item.next, item.end )); -#endif - break; - } - else if ( mem[index].KeyEqual( item ) ) { - MPASSERT( (mem[index].next && item.next) || (mem[index].next==0 && item.next == 0) ); - // do nothing; in cache - break; - } - ++index; - if ( index == allocated ) - index = 0; - } -} - - -const PathCache::Item* PathCache::Find( void* start, void* end ) -{ - MPASSERT( allocated ); - Item fake = { start, end, 0, 0 }; - unsigned index = fake.Hash() % allocated; - while( true ) { - if ( mem[index].Empty() ) { - return 0; - } - if ( mem[index].KeyEqual( fake )) { - return mem + index; - } - ++index; - if ( index == allocated ) - index = 0; - } -} - - -void MicroPather::GetCacheData( CacheData* data ) -{ - memset( data, 0, sizeof(*data) ); - - if ( pathCache ) { - data->nBytesAllocated = pathCache->AllocatedBytes(); - data->nBytesUsed = pathCache->UsedBytes(); - data->memoryFraction = (float)( (double)data->nBytesUsed / (double)data->nBytesAllocated ); - - data->hit = pathCache->hit; - data->miss = pathCache->miss; - if ( data->hit + data->miss ) { - data->hitFraction = (float)( (double)(data->hit) / (double)(data->hit + data->miss) ); - } - else { - data->hitFraction = 0; - } - } -} - - - -int MicroPather::Solve( void* startNode, void* endNode, MP_VECTOR< void* >* path, float* cost ) -{ - // Important to clear() in case the caller doesn't check the return code. There - // can easily be a left over path from a previous call. - path->clear(); - - #ifdef DEBUG_PATH - printf( "Path: " ); - graph->PrintStateInfo( startNode ); - printf( " --> " ); - graph->PrintStateInfo( endNode ); - printf( " min cost=%f\n", graph->LeastCostEstimate( startNode, endNode ) ); - #endif - - *cost = 0.0f; - - if ( startNode == endNode ) - return START_END_SAME; - - if ( pathCache ) { - int cacheResult = pathCache->Solve( startNode, endNode, path, cost ); - if ( cacheResult == SOLVED || cacheResult == NO_SOLUTION ) { - #ifdef DEBUG_CACHING - GLOUTPUT(( "PathCache hit. result=%s\n", cacheResult == SOLVED ? "solved" : "no_solution" )); - #endif - return cacheResult; - } - #ifdef DEBUG_CACHING - GLOUTPUT(( "PathCache miss\n" )); - #endif - } - - ++frame; - - OpenQueue open( graph ); - ClosedSet closed( graph ); - - PathNode* newPathNode = pathNodePool.GetPathNode( frame, - startNode, - 0, - graph->LeastCostEstimate( startNode, endNode ), - 0 ); - - open.Push( newPathNode ); - stateCostVec.resize(0); - nodeCostVec.resize(0); - - while ( !open.Empty() ) - { - PathNode* node = open.Pop(); - - if ( node->state == endNode ) - { - GoalReached( node, startNode, endNode, path ); - *cost = node->costFromStart; - #ifdef DEBUG_PATH - DumpStats(); - #endif - return SOLVED; - } - else - { - closed.Add( node ); - - // We have not reached the goal - add the neighbors. - GetNodeNeighbors( node, &nodeCostVec ); - - for( int i=0; inumAdjacent; ++i ) - { - // Not actually a neighbor, but useful. Filter out infinite cost. - if ( nodeCostVec[i].cost == FLT_MAX ) { - continue; - } - PathNode* child = nodeCostVec[i].node; - float newCost = node->costFromStart + nodeCostVec[i].cost; - - PathNode* inOpen = child->inOpen ? child : 0; - PathNode* inClosed = child->inClosed ? child : 0; - PathNode* inEither = (PathNode*)( ((MP_UPTR)inOpen) | ((MP_UPTR)inClosed) ); - - MPASSERT( inEither != node ); - MPASSERT( !( inOpen && inClosed ) ); - - if ( inEither ) { - if ( newCost < child->costFromStart ) { - child->parent = node; - child->costFromStart = newCost; - child->estToGoal = graph->LeastCostEstimate( child->state, endNode ); - child->CalcTotalCost(); - if ( inOpen ) { - open.Update( child ); - } - } - } - else { - child->parent = node; - child->costFromStart = newCost; - child->estToGoal = graph->LeastCostEstimate( child->state, endNode ), - child->CalcTotalCost(); - - MPASSERT( !child->inOpen && !child->inClosed ); - open.Push( child ); - } - } - } - } - #ifdef DEBUG_PATH - DumpStats(); - #endif - if ( pathCache ) { - // Could add a bunch more with a little tracking. - pathCache->AddNoSolution( endNode, &startNode, 1 ); - } - return NO_SOLUTION; -} - - -int MicroPather::SolveForNearStates( void* startState, MP_VECTOR< StateCost >* near, float maxCost ) -{ - /* http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm - - 1 function Dijkstra(Graph, source): - 2 for each vertex v in Graph: // Initializations - 3 dist[v] := infinity // Unknown distance function from source to v - 4 previous[v] := undefined // Previous node in optimal path from source - 5 dist[source] := 0 // Distance from source to source - 6 Q := the set of all nodes in Graph - // All nodes in the graph are unoptimized - thus are in Q - 7 while Q is not empty: // The main loop - 8 u := vertex in Q with smallest dist[] - 9 if dist[u] = infinity: - 10 break // all remaining vertices are inaccessible from source - 11 remove u from Q - 12 for each neighbor v of u: // where v has not yet been removed from Q. - 13 alt := dist[u] + dist_between(u, v) - 14 if alt < dist[v]: // Relax (u,v,a) - 15 dist[v] := alt - 16 previous[v] := u - 17 return dist[] - */ - - ++frame; - - OpenQueue open( graph ); // nodes to look at - ClosedSet closed( graph ); - - nodeCostVec.resize(0); - stateCostVec.resize(0); - - PathNode closedSentinel; - closedSentinel.Clear(); - closedSentinel.Init( frame, 0, FLT_MAX, FLT_MAX, 0 ); - closedSentinel.next = closedSentinel.prev = &closedSentinel; - - PathNode* newPathNode = pathNodePool.GetPathNode( frame, startState, 0, 0, 0 ); - open.Push( newPathNode ); - - while ( !open.Empty() ) - { - PathNode* node = open.Pop(); // smallest dist - closed.Add( node ); // add to the things we've looked at - closedSentinel.AddBefore( node ); - - if ( node->totalCost > maxCost ) - continue; // Too far away to ever get here. - - GetNodeNeighbors( node, &nodeCostVec ); - - for( int i=0; inumAdjacent; ++i ) - { - MPASSERT( node->costFromStart < FLT_MAX ); - float newCost = node->costFromStart + nodeCostVec[i].cost; - - PathNode* inOpen = nodeCostVec[i].node->inOpen ? nodeCostVec[i].node : 0; - PathNode* inClosed = nodeCostVec[i].node->inClosed ? nodeCostVec[i].node : 0; - MPASSERT( !( inOpen && inClosed ) ); - PathNode* inEither = inOpen ? inOpen : inClosed; - MPASSERT( inEither != node ); - - if ( inEither && inEither->costFromStart <= newCost ) { - continue; // Do nothing. This path is not better than existing. - } - // Groovy. We have new information or improved information. - PathNode* child = nodeCostVec[i].node; - MPASSERT( child->state != newPathNode->state ); // should never re-process the parent. - - child->parent = node; - child->costFromStart = newCost; - child->estToGoal = 0; - child->totalCost = child->costFromStart; - - if ( inOpen ) { - open.Update( inOpen ); - } - else if ( !inClosed ) { - open.Push( child ); - } - } - } - near->clear(); - - for( PathNode* pNode=closedSentinel.next; pNode != &closedSentinel; pNode=pNode->next ) { - if ( pNode->totalCost <= maxCost ) { - StateCost sc; - sc.cost = pNode->totalCost; - sc.state = pNode->state; - - near->push_back( sc ); - } - } -#ifdef DEBUG - for( unsigned i=0; isize(); ++i ) { - for( unsigned k=i+1; ksize(); ++k ) { - MPASSERT( (*near)[i].state != (*near)[k].state ); - } - } -#endif - - return SOLVED; -} - - - - diff --git a/external/MicroPather/micropather.h b/external/MicroPather/micropather.h deleted file mode 100755 index 1c63414d..00000000 --- a/external/MicroPather/micropather.h +++ /dev/null @@ -1,509 +0,0 @@ -/* -Copyright (c) 2000-2013 Lee Thomason (www.grinninglizard.com) -Micropather - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - - -#ifndef GRINNINGLIZARD_MICROPATHER_INCLUDED -#define GRINNINGLIZARD_MICROPATHER_INCLUDED - - -/** @mainpage MicroPather - - MicroPather is a path finder and A* solver (astar or a-star) written in platform independent - C++ that can be easily integrated into existing code. MicroPather focuses on being a path - finding engine for video games but is a generic A* solver. MicroPather is open source, with - a license suitable for open source or commercial use. -*/ - -// This probably works to remove, but isn't currently tested in STL mode. -#define GRINLIZ_NO_STL - -#ifdef GRINLIZ_NO_STL -# define MP_VECTOR micropather::MPVector -#else -# include -# define MP_VECTOR std::vector -#endif -#include - -#ifdef _DEBUG - #ifndef DEBUG - #define DEBUG - #endif -#endif - - -#if defined(DEBUG) -# if defined(_MSC_VER) -# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like -# define MPASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } //if ( !(x)) WinDebugBreak() -# elif defined (ANDROID_NDK) -# include -# define MPASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } -# else -# include -# define MPASSERT assert -# endif -# else -# define MPASSERT( x ) {} -#endif - - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - #include - typedef uintptr_t MP_UPTR; -#elif defined (__GNUC__) && (__GNUC__ >= 3 ) - #include - #include - typedef uintptr_t MP_UPTR; -#else - // Assume not 64 bit pointers. Get a new compiler. - typedef unsigned MP_UPTR; -#endif - -namespace micropather -{ -#ifdef GRINLIZ_NO_STL - - /* WARNING: vector partial replacement. Does everything needed to replace std::vector - for micropather, but only works on Plain Old Data types. Doesn't call copy/construct/destruct - correctly for general use. - */ - template - class MPVector { - public: - MPVector() : m_allocated( 0 ), m_size( 0 ), m_buf ( 0 ) {} - ~MPVector() { delete [] m_buf; } - - void clear() { m_size = 0; } // see warning above - void resize( unsigned s ) { capacity( s ); - m_size = s; - } - T& operator[](unsigned i) { MPASSERT( i>=0 && i=0 && i *adjacent ) = 0; - - /** - This function is only used in DEBUG mode - it dumps output to stdout. Since void* - aren't really human readable, normally you print out some concise info (like "(1,2)") - without an ending newline. - */ - virtual void PrintStateInfo( void* state ) = 0; - }; - - - class PathNode; - - struct NodeCost - { - PathNode* node; - float cost; - }; - - - /* - Every state (void*) is represented by a PathNode in MicroPather. There - can only be one PathNode for a given state. - */ - class PathNode - { - public: - void Init( unsigned _frame, - void* _state, - float _costFromStart, - float _estToGoal, - PathNode* _parent ); - - void Clear(); - void InitSentinel() { - Clear(); - Init( 0, 0, FLT_MAX, FLT_MAX, 0 ); - prev = next = this; - } - - void *state; // the client state - float costFromStart; // exact - float estToGoal; // estimated - float totalCost; // could be a function, but save some math. - PathNode* parent; // the parent is used to reconstruct the path - unsigned frame; // unique id for this path, so the solver can distinguish - // correct from stale values - - int numAdjacent; // -1 is unknown & needs to be queried - int cacheIndex; // position in cache - - PathNode *child[2]; // Binary search in the hash table. [left, right] - PathNode *next, *prev; // used by open queue - - bool inOpen; - bool inClosed; - - void Unlink() { - next->prev = prev; - prev->next = next; - next = prev = 0; - } - void AddBefore( PathNode* addThis ) { - addThis->next = this; - addThis->prev = prev; - prev->next = addThis; - prev = addThis; - } - #ifdef DEBUG - void CheckList() - { - MPASSERT( totalCost == FLT_MAX ); - for( PathNode* it = next; it != this; it=it->next ) { - MPASSERT( it->prev == this || it->totalCost >= it->prev->totalCost ); - MPASSERT( it->totalCost <= it->next->totalCost ); - } - } - #endif - - void CalcTotalCost() { - if ( costFromStart < FLT_MAX && estToGoal < FLT_MAX ) - totalCost = costFromStart + estToGoal; - else - totalCost = FLT_MAX; - } - - private: - - void operator=( const PathNode& ); - }; - - - /* Memory manager for the PathNodes. */ - class PathNodePool - { - public: - PathNodePool( unsigned allocate, unsigned typicalAdjacent ); - ~PathNodePool(); - - // Free all the memory except the first block. Resets all memory. - void Clear(); - - // Essentially: - // pNode = Find(); - // if ( !pNode ) - // pNode = New(); - // - // Get the PathNode associated with this state. If the PathNode already - // exists (allocated and is on the current frame), it will be returned. - // Else a new PathNode is allocated and returned. The returned object - // is always fully initialized. - // - // NOTE: if the pathNode exists (and is current) all the initialization - // parameters are ignored. - PathNode* GetPathNode( unsigned frame, - void* _state, - float _costFromStart, - float _estToGoal, - PathNode* _parent ); - - // Get a pathnode that is already in the pool. - PathNode* FetchPathNode( void* state ); - - // Store stuff in cache - bool PushCache( const NodeCost* nodes, int nNodes, int* start ); - - // Get neighbors from the cache - // Note - always access this with an offset. Can get re-allocated. - void GetCache( int start, int nNodes, NodeCost* nodes ); - - // Return all the allocated states. Useful for visuallizing what - // the pather is doing. - void AllStates( unsigned frame, MP_VECTOR< void* >* stateVec ); - - private: - struct Block - { - Block* nextBlock; - PathNode pathNode[1]; - }; - - unsigned Hash( void* voidval ); - unsigned HashSize() const { return 1<& path, const MP_VECTOR< float >& cost ); - void AddNoSolution( void* end, void* states[], int count ); - int Solve( void* startState, void* endState, MP_VECTOR< void* >* path, float* totalCost ); - - int AllocatedBytes() const { return allocated * sizeof(Item); } - int UsedBytes() const { return nItems * sizeof(Item); } - - int hit; - int miss; - - private: - void AddItem( const Item& item ); - const Item* Find( void* start, void* end ); - - Item* mem; - int allocated; - int nItems; - }; - - struct CacheData { - CacheData() : nBytesAllocated(0), nBytesUsed(0), memoryFraction(0), hit(0), miss(0), hitFraction(0) {} - int nBytesAllocated; - int nBytesUsed; - float memoryFraction; - - int hit; - int miss; - float hitFraction; - }; - - /** - Create a MicroPather object to solve for a best path. Detailed usage notes are - on the main page. - */ - class MicroPather - { - friend class micropather::PathNode; - - public: - enum - { - SOLVED, - NO_SOLUTION, - START_END_SAME, - - // internal - NOT_CACHED - }; - - /** - Construct the pather, passing a pointer to the object that implements - the Graph callbacks. - - @param graph The "map" that implements the Graph callbacks. - @param allocate How many states should be internally allocated at a time. This - can be hard to get correct. The higher the value, the more memory - MicroPather will use. - - If you have a small map (a few thousand states?) it may make sense - to pass in the maximum value. This will cache everything, and MicroPather - will only need one main memory allocation. For a chess board, allocate - would be set to 8x8 (64) - - If your map is large, something like 1/4 the number of possible - states is good. - - If your state space is huge, use a multiple (5-10x) of the normal - path. "Occasionally" call Reset() to free unused memory. - @param typicalAdjacent Used to determine cache size. The typical number of adjacent states - to a given state. (On a chessboard, 8.) Higher values use a little - more memory. - @param cache Turn on path caching. Uses more memory (yet again) but at a huge speed - advantage if you may call the pather with the same path or sub-path, which - is common for pathing over maps in games. - */ - MicroPather( Graph* graph, unsigned allocate = 250, unsigned typicalAdjacent=6, bool cache=true ); - ~MicroPather(); - - /** - Solve for the path from start to end. - - @param startState Input, the starting state for the path. - @param endState Input, the ending state for the path. - @param path Output, a vector of states that define the path. Empty if not found. - @param totalCost Output, the cost of the path, if found. - @return Success or failure, expressed as SOLVED, NO_SOLUTION, or START_END_SAME. - */ - int Solve( void* startState, void* endState, MP_VECTOR< void* >* path, float* totalCost ); - - /** - Find all the states within a given cost from startState. - - @param startState Input, the starting state for the path. - @param near All the states within 'maxCost' of 'startState', and cost to that state. - @param maxCost Input, the maximum cost that will be returned. (Higher values return - larger 'near' sets and take more time to compute.) - @return Success or failure, expressed as SOLVED or NO_SOLUTION. - */ - int SolveForNearStates( void* startState, MP_VECTOR< StateCost >* near, float maxCost ); - - /** Should be called whenever the cost between states or the connection between states changes. - Also frees overhead memory used by MicroPather, and calling will free excess memory. - */ - void Reset(); - - // Debugging function to return all states that were used by the last "solve" - void StatesInPool( MP_VECTOR< void* >* stateVec ); - void GetCacheData( CacheData* data ); - - private: - MicroPather( const MicroPather& ); // undefined and unsupported - void operator=( const MicroPather ); // undefined and unsupported - - void GoalReached( PathNode* node, void* start, void* end, MP_VECTOR< void* > *path ); - - void GetNodeNeighbors( PathNode* node, MP_VECTOR< NodeCost >* neighborNode ); - - #ifdef DEBUG - //void DumpStats(); - #endif - - PathNodePool pathNodePool; - MP_VECTOR< StateCost > stateCostVec; // local to Solve, but put here to reduce memory allocation - MP_VECTOR< NodeCost > nodeCostVec; // local to Solve, but put here to reduce memory allocation - MP_VECTOR< float > costVec; - - Graph* graph; - unsigned frame; // incremented with every solve, used to determine if cached data needs to be refreshed - PathCache* pathCache; - }; -}; // namespace grinliz - -#endif - diff --git a/external/MicroPather/readme.md b/external/MicroPather/readme.md deleted file mode 100755 index 6c09c96d..00000000 --- a/external/MicroPather/readme.md +++ /dev/null @@ -1,181 +0,0 @@ -MicroPather -=========== - -MicroPather is a path finder and A* solver (astar or a-star) written in platform -independent C++ that can be easily integrated into existing code. MicroPather -focuses on being a path finding engine for video games but is a generic A* solver. -MicroPather is open source, with a license suitable for open source or commercial -use. - -The goals of MicroPather are: -* Easy integration into games and other software -* Easy to use and simple interface -* Fast enough - -Demo ----- - -MicroPather comes with a demo application - dungeon.cpp - to show off pathing. -It's ASCII art dungeon exploring at its finest. - -The demo shows an ASCII art dungeon. You can move around by typing a new location, and it will -print the path to that location. In the screen shot above, the path starts in -the upper left corner, and steps to the 'i' at about the middle of -the screen avoiding ASCII walls on the way. The numbers show the path from 0 -to 9 then back to 0. - -You can even open and close the doors to change possible paths. 'd' at the command prompt. - -A Windows Visual C++ 2010 project file and a Linux Makefile are provided. Building it -for another environment is trivial: just compile dungeon.cpp, micropather.h, and -micropather.cpp to a command line app in your environment. - -About A* --------- -In video games, the pathfinding problem comes up in many modern games. What -is the shortest distance from point A to point B? Humans are good at that problem -- you pathfind naturally almost every time you move - but tricky to express -as a computer algorithm. A* is the workhorse technique to solve pathing. It -is directed, meaning that it is optimized to find a solution quickly rather -than by brute force, but it will never fail to find a solution if there is one. - -A* is much more universal that just pathfinding. A* and MicroPather could be -used to find the solution to general state problems, for example it could be -used to solve for a rubiks cube puzzle. - -Terminology ------------ - -The *Graph* is the search space. For the pathfinding problem, -this is your Map. It can be any kind of map: squares like the dungeon example, -polygonal, 3D, hexagons, etc. - -In pathfinding, a *State* is just a position on the Map. In -the demo, the player starts at State (0,0). Adjacent states are very important, -as you might image. If something is at state (1,1) in the dungeon example, -it has 8 adjacent states (0,1), (2,1) etc. it can move to. Why State instead -of location or node? The terminology comes from the more general application. -The states of a cube puzzle aren't locations, for example. - -States are separated by *Cost*. For simple pathfinding in -the dungeon, the *Cost* is simply distance. The cost from state -(0,0) to (1,0) is 1.0, and the cost from (0,0) to (1,1) is sqrt(2), about -1.4. *Cost* is challenging and interesting because it can be -distance, time, or difficulty. -* using distance as the cost will give the shortest length path -* using traversal time as the cost will give the fastest path -* using difficulty as the cost will give the easiest path -etc. - -More info: http://www-cs-students.stanford.edu/~amitp/gameprog.html#paths - -Integrating MicroPather Into Your Code --------------------------------------- -Nothing could by simpler! Or at least that's the goal. More importantly, none -of your game data structures need to change to use MicroPather. The steps, in -brief, are: - -1. Include MicroPather files

-2. Implement the Graph interface

-3. Call the Solver

- -*Include files* - -There are only 2 files for micropather: micropather.cpp and micropather.h. -So there's no build, no make, just add the 2 files to your project. That's it. -They are standard C++ and don't require exceptions or RTTI. (I know, a bunch -of you like exceptions and RTTI. But it does make it less portable and slightly -slower to use them.) - -Assuming you build a debug version of your project with _DEBUG or DEBUG (and -everyone does) MicroPather will run extra checking in these modes. - -*Implement Graph Interface* - -You have some class called Game, or Map, or World, that organizes and stores -your game map. This object (we'll call it Map) needs to inherit from the abstract -class Graph: - - class Map : public Graph - -Graph is pure abstract, so your map class won't be changed by it (except for -possibly gaining a vtable), or have strange conflicts. -Before getting to the methods of Graph, lets think states, as in: - - void Foo( void* state ) - -The state pointer is provided by you, the game programmer. What it is? It is -a unique id for a state. For something like a 3D terrain map, like Lilith3D -uses, the states are pointers to a map structure, a 'QuadNode' in -this case. So the state would simply be: - - void* state = (void*) quadNode; - -On the other hand, the Dungeon example doesn't have an object per map location, -just an x and y. It then uses: - - void* state = (void*)( y * MAPX + x ); - -The state can be anything you want, as long as it is unique and you can convert -to it and from it. - -Now, the methods of Graph. - - /** - Return the least possible cost between 2 states. For example, if your pathfinding - is based on distance, this is simply the straight distance between 2 points on the - map. If you pathfinding is based on minimum time, it is the minimal travel time - between 2 points given the best possible terrain. - */ - virtual float LeastCostEstimate( void* stateStart, void* stateEnd ) = 0; - - /** - Return the exact cost from the given state to all its neighboring states. This - may be called multiple times, or cached by the solver. It *must* return the same - exact values for every call to MicroPather::Solve(). It should generally be a simple, - fast function with no callbacks into the pather. - */ - virtual void AdjacentCost( void* state, MP_VECTOR< micropather::StateCost > *adjacent ) = 0; - - /** - This function is only used in DEBUG mode - it dumps output to stdout. Since void* - aren't really human readable, normally you print out some concise info (like "(1,2)") - without an ending newline. - */ - virtual void PrintStateInfo( void* state ) = 0; - -*Call the Solver* - - MicroPather* pather = new MicroPather( myGraph ); // Although you really should set the default params for your game. - - micropather::MPVector< void* > path; - float totalCost = 0; - int result = pather->Solve( startState, endState, &path, &totalCost ); - -That's it. Given the start state and the end state, the sequence of states -from start to end will be written to the vector. - -MicroPather does a lot of caching. You want to create one and keep in around. -It will cache lots of information about your graph, and get faster as it is -called. However, for caching to work, the connections between states and the -costs of those connections must stay the same. (Else the cache information will -be invalid.) If costs between connections does change, be sure to call Reset(). - - pather->Reset(); - -Reset() is a fast call if it doesn't need to do anything. - -Future Improvements and Social Coding -------------------------------------- - -I really like getting patches, improvements, and performance enhancements. -Some guidelines: -* Pull requests are the best way to send a change. -* The "ease of use" goal is important to this project. It can be sped up by - deeper integration into the client code (all states must subclass the State - object, for example) but that dramatically reduces usability. - -Thanks for checking out MicroPather! - -Lee Thomason - \ No newline at end of file diff --git a/external/MicroPather/reset0.gif b/external/MicroPather/reset0.gif deleted file mode 100755 index 42e74168..00000000 Binary files a/external/MicroPather/reset0.gif and /dev/null differ diff --git a/external/MicroPather/setversion.py b/external/MicroPather/setversion.py deleted file mode 100755 index 4697063b..00000000 --- a/external/MicroPather/setversion.py +++ /dev/null @@ -1,54 +0,0 @@ -# Python program to set the version. -############################################## - - -def fileProcess( name, lineFunction ): - filestream = open( name, 'r' ) - if filestream.closed: - print( "file " + name + " not open." ) - return - - output = "" - print( "--- Processing " + name + " ---------" ) - while 1: - line = filestream.readline() - if not line: break - output += lineFunction( line ) - filestream.close() - - if not output: return # basic error checking - - print( "Writing file " + name ) - filestream = open( name, "w" ); - filestream.write( output ); - filestream.close() - - -def echoInput( line ): - return line - - -import sys -major = input( "Major: " ) -minor = input( "Minor: " ) -build = input( "Build: " ) - -print "Version: " + `major` + "." + `minor` + "." + `build` - -#### Write the buildlilith ##### - -def buildlinuxRule( line ): - - i = line.rfind( "_" ) - - if i >= 4 and line[i] == "_" and line[i-2] == "_" and line[i-4] == "_": - # This is ghetto. Should really use regular expressions. - i -= 4 - print "buildmicro instance found" - return line[0:i] + "_" + `major` + "_" + `minor` + "_" + `build` + line[i+6:] - else: - return line - -fileProcess( "buildmicro", buildlinuxRule ) - - diff --git a/external/MicroPather/speed.cpp b/external/MicroPather/speed.cpp deleted file mode 100755 index 2c7c312e..00000000 --- a/external/MicroPather/speed.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/* -Copyright (c) 2000-2012 Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "micropather.h" -using namespace micropather; - -#ifdef _MSC_VER -#include -// The std::chronos high resolution clocks are no where near accurate enough on Windows 10. -// Many calls come back at 0 time. -typedef uint64_t TimePoint; - -inline uint64_t FastTime() -{ - uint64_t t; - QueryPerformanceCounter((LARGE_INTEGER*)&t); - return t; -} - -inline int64_t Nanoseconds(TimePoint start, TimePoint end) -{ - uint64_t freq; - QueryPerformanceFrequency((LARGE_INTEGER*)&freq); - return (end - start) * 1000 * 1000 * 1000 / freq; -} -#else -typedef std::chrono::time_point TimePoint; - -inline TimePoint FastTime() -{ - return std::chrono::high_resolution_clock::now(); -} - -inline int64_t Nanoseconds(TimePoint start, TimePoint end) -{ - return std::chrono::duration_cast(end - start).count(); -} -#endif - - -const int MAPX = 90; -const int MAPY = 20; -const char gMap[MAPX*MAPY+1] = - //"012345678901234567890123456789" - " | | | | || | | | |" - " | |----+ | + | ||---+ | + | |----+ | +" - "---+ +--- -+ +--+--+ ---+ +--- -+| +--+--+ ---+ +--- -+ +--+--+ " - " | +-- + | || +-- + | +-- +" - " +----+ +---+ || +---+ +----+ +---+ " - "---+ + + + | ---+ + | 2---+ + + + | " - " | | +----+ +----+ +--+ | | || +----+ +--+322| | +----+ +----+ +--+" - " | | | | || | | 222232 | | | " - " | +-------+ +-+ |------------------+| +-+ |--+ 2223| +-------+ +-+ |--+ " - "---+ | || | 222+---+ | +" - " | | | || 22233| | | |" - " | |----+ ++ ||---+3333| 22+ | |----+ | +" - "---+ +--- -+ +--+-------------------||22223+--+--+ ---+ +--- -+ +--+--+ " - " | +-- + | 22223333 +-- + | +-- +" - " +----+ +---+ +---+| +---+ 222 +----+ +---+ " - "---+ + + + | ---+ + + +| |222---+ + + + | " - " | | +----+ +----+ +--+ | | +---+| 22+----+ +--+233|2| +----+ +----+ +--+" - " | | | | ||2222| | 2223333| | | " - " | +-------+ +-+ |--+ | +------++ +-+ |--+ |2+-------+ +-+ |--+ " - "---+ | +---+ || | +---+ | "; - -class Dungeon : public Graph -{ - public: - MPVector path; - MicroPather* aStar; - int maxDir; - - Dungeon() { - aStar = new MicroPather( this, MAPX*MAPY, 6 ); - maxDir = 4; - } - - virtual ~Dungeon() { - delete aStar; - } - - int Passable( int nx, int ny ) - { - if ( nx >= 0 && nx < MAPX - && ny >= 0 && ny < MAPY ) - { - int index = ny*MAPX+nx; - char c = gMap[ index ]; - if ( c == ' ' ) - return 1; - else if ( c >= '1' && c <= '9' ) { - int val = c-'0'; - MPASSERT( val > 0 ); - return val; - } - } - return 0; - } - - void NodeToXY( void* node, int* x, int* y ) - { - int index = (int)((intptr_t)node); - *y = index / MAPX; - *x = index - *y * MAPX; - } - - void* XYToNode( int x, int y ) - { - return (void*) ( y*MAPX + x ); - } - - - virtual float LeastCostEstimate( void* nodeStart, void* nodeEnd ) - { - int xStart, yStart, xEnd, yEnd; - NodeToXY( nodeStart, &xStart, &yStart ); - NodeToXY( nodeEnd, &xEnd, &yEnd ); - - int dx = xStart - xEnd; - int dy = yStart - yEnd; - return (float) sqrt( (double)(dx*dx) + (double)(dy*dy) ); - } - - virtual void AdjacentCost( void* node, MPVector< StateCost > *neighbors ) - { - int x, y; - // E N W S NE NW SW SE - const int dx[8] = { 1, 0, -1, 0, 1, -1, -1, 1 }; - const int dy[8] = { 0, -1, 0, 1, -1, -1, 1, 1 }; - const float cost[8] = { 1.0f, 1.0f, 1.0f, 1.0f, - 1.41f, 1.41f, 1.41f, 1.41f }; - - NodeToXY( node, &x, &y ); - - for( int i=0; i 0 ) { - // Normal floor - StateCost nodeCost = { XYToNode( nx, ny ), cost[i] * (float)(pass) }; - neighbors->push_back( nodeCost ); - } - } - } - - virtual void PrintStateInfo( void* node ) - { - int x, y; - NodeToXY( node, &x, &y ); - printf( "(%2d,%2d)", x, y ); - } - -}; - - -int main( int argc, const char* argv[] ) -{ - Dungeon dungeon; - - const int NUM_TEST = 389; - - int indexArray[ NUM_TEST ]; // a bunch of locations to go from-to - float costArray[ NUM_TEST ]; - int64_t timeArray[ NUM_TEST ]; - int resultArray[ NUM_TEST ]; - - bool useBinaryHash = false; - bool useList = false; - bool debug = false; - - #ifdef DEBUG - debug = true; - #endif - #ifdef USE_BINARY_HASH - useBinaryHash = true; - #endif - #ifdef USE_LIST - useList = true; - #endif - - printf( "SpeedTest binaryHash=%s list=%s debug=%s\n", - useBinaryHash ? "true" : "false", - useList ? "true" : "false", - debug ? "true" : "false" ); - - // Set up the test locations, making sure they - // are all valid. - for (int i = 0; i < NUM_TEST; ++i) { - indexArray[i] = (MAPX*MAPY) * i / NUM_TEST; - costArray[i] = 0.0f; - - int y = indexArray[i] / MAPX; - int x = indexArray[i] - MAPX*y; - while (!dungeon.Passable(x, y)) { - indexArray[i] += 1; - y = indexArray[i] / MAPX; - x = indexArray[i] - MAPX*y; - } - } - // Randomize the locations. - for (int i = 0; i < NUM_TEST; ++i) - { - int swapWith = rand() % NUM_TEST; - int temp = indexArray[i]; - indexArray[i] = indexArray[swapWith]; - indexArray[swapWith] = temp; - } - - int64_t compositeScore = 0; - for ( int numDir=4; numDir<=8; numDir+=4 ) - { - dungeon.maxDir = numDir; - dungeon.aStar->Reset(); - - static const int SHORT_PATH = 0; - static const int MED_PATH = 1; - static const int LONG_PATH = 2; - static const int FAIL_SHORT = 3; - static const int FAIL_LONG = 4; - - for( int reset=0; reset<=1; ++reset ) - { - TimePoint clockStart = FastTime(); - for( int i=0; iReset(); - - int startState = indexArray[i]; - int endState = indexArray[ (i==(NUM_TEST-1)) ? 0 : i+1]; - - TimePoint start = FastTime(); - resultArray[i] = dungeon.aStar->Solve( (void*)startState, (void*)endState, &dungeon.path, &costArray[i] ); - TimePoint end = FastTime(); - - timeArray[i] = Nanoseconds(start, end); - MPASSERT(timeArray[i]); - } - TimePoint clockEnd = FastTime(); - - #ifndef PROFILING_RUN - // -------- Results ------------ // - const float shortPath = (float)(MAPX / 4); - const float medPath = (float)(MAPX / 2 ); - - int count[5] = { 0 }; // short, med, long, fail short, fail long - int64_t time[5] = { 0 }; - - for(int i=0; i - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - {C36D6BFA-8F57-483C-83FB-5BBBDF3F4036} - - - - Application - false - MultiByte - v120 - - - Application - false - MultiByte - v120 - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - .\Debug\ - .\Debug\ - true - .\Release\ - .\Release\ - false - - - - .\Debug/micropather.tlb - - - - - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\Debug/micropather.pch - .\Debug/ - .\Debug/ - .\Debug/ - true - Level3 - true - EditAndContinue - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - .\Debug/micropather.exe - true - true - .\Debug/micropather.pdb - Console - false - - - MachineX86 - - - true - .\Debug/micropather.bsc - - - - - .\Release/micropather.tlb - - - - - MaxSpeed - OnlyExplicitInline - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\Release/micropather.pch - .\Release/ - .\Release/ - .\Release/ - Level3 - true - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - .\Release/micropather.exe - true - .\Release/micropather.pdb - Console - false - - - MachineX86 - - - true - .\Release/micropather.bsc - - - - - - \ No newline at end of file diff --git a/external/MicroPather/visstudio/speed.vcxproj b/external/MicroPather/visstudio/speed.vcxproj deleted file mode 100755 index 273fc814..00000000 --- a/external/MicroPather/visstudio/speed.vcxproj +++ /dev/null @@ -1,142 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - {6E0FBADD-648B-4FAF-93DB-29830058D935} - - - - Application - false - MultiByte - v120 - - - Application - false - MultiByte - v120 - - - - - - - - - - - - - - - <_ProjectFileVersion>10.0.40219.1 - .\speed___Win32_Release\ - .\speed___Win32_Release\ - false - .\speed___Win32_Debug\ - .\speed___Win32_Debug\ - true - - - - .\speed___Win32_Release/speed.tlb - - - - - MaxSpeed - OnlyExplicitInline - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - MultiThreaded - true - .\speed___Win32_Release/speed.pch - .\speed___Win32_Release/ - .\speed___Win32_Release/ - .\speed___Win32_Release/ - Level3 - true - ProgramDatabase - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - .\speed___Win32_Release/speed.exe - true - true - .\speed___Win32_Release/speed.pdb - Console - false - - - MachineX86 - - - true - .\speed___Win32_Release/speed.bsc - - - - - .\speed___Win32_Debug/speed.tlb - - - - - Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - EnableFastChecks - MultiThreadedDebug - .\speed___Win32_Debug/speed.pch - .\speed___Win32_Debug/ - .\speed___Win32_Debug/ - .\speed___Win32_Debug/ - true - Level3 - true - EditAndContinue - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - .\speed___Win32_Debug/speed.exe - true - true - .\speed___Win32_Debug/speed.pdb - Console - false - - - MachineX86 - - - true - .\speed___Win32_Debug/speed.bsc - - - - - - \ No newline at end of file diff --git a/include/TF2_NavFile_Reader b/include/TF2_NavFile_Reader new file mode 160000 index 00000000..d20b162b --- /dev/null +++ b/include/TF2_NavFile_Reader @@ -0,0 +1 @@ +Subproject commit d20b162b0f5803414b0229180441137e7abf7f4f diff --git a/include/TF2_NavFile_Reader/CMakeLists.txt b/include/TF2_NavFile_Reader/CMakeLists.txt deleted file mode 100644 index 051f2c35..00000000 --- a/include/TF2_NavFile_Reader/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -target_sources(cathook PRIVATE - "${CMAKE_CURRENT_LIST_DIR}/astar.h" - "${CMAKE_CURRENT_LIST_DIR}/CNavFile.h" - "${CMAKE_CURRENT_LIST_DIR}/nav.h") - -target_include_directories(cathook PRIVATE "${CMAKE_CURRENT_LIST_DIR}") diff --git a/include/TF2_NavFile_Reader/CNavFile.h b/include/TF2_NavFile_Reader/CNavFile.h deleted file mode 100755 index 5f8a8b9d..00000000 --- a/include/TF2_NavFile_Reader/CNavFile.h +++ /dev/null @@ -1,267 +0,0 @@ -#pragma once - -#include "nav.h" -#include - -class CNavFile -{ -public: - // Intended to use with engine->GetLevelName() or mapname from server_spawn - // GameEvent Change it if you get the nav file from elsewhere - CNavFile(const char *szLevelname) - { - if (!szLevelname) - return; - - m_mapName = std::string("tf/"); - std::string map(szLevelname); - - if (map.find("maps/") == std::string::npos) - m_mapName.append("maps/"); - - m_mapName.append(szLevelname); - int dotpos = m_mapName.find('.'); - m_mapName = m_mapName.substr(0, dotpos); - m_mapName.append(".nav"); - - std::ifstream fs(m_mapName, std::ios::binary); - - if (!fs.is_open()) - { - //.nav file does not exist - return; - } - - uint32_t magic; - fs.read((char *) &magic, sizeof(uint32_t)); - - if (magic != 0xFEEDFACE) - { - // Wrong magic number - return; - } - - uint32_t version; - fs.read((char *) &version, sizeof(uint32_t)); - - if (version < 16) // 16 is latest for TF2 - { - // Version is too old - return; - } - - uint32_t subVersion; - fs.read((char *) &subVersion, sizeof(uint32_t)); - - if (subVersion != 2) // 2 for TF2 - { - // Not TF2 nav file - return; - } - - // We do not really need to check the size - uint32_t bspSize; - fs.read((char *) &bspSize, sizeof(uint32_t)); - fs.read((char *) &m_isAnalized, sizeof(unsigned char)); - uint16_t placeCount; - fs.read((char *) &placeCount, sizeof(uint16_t)); - - // TF2 does not use places, but in case they exist - uint16_t len; - for (int i = 0; i < placeCount; i++) - { - fs.read((char *) &len, sizeof(uint16_t)); - - CNavPlace place; - - fs.read((char *) &place.m_name, len); - - m_places.push_back(place); - } - - fs.read((char *) &m_hasUnnamedAreas, sizeof(unsigned char)); - - uint32_t areaCount; - fs.read((char *) &areaCount, sizeof(uint32_t)); - - for (size_t i = 0; i < areaCount; i++) - { - CNavArea area; - fs.read((char *) &area.m_id, sizeof(uint32_t)); - fs.read((char *) &area.m_attributeFlags, sizeof(uint32_t)); - fs.read((char *) &area.m_nwCorner, sizeof(Vector)); - fs.read((char *) &area.m_seCorner, sizeof(Vector)); - fs.read((char *) &area.m_neY, sizeof(float)); - fs.read((char *) &area.m_swY, sizeof(float)); - - area.m_center[0] = (area.m_nwCorner[0] + area.m_seCorner[0]) / 2.0f; - area.m_center[1] = (area.m_nwCorner[1] + area.m_seCorner[1]) / 2.0f; - area.m_center[2] = (area.m_nwCorner[2] + area.m_seCorner[2]) / 2.0f; - - if ((area.m_seCorner.x - area.m_nwCorner.x) > 0.0f && - (area.m_seCorner.y - area.m_nwCorner.y) > 0.0f) - { - area.m_invDxCorners = - 1.0f / (area.m_seCorner.x - area.m_nwCorner.x); - area.m_invDzCorners = - 1.0f / (area.m_seCorner.z - area.m_nwCorner.z); - } - else - area.m_invDxCorners = area.m_invDzCorners = 0.0f; - - // Change the tolerance if you wish - area.m_minZ = min(area.m_seCorner.z, area.m_nwCorner.z) - 18.f; - area.m_maxZ = max(area.m_seCorner.z, area.m_nwCorner.z) + 18.f; - - for (int dir = 0; dir < NUM_DIRECTIONS; dir++) - { - uint32_t connectionCount; - fs.read((char *) &connectionCount, sizeof(uint32_t)); - - for (size_t j = 0; j < connectionCount; j++) - { - NavConnect connect; - - fs.read((char *) &connect.id, sizeof(uint32_t)); - - // Connection to the same area? - if (connect.id == area.m_id) - continue; - - // Note: If connection directions matter to you, uncomment - // this - area.m_connections /*[dir]*/.push_back(connect); - } - } - - uint8_t hidingSpotCount; - fs.read((char *) &hidingSpotCount, sizeof(uint8_t)); - - for (size_t j = 0; j < hidingSpotCount; j++) - { - HidingSpot spot; - fs.read((char *) &spot.m_id, sizeof(uint32_t)); - fs.read((char *) &spot.m_pos, sizeof(Vector)); - fs.read((char *) &spot.m_flags, sizeof(unsigned char)); - - area.m_hidingSpots.push_back(spot); - } - - fs.read((char *) &area.m_encounterSpotCount, sizeof(uint32_t)); - - for (size_t j = 0; j < area.m_encounterSpotCount; j++) - { - SpotEncounter spot; - fs.read((char *) &spot.from.id, sizeof(uint32_t)); - fs.read((char *) &spot.fromDir, sizeof(unsigned char)); - fs.read((char *) &spot.to.id, sizeof(uint32_t)); - fs.read((char *) &spot.toDir, sizeof(unsigned char)); - - unsigned char spotcount; - fs.read((char *) &spotcount, sizeof(unsigned char)); - - for (int s = 0; s < spotcount; ++s) - { - SpotOrder order; - fs.read((char *) &order.id, sizeof(uint32_t)); - fs.read((char *) &order.t, sizeof(unsigned char)); - spot.spots.push_back(order); - } - - area.m_spotEncounters.push_back(spot); - } - - fs.read((char *) &area.m_indexType, sizeof(uint16_t)); - - // TF2 does not use ladders either - for (int dir = 0; dir < NUM_LADDER_DIRECTIONS; dir++) - { - uint32_t laddercount; - fs.read((char *) &laddercount, sizeof(uint32_t)); - - for (size_t j = 0; j < laddercount; j++) - { - int temp; - fs.read((char *) &temp, sizeof(uint32_t)); - } - } - - for (int j = 0; j < MAX_NAV_TEAMS; j++) - fs.read((char *) &area.m_earliestOccupyTime[j], sizeof(float)); - - for (int j = 0; j < NUM_CORNERS; ++j) - fs.read((char *) &area.m_lightIntensity[j], sizeof(float)); - - fs.read((char *) &area.m_visibleAreaCount, sizeof(uint32_t)); - - for (size_t j = 0; j < area.m_visibleAreaCount; ++j) - { - AreaBindInfo info; - fs.read((char *) &info.id, sizeof(uint32_t)); - fs.read((char *) &info.attributes, sizeof(unsigned char)); - - area.m_potentiallyVisibleAreas.push_back(info); - } - - fs.read((char *) &area.m_inheritVisibilityFrom, sizeof(uint32_t)); - - // Unknown 4 bytes - uint32_t unk; - fs.read((char *) &unk, sizeof(uint32_t)); - - m_areas.push_back(area); - } - - fs.close(); - - // Fill connection for every area with their area ptrs instead of IDs - // This will come in handy in path finding - - for (auto it = m_areas.begin(); it != m_areas.end(); it++) - { - CNavArea &area = *it; - - for (auto it2 = area.m_connections.begin(); - it2 != area.m_connections.end(); it2++) - { - NavConnect &connection = *it2; - - for (auto it3 = m_areas.begin(); it3 != m_areas.end(); it3++) - { - CNavArea &connectedarea = *it3; - - if (connection.id == connectedarea.m_id) - { - connection.area = &connectedarea; - } - } - } - - // Fill potentially visible areas as well - for (auto it2 = area.m_potentiallyVisibleAreas.begin(); - it2 != area.m_potentiallyVisibleAreas.end(); it2++) - { - AreaBindInfo &bindinfo = *it2; - - for (auto it3 = m_areas.begin(); it3 != m_areas.end(); it3++) - { - CNavArea &boundarea = *it3; - - if (bindinfo.id == boundarea.m_id) - { - bindinfo.area = &boundarea; - } - } - } - } - - m_isOK = true; - } - - std::string m_mapName; - bool m_isAnalized; - std::vector m_places; - bool m_hasUnnamedAreas; - std::vector m_areas; - bool m_isOK = false; -}; diff --git a/include/TF2_NavFile_Reader/README.md b/include/TF2_NavFile_Reader/README.md deleted file mode 100755 index ae1cabc1..00000000 --- a/include/TF2_NavFile_Reader/README.md +++ /dev/null @@ -1,15 +0,0 @@ -TF2 NavFile Reader - -How to use: - -1.) Include CNavFile.h - -2.) Instantiate CNavFile, map name argument in -constructor is intended to work with level name from engine->GetLevelName() or with "mapname" string from "server_spawn" GameEvent - -3.) Check if m_isOK -> no errors while reading - -4.) Done! - -License: http://www.wtfpl.net/txt/copying/ -Credits: Source Engine SDK by Valve -> cool and good header files diff --git a/include/TF2_NavFile_Reader/astar.h b/include/TF2_NavFile_Reader/astar.h deleted file mode 100755 index 2a12acc6..00000000 --- a/include/TF2_NavFile_Reader/astar.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "common.hpp" -#include -namespace nav { - struct singleNode { - typedef singleNode ThisClass; - int id{-1}; - Vector pos; - std::vector children; - - inline void addChildren(singleNode *node) { - children.push_back(node); - } - - inline std::vector FindPath(singleNode *goal) { - std::vector ret; - singleNode *node = nullptr; - singleNode *target = this; - for (int i = 0; i < 100; i++) { - float bestscr = 99999999.0f; - if (node) { - if (node->id == goal->id) - break; - ret.push_back(node); - } - for (auto child : target->children) { - bool rett = false; - for (auto sub : ret) { - if (sub->id == child->id) { - rett = true; - break; - } - } - if (rett) - continue; - if (child->id == goal->id) { - ret.push_back(child); - node = child; - target = child; - break; - } - if (child->pos.DistTo(goal->pos) < bestscr) { - bestscr = child->pos.DistTo(goal->pos); - node = child; - target = child; - } - } - } - return ret; - } - }; -} \ No newline at end of file diff --git a/include/TF2_NavFile_Reader/nav.h b/include/TF2_NavFile_Reader/nav.h deleted file mode 100755 index e39213b8..00000000 --- a/include/TF2_NavFile_Reader/nav.h +++ /dev/null @@ -1,272 +0,0 @@ -#pragma once - -#include "common.hpp" - -struct SpotEncounter; - -class CNavPlace -{ -public: - char m_name[256]; -}; - -enum NavDirType -{ - NORTH = 0, - EAST = 1, - SOUTH = 2, - WEST = 3, - - NUM_DIRECTIONS -}; - -enum -{ - MAX_NAV_TEAMS = 2 -}; - -/** - * A HidingSpot is a good place for a bot to crouch and wait for enemies - */ -class HidingSpot -{ -public: - enum - { - IN_COVER = 0x01, // in a corner with good hard cover nearby - GOOD_SNIPER_SPOT = 0x02, // had at least one decent sniping corridor - IDEAL_SNIPER_SPOT = - 0x04, // can see either very far, or a large area, or both - EXPOSED = 0x08 // spot in the open, usually on a ledge or cliff - }; - - bool HasGoodCover(void) const - { - return (m_flags & IN_COVER) ? true : false; - } // return true if hiding spot in in cover - bool IsGoodSniperSpot(void) const - { - return (m_flags & GOOD_SNIPER_SPOT) ? true : false; - } - bool IsIdealSniperSpot(void) const - { - return (m_flags & IDEAL_SNIPER_SPOT) ? true : false; - } - bool IsExposed(void) const - { - return (m_flags & EXPOSED) ? true : false; - } - - Vector m_pos; // world coordinates of the spot - unsigned int m_id; // this spot's unique ID - unsigned char m_flags; // bit flags -}; - -class CNavArea; -struct SpotEncounter; -struct NavConnect; - -struct AreaBindInfo -{ - union { - CNavArea *area; - unsigned int id = 0; - }; - - unsigned char attributes; // VisibilityType -}; - -enum NavAttributeType -{ - NAV_MESH_INVALID = 0, - NAV_MESH_CROUCH = 0x0000001, // must crouch to use this node/area - NAV_MESH_JUMP = 0x0000002, // must jump to traverse this area (only used - // during generation) - NAV_MESH_PRECISE = - 0x0000004, // do not adjust for obstacles, just move along area - NAV_MESH_NO_JUMP = 0x0000008, // inhibit discontinuity jumping - NAV_MESH_STOP = 0x0000010, // must stop when entering this area - NAV_MESH_RUN = 0x0000020, // must run to traverse this area - NAV_MESH_WALK = 0x0000040, // must walk to traverse this area - NAV_MESH_AVOID = - 0x0000080, // avoid this area unless alternatives are too dangerous - NAV_MESH_TRANSIENT = 0x0000100, // area may become blocked, and should be - // periodically checked - NAV_MESH_DONT_HIDE = - 0x0000200, // area should not be considered for hiding spot generation - NAV_MESH_STAND = 0x0000400, // bots hiding in this area should stand - NAV_MESH_NO_HOSTAGES = 0x0000800, // hostages shouldn't use this area - NAV_MESH_STAIRS = 0x0001000, // this area represents stairs, do not attempt - // to climb or jump them - just walk up - NAV_MESH_NO_MERGE = 0x0002000, // don't merge this area with adjacent areas - NAV_MESH_OBSTACLE_TOP = - 0x0004000, // this nav area is the climb point on the tip of an obstacle - NAV_MESH_CLIFF = 0x0008000, // this nav area is adjacent to a drop of at - // least CliffHeight - - NAV_MESH_FIRST_CUSTOM = 0x00010000, // apps may define custom app-specific - // bits starting with this value - NAV_MESH_LAST_CUSTOM = - 0x04000000, // apps must not define custom app-specific bits higher than - // with this value - - NAV_MESH_HAS_ELEVATOR = 0x40000000, // area is in an elevator's path - NAV_MESH_NAV_BLOCKER = - 0x80000000, // area is blocked by nav blocker ( Alas, needed to hijack a - // bit in the attributes to get within a cache line - // [7/24/2008 tom]) -}; - -enum NavTraverseType -{ - // NOTE: First 4 directions MUST match NavDirType - GO_NORTH = 0, - GO_EAST, - GO_SOUTH, - GO_WEST, - - GO_LADDER_UP, - GO_LADDER_DOWN, - GO_JUMP, - GO_ELEVATOR_UP, - GO_ELEVATOR_DOWN, - - NUM_TRAVERSE_TYPES -}; - -enum NavCornerType -{ - NORTH_WEST = 0, - NORTH_EAST = 1, - SOUTH_EAST = 2, - SOUTH_WEST = 3, - - NUM_CORNERS -}; - -enum NavRelativeDirType -{ - FORWARD = 0, - RIGHT, - BACKWARD, - LEFT, - UP, - DOWN, - - NUM_RELATIVE_DIRECTIONS -}; - -enum LadderDirectionType -{ - LADDER_UP = 0, - LADDER_DOWN, - - NUM_LADDER_DIRECTIONS -}; - -class CNavArea -{ -public: - uint32_t m_id; - int32_t m_attributeFlags; - Vector m_nwCorner; - Vector m_seCorner; - Vector m_center; - float m_invDzCorners; - float m_invDxCorners; - float m_neY; - float m_swY; - float m_minZ; - float m_maxZ; - std::vector m_connections; - std::vector m_hidingSpots; - std::vector m_spotEncounters; - uint32_t m_encounterSpotCount; - uint16_t m_indexType; - float m_earliestOccupyTime[MAX_NAV_TEAMS]; - float m_lightIntensity[NUM_CORNERS]; - uint32_t m_visibleAreaCount; - uint32_t m_inheritVisibilityFrom; - std::vector m_potentiallyVisibleAreas; - - // Check if the given point is overlapping the area - // @return True if 'pos' is within 2D extents of area. - bool IsOverlapping(const Vector &vecPos, float flTolerance = 0) - { - if (vecPos.x + flTolerance < this->m_nwCorner.x) - return false; - - if (vecPos.x - flTolerance > this->m_seCorner.x) - return false; - - if (vecPos.y + flTolerance < this->m_nwCorner.y) - return false; - - if (vecPos.y - flTolerance > this->m_seCorner.y) - return false; - - return true; - } - - // Check if the point is within the 3D bounds of this area - bool Contains(Vector &vecPoint) - { - if (!IsOverlapping(vecPoint)) - return false; - - if (vecPoint.z > m_maxZ) - return false; - - if (vecPoint.z < m_minZ) - return false; - - return true; - } -}; - -struct NavConnect -{ - NavConnect() - { - id = 0; - length = -1; - } - - union { - unsigned int id; - CNavArea *area; - }; - - mutable float length; - - bool operator==(const NavConnect &other) const - { - return (area == other.area) ? true : false; - } -}; - -struct SpotOrder -{ - float t; // parametric distance along ray where this spot first has LOS to - // our path - - union { - HidingSpot *spot; // the spot to look at - unsigned int id; // spot ID for save/load - }; -}; - -/** - * This struct stores possible path segments thru a CNavArea, and the dangerous - * spots to look at as we traverse that path segment. - */ -struct SpotEncounter -{ - NavConnect from; - NavDirType fromDir; - NavConnect to; - NavDirType toDir; - // Ray path; // the path segment - std::vector - spots; // list of spots to look at, in order of occurrence -};