From fa0ba8d86bef199ce50e33876d99660cad44a5ce Mon Sep 17 00:00:00 2001 From: bencat07 Date: Tue, 14 Aug 2018 00:32:23 +0200 Subject: [PATCH] Fix submodules tf --- .gitmodules | 4 +- external/MicroPather | 1 + external/MicroPather/MakefileSpeed | 113 -- external/MicroPather/demo0.gif | Bin 4249 -> 0 bytes external/MicroPather/demo0.png | Bin 34963 -> 0 bytes external/MicroPather/dungeon.cpp | 305 ----- external/MicroPather/micropather.cpp | 1078 ----------------- external/MicroPather/micropather.h | 509 -------- external/MicroPather/readme.md | 181 --- external/MicroPather/reset0.gif | Bin 2946 -> 0 bytes external/MicroPather/setversion.py | 54 - external/MicroPather/speed.cpp | 339 ------ external/MicroPather/speed.txt | 30 - .../MicroPather/visstudio/micropather.sln | 26 - .../MicroPather/visstudio/micropather.vcxproj | 140 --- external/MicroPather/visstudio/speed.vcxproj | 142 --- include/TF2_NavFile_Reader | 1 + include/TF2_NavFile_Reader/CMakeLists.txt | 6 - include/TF2_NavFile_Reader/CNavFile.h | 267 ---- include/TF2_NavFile_Reader/README.md | 15 - include/TF2_NavFile_Reader/astar.h | 53 - include/TF2_NavFile_Reader/nav.h | 272 ----- 22 files changed, 4 insertions(+), 3532 deletions(-) create mode 160000 external/MicroPather delete mode 100755 external/MicroPather/MakefileSpeed delete mode 100755 external/MicroPather/demo0.gif delete mode 100755 external/MicroPather/demo0.png delete mode 100755 external/MicroPather/dungeon.cpp delete mode 100755 external/MicroPather/micropather.cpp delete mode 100755 external/MicroPather/micropather.h delete mode 100755 external/MicroPather/readme.md delete mode 100755 external/MicroPather/reset0.gif delete mode 100755 external/MicroPather/setversion.py delete mode 100755 external/MicroPather/speed.cpp delete mode 100755 external/MicroPather/speed.txt delete mode 100755 external/MicroPather/visstudio/micropather.sln delete mode 100755 external/MicroPather/visstudio/micropather.vcxproj delete mode 100755 external/MicroPather/visstudio/speed.vcxproj create mode 160000 include/TF2_NavFile_Reader delete mode 100644 include/TF2_NavFile_Reader/CMakeLists.txt delete mode 100755 include/TF2_NavFile_Reader/CNavFile.h delete mode 100755 include/TF2_NavFile_Reader/README.md delete mode 100755 include/TF2_NavFile_Reader/astar.h delete mode 100755 include/TF2_NavFile_Reader/nav.h 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 74bd087ecd3f1d93c73e912cd8c51f99498b4d79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4249 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 z 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 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 42e74168c6c320ad98667e984eb75bddc2749455..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2946 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 S(T{@war`eZpShrr>;C~#34Nmg 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 -};