From b17ce80ce6b1545897ebb3a907f9c6426ba6379e Mon Sep 17 00:00:00 2001 From: Mordred Date: Sun, 20 Jan 2019 13:36:34 +0100 Subject: [PATCH 01/50] BNpcs will now also initiate combat when being attacked, Death should reset them properly. --- src/world/Actor/BNpc.cpp | 17 +++++++++++++++++ src/world/Actor/BNpc.h | 4 ++++ src/world/Actor/Chara.cpp | 6 +++--- src/world/Actor/Chara.h | 2 +- src/world/Actor/Player.cpp | 2 +- .../Network/PacketWrappers/NpcSpawnPacket.h | 19 ------------------- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 86b5f402..44590d6a 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -389,3 +389,20 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) } } } + +void Sapphire::Entity::BNpc::onActionHostile( Sapphire::Entity::CharaPtr pSource ) +{ + if( !hateListGetHighest() ) + aggro( pSource ); + + //if( !getClaimer() ) + // setOwner( pSource->getAsPlayer() ); +} + +void Sapphire::Entity::BNpc::onDeath() +{ + setTargetId( INVALID_GAME_OBJECT_ID ); + m_currentStance = Stance::Passive; + m_state = BNpcState::Dead; + hateListClear(); +} diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index 04a39164..db0fb1f9 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -79,6 +79,10 @@ namespace Sapphire::Entity void update( int64_t currTime ) override; + void onActionHostile( CharaPtr pSource ) override; + + void onDeath() override; + private: uint32_t m_bNpcBaseId; uint32_t m_bNpcNameId; diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 3a42f836..65c59cf2 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -393,7 +393,7 @@ void Sapphire::Entity::Chara::autoAttack( CharaPtr pTarget ) if( ( tick - m_lastAttack ) > 2500 ) { - pTarget->onActionHostile( *this ); + pTarget->onActionHostile( getAsChara() ); m_lastAttack = tick; srand( static_cast< uint32_t >( tick ) ); @@ -461,7 +461,7 @@ void Sapphire::Entity::Chara::handleScriptSkill( uint32_t type, uint16_t actionI sendToInRangeSet( effectPacket, true ); if( target.isAlive() ) - target.onActionHostile( *this ); + target.onActionHostile( getAsChara() ); target.takeDamage( static_cast< uint32_t >( param1 ) ); @@ -481,7 +481,7 @@ void Sapphire::Entity::Chara::handleScriptSkill( uint32_t type, uint16_t actionI if( pHitActor->getAsChara()->isAlive() ) - pHitActor->getAsChara()->onActionHostile( *this ); + pHitActor->getAsChara()->onActionHostile( getAsChara() ); pHitActor->getAsChara()->takeDamage( static_cast< uint32_t >( param1 ) ); diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 6d8d0748..94de7845 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -219,7 +219,7 @@ namespace Sapphire::Entity virtual void onDamageTaken( Chara& pSource ) {}; - virtual void onActionHostile( Chara& source ) {}; + virtual void onActionHostile( CharaPtr pSource ) {}; virtual void onActionFriendly( Chara& pSource ) {}; diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index aef8cbe2..9af7e25e 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -1515,7 +1515,7 @@ void Sapphire::Entity::Player::autoAttack( CharaPtr pTarget ) auto mainWeap = getItemAt( Common::GearSet0, Common::GearSetSlot::MainHand ); - pTarget->onActionHostile( *this ); + pTarget->onActionHostile( getAsChara() ); //uint64_t tick = Util::getTimeMs(); //srand(static_cast< uint32_t >(tick)); diff --git a/src/world/Network/PacketWrappers/NpcSpawnPacket.h b/src/world/Network/PacketWrappers/NpcSpawnPacket.h index f487c50f..1c9567b0 100644 --- a/src/world/Network/PacketWrappers/NpcSpawnPacket.h +++ b/src/world/Network/PacketWrappers/NpcSpawnPacket.h @@ -44,12 +44,8 @@ namespace Sapphire::Network::Packets::Server m_data.pose = bnpc.getPose(); memcpy( m_data.look, bnpc.getLookArray(), sizeof( m_data.look ) ); - - auto models = bnpc.getModelArray(); memcpy( m_data.models, bnpc.getModelArray(), sizeof( m_data.models ) ); - memcpy( m_data.look, bnpc.getLookArray(), sizeof( m_data.look ) ); - m_data.pos.x = bnpc.getPos().x; m_data.pos.y = bnpc.getPos().y; m_data.pos.z = bnpc.getPos().z; @@ -61,10 +57,6 @@ namespace Sapphire::Network::Packets::Server m_data.aggressionMode = bnpc.getAggressionMode(); m_data.classJob = 0; - //m_data.voice = bnpc.getVoiceId(); - //m_data.currentMount = bnpc.getCurrentMount(); - - //m_data.onlineStatus = static_cast< uint8_t >( bnpc.getOnlineStatus() ); //m_data.u23 = 0x04; //m_data.u24 = 256; @@ -85,21 +77,10 @@ namespace Sapphire::Network::Packets::Server // 0x20 == spawn hidden to be displayed by the spawneffect control //m_data.displayFlags = bnpc.getDisplayFlags(); - /*if( bnpc.getZoningType() != Common::ZoneingType::None ) - { - m_data.displayFlags |= static_cast< uint16_t >( Common::DisplayFlags::Invisible ); - }*/ - //m_data.currentMount = bnpc.getCurrentMount(); //m_data.persistentEmote = bnpc.getPersistentEmote(); m_data.targetId = static_cast< uint64_t >( bnpc.getTargetId() ); - //m_data.type = 1; - //m_data.unknown_33 = 4; - //m_data.unknown_38 = 0x70; - //m_data.unknown_60 = 3; - //m_data.unknown_61 = 7; - uint64_t currentTimeMs = Sapphire::Util::getTimeMs(); From a29039b1640b3f2d63a9aaecf825fdc3dd308f27 Mon Sep 17 00:00:00 2001 From: goaaats Date: Sun, 20 Jan 2019 16:10:48 +0100 Subject: [PATCH 02/50] pathing things? --- src/world/CMakeLists.txt | 9 +- src/world/Manager/TerritoryMgr.cpp | 11 +-- src/world/Navi/NaviProvider.cpp | 135 +++++++++++++++++++++++++++++ src/world/Navi/NaviProvider.h | 51 +++++++++++ src/world/Territory/Zone.cpp | 13 ++- src/world/Territory/Zone.h | 5 ++ 6 files changed, 214 insertions(+), 10 deletions(-) create mode 100644 src/world/Navi/NaviProvider.cpp create mode 100644 src/world/Navi/NaviProvider.h diff --git a/src/world/CMakeLists.txt b/src/world/CMakeLists.txt index 7d25aae6..7954ae70 100644 --- a/src/world/CMakeLists.txt +++ b/src/world/CMakeLists.txt @@ -19,7 +19,8 @@ file( GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} Script/*.c* StatusEffect/*.c* Territory/*.c* - Territory/Housing/*.c*) + Territory/Housing/*.c* + Navi/*.c*) add_executable( world ${SERVER_SOURCE_FILES} ) @@ -30,10 +31,12 @@ set_target_properties( world target_link_libraries( world PUBLIC - common ) + common + Detour) target_include_directories( world PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}" ) + "${CMAKE_CURRENT_SOURCE_DIR}" + Detour ) if( UNIX ) diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index 81c1aa96..f4abad11 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -163,16 +163,17 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() uint32_t guid = getNextInstanceId(); - Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}", + auto pZone = make_Zone( territoryTypeId, guid, territoryInfo->name, pPlaceName->name, framework() ); + pZone->init(); + + Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", territoryTypeId, guid, territoryInfo->territoryIntendedUse, territoryInfo->name, ( isPrivateTerritory( territoryTypeId ) ? "PRIVATE" : "PUBLIC" ), - pPlaceName->name ); - - auto pZone = make_Zone( territoryTypeId, guid, territoryInfo->name, pPlaceName->name, framework() ); - pZone->init(); + pPlaceName->name, + pZone->GetNaviProvider()->HasNaviMesh() ? "NAVI" : ""); InstanceIdToZonePtrMap instanceMap; instanceMap[ guid ] = pZone; diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp new file mode 100644 index 00000000..a252b772 --- /dev/null +++ b/src/world/Navi/NaviProvider.cpp @@ -0,0 +1,135 @@ +#include +#include + +#include "Framework.h" +#include "NaviProvider.h" +#include +#include +#include +#include + +#include "../Territory/Zone.h" +#include + +Sapphire::NaviProvider::NaviProvider( Sapphire::ZonePtr pZone, Sapphire::FrameworkPtr pFw ) : + m_pFw( pFw ), + m_pZone( pZone ), + m_naviMesh( nullptr ), + m_naviMeshQuery( nullptr ) +{ +} + +void Sapphire::NaviProvider::init() +{ + auto meshesFolder = std::filesystem::path( "navi" ); + auto meshFolder = meshesFolder / std::filesystem::path( m_pZone->getInternalName() ); + + if( std::filesystem::exists( meshFolder ) ) + { + auto baseMesh = meshFolder / std::filesystem::path( m_pZone->getInternalName() + ".nav" ); + + //m_naviMesh = LoadMesh( baseMesh.string() ); + + // Load all meshes for testing + for( const auto & entry : std::filesystem::directory_iterator( meshFolder ) ) + { + if( entry.path().extension().string() == ".nav" ) + { + Logger::debug( "Loading " + entry.path().string() ); + LoadMesh( entry.path().string() ); + } + } + + InitQuery(); + } +} + +bool Sapphire::NaviProvider::HasNaviMesh() const +{ + return m_naviMesh != nullptr; +} + +void Sapphire::NaviProvider::InitQuery() +{ + if(m_naviMeshQuery != nullptr) + dtFreeNavMeshQuery( m_naviMeshQuery ); + + m_naviMeshQuery = dtAllocNavMeshQuery(); + m_naviMeshQuery->init( m_naviMesh, 2048 ); +} + +void Sapphire::NaviProvider::LoadMesh( std::string path ) +{ + FILE* fp = fopen( path.c_str(), "rb" ); + if( !fp ) + throw std::exception( "Could open navimesh file" ); + + // Read header. + NavMeshSetHeader header; + + size_t readLen = fread( &header, sizeof( NavMeshSetHeader ), 1, fp ); + if( readLen != 1 ) + { + fclose( fp ); + throw std::exception( "Could not read NavMeshSetHeader" ); + } + + if( header.magic != NAVMESHSET_MAGIC ) + { + fclose( fp ); + throw std::exception( "Not a NavMeshSet" ); + } + + if( header.version != NAVMESHSET_VERSION ) + { + fclose( fp ); + throw std::exception( "Invalid NavMeshSet version" ); + } + + if( !m_naviMesh ) + { + m_naviMesh = dtAllocNavMesh(); + if( !m_naviMesh ) + { + fclose( fp ); + throw std::exception( "Could not allocate dtNavMesh" ); + } + + dtStatus status = m_naviMesh->init( &header.params ); + if( dtStatusFailed( status ) ) + { + fclose( fp ); + throw std::exception( "Could not initialize dtNavMesh" ); + } + } + + // Read tiles. + for( int i = 0; i < header.numTiles; ++i ) + { + NavMeshTileHeader tileHeader; + readLen = fread( &tileHeader, sizeof( tileHeader ), 1, fp ); + if( readLen != 1 ) + { + fclose( fp ); + throw std::exception( "Could not read NavMeshTileHeader" ); + } + + if( !tileHeader.tileRef || !tileHeader.dataSize ) + break; + + unsigned char* data = (unsigned char*)dtAlloc( tileHeader.dataSize, DT_ALLOC_PERM ); + if( !data ) break; + memset( data, 0, tileHeader.dataSize ); + readLen = fread( data, tileHeader.dataSize, 1, fp ); + if( readLen != 1 ) + { + dtFree( data ); + fclose( fp ); + throw std::exception( "Could not read tile data" ); + } + + m_naviMesh->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0 ); + } + + fclose( fp ); +} \ No newline at end of file diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h new file mode 100644 index 00000000..706c3a9b --- /dev/null +++ b/src/world/Navi/NaviProvider.h @@ -0,0 +1,51 @@ +#ifndef _NAVIPROVIDER_H_ +#define _NAVIPROVIDER_H_ + +#include +#include "ForwardsZone.h" +#include +#include + +namespace Sapphire +{ + + class NaviProvider + { + + static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET'; + static const int NAVMESHSET_VERSION = 1; + + struct NavMeshSetHeader + { + int magic; + int version; + int numTiles; + dtNavMeshParams params; + }; + + struct NavMeshTileHeader + { + dtTileRef tileRef; + int dataSize; + }; + + public: + NaviProvider( const ZonePtr pZone, Sapphire::FrameworkPtr pFw ); + + void init(); + void LoadMesh( std::string path ); + void InitQuery(); + + bool HasNaviMesh() const; + + protected: + FrameworkPtr m_pFw; + ZonePtr m_pZone; + + dtNavMesh* m_naviMesh; + dtNavMeshQuery* m_naviMeshQuery; + }; + +} + +#endif diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 21817559..0cd0c9fc 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -124,6 +124,9 @@ bool Sapphire::Zone::init() // all good } + m_naviProvider = new NaviProvider( shared_from_this(), m_pFw ); + m_naviProvider->init(); + return true; } @@ -782,7 +785,7 @@ bool Sapphire::Zone::loadSpawnGroups() m_spawnGroups.emplace_back( id, templateId, level, maxHp ); - Logger::debug( "id: {0}, template: {1}, level: {2}, maxHp: {3}", id, m_spawnGroups.back().getTemplateId(), level, maxHp ); + //Logger::debug( "id: {0}, template: {1}, level: {2}, maxHp: {3}", id, m_spawnGroups.back().getTemplateId(), level, maxHp ); } res.reset(); @@ -805,7 +808,7 @@ bool Sapphire::Zone::loadSpawnGroups() group.getSpawnPointList().emplace_back( std::make_shared< Entity::SpawnPoint >( x, y, z, r, gimmickId ) ); - Logger::debug( "id: {0}, x: {1}, y: {2}, z: {3}, gimmickId: {4}", id, x, y, z, gimmickId ); + //Logger::debug( "id: {0}, x: {1}, y: {2}, z: {3}, gimmickId: {4}", id, x, y, z, gimmickId ); } } return false; @@ -848,3 +851,9 @@ void Sapphire::Zone::updateSpawnPoints() } } + +Sapphire::NaviProvider* Sapphire::Zone::GetNaviProvider() const +{ + return m_naviProvider; +} + diff --git a/src/world/Territory/Zone.h b/src/world/Territory/Zone.h index 657b99c9..192c3fde 100644 --- a/src/world/Territory/Zone.h +++ b/src/world/Territory/Zone.h @@ -6,6 +6,7 @@ #include "Cell.h" #include "CellHandler.h" +#include "Navi/NaviProvider.h" #include "ForwardsZone.h" @@ -62,6 +63,8 @@ namespace Sapphire std::vector< Entity::SpawnGroup > m_spawnGroups; + NaviProvider* m_naviProvider; + public: Zone(); @@ -158,6 +161,8 @@ namespace Sapphire InstanceContentPtr getAsInstanceContent(); void updateSpawnPoints(); + + NaviProvider* GetNaviProvider() const; }; } From 239a98848e7958fadf405245d83640690f555aec Mon Sep 17 00:00:00 2001 From: Mordred Date: Sun, 20 Jan 2019 23:49:43 +0100 Subject: [PATCH 03/50] Fixed bnpc movement --- src/world/Actor/BNpc.cpp | 4 ++-- src/world/Actor/Chara.cpp | 2 +- src/world/Network/PacketWrappers/NpcSpawnPacket.h | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 44590d6a..2fb811a3 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -73,6 +73,7 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX m_mp = 200; m_state = BNpcState::Idle; + m_status = ActorStatus::Idle; m_baseStats.max_hp = maxHp; m_baseStats.max_mp = 200; @@ -128,7 +129,7 @@ uint32_t Sapphire::Entity::BNpc::getBNpcNameId() const void Sapphire::Entity::BNpc::spawn( PlayerPtr pTarget ) { - pTarget->queuePacket( std::make_shared< NpcSpawnPacket >( *getAsBNpc(), *pTarget ) ); + pTarget->queuePacket( std::make_shared< NpcSpawnPacket >( *this, *pTarget ) ); } void Sapphire::Entity::BNpc::despawn( PlayerPtr pTarget ) @@ -166,7 +167,6 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) setPos( newPos ); Common::FFXIVARR_POSITION3 tmpPos{ getPos().x + x, y, getPos().z + z }; - setPos( tmpPos ); setRot( newRot ); diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 65c59cf2..066a6355 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -244,7 +244,7 @@ bool Sapphire::Entity::Chara::face( const Common::FFXIVARR_POSITION3& p ) setRot( newRot ); - return oldRot != newRot ? true : false; + return oldRot != newRot; } /*! diff --git a/src/world/Network/PacketWrappers/NpcSpawnPacket.h b/src/world/Network/PacketWrappers/NpcSpawnPacket.h index 1c9567b0..caa0d072 100644 --- a/src/world/Network/PacketWrappers/NpcSpawnPacket.h +++ b/src/world/Network/PacketWrappers/NpcSpawnPacket.h @@ -39,7 +39,6 @@ namespace Sapphire::Network::Packets::Server m_data.mPMax = bnpc.getMaxMp(); m_data.subtype = 5; - //m_data.tPMax = 3000; m_data.level = bnpc.getLevel(); m_data.pose = bnpc.getPose(); @@ -73,7 +72,6 @@ namespace Sapphire::Network::Packets::Server if( !target.isActorSpawnIdValid( m_data.spawnIndex ) ) return; - // 0x20 == spawn hidden to be displayed by the spawneffect control //m_data.displayFlags = bnpc.getDisplayFlags(); From 916aa81441225af5031e48787183fc3b43ee3d14 Mon Sep 17 00:00:00 2001 From: goaaats Date: Mon, 21 Jan 2019 02:08:38 +0100 Subject: [PATCH 04/50] Add path follow code from RecastDemo --- src/world/Navi/NaviProvider.cpp | 320 +++++++++++++++++++++++++++++++- src/world/Navi/NaviProvider.h | 7 + 2 files changed, 324 insertions(+), 3 deletions(-) diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index a252b772..46b4c83b 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -10,13 +10,19 @@ #include "../Territory/Zone.h" #include +#include +#include Sapphire::NaviProvider::NaviProvider( Sapphire::ZonePtr pZone, Sapphire::FrameworkPtr pFw ) : m_pFw( pFw ), m_pZone( pZone ), m_naviMesh( nullptr ), m_naviMeshQuery( nullptr ) -{ +{ + // Set defaults + m_polyFindRange[0] = 2; + m_polyFindRange[1] = 4; + m_polyFindRange[2] = 2; } void Sapphire::NaviProvider::init() @@ -51,11 +57,319 @@ bool Sapphire::NaviProvider::HasNaviMesh() const void Sapphire::NaviProvider::InitQuery() { - if(m_naviMeshQuery != nullptr) + if( m_naviMeshQuery != nullptr ) dtFreeNavMeshQuery( m_naviMeshQuery ); m_naviMeshQuery = dtAllocNavMeshQuery(); m_naviMeshQuery->init( m_naviMesh, 2048 ); +} + +static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, + const dtPolyRef* visited, const int nvisited ) +{ + int furthestPath = -1; + int furthestVisited = -1; + + // Find furthest common polygon. + for( int i = npath - 1; i >= 0; --i ) + { + bool found = false; + for( int j = nvisited - 1; j >= 0; --j ) + { + if( path[i] == visited[j] ) + { + furthestPath = i; + furthestVisited = j; + found = true; + } + } + if( found ) + break; + } + + // If no intersection found just return current path. + if( furthestPath == -1 || furthestVisited == -1 ) + return npath; + + // Concatenate paths. + + // Adjust beginning of the buffer to include the visited. + const int req = nvisited - furthestVisited; + const int orig = rcMin( furthestPath + 1, npath ); + int size = rcMax( 0, npath - orig ); + if( req + size > maxPath ) + size = maxPath - req; + if( size ) + memmove( path + req, path + orig, size * sizeof( dtPolyRef ) ); + + // Store visited + for( int i = 0; i < req; ++i ) + path[i] = visited[( nvisited - 1 ) - i]; + + return req + size; +} + +static int fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ) +{ + if( npath < 3 ) + return npath; + + // Get connected polygons + static const int maxNeis = 16; + dtPolyRef neis[maxNeis]; + int nneis = 0; + + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if( dtStatusFailed( navQuery->getAttachedNavMesh()->getTileAndPolyByRef( path[0], &tile, &poly ) ) ) + return npath; + + for( unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next ) + { + const dtLink* link = &tile->links[k]; + if( link->ref != 0 ) + { + if( nneis < maxNeis ) + neis[nneis++] = link->ref; + } + } + + // If any of the neighbour polygons is within the next few polygons + // in the path, short cut to that polygon directly. + static const int maxLookAhead = 6; + int cut = 0; + for( int i = dtMin( maxLookAhead, npath ) - 1; i > 1 && cut == 0; i-- ) { + for( int j = 0; j < nneis; j++ ) + { + if( path[i] == neis[j] ) { + cut = i; + break; + } + } + } + if( cut > 1 ) + { + int offset = cut - 1; + npath -= offset; + for( int i = 1; i < npath; i++ ) + path[i] = path[i + offset]; + } + + return npath; +} + +inline bool inRange( const float* v1, const float* v2, const float r, const float h ) +{ + const float dx = v2[0] - v1[0]; + const float dy = v2[1] - v1[1]; + const float dz = v2[2] - v1[2]; + return ( dx*dx + dz * dz ) < r*r && fabsf( dy ) < h; +} + +static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, + const float minTargetDist, + const dtPolyRef* path, const int pathSize, + float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, + float* outPoints = 0, int* outPointCount = 0 ) +{ + // Find steer target. + static const int MAX_STEER_POINTS = 3; + float steerPath[MAX_STEER_POINTS * 3]; + unsigned char steerPathFlags[MAX_STEER_POINTS]; + dtPolyRef steerPathPolys[MAX_STEER_POINTS]; + int nsteerPath = 0; + navQuery->findStraightPath( startPos, endPos, path, pathSize, + steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS ); + if( !nsteerPath ) + return false; + + if( outPoints && outPointCount ) + { + *outPointCount = nsteerPath; + for( int i = 0; i < nsteerPath; ++i ) + dtVcopy( &outPoints[i * 3], &steerPath[i * 3] ); + } + + + // Find vertex far enough to steer to. + int ns = 0; + while( ns < nsteerPath ) + { + // Stop at Off-Mesh link or when point is further than slop away. + if( ( steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) || + !inRange( &steerPath[ns * 3], startPos, minTargetDist, 1000.0f ) ) + break; + ns++; + } + // Failed to find good point to steer to. + if( ns >= nsteerPath ) + return false; + + dtVcopy( steerPos, &steerPath[ns * 3] ); + steerPos[1] = startPos[1]; + steerPosFlag = steerPathFlags[ns]; + steerPosRef = steerPathPolys[ns]; + + return true; +} + +std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::PathFindFollow( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) +{ + if( !m_naviMesh || !m_naviMeshQuery ) + throw std::exception( "No navimesh loaded" ); + + dtPolyRef startRef, endRef = 0; + + float start[3] = { startPos.x, startPos.y, startPos.z }; + float end[3] = { startPos.x, startPos.y, startPos.z }; + + dtQueryFilter filter; + filter.setAreaCost( 0, 0 ); + + m_naviMeshQuery->findNearestPoly( start, m_polyFindRange, &filter, &startRef, 0 ); + m_naviMeshQuery->findNearestPoly( end, m_polyFindRange, &filter, &endRef, 0 ); + + // Couldn't find any close polys to navigate from + if( !startRef || !endRef ) + return {}; + + auto pathFindStatus = DT_FAILURE; + + auto pathIterNum = 0; + dtPolyRef polys[MAX_POLYS]; + int numPolys = 0; + + m_naviMeshQuery->findPath( startRef, endRef, start, end, &filter, polys, &numPolys, MAX_POLYS ); + + // Check if we got polys back for navigation + if( numPolys ) + { + // Iterate over the path to find smooth path on the detail mesh surface. + dtPolyRef polys[MAX_POLYS]; + memcpy( polys, polys, sizeof( dtPolyRef )*numPolys ); + int npolys = numPolys; + + float iterPos[3], targetPos[3]; + m_naviMeshQuery->closestPointOnPoly( startRef, start, iterPos, 0 ); + m_naviMeshQuery->closestPointOnPoly( polys[npolys - 1], end, targetPos, 0 ); + + static const float STEP_SIZE = 0.5f; + static const float SLOP = 0.01f; + + int numSmoothPath = 0; + float smoothPath[MAX_SMOOTH * 3]; + + dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); + numSmoothPath++; + + // Move towards target a small advancement at a time until target reached or + // when ran out of memory to store the path. + while( npolys && numSmoothPath < MAX_SMOOTH ) + { + // Find location to steer towards. + float steerPos[3]; + unsigned char steerPosFlag; + dtPolyRef steerPosRef; + + if( !getSteerTarget( m_naviMeshQuery, iterPos, targetPos, SLOP, + polys, npolys, steerPos, steerPosFlag, steerPosRef ) ) + break; + + bool endOfPath = ( steerPosFlag & DT_STRAIGHTPATH_END ) ? true : false; + bool offMeshConnection = ( steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) ? true : false; + + // Find movement delta. + float delta[3], len; + dtVsub( delta, steerPos, iterPos ); + len = dtMathSqrtf( dtVdot( delta, delta ) ); + // If the steer target is end of path or off-mesh link, do not move past the location. + if( ( endOfPath || offMeshConnection ) && len < STEP_SIZE ) + len = 1; + else + len = STEP_SIZE / len; + float moveTgt[3]; + dtVmad( moveTgt, iterPos, delta, len ); + + // Move + float result[3]; + dtPolyRef visited[16]; + int nvisited = 0; + m_naviMeshQuery->moveAlongSurface( polys[0], iterPos, moveTgt, &filter, + result, visited, &nvisited, 16 ); + + npolys = fixupCorridor( polys, npolys, MAX_POLYS, visited, nvisited ); + npolys = fixupShortcuts( polys, npolys, m_naviMeshQuery ); + + float h = 0; + m_naviMeshQuery->getPolyHeight( polys[0], result, &h ); + result[1] = h; + dtVcopy( iterPos, result ); + + // Handle end of path and off-mesh links when close enough. + if( endOfPath && inRange( iterPos, steerPos, SLOP, 1.0f ) ) + { + // Reached end of path. + dtVcopy( iterPos, targetPos ); + if( numSmoothPath < MAX_SMOOTH ) + { + dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); + numSmoothPath++; + } + break; + } + else if( offMeshConnection && inRange( iterPos, steerPos, SLOP, 1.0f ) ) + { + // Reached off-mesh connection. + float startPos[3], endPos[3]; + + // Advance the path up to and over the off-mesh connection. + dtPolyRef prevRef = 0, polyRef = polys[0]; + int npos = 0; + while( npos < npolys && polyRef != steerPosRef ) + { + prevRef = polyRef; + polyRef = polys[npos]; + npos++; + } + for( int i = npos; i < npolys; ++i ) + polys[i - npos] = polys[i]; + npolys -= npos; + + // Handle the connection. + dtStatus status = m_naviMesh->getOffMeshConnectionPolyEndPoints( prevRef, polyRef, startPos, endPos ); + if( dtStatusSucceed( status ) ) + { + if( numSmoothPath < MAX_SMOOTH ) + { + dtVcopy( &smoothPath[numSmoothPath * 3], startPos ); + numSmoothPath++; + // Hack to make the dotted path not visible during off-mesh connection. + if( numSmoothPath & 1 ) + { + dtVcopy( &smoothPath[numSmoothPath * 3], startPos ); + numSmoothPath++; + } + } + // Move position at the other side of the off-mesh link. + dtVcopy( iterPos, endPos ); + float eh = 0.0f; + m_naviMeshQuery->getPolyHeight( polys[0], iterPos, &eh ); + iterPos[1] = eh; + } + } + + // Store results. + if( numSmoothPath < MAX_SMOOTH ) + { + dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); + numSmoothPath++; + } + } + } + else + { + return {}; + } } void Sapphire::NaviProvider::LoadMesh( std::string path ) @@ -130,6 +444,6 @@ void Sapphire::NaviProvider::LoadMesh( std::string path ) m_naviMesh->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0 ); } - + fclose( fp ); } \ No newline at end of file diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 706c3a9b..1e70753b 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -29,6 +29,9 @@ namespace Sapphire int dataSize; }; + static const int MAX_POLYS = 256; + static const int MAX_SMOOTH = 2048; + public: NaviProvider( const ZonePtr pZone, Sapphire::FrameworkPtr pFw ); @@ -36,6 +39,8 @@ namespace Sapphire void LoadMesh( std::string path ); void InitQuery(); + std::vector< Sapphire::Common::FFXIVARR_POSITION3 > PathFindFollow(Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos); + bool HasNaviMesh() const; protected: @@ -44,6 +49,8 @@ namespace Sapphire dtNavMesh* m_naviMesh; dtNavMeshQuery* m_naviMeshQuery; + + float m_polyFindRange[3]; }; } From 78c0878610f4b7901f198d11ee10006500edfbe8 Mon Sep 17 00:00:00 2001 From: goaaats Date: Mon, 21 Jan 2019 02:42:47 +0100 Subject: [PATCH 05/50] path things --- src/world/Actor/BNpc.cpp | 138 +++++++++++++++++--------------- src/world/Navi/NaviProvider.cpp | 25 ++++-- 2 files changed, 91 insertions(+), 72 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 2fb811a3..dca819f7 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -153,6 +153,11 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) // reached destination return true; + auto path = m_pCurrentZone->GetNaviProvider()->PathFindFollow( m_pos, pos ); + + //face( path[0] ); + + /* float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); float newRot = PI - rot + ( PI / 2 ); @@ -171,6 +176,7 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) setRot( newRot ); sendPositionUpdate(); + */ return false; } @@ -306,87 +312,87 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) switch( m_state ) { - case BNpcState::Retreat: + case BNpcState::Retreat: + { + if( moveTo( m_spawnPos ) ) + m_state = BNpcState::Idle; + } + break; + + case BNpcState::Idle: + { + // passive mobs should ignore players unless aggro'd + if( m_aggressionMode == 1 ) + return; + + CharaPtr pClosestChara = getClosestChara(); + + if( pClosestChara && pClosestChara->isAlive() ) { - if( moveTo( m_spawnPos ) ) - m_state = BNpcState::Idle; + auto distance = Util::distance( getPos().x, getPos().y, getPos().z, + pClosestChara->getPos().x, + pClosestChara->getPos().y, + pClosestChara->getPos().z ); + + if( distance < aggroRange && pClosestChara->isPlayer() ) + aggro( pClosestChara ); + //if( distance < aggroRange && getbehavior() == 2 ) + // aggro( pClosestActor ); } - break; + } - case BNpcState::Idle: + case BNpcState::Combat: + { + auto pHatedActor = hateListGetHighest(); + if( !pHatedActor ) + return; + + auto distanceOrig = Util::distance( getPos().x, getPos().y, getPos().z, + m_spawnPos.x, + m_spawnPos.y, + m_spawnPos.z ); + + if( pHatedActor && !pHatedActor->isAlive() ) { - // passive mobs should ignore players unless aggro'd - if( m_aggressionMode == 1 ) - return; - - CharaPtr pClosestChara = getClosestChara(); - - if( pClosestChara && pClosestChara->isAlive() ) - { - auto distance = Util::distance( getPos().x, getPos().y, getPos().z, - pClosestChara->getPos().x, - pClosestChara->getPos().y, - pClosestChara->getPos().z ); - - if( distance < aggroRange && pClosestChara->isPlayer() ) - aggro( pClosestChara ); - //if( distance < aggroRange && getbehavior() == 2 ) - // aggro( pClosestActor ); - } + hateListRemove( pHatedActor ); + pHatedActor = hateListGetHighest(); } - case BNpcState::Combat: + if( pHatedActor ) { - auto pHatedActor = hateListGetHighest(); - if( !pHatedActor ) - return; + auto distance = Util::distance( getPos().x, getPos().y, getPos().z, + pHatedActor->getPos().x, + pHatedActor->getPos().y, + pHatedActor->getPos().z ); - auto distanceOrig = Util::distance( getPos().x, getPos().y, getPos().z, - m_spawnPos.x, - m_spawnPos.y, - m_spawnPos.z ); - - if( pHatedActor && !pHatedActor->isAlive() ) - { - hateListRemove( pHatedActor ); - pHatedActor = hateListGetHighest(); - } - - if( pHatedActor ) - { - auto distance = Util::distance( getPos().x, getPos().y, getPos().z, - pHatedActor->getPos().x, - pHatedActor->getPos().y, - pHatedActor->getPos().z ); - - if( distanceOrig > maxDistanceToOrigin ) - { - hateListClear(); - changeTarget( INVALID_GAME_OBJECT_ID ); - setStance( Stance::Passive ); - //setOwner( nullptr ); - m_state = BNpcState::Retreat; - break; - } - - if( distance > minActorDistance ) - moveTo( pHatedActor->getPos() ); - else - { - if( face( pHatedActor->getPos() ) ) - sendPositionUpdate(); - // in combat range. ATTACK! - autoAttack( pHatedActor ); - } - } - else + if( distanceOrig > maxDistanceToOrigin ) { + hateListClear(); changeTarget( INVALID_GAME_OBJECT_ID ); setStance( Stance::Passive ); //setOwner( nullptr ); m_state = BNpcState::Retreat; + break; + } + + if( distance > minActorDistance ) + moveTo( pHatedActor->getPos() ); + else + { + if( face( pHatedActor->getPos() ) ) + sendPositionUpdate(); + // in combat range. ATTACK! + autoAttack( pHatedActor ); } } + else + { + changeTarget( INVALID_GAME_OBJECT_ID ); + setStance( Stance::Passive ); + //setOwner( nullptr ); + m_state = BNpcState::Retreat; + } + } } } diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 46b4c83b..9e80a007 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -20,9 +20,9 @@ Sapphire::NaviProvider::NaviProvider( Sapphire::ZonePtr pZone, Sapphire::Framewo m_naviMeshQuery( nullptr ) { // Set defaults - m_polyFindRange[0] = 2; - m_polyFindRange[1] = 4; - m_polyFindRange[2] = 2; + m_polyFindRange[0] = 10; + m_polyFindRange[1] = 20; + m_polyFindRange[2] = 10; } void Sapphire::NaviProvider::init() @@ -34,9 +34,11 @@ void Sapphire::NaviProvider::init() { auto baseMesh = meshFolder / std::filesystem::path( m_pZone->getInternalName() + ".nav" ); - //m_naviMesh = LoadMesh( baseMesh.string() ); + LoadMesh( baseMesh.string() ); // Load all meshes for testing + + /* for( const auto & entry : std::filesystem::directory_iterator( meshFolder ) ) { if( entry.path().extension().string() == ".nav" ) @@ -45,6 +47,7 @@ void Sapphire::NaviProvider::init() LoadMesh( entry.path().string() ); } } + */ InitQuery(); } @@ -221,10 +224,11 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::Path dtPolyRef startRef, endRef = 0; float start[3] = { startPos.x, startPos.y, startPos.z }; - float end[3] = { startPos.x, startPos.y, startPos.z }; + float end[3] = { endPos.x, endPos.y, endPos.z }; dtQueryFilter filter; - filter.setAreaCost( 0, 0 ); + filter.setIncludeFlags(0xffff); + filter.setExcludeFlags(0); m_naviMeshQuery->findNearestPoly( start, m_polyFindRange, &filter, &startRef, 0 ); m_naviMeshQuery->findNearestPoly( end, m_polyFindRange, &filter, &endRef, 0 ); @@ -262,6 +266,8 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::Path dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); numSmoothPath++; + auto resultCoords = std::vector< Common::FFXIVARR_POSITION3 >(); + // Move towards target a small advancement at a time until target reached or // when ran out of memory to store the path. while( npolys && numSmoothPath < MAX_SMOOTH ) @@ -365,6 +371,13 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::Path numSmoothPath++; } } + + for( int i = 0; i < numSmoothPath; i += 3 ) + { + resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[i], smoothPath[i + 2], smoothPath[i + 3] } ); + } + + return resultCoords; } else { From 8c6c8bc787e70fffad66ec69941ad11d23c6d619 Mon Sep 17 00:00:00 2001 From: goaaats Date: Mon, 21 Jan 2019 14:02:47 +0100 Subject: [PATCH 06/50] More pathing --- src/world/Actor/BNpc.cpp | 2 +- src/world/Manager/TerritoryMgr.cpp | 2 +- src/world/Navi/NaviProvider.cpp | 59 ++++++++++++++++-------------- src/world/Navi/NaviProvider.h | 10 +++-- src/world/Territory/Zone.cpp | 2 +- src/world/Territory/Zone.h | 2 +- 6 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index dca819f7..1e105142 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -153,7 +153,7 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) // reached destination return true; - auto path = m_pCurrentZone->GetNaviProvider()->PathFindFollow( m_pos, pos ); + auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos ); //face( path[0] ); diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index f4abad11..09885244 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -173,7 +173,7 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() territoryInfo->name, ( isPrivateTerritory( territoryTypeId ) ? "PRIVATE" : "PUBLIC" ), pPlaceName->name, - pZone->GetNaviProvider()->HasNaviMesh() ? "NAVI" : ""); + pZone->getNaviProvider()->hasNaviMesh() ? "NAVI" : ""); InstanceIdToZonePtrMap instanceMap; instanceMap[ guid ] = pZone; diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 9e80a007..5bb3e031 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -34,7 +34,7 @@ void Sapphire::NaviProvider::init() { auto baseMesh = meshFolder / std::filesystem::path( m_pZone->getInternalName() + ".nav" ); - LoadMesh( baseMesh.string() ); + loadMesh( baseMesh.string() ); // Load all meshes for testing @@ -49,16 +49,16 @@ void Sapphire::NaviProvider::init() } */ - InitQuery(); + initQuery(); } } -bool Sapphire::NaviProvider::HasNaviMesh() const +bool Sapphire::NaviProvider::hasNaviMesh() const { return m_naviMesh != nullptr; } -void Sapphire::NaviProvider::InitQuery() +void Sapphire::NaviProvider::initQuery() { if( m_naviMeshQuery != nullptr ) dtFreeNavMeshQuery( m_naviMeshQuery ); @@ -216,34 +216,45 @@ static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, con return true; } -std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::PathFindFollow( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) +void Sapphire::NaviProvider::toDetourPos( const Sapphire::Common::FFXIVARR_POSITION3 pos, float* out ) { + float y = pos.y; + float z = pos.z; + + out[0] = pos.x; + out[1] = y * -1; + out[2] = z * -1; +} + +std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::findFollowPath( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) { if( !m_naviMesh || !m_naviMeshQuery ) throw std::exception( "No navimesh loaded" ); + auto resultCoords = std::vector< Common::FFXIVARR_POSITION3 >(); + dtPolyRef startRef, endRef = 0; - float start[3] = { startPos.x, startPos.y, startPos.z }; - float end[3] = { endPos.x, endPos.y, endPos.z }; + float spos[3]; + NaviProvider::toDetourPos( startPos, spos ); + + float epos[3]; + NaviProvider::toDetourPos( endPos, epos ); dtQueryFilter filter; - filter.setIncludeFlags(0xffff); - filter.setExcludeFlags(0); + filter.setIncludeFlags( 0xffff ); + filter.setExcludeFlags( 0 ); - m_naviMeshQuery->findNearestPoly( start, m_polyFindRange, &filter, &startRef, 0 ); - m_naviMeshQuery->findNearestPoly( end, m_polyFindRange, &filter, &endRef, 0 ); + m_naviMeshQuery->findNearestPoly( spos, m_polyFindRange, &filter, &startRef, 0 ); + m_naviMeshQuery->findNearestPoly( epos, m_polyFindRange, &filter, &endRef, 0 ); // Couldn't find any close polys to navigate from if( !startRef || !endRef ) - return {}; + return resultCoords; - auto pathFindStatus = DT_FAILURE; - - auto pathIterNum = 0; dtPolyRef polys[MAX_POLYS]; int numPolys = 0; - m_naviMeshQuery->findPath( startRef, endRef, start, end, &filter, polys, &numPolys, MAX_POLYS ); + m_naviMeshQuery->findPath( startRef, endRef, spos, epos, &filter, polys, &numPolys, MAX_POLYS ); // Check if we got polys back for navigation if( numPolys ) @@ -254,8 +265,8 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::Path int npolys = numPolys; float iterPos[3], targetPos[3]; - m_naviMeshQuery->closestPointOnPoly( startRef, start, iterPos, 0 ); - m_naviMeshQuery->closestPointOnPoly( polys[npolys - 1], end, targetPos, 0 ); + m_naviMeshQuery->closestPointOnPoly( startRef, spos, iterPos, 0 ); + m_naviMeshQuery->closestPointOnPoly( polys[npolys - 1], epos, targetPos, 0 ); static const float STEP_SIZE = 0.5f; static const float SLOP = 0.01f; @@ -266,8 +277,6 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::Path dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); numSmoothPath++; - auto resultCoords = std::vector< Common::FFXIVARR_POSITION3 >(); - // Move towards target a small advancement at a time until target reached or // when ran out of memory to store the path. while( npolys && numSmoothPath < MAX_SMOOTH ) @@ -376,16 +385,12 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::Path { resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[i], smoothPath[i + 2], smoothPath[i + 3] } ); } + } - return resultCoords; - } - else - { - return {}; - } + return resultCoords; } -void Sapphire::NaviProvider::LoadMesh( std::string path ) +void Sapphire::NaviProvider::loadMesh( std::string path ) { FILE* fp = fopen( path.c_str(), "rb" ); if( !fp ) diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 1e70753b..a4d598da 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -36,12 +36,14 @@ namespace Sapphire NaviProvider( const ZonePtr pZone, Sapphire::FrameworkPtr pFw ); void init(); - void LoadMesh( std::string path ); - void InitQuery(); + void loadMesh( std::string path ); + void initQuery(); - std::vector< Sapphire::Common::FFXIVARR_POSITION3 > PathFindFollow(Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos); + void toDetourPos(const Common::FFXIVARR_POSITION3 position, float* out); - bool HasNaviMesh() const; + std::vector< Sapphire::Common::FFXIVARR_POSITION3 > findFollowPath(Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos); + + bool hasNaviMesh() const; protected: FrameworkPtr m_pFw; diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 0cd0c9fc..5e53b5eb 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -852,7 +852,7 @@ void Sapphire::Zone::updateSpawnPoints() } -Sapphire::NaviProvider* Sapphire::Zone::GetNaviProvider() const +Sapphire::NaviProvider* Sapphire::Zone::getNaviProvider() const { return m_naviProvider; } diff --git a/src/world/Territory/Zone.h b/src/world/Territory/Zone.h index 192c3fde..b556bff4 100644 --- a/src/world/Territory/Zone.h +++ b/src/world/Territory/Zone.h @@ -162,7 +162,7 @@ namespace Sapphire void updateSpawnPoints(); - NaviProvider* GetNaviProvider() const; + NaviProvider* getNaviProvider() const; }; } From 1927b2b1a44d0c60ec2c903311a0c46ba1dc8f53 Mon Sep 17 00:00:00 2001 From: goaaats Date: Mon, 21 Jan 2019 15:15:28 +0100 Subject: [PATCH 07/50] More pathing --- src/world/Actor/BNpc.cpp | 12 +++++++++++- src/world/Navi/NaviProvider.cpp | 14 +++++++++++++- src/world/Navi/NaviProvider.h | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 1e105142..6da0e92f 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -31,6 +31,7 @@ #include "BNpcTemplate.h" #include "Manager/TerritoryMgr.h" #include "Common.h" +#include using namespace Sapphire::Common; using namespace Sapphire::Network::Packets; @@ -155,7 +156,16 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos ); - //face( path[0] ); + if(!path.empty()) + { + for(int i = 0; i < path.size(); i++) + Logger::debug("{0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z); + + face( path[0] ); + setPos(path[0]); + } + + sendPositionUpdate(); /* float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 5bb3e031..eb3bf39e 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -225,6 +225,13 @@ void Sapphire::NaviProvider::toDetourPos( const Sapphire::Common::FFXIVARR_POSIT out[2] = z * -1; } +Sapphire::Common::FFXIVARR_POSITION3 Sapphire::NaviProvider::toGamePos( float* pos ) { + float y = pos[1]; + float z = pos[2]; + + return Common::FFXIVARR_POSITION3 { pos[0], y * -1, z * -1 }; +} + std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::findFollowPath( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) { if( !m_naviMesh || !m_naviMeshQuery ) @@ -234,11 +241,16 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::find dtPolyRef startRef, endRef = 0; + /* float spos[3]; NaviProvider::toDetourPos( startPos, spos ); float epos[3]; NaviProvider::toDetourPos( endPos, epos ); + */ + + float spos[3] = {startPos.x, startPos.y, startPos.z}; + float epos[3] = {endPos.x, endPos.y, endPos.z}; dtQueryFilter filter; filter.setIncludeFlags( 0xffff ); @@ -383,7 +395,7 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::find for( int i = 0; i < numSmoothPath; i += 3 ) { - resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[i], smoothPath[i + 2], smoothPath[i + 3] } ); + resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[i], smoothPath[i + 1], smoothPath[i + 2] } ); } } diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index a4d598da..5b9592da 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -40,6 +40,7 @@ namespace Sapphire void initQuery(); void toDetourPos(const Common::FFXIVARR_POSITION3 position, float* out); + Sapphire::Common::FFXIVARR_POSITION3 toGamePos( float* pos ); std::vector< Sapphire::Common::FFXIVARR_POSITION3 > findFollowPath(Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos); From 30f4040774d74ccfe764e7386f08820661757f1f Mon Sep 17 00:00:00 2001 From: goaaats Date: Mon, 21 Jan 2019 18:25:22 +0100 Subject: [PATCH 08/50] More pathfinding --- src/world/Actor/BNpc.cpp | 2 +- src/world/Manager/DebugCommandMgr.cpp | 12 ++++++++++++ src/world/Navi/NaviProvider.cpp | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 6da0e92f..48bf3c3e 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -315,7 +315,7 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) { const uint8_t minActorDistance = 4; const uint8_t aggroRange = 8; - const uint8_t maxDistanceToOrigin = 30; + const uint8_t maxDistanceToOrigin = 1000; if( m_status == ActorStatus::Dead ) return; diff --git a/src/world/Manager/DebugCommandMgr.cpp b/src/world/Manager/DebugCommandMgr.cpp index 957b1320..70bec2eb 100644 --- a/src/world/Manager/DebugCommandMgr.cpp +++ b/src/world/Manager/DebugCommandMgr.cpp @@ -367,6 +367,18 @@ void Sapphire::World::Manager::DebugCommandMgr::set( char* data, Entity::Player& } } } + else if( subCommand == "mobaggro" ) + { + auto inRange = player.getInRangeActors(); + + for( auto actor : inRange ) + { + if( actor->getId() == player.getTargetId() && actor->getAsChara()->isAlive() ) + { + actor->getAsBNpc()->onActionHostile( player.getAsChara() ); + } + } + } else { player.sendUrgent( "{0} is not a valid SET command.", subCommand ); diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index eb3bf39e..4694179c 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -280,6 +280,8 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::find m_naviMeshQuery->closestPointOnPoly( startRef, spos, iterPos, 0 ); m_naviMeshQuery->closestPointOnPoly( polys[npolys - 1], epos, targetPos, 0 ); + Logger::debug("IterPos: {0} {1} {2}; TargetPos: {3} {4} {5}", iterPos[0], iterPos[1], iterPos[2], targetPos[0], targetPos[1], targetPos[2]); + static const float STEP_SIZE = 0.5f; static const float SLOP = 0.01f; From 65e9d578806e669d2fcf9d195ee516253d2f5a04 Mon Sep 17 00:00:00 2001 From: goaaats Date: Tue, 22 Jan 2019 00:41:18 +0100 Subject: [PATCH 09/50] it works --- src/world/Actor/BNpc.cpp | 4 ++-- src/world/Navi/NaviProvider.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 48bf3c3e..81c36de8 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -161,8 +161,8 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) for(int i = 0; i < path.size(); i++) Logger::debug("{0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z); - face( path[0] ); - setPos(path[0]); + face( path[1] ); + setPos(path[1]); } sendPositionUpdate(); diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 4694179c..c375f3a1 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -272,7 +272,6 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::find if( numPolys ) { // Iterate over the path to find smooth path on the detail mesh surface. - dtPolyRef polys[MAX_POLYS]; memcpy( polys, polys, sizeof( dtPolyRef )*numPolys ); int npolys = numPolys; From 09ce04b37e61d6a654b3f8da86515bfd4c2a50e5 Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 17:07:40 +0100 Subject: [PATCH 10/50] change movement in BNpc --- src/world/Actor/BNpc.cpp | 90 ++++++++++++++++++++++++++++++--- src/world/Actor/BNpc.h | 8 +++ src/world/Navi/NaviProvider.cpp | 4 +- 3 files changed, 92 insertions(+), 10 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 81c36de8..7c95aed2 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -148,24 +148,96 @@ void Sapphire::Entity::BNpc::setState( BNpcState state ) m_state = state; } +void Sapphire::Entity::BNpc::step() +{ + if( m_naviLastPath.empty() ) + // No path to track + return; + + if( Util::distance( getPos().x, getPos().y, getPos().z, m_naviTarget.x, m_naviTarget.y, m_naviTarget.z ) <= 4 ) + { + // Reached target + m_naviLastPath.clear(); + return; + } + + // Check if we have to recalculate + if( Util::getTimeMs() - m_lastUpdate > 500 ) + { + auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, m_naviTarget ); + + if( !path.empty() ) + { + for( int i = 0; i < path.size(); i++ ) + Logger::debug( "[STEP] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z ); + + m_naviLastPath = path; + m_naviLastUpdate = Util::getTimeMs(); + m_naviPathStep = 0; + } + else + { + Logger::debug( "No path found for target: {0} {1} {2}", m_naviTarget.x, m_naviTarget.y, m_naviTarget.z ); + } + } + + auto stepPos = m_naviLastPath[m_naviPathStep]; + + if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 ) + { + // Reached step in path + m_naviPathStep++; + Logger::debug( "Reached step {0}", m_naviPathStep ); + + stepPos = m_naviLastPath[m_naviPathStep]; + } + + float rot = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ); + float newRot = PI - rot + ( PI / 2 ); + + face( stepPos ); + float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI; + + auto x = ( cosf( angle ) * 1.1f ); + auto y = ( getPos().y + stepPos.y ) * 0.5f; // fake value while there is no collision + auto z = ( sinf( angle ) * 1.1f ); + + Common::FFXIVARR_POSITION3 newPos{ getPos().x + x, y, getPos().z + z }; + setPos( newPos ); + + Common::FFXIVARR_POSITION3 tmpPos{ getPos().x + x, y, getPos().z + z }; + setPos( tmpPos ); + setRot( newRot ); + + sendPositionUpdate(); +} + bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) { if( Util::distance( getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z ) <= 4 ) - // reached destination + // Reached destination return true; + if( m_naviTarget.x == pos.x && m_naviTarget.y == pos.y && m_naviTarget.z == pos.z ) + // Targets are the same + return false; + auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos ); - if(!path.empty()) + if( !path.empty() ) { - for(int i = 0; i < path.size(); i++) - Logger::debug("{0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z); + for( int i = 0; i < path.size(); i++ ) + Logger::debug( "[MOVETO] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z ); - face( path[1] ); - setPos(path[1]); + m_naviLastPath = path; + m_naviTarget = pos; + m_naviPathStep = 0; + m_naviLastUpdate = Util::getTimeMs(); + } + else + { + Logger::debug( "No path found for target: {0} {1} {2}", pos.x, pos.y, pos.z ); } - - sendPositionUpdate(); /* float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); @@ -404,6 +476,8 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) } } } + + step(); } void Sapphire::Entity::BNpc::onActionHostile( Sapphire::Entity::CharaPtr pSource ) diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index db0fb1f9..aa546747 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -62,6 +62,9 @@ namespace Sapphire::Entity // return true if it reached the position bool moveTo( const Common::FFXIVARR_POSITION3& pos ); + // processes movement + void step(); + void sendPositionUpdate(); BNpcState getState() const; @@ -101,6 +104,11 @@ namespace Sapphire::Entity BNpcState m_state; std::set< std::shared_ptr< HateListEntry > > m_hateList; + uint64_t m_naviLastUpdate; + std::vector< Common::FFXIVARR_POSITION3 > m_naviLastPath; + uint8_t m_naviPathStep; + Common::FFXIVARR_POSITION3 m_naviTarget; + }; } diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index c375f3a1..064095dc 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -281,8 +281,8 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::find Logger::debug("IterPos: {0} {1} {2}; TargetPos: {3} {4} {5}", iterPos[0], iterPos[1], iterPos[2], targetPos[0], targetPos[1], targetPos[2]); - static const float STEP_SIZE = 0.5f; - static const float SLOP = 0.01f; + static const float STEP_SIZE = 1.2f; + static const float SLOP = 0.15f; int numSmoothPath = 0; float smoothPath[MAX_SMOOTH * 3]; From 7552c08ae3e3e060e5abd248703e7a81cd6e76e7 Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 17:42:09 +0100 Subject: [PATCH 11/50] Handle steps better --- src/world/Actor/BNpc.cpp | 59 ++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 7c95aed2..92d020bd 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -161,29 +161,9 @@ void Sapphire::Entity::BNpc::step() return; } - // Check if we have to recalculate - if( Util::getTimeMs() - m_lastUpdate > 500 ) - { - auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, m_naviTarget ); - - if( !path.empty() ) - { - for( int i = 0; i < path.size(); i++ ) - Logger::debug( "[STEP] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z ); - - m_naviLastPath = path; - m_naviLastUpdate = Util::getTimeMs(); - m_naviPathStep = 0; - } - else - { - Logger::debug( "No path found for target: {0} {1} {2}", m_naviTarget.x, m_naviTarget.y, m_naviTarget.z ); - } - } - auto stepPos = m_naviLastPath[m_naviPathStep]; - if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 ) + if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 && m_naviPathStep < m_naviLastPath.size() - 1 ) { // Reached step in path m_naviPathStep++; @@ -192,6 +172,7 @@ void Sapphire::Entity::BNpc::step() stepPos = m_naviLastPath[m_naviPathStep]; } + // This is probably not a good way to do it but works fine for now float rot = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ); float newRot = PI - rot + ( PI / 2 ); @@ -199,14 +180,11 @@ void Sapphire::Entity::BNpc::step() float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI; auto x = ( cosf( angle ) * 1.1f ); - auto y = ( getPos().y + stepPos.y ) * 0.5f; // fake value while there is no collision + auto y = ( getPos().y + stepPos.y ) * 0.5f; // Get speed from somewhere else? auto z = ( sinf( angle ) * 1.1f ); Common::FFXIVARR_POSITION3 newPos{ getPos().x + x, y, getPos().z + z }; setPos( newPos ); - - Common::FFXIVARR_POSITION3 tmpPos{ getPos().x + x, y, getPos().z + z }; - setPos( tmpPos ); setRot( newRot ); sendPositionUpdate(); @@ -222,23 +200,26 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) // Targets are the same return false; - auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos ); - - if( !path.empty() ) + // Check if we have to recalculate + if( Util::getTimeMs() - m_naviLastUpdate > 500 ) { - for( int i = 0; i < path.size(); i++ ) - Logger::debug( "[MOVETO] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z ); + auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos ); - m_naviLastPath = path; - m_naviTarget = pos; - m_naviPathStep = 0; - m_naviLastUpdate = Util::getTimeMs(); - } - else - { - Logger::debug( "No path found for target: {0} {1} {2}", pos.x, pos.y, pos.z ); - } + if( !path.empty() ) + { + for( int i = 0; i < path.size(); i++ ) + Logger::debug( "[MOVETO] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z ); + m_naviLastPath = path; + m_naviTarget = pos; + m_naviPathStep = 0; + m_naviLastUpdate = Util::getTimeMs(); + } + else + { + Logger::debug( "No path found for target: {0} {1} {2}", pos.x, pos.y, pos.z ); + } + } /* float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); float newRot = PI - rot + ( PI / 2 ); From 9423a8576edd9913a4e48f0089013cb770ab139e Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 19:23:49 +0100 Subject: [PATCH 12/50] Add NaviMgr --- src/world/Actor/BNpc.cpp | 13 ++++++++++++- src/world/Manager/NaviMgr.cpp | 29 ++++++++++++++++++++++++++++ src/world/Manager/NaviMgr.h | 31 ++++++++++++++++++++++++++++++ src/world/Manager/TerritoryMgr.cpp | 10 +++++++--- src/world/Navi/NaviProvider.cpp | 17 +++++++++------- src/world/Navi/NaviProvider.h | 7 +++---- src/world/ServerMgr.cpp | 20 +++++++++++-------- src/world/Territory/Zone.cpp | 8 -------- src/world/Territory/Zone.h | 4 ---- 9 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 src/world/Manager/NaviMgr.cpp create mode 100644 src/world/Manager/NaviMgr.h diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 92d020bd..1f1f7ee7 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -31,7 +31,9 @@ #include "BNpcTemplate.h" #include "Manager/TerritoryMgr.h" #include "Common.h" +#include "Framework.h" #include +#include using namespace Sapphire::Common; using namespace Sapphire::Network::Packets; @@ -203,7 +205,16 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) // Check if we have to recalculate if( Util::getTimeMs() - m_naviLastUpdate > 500 ) { - auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos ); + auto pNaviMgr = m_pFw->get< World::Manager::NaviMgr >(); + auto pNaviProvider = pNaviMgr->getNaviProvider( m_pCurrentZone->getInternalName() ); + + if(!pNaviProvider) + { + Logger::error( "No NaviProvider for zone#{0} - {1}", m_pCurrentZone->getGuId(), m_pCurrentZone->getInternalName() ); + return false; + } + + auto path = pNaviProvider->findFollowPath( m_pos, pos ); if( !path.empty() ) { diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp new file mode 100644 index 00000000..9b6f161c --- /dev/null +++ b/src/world/Manager/NaviMgr.cpp @@ -0,0 +1,29 @@ +#include "NaviMgr.h" +#include + +Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : + BaseManager( pFw ), + m_pFw( pFw ) +{ +} + +bool Sapphire::World::Manager::NaviMgr::setupTerritory( std::string internalName ) +{ + auto provider = new NaviProvider( internalName ); + + if( provider->init() ) + { + m_naviProviderTerritoryMap.insert( std::make_pair( internalName, provider ) ); + return true; + } + + return false; +} + +Sapphire::NaviProvider* Sapphire::World::Manager::NaviMgr::getNaviProvider( std::string internalName ) +{ + if( m_naviProviderTerritoryMap.find( internalName ) != m_naviProviderTerritoryMap.end() ) + return m_naviProviderTerritoryMap[internalName]; + + return nullptr; +} diff --git a/src/world/Manager/NaviMgr.h b/src/world/Manager/NaviMgr.h new file mode 100644 index 00000000..5dee0c87 --- /dev/null +++ b/src/world/Manager/NaviMgr.h @@ -0,0 +1,31 @@ +#ifndef SAPPHIRE_NAVIMGR_H +#define SAPPHIRE_NAVIMGR_H + +#include "Forwards.h" +#include "BaseManager.h" + +#include +#include + +namespace Sapphire::World::Manager +{ + class NaviMgr : public BaseManager + { + + public: + + NaviMgr( FrameworkPtr pFw ); + virtual ~NaviMgr() = default; + + bool setupTerritory( std::string internalName ); + NaviProvider* getNaviProvider( std::string internalName ); + + private: + FrameworkPtr m_pFw; + + std::unordered_map m_naviProviderTerritoryMap; + }; + +} + +#endif // SAPPHIRE_NAVIMGR_H diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index 09885244..fcbb4223 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -16,6 +16,7 @@ #include "Territory/Land.h" #include "Territory/House.h" #include "Territory/Housing/HousingInteriorTerritory.h" +#include "NaviMgr.h" Sapphire::World::Manager::TerritoryMgr::TerritoryMgr( Sapphire::FrameworkPtr pFw ) : BaseManager( pFw ), @@ -163,8 +164,8 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() uint32_t guid = getNextInstanceId(); - auto pZone = make_Zone( territoryTypeId, guid, territoryInfo->name, pPlaceName->name, framework() ); - pZone->init(); + auto pNaviMgr = framework()->get< Manager::NaviMgr >(); + bool hasNaviMesh = pNaviMgr->setupTerritory( territoryInfo->name ); Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", territoryTypeId, @@ -173,7 +174,10 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() territoryInfo->name, ( isPrivateTerritory( territoryTypeId ) ? "PRIVATE" : "PUBLIC" ), pPlaceName->name, - pZone->getNaviProvider()->hasNaviMesh() ? "NAVI" : ""); + hasNaviMesh ? "NAVI" : ""); + + auto pZone = make_Zone( territoryTypeId, guid, territoryInfo->name, pPlaceName->name, framework() ); + pZone->init(); InstanceIdToZonePtrMap instanceMap; instanceMap[ guid ] = pZone; diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 064095dc..e50660c3 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -13,11 +13,10 @@ #include #include -Sapphire::NaviProvider::NaviProvider( Sapphire::ZonePtr pZone, Sapphire::FrameworkPtr pFw ) : - m_pFw( pFw ), - m_pZone( pZone ), +Sapphire::NaviProvider::NaviProvider( std::string internalName ) : m_naviMesh( nullptr ), - m_naviMeshQuery( nullptr ) + m_naviMeshQuery( nullptr ), + m_internalName( internalName ) { // Set defaults m_polyFindRange[0] = 10; @@ -25,14 +24,14 @@ Sapphire::NaviProvider::NaviProvider( Sapphire::ZonePtr pZone, Sapphire::Framewo m_polyFindRange[2] = 10; } -void Sapphire::NaviProvider::init() +bool Sapphire::NaviProvider::init() { auto meshesFolder = std::filesystem::path( "navi" ); - auto meshFolder = meshesFolder / std::filesystem::path( m_pZone->getInternalName() ); + auto meshFolder = meshesFolder / std::filesystem::path( m_internalName ); if( std::filesystem::exists( meshFolder ) ) { - auto baseMesh = meshFolder / std::filesystem::path( m_pZone->getInternalName() + ".nav" ); + auto baseMesh = meshFolder / std::filesystem::path( m_internalName + ".nav" ); loadMesh( baseMesh.string() ); @@ -50,7 +49,11 @@ void Sapphire::NaviProvider::init() */ initQuery(); + + return true; } + + return false; } bool Sapphire::NaviProvider::hasNaviMesh() const diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 5b9592da..3b0424db 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -33,9 +33,9 @@ namespace Sapphire static const int MAX_SMOOTH = 2048; public: - NaviProvider( const ZonePtr pZone, Sapphire::FrameworkPtr pFw ); + NaviProvider( const std::string internalName ); - void init(); + bool init(); void loadMesh( std::string path ); void initQuery(); @@ -47,8 +47,7 @@ namespace Sapphire bool hasNaviMesh() const; protected: - FrameworkPtr m_pFw; - ZonePtr m_pZone; + std::string m_internalName; dtNavMesh* m_naviMesh; dtNavMeshQuery* m_naviMeshQuery; diff --git a/src/world/ServerMgr.cpp b/src/world/ServerMgr.cpp index 3de61f73..24624678 100644 --- a/src/world/ServerMgr.cpp +++ b/src/world/ServerMgr.cpp @@ -41,6 +41,7 @@ #include "Manager/ItemMgr.h" #include "Manager/MarketMgr.h" #include "Manager/RNGMgr.h" +#include "Manager/NaviMgr.h" using namespace Sapphire::World::Manager; @@ -166,6 +167,9 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] ) loadBNpcTemplates(); + auto pNaviMgr = std::make_shared< Manager::NaviMgr >( framework() ); + framework()->set< Manager::NaviMgr >( pNaviMgr ); + Logger::info( "TerritoryMgr: Setting up zones" ); auto pTeriMgr = std::make_shared< Manager::TerritoryMgr >( framework() ); auto pHousingMgr = std::make_shared< Manager::HousingMgr >( framework() ); @@ -343,7 +347,7 @@ bool Sapphire::World::ServerMgr::createSession( uint32_t sessionId ) Logger::info( "[{0}] Creating new session", session_id_str ); std::shared_ptr< Session > newSession( new Session( sessionId, framework() ) ); - m_sessionMapById[ sessionId ] = newSession; + m_sessionMapById[sessionId] = newSession; if( !newSession->loadPlayer() ) { @@ -351,7 +355,7 @@ bool Sapphire::World::ServerMgr::createSession( uint32_t sessionId ) return false; } - m_sessionMapByName[ newSession->getPlayer()->getName() ] = newSession; + m_sessionMapByName[newSession->getPlayer()->getName()] = newSession; return true; @@ -420,7 +424,7 @@ std::string Sapphire::World::ServerMgr::getPlayerNameFromDb( uint32_t playerId, void Sapphire::World::ServerMgr::updatePlayerName( uint32_t playerId, const std::string & playerNewName ) { - m_playerNameMapById[ playerId ] = playerNewName; + m_playerNameMapById[playerId] = playerNewName; } void Sapphire::World::ServerMgr::loadBNpcTemplates() @@ -452,12 +456,12 @@ void Sapphire::World::ServerMgr::loadBNpcTemplates() auto models = res->getBlobVector( 13 ); auto bnpcTemplate = std::make_shared< Entity::BNpcTemplate >( - id, bNPCBaseId, bNPCNameId, mainWeaponModel, secWeaponModel, - aggressionMode, enemyType, 0, pose, modelChara, displayFlags, - reinterpret_cast< uint32_t* >( &models[ 0 ] ), - reinterpret_cast< uint8_t* >( &look[ 0 ] ) ); + id, bNPCBaseId, bNPCNameId, mainWeaponModel, secWeaponModel, + aggressionMode, enemyType, 0, pose, modelChara, displayFlags, + reinterpret_cast( &models[0] ), + reinterpret_cast( &look[0] ) ); - m_bNpcTemplateMap[ name ] = bnpcTemplate; + m_bNpcTemplateMap[name] = bnpcTemplate; } Logger::debug( "BNpc Templates loaded: {0}", m_bNpcTemplateMap.size() ); diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 5e53b5eb..6dcad395 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -124,9 +124,6 @@ bool Sapphire::Zone::init() // all good } - m_naviProvider = new NaviProvider( shared_from_this(), m_pFw ); - m_naviProvider->init(); - return true; } @@ -852,8 +849,3 @@ void Sapphire::Zone::updateSpawnPoints() } -Sapphire::NaviProvider* Sapphire::Zone::getNaviProvider() const -{ - return m_naviProvider; -} - diff --git a/src/world/Territory/Zone.h b/src/world/Territory/Zone.h index b556bff4..ac436be7 100644 --- a/src/world/Territory/Zone.h +++ b/src/world/Territory/Zone.h @@ -63,8 +63,6 @@ namespace Sapphire std::vector< Entity::SpawnGroup > m_spawnGroups; - NaviProvider* m_naviProvider; - public: Zone(); @@ -161,8 +159,6 @@ namespace Sapphire InstanceContentPtr getAsInstanceContent(); void updateSpawnPoints(); - - NaviProvider* getNaviProvider() const; }; } From 1ac495192a8f8884b6aeba96a6a2708f066207bb Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 21:10:53 +0100 Subject: [PATCH 13/50] Cleanup --- src/world/Actor/BNpc.cpp | 10 ++---- src/world/Navi/NaviProvider.cpp | 60 +++++++++------------------------ src/world/Navi/NaviProvider.h | 12 +++++++ src/world/Territory/Zone.cpp | 4 +-- src/world/Territory/Zone.h | 1 - 5 files changed, 32 insertions(+), 55 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 1f1f7ee7..93de9409 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -169,7 +169,6 @@ void Sapphire::Entity::BNpc::step() { // Reached step in path m_naviPathStep++; - Logger::debug( "Reached step {0}", m_naviPathStep ); stepPos = m_naviLastPath[m_naviPathStep]; } @@ -218,9 +217,6 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) if( !path.empty() ) { - for( int i = 0; i < path.size(); i++ ) - Logger::debug( "[MOVETO] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z ); - m_naviLastPath = path; m_naviTarget = pos; m_naviPathStep = 0; @@ -228,11 +224,11 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) } else { - Logger::debug( "No path found for target: {0} {1} {2}", pos.x, pos.y, pos.z ); + Logger::debug( "No path found for target: {0} {1} {2} in ", pos.x, pos.y, pos.z, m_pCurrentZone->getInternalName() ); } } /* - float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); + float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); float newRot = PI - rot + ( PI / 2 ); face( pos ); @@ -379,7 +375,7 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) { const uint8_t minActorDistance = 4; const uint8_t aggroRange = 8; - const uint8_t maxDistanceToOrigin = 1000; + const uint8_t maxDistanceToOrigin = 40; if( m_status == ActorStatus::Dead ) return; diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index e50660c3..dbba2801 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -1,17 +1,16 @@ #include -#include +#include +#include +#include -#include "Framework.h" #include "NaviProvider.h" + #include #include -#include -#include - -#include "../Territory/Zone.h" -#include #include #include +#include + Sapphire::NaviProvider::NaviProvider( std::string internalName ) : m_naviMesh( nullptr ), @@ -26,28 +25,15 @@ Sapphire::NaviProvider::NaviProvider( std::string internalName ) : bool Sapphire::NaviProvider::init() { - auto meshesFolder = std::filesystem::path( "navi" ); - auto meshFolder = meshesFolder / std::filesystem::path( m_internalName ); + auto meshesFolder = std::experimental::filesystem::path( "navi" ); + auto meshFolder = meshesFolder / std::experimental::filesystem::path( m_internalName ); - if( std::filesystem::exists( meshFolder ) ) + if( std::experimental::filesystem::exists( meshFolder ) ) { - auto baseMesh = meshFolder / std::filesystem::path( m_internalName + ".nav" ); + auto baseMesh = meshFolder / std::experimental::filesystem::path( m_internalName + ".nav" ); loadMesh( baseMesh.string() ); - // Load all meshes for testing - - /* - for( const auto & entry : std::filesystem::directory_iterator( meshFolder ) ) - { - if( entry.path().extension().string() == ".nav" ) - { - Logger::debug( "Loading " + entry.path().string() ); - LoadMesh( entry.path().string() ); - } - } - */ - initQuery(); return true; @@ -70,7 +56,7 @@ void Sapphire::NaviProvider::initQuery() m_naviMeshQuery->init( m_naviMesh, 2048 ); } -static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, +int Sapphire::NaviProvider::fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, const dtPolyRef* visited, const int nvisited ) { int furthestPath = -1; @@ -115,7 +101,7 @@ static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, return req + size; } -static int fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ) +int Sapphire::NaviProvider::fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ) { if( npath < 3 ) return npath; @@ -164,7 +150,7 @@ static int fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery return npath; } -inline bool inRange( const float* v1, const float* v2, const float r, const float h ) +bool Sapphire::NaviProvider::inRange( const float* v1, const float* v2, const float r, const float h ) { const float dx = v2[0] - v1[0]; const float dy = v2[1] - v1[1]; @@ -172,11 +158,11 @@ inline bool inRange( const float* v1, const float* v2, const float r, const floa return ( dx*dx + dz * dz ) < r*r && fabsf( dy ) < h; } -static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, +bool Sapphire::NaviProvider::getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, const float minTargetDist, const dtPolyRef* path, const int pathSize, float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, - float* outPoints = 0, int* outPointCount = 0 ) + float* outPoints, int* outPointCount ) { // Find steer target. static const int MAX_STEER_POINTS = 3; @@ -219,22 +205,6 @@ static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, con return true; } -void Sapphire::NaviProvider::toDetourPos( const Sapphire::Common::FFXIVARR_POSITION3 pos, float* out ) { - float y = pos.y; - float z = pos.z; - - out[0] = pos.x; - out[1] = y * -1; - out[2] = z * -1; -} - -Sapphire::Common::FFXIVARR_POSITION3 Sapphire::NaviProvider::toGamePos( float* pos ) { - float y = pos[1]; - float z = pos[2]; - - return Common::FFXIVARR_POSITION3 { pos[0], y * -1, z * -1 }; -} - std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::findFollowPath( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) { if( !m_naviMesh || !m_naviMeshQuery ) diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 3b0424db..3108a47a 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -53,6 +53,18 @@ namespace Sapphire dtNavMeshQuery* m_naviMeshQuery; float m_polyFindRange[3]; + + private: + static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, + const dtPolyRef* visited, const int nvisited ); + static int fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ); + inline static bool inRange( const float* v1, const float* v2, const float r, const float h ); + static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, + const float minTargetDist, + const dtPolyRef* path, const int pathSize, + float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, + float* outPoints = 0, int* outPointCount = 0 ); + }; } diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 6dcad395..f5be48e3 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -722,7 +722,7 @@ void Sapphire::Zone::registerEObj( Entity::EventObjectPtr object ) onRegisterEObj( object ); - //Logger::debug( "Registered instance eobj: " + std::to_string( object->getId() ) ); + Logger::debug( "Registered instance eobj: " + std::to_string( object->getId() ) ); } Sapphire::Entity::EventObjectPtr Sapphire::Zone::getEObj( uint32_t objId ) @@ -782,7 +782,7 @@ bool Sapphire::Zone::loadSpawnGroups() m_spawnGroups.emplace_back( id, templateId, level, maxHp ); - //Logger::debug( "id: {0}, template: {1}, level: {2}, maxHp: {3}", id, m_spawnGroups.back().getTemplateId(), level, maxHp ); + Logger::debug( "id: {0}, template: {1}, level: {2}, maxHp: {3}", id, m_spawnGroups.back().getTemplateId(), level, maxHp ); } res.reset(); diff --git a/src/world/Territory/Zone.h b/src/world/Territory/Zone.h index ac436be7..657b99c9 100644 --- a/src/world/Territory/Zone.h +++ b/src/world/Territory/Zone.h @@ -6,7 +6,6 @@ #include "Cell.h" #include "CellHandler.h" -#include "Navi/NaviProvider.h" #include "ForwardsZone.h" From 116af3409e6105bf62e543c1ac49cd09aee872c0 Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 21:14:17 +0100 Subject: [PATCH 14/50] Uncomment logs --- src/world/Territory/Zone.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index f5be48e3..e1594560 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -722,7 +722,7 @@ void Sapphire::Zone::registerEObj( Entity::EventObjectPtr object ) onRegisterEObj( object ); - Logger::debug( "Registered instance eobj: " + std::to_string( object->getId() ) ); + //Logger::debug( "Registered instance eobj: " + std::to_string( object->getId() ) ); } Sapphire::Entity::EventObjectPtr Sapphire::Zone::getEObj( uint32_t objId ) @@ -805,7 +805,7 @@ bool Sapphire::Zone::loadSpawnGroups() group.getSpawnPointList().emplace_back( std::make_shared< Entity::SpawnPoint >( x, y, z, r, gimmickId ) ); - //Logger::debug( "id: {0}, x: {1}, y: {2}, z: {3}, gimmickId: {4}", id, x, y, z, gimmickId ); + Logger::debug( "id: {0}, x: {1}, y: {2}, z: {3}, gimmickId: {4}", id, x, y, z, gimmickId ); } } return false; From 258eb1d1abd7b30494446c77a3d06deaf4a521a8 Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 21:26:48 +0100 Subject: [PATCH 15/50] Style --- src/world/Actor/BNpc.cpp | 2 +- src/world/Manager/DebugCommandMgr.cpp | 8 ++++---- src/world/Navi/NaviProvider.h | 9 ++------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 78990c0f..70250b62 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -211,7 +211,7 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) auto pNaviMgr = m_pFw->get< World::Manager::NaviMgr >(); auto pNaviProvider = pNaviMgr->getNaviProvider( m_pCurrentZone->getInternalName() ); - if(!pNaviProvider) + if( !pNaviProvider ) { Logger::error( "No NaviProvider for zone#{0} - {1}", m_pCurrentZone->getGuId(), m_pCurrentZone->getInternalName() ); return false; diff --git a/src/world/Manager/DebugCommandMgr.cpp b/src/world/Manager/DebugCommandMgr.cpp index 70bec2eb..e79e3d2c 100644 --- a/src/world/Manager/DebugCommandMgr.cpp +++ b/src/world/Manager/DebugCommandMgr.cpp @@ -372,12 +372,12 @@ void Sapphire::World::Manager::DebugCommandMgr::set( char* data, Entity::Player& auto inRange = player.getInRangeActors(); for( auto actor : inRange ) + { + if( actor->getId() == player.getTargetId() && actor->getAsChara()->isAlive() ) { - if( actor->getId() == player.getTargetId() && actor->getAsChara()->isAlive() ) - { - actor->getAsBNpc()->onActionHostile( player.getAsChara() ); - } + actor->getAsBNpc()->onActionHostile( player.getAsChara() ); } + } } else { diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 3108a47a..3f54c502 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -55,15 +55,10 @@ namespace Sapphire float m_polyFindRange[3]; private: - static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, - const dtPolyRef* visited, const int nvisited ); + static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, const dtPolyRef* visited, const int nvisited ); static int fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ); inline static bool inRange( const float* v1, const float* v2, const float r, const float h ); - static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, - const float minTargetDist, - const dtPolyRef* path, const int pathSize, - float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, - float* outPoints = 0, int* outPointCount = 0 ); + static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, const float minTargetDist, const dtPolyRef* path, const int pathSize, float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, float* outPoints = 0, int* outPointCount = 0 ); }; From 25478e3a1c4ebb8ebbf911e28a083bdc2a5c7a72 Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 21:36:26 +0100 Subject: [PATCH 16/50] Spaces --- src/world/Actor/BNpc.cpp | 4 ++-- src/world/Manager/NaviMgr.cpp | 2 +- src/world/ServerMgr.cpp | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 70250b62..c13f2fe3 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -167,14 +167,14 @@ void Sapphire::Entity::BNpc::step() return; } - auto stepPos = m_naviLastPath[m_naviPathStep]; + auto stepPos = m_naviLastPath[ m_naviPathStep ]; if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 && m_naviPathStep < m_naviLastPath.size() - 1 ) { // Reached step in path m_naviPathStep++; - stepPos = m_naviLastPath[m_naviPathStep]; + stepPos = m_naviLastPath[ m_naviPathStep ]; } // This is probably not a good way to do it but works fine for now diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp index 9b6f161c..9300aa3f 100644 --- a/src/world/Manager/NaviMgr.cpp +++ b/src/world/Manager/NaviMgr.cpp @@ -23,7 +23,7 @@ bool Sapphire::World::Manager::NaviMgr::setupTerritory( std::string internalName Sapphire::NaviProvider* Sapphire::World::Manager::NaviMgr::getNaviProvider( std::string internalName ) { if( m_naviProviderTerritoryMap.find( internalName ) != m_naviProviderTerritoryMap.end() ) - return m_naviProviderTerritoryMap[internalName]; + return m_naviProviderTerritoryMap[ internalName ]; return nullptr; } diff --git a/src/world/ServerMgr.cpp b/src/world/ServerMgr.cpp index 24624678..be23f8c9 100644 --- a/src/world/ServerMgr.cpp +++ b/src/world/ServerMgr.cpp @@ -347,7 +347,7 @@ bool Sapphire::World::ServerMgr::createSession( uint32_t sessionId ) Logger::info( "[{0}] Creating new session", session_id_str ); std::shared_ptr< Session > newSession( new Session( sessionId, framework() ) ); - m_sessionMapById[sessionId] = newSession; + m_sessionMapById[ sessionId ] = newSession; if( !newSession->loadPlayer() ) { @@ -355,7 +355,7 @@ bool Sapphire::World::ServerMgr::createSession( uint32_t sessionId ) return false; } - m_sessionMapByName[newSession->getPlayer()->getName()] = newSession; + m_sessionMapByName[ newSession->getPlayer()->getName() ] = newSession; return true; @@ -424,7 +424,7 @@ std::string Sapphire::World::ServerMgr::getPlayerNameFromDb( uint32_t playerId, void Sapphire::World::ServerMgr::updatePlayerName( uint32_t playerId, const std::string & playerNewName ) { - m_playerNameMapById[playerId] = playerNewName; + m_playerNameMapById[ playerId ] = playerNewName; } void Sapphire::World::ServerMgr::loadBNpcTemplates() @@ -455,13 +455,13 @@ void Sapphire::World::ServerMgr::loadBNpcTemplates() auto look = res->getBlobVector( 12 ); auto models = res->getBlobVector( 13 ); - auto bnpcTemplate = std::make_shared< Entity::BNpcTemplate >( - id, bNPCBaseId, bNPCNameId, mainWeaponModel, secWeaponModel, - aggressionMode, enemyType, 0, pose, modelChara, displayFlags, - reinterpret_cast( &models[0] ), - reinterpret_cast( &look[0] ) ); + auto bnpcTemplate = std::make_shared< Entity::BNpcTemplate >( + id, bNPCBaseId, bNPCNameId, mainWeaponModel, secWeaponModel, + aggressionMode, enemyType, 0, pose, modelChara, displayFlags, + reinterpret_cast< uint32_t* >( &models[ 0 ] ), + reinterpret_cast< uint8_t* >( &look[ 0 ] ) ); - m_bNpcTemplateMap[name] = bnpcTemplate; + m_bNpcTemplateMap[ name ] = bnpcTemplate; } Logger::debug( "BNpc Templates loaded: {0}", m_bNpcTemplateMap.size() ); From 2973b27e26a835e5b5c5cbc42cf9c3fa767bb949 Mon Sep 17 00:00:00 2001 From: Mordred Date: Wed, 23 Jan 2019 22:37:55 +0100 Subject: [PATCH 17/50] Despawn and respawn dead bnpcs --- src/world/Actor/BNpc.cpp | 27 +++++++-- src/world/Actor/BNpc.h | 5 ++ .../Network/PacketWrappers/NpcSpawnPacket.h | 3 + src/world/Territory/Zone.cpp | 59 +++++++------------ 4 files changed, 50 insertions(+), 44 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 2fb811a3..dffaf929 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "Forwards.h" #include "Action/Action.h" @@ -67,6 +68,8 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX m_spawnPos = m_pos; + m_timeOfDeath = 0; + m_maxHp = maxHp; m_maxMp = 200; m_hp = maxHp; @@ -135,6 +138,7 @@ void Sapphire::Entity::BNpc::spawn( PlayerPtr pTarget ) void Sapphire::Entity::BNpc::despawn( PlayerPtr pTarget ) { pTarget->freePlayerSpawnId( getId() ); + pTarget->queuePacket( makeActorControl143( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ) ); } Sapphire::Entity::BNpcState Sapphire::Entity::BNpc::getState() const @@ -278,7 +282,7 @@ void Sapphire::Entity::BNpc::aggro( Sapphire::Entity::CharaPtr pChara ) if( pChara->isPlayer() ) { PlayerPtr tmpPlayer = pChara->getAsPlayer(); - tmpPlayer->queuePacket( makeActorControl142( getId(), ActorControlType::ToggleWeapon, 0, 1, 1 ) ); + tmpPlayer->queuePacket( makeActorControl142( getId(), ActorControlType::ToggleWeapon, 1, 1, 1 ) ); tmpPlayer->onMobAggro( getAsBNpc() ); } } @@ -291,6 +295,7 @@ void Sapphire::Entity::BNpc::deaggro( Sapphire::Entity::CharaPtr pChara ) if( pChara->isPlayer() ) { PlayerPtr tmpPlayer = pChara->getAsPlayer(); + tmpPlayer->queuePacket( makeActorControl142( getId(), ActorControlType::ToggleWeapon, 0, 1, 1 ) ); tmpPlayer->onMobDeaggro( getAsBNpc() ); } } @@ -301,11 +306,12 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) const uint8_t aggroRange = 8; const uint8_t maxDistanceToOrigin = 30; - if( m_status == ActorStatus::Dead ) - return; - switch( m_state ) { + case BNpcState::Dead: + case BNpcState::JustDied: + return; + case BNpcState::Retreat: { if( moveTo( m_spawnPos ) ) @@ -330,8 +336,6 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) if( distance < aggroRange && pClosestChara->isPlayer() ) aggro( pClosestChara ); - //if( distance < aggroRange && getbehavior() == 2 ) - // aggro( pClosestActor ); } } @@ -404,5 +408,16 @@ void Sapphire::Entity::BNpc::onDeath() setTargetId( INVALID_GAME_OBJECT_ID ); m_currentStance = Stance::Passive; m_state = BNpcState::Dead; + m_timeOfDeath = Util::getTimeSeconds(); hateListClear(); } + +uint32_t Sapphire::Entity::BNpc::getTimeOfDeath() const +{ + return m_timeOfDeath; +} + +void Sapphire::Entity::BNpc::setTimeOfDeath( uint32_t timeOfDeath ) +{ + m_timeOfDeath = timeOfDeath; +} diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index db0fb1f9..aed22678 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -83,6 +83,9 @@ namespace Sapphire::Entity void onDeath() override; + uint32_t getTimeOfDeath() const; + void setTimeOfDeath( uint32_t timeOfDeath); + private: uint32_t m_bNpcBaseId; uint32_t m_bNpcNameId; @@ -96,6 +99,8 @@ namespace Sapphire::Entity uint32_t m_displayFlags; uint8_t m_level; + uint32_t m_timeOfDeath; + Common::FFXIVARR_POSITION3 m_spawnPos; BNpcState m_state; diff --git a/src/world/Network/PacketWrappers/NpcSpawnPacket.h b/src/world/Network/PacketWrappers/NpcSpawnPacket.h index caa0d072..77d50e7e 100644 --- a/src/world/Network/PacketWrappers/NpcSpawnPacket.h +++ b/src/world/Network/PacketWrappers/NpcSpawnPacket.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "Actor/Player.h" #include "Actor/BNpc.h" #include "Forwards.h" @@ -57,6 +58,8 @@ namespace Sapphire::Network::Packets::Server m_data.classJob = 0; + m_data.targetId = Common::INVALID_GAME_OBJECT_ID; + //m_data.u23 = 0x04; //m_data.u24 = 256; m_data.state = static_cast< uint8_t >( bnpc.getStatus() ); diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 21817559..04be405e 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -381,49 +381,27 @@ bool Sapphire::Zone::checkWeather() void Sapphire::Zone::updateBNpcs( int64_t tickCount ) { - if( ( tickCount - m_lastMobUpdate ) > 250 ) - { - m_lastMobUpdate = tickCount; - uint32_t currTime = Sapphire::Util::getTimeSeconds(); + if( ( tickCount - m_lastMobUpdate ) <= 250 ) + return; - /*for( auto it3 = m_BattleNpcDeadMap.begin(); it3 != m_BattleNpcDeadMap.end(); ++it3 ) - { + m_lastMobUpdate = tickCount; + uint32_t currTime = Sapphire::Util::getTimeSeconds(); - Entity::BattleNpcPtr pBNpc = *it3; + for( auto entry : m_bNpcMap ) + { + Entity::BNpcPtr pBNpc = entry.second; - if( ( currTime - pBNpc->getTimeOfDeath() ) > 60 ) - { + if( !pBNpc ) + continue; - pBNpc->resetHp(); - pBNpc->resetMp(); - pBNpc->resetPos(); - pushActor( pBNpc ); + if( !pBNpc->isAlive() && currTime - pBNpc->getTimeOfDeath() > 10 ) + { + removeActor( pBNpc ); + break; + } - m_BattleNpcDeadMap.erase( it3 ); - - break; - } - }*/ - - - for( auto entry : m_bNpcMap ) - { - Entity::BNpcPtr pBNpc = entry.second; - - if( !pBNpc ) - continue; - - //if( !pBNpc->isAlive() && currTime - pBNpc->getTimeOfDeath() > ( 10 ) ) - //{ - // removeActor( pBNpc ); - // m_BattleNpcDeadMap.insert( pBNpc ); - // break; - //} - - pBNpc->update( tickCount ); - - } - } + pBNpc->update( tickCount ); + } } @@ -844,6 +822,11 @@ void Sapphire::Zone::updateSpawnPoints() pushActor( pBNpc ); } + else if( point->getLinkedBNpc() && !point->getLinkedBNpc()->isAlive() ) + { + point->setTimeOfDeath( Util::getTimeSeconds() ); + point->setLinkedBNpc( nullptr ); + } } } From 8da67dd2c5b6ded9587d4c7fea3d20efbd93366f Mon Sep 17 00:00:00 2001 From: goaaats Date: Wed, 23 Jan 2019 23:50:31 +0100 Subject: [PATCH 18/50] Fix logging, Linux --- src/world/Actor/BNpc.cpp | 2 +- src/world/Navi/NaviProvider.cpp | 26 +++++++++----------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index c13f2fe3..f1cce290 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -228,7 +228,7 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) } else { - Logger::debug( "No path found for target: {0} {1} {2} in ", pos.x, pos.y, pos.z, m_pCurrentZone->getInternalName() ); + Logger::debug( "No path found from x{0} y{1} z{2} to x{3} y{4} z{5} in {6}", getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z, m_pCurrentZone->getInternalName() ); } } /* diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index dbba2801..4e656796 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -208,20 +208,12 @@ bool Sapphire::NaviProvider::getSteerTarget( dtNavMeshQuery* navQuery, const flo std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::findFollowPath( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) { if( !m_naviMesh || !m_naviMeshQuery ) - throw std::exception( "No navimesh loaded" ); + throw std::runtime_error( "No navimesh loaded" ); auto resultCoords = std::vector< Common::FFXIVARR_POSITION3 >(); dtPolyRef startRef, endRef = 0; - /* - float spos[3]; - NaviProvider::toDetourPos( startPos, spos ); - - float epos[3]; - NaviProvider::toDetourPos( endPos, epos ); - */ - float spos[3] = {startPos.x, startPos.y, startPos.z}; float epos[3] = {endPos.x, endPos.y, endPos.z}; @@ -380,7 +372,7 @@ void Sapphire::NaviProvider::loadMesh( std::string path ) { FILE* fp = fopen( path.c_str(), "rb" ); if( !fp ) - throw std::exception( "Could open navimesh file" ); + throw std::runtime_error( "Could open navimesh file" ); // Read header. NavMeshSetHeader header; @@ -389,19 +381,19 @@ void Sapphire::NaviProvider::loadMesh( std::string path ) if( readLen != 1 ) { fclose( fp ); - throw std::exception( "Could not read NavMeshSetHeader" ); + throw std::runtime_error( "Could not read NavMeshSetHeader" ); } if( header.magic != NAVMESHSET_MAGIC ) { fclose( fp ); - throw std::exception( "Not a NavMeshSet" ); + throw std::runtime_error( "Not a NavMeshSet" ); } if( header.version != NAVMESHSET_VERSION ) { fclose( fp ); - throw std::exception( "Invalid NavMeshSet version" ); + throw std::runtime_error( "Invalid NavMeshSet version" ); } if( !m_naviMesh ) @@ -410,14 +402,14 @@ void Sapphire::NaviProvider::loadMesh( std::string path ) if( !m_naviMesh ) { fclose( fp ); - throw std::exception( "Could not allocate dtNavMesh" ); + throw std::runtime_error( "Could not allocate dtNavMesh" ); } dtStatus status = m_naviMesh->init( &header.params ); if( dtStatusFailed( status ) ) { fclose( fp ); - throw std::exception( "Could not initialize dtNavMesh" ); + throw std::runtime_error( "Could not initialize dtNavMesh" ); } } @@ -429,7 +421,7 @@ void Sapphire::NaviProvider::loadMesh( std::string path ) if( readLen != 1 ) { fclose( fp ); - throw std::exception( "Could not read NavMeshTileHeader" ); + throw std::runtime_error( "Could not read NavMeshTileHeader" ); } if( !tileHeader.tileRef || !tileHeader.dataSize ) @@ -443,7 +435,7 @@ void Sapphire::NaviProvider::loadMesh( std::string path ) { dtFree( data ); fclose( fp ); - throw std::exception( "Could not read tile data" ); + throw std::runtime_error( "Could not read tile data" ); } m_naviMesh->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0 ); From 625bcaebc7c1dea882386078bcd9ac2723112a24 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Thu, 24 Jan 2019 19:04:37 +1100 Subject: [PATCH 19/50] fix pcb_reader compiler warnings --- src/tools/pcb_reader/main.cpp | 15 +++++++-------- src/tools/pcb_reader/navmesh_exporter.h | 4 ++-- src/tools/pcb_reader/obj_exporter.h | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index 53a1bb7c..bb90af0f 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -399,13 +399,13 @@ int main( int argc, char* argv[] ) //exportMgr.exportZone( exportedZone, ( ExportFileType )exportFileType ); - printf( "Exported %s in %u seconds \n", + printf( "Exported %s in %lu seconds \n", zoneName.c_str(), - std::chrono::duration_cast< std::chrono::seconds >( std::chrono::high_resolution_clock::now() - entryStartTime ) ); + std::chrono::duration_cast< std::chrono::seconds >( std::chrono::high_resolution_clock::now() - entryStartTime ).count() ); } catch( std::exception& e ) { - printf( ( std::string( e.what() ) + "\n" ).c_str() ); + printf( "%s", ( std::string( e.what() ) + "\n" ).c_str() ); printf( "Unable to extract collision data.\n" ); printf( "Usage: pcb_reader2 territory \"path/to/game/sqpack/ffxiv\"\n" ); } @@ -413,14 +413,13 @@ int main( int argc, char* argv[] ) exportMgr.waitForTasks(); std::cout << "\n\n\n"; - printf( "Finished all tasks in %u seconds\n", + printf( "Finished all tasks in %lu seconds\n", std::chrono::duration_cast< std::chrono::seconds >( std::chrono::high_resolution_clock::now() - startTime ).count() ); getchar(); - if( eData ) - delete eData; - if( data1 ) - delete data1; + delete eData; + delete data1; + return 0; } diff --git a/src/tools/pcb_reader/navmesh_exporter.h b/src/tools/pcb_reader/navmesh_exporter.h index e2727eb2..c3d4d0c6 100644 --- a/src/tools/pcb_reader/navmesh_exporter.h +++ b/src/tools/pcb_reader/navmesh_exporter.h @@ -30,7 +30,7 @@ public: auto fileName = zone.name + ".obj"; auto end = std::chrono::high_resolution_clock::now(); - printf( "[Navmesh] Finished exporting %s in %u ms\n", + printf( "[Navmesh] Finished exporting %s in %lu ms\n", fileName.c_str(), std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } @@ -43,7 +43,7 @@ public: auto end = std::chrono::high_resolution_clock::now(); - printf( "[Navmesh] Finished exporting %s in %u ms\n", + printf( "[Navmesh] Finished exporting %s in %lu ms\n", fileName.c_str(), std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } diff --git a/src/tools/pcb_reader/obj_exporter.h b/src/tools/pcb_reader/obj_exporter.h index 378e14fb..e1a35747 100644 --- a/src/tools/pcb_reader/obj_exporter.h +++ b/src/tools/pcb_reader/obj_exporter.h @@ -50,7 +50,7 @@ public: } auto end = std::chrono::high_resolution_clock::now(); - printf( "[Obj] Finished exporting %s in %u ms\n", + printf( "[Obj] Finished exporting %s in %lu ms\n", fileName.c_str(), std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } @@ -87,7 +87,7 @@ public: } auto end = std::chrono::high_resolution_clock::now(); - printf( "[Obj] Finished exporting %s in %u ms\n", + printf( "[Obj] Finished exporting %s in %lu ms\n", fileName.c_str(), std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } From 1acacdbb3c39e7b1493479432cb69ad4ab9e2478 Mon Sep 17 00:00:00 2001 From: mordred Date: Thu, 24 Jan 2019 12:10:31 +0100 Subject: [PATCH 20/50] Fixing code for consistency, const correctness, include tree and style. --- src/world/Actor/BNpc.cpp | 1 + src/world/ForwardsZone.h | 5 + src/world/Manager/NaviMgr.cpp | 7 +- src/world/Manager/NaviMgr.h | 9 +- src/world/Navi/NaviProvider.cpp | 883 ++++++++++++++++---------------- src/world/Navi/NaviProvider.h | 56 +- 6 files changed, 487 insertions(+), 474 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index f1cce290..d4c93678 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -20,6 +20,7 @@ #include "Network/PacketWrappers/UpdateHpMpTpPacket.h" #include "Network/PacketWrappers/NpcSpawnPacket.h" #include "Network/PacketWrappers/MoveActorPacket.h" +#include "Navi/NaviProvider.h" #include "StatusEffect/StatusEffect.h" #include "Action/ActionCollision.h" diff --git a/src/world/ForwardsZone.h b/src/world/ForwardsZone.h index 4437ac84..46140156 100644 --- a/src/world/ForwardsZone.h +++ b/src/world/ForwardsZone.h @@ -35,6 +35,11 @@ namespace World TYPE_FORWARD( Session ); } +namespace World::Navi +{ +TYPE_FORWARD( NaviProvider ); +} + namespace World::Territory::Housing { TYPE_FORWARD( HousingInteriorTerritory ); diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp index 9300aa3f..4bb04179 100644 --- a/src/world/Manager/NaviMgr.cpp +++ b/src/world/Manager/NaviMgr.cpp @@ -1,4 +1,5 @@ #include "NaviMgr.h" +#include "Navi/NaviProvider.h" #include Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : @@ -7,9 +8,9 @@ Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : { } -bool Sapphire::World::Manager::NaviMgr::setupTerritory( std::string internalName ) +bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& internalName ) { - auto provider = new NaviProvider( internalName ); + auto provider = Navi::make_NaviProvider( internalName ); if( provider->init() ) { @@ -20,7 +21,7 @@ bool Sapphire::World::Manager::NaviMgr::setupTerritory( std::string internalName return false; } -Sapphire::NaviProvider* Sapphire::World::Manager::NaviMgr::getNaviProvider( std::string internalName ) +Sapphire::World::Navi::NaviProviderPtr Sapphire::World::Manager::NaviMgr::getNaviProvider( const std::string& internalName ) { if( m_naviProviderTerritoryMap.find( internalName ) != m_naviProviderTerritoryMap.end() ) return m_naviProviderTerritoryMap[ internalName ]; diff --git a/src/world/Manager/NaviMgr.h b/src/world/Manager/NaviMgr.h index 5dee0c87..5ebd9879 100644 --- a/src/world/Manager/NaviMgr.h +++ b/src/world/Manager/NaviMgr.h @@ -1,11 +1,10 @@ #ifndef SAPPHIRE_NAVIMGR_H #define SAPPHIRE_NAVIMGR_H -#include "Forwards.h" +#include "ForwardsZone.h" #include "BaseManager.h" #include -#include namespace Sapphire::World::Manager { @@ -17,13 +16,13 @@ namespace Sapphire::World::Manager NaviMgr( FrameworkPtr pFw ); virtual ~NaviMgr() = default; - bool setupTerritory( std::string internalName ); - NaviProvider* getNaviProvider( std::string internalName ); + bool setupTerritory( const std::string& internalName ); + Navi::NaviProviderPtr getNaviProvider( const std::string& internalName ); private: FrameworkPtr m_pFw; - std::unordered_map m_naviProviderTerritoryMap; + std::unordered_map< std::string, Navi::NaviProviderPtr > m_naviProviderTerritoryMap; }; } diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 4e656796..2458779d 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -1,445 +1,450 @@ -#include -#include -#include -#include - -#include "NaviProvider.h" - -#include -#include -#include -#include -#include - - -Sapphire::NaviProvider::NaviProvider( std::string internalName ) : - m_naviMesh( nullptr ), - m_naviMeshQuery( nullptr ), - m_internalName( internalName ) -{ - // Set defaults - m_polyFindRange[0] = 10; - m_polyFindRange[1] = 20; - m_polyFindRange[2] = 10; -} - -bool Sapphire::NaviProvider::init() -{ - auto meshesFolder = std::experimental::filesystem::path( "navi" ); - auto meshFolder = meshesFolder / std::experimental::filesystem::path( m_internalName ); +#include +#include +#include +#include - if( std::experimental::filesystem::exists( meshFolder ) ) - { - auto baseMesh = meshFolder / std::experimental::filesystem::path( m_internalName + ".nav" ); - - loadMesh( baseMesh.string() ); - - initQuery(); - - return true; - } - - return false; -} - -bool Sapphire::NaviProvider::hasNaviMesh() const -{ - return m_naviMesh != nullptr; -} - -void Sapphire::NaviProvider::initQuery() -{ - if( m_naviMeshQuery != nullptr ) - dtFreeNavMeshQuery( m_naviMeshQuery ); - - m_naviMeshQuery = dtAllocNavMeshQuery(); - m_naviMeshQuery->init( m_naviMesh, 2048 ); -} - -int Sapphire::NaviProvider::fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, - const dtPolyRef* visited, const int nvisited ) -{ - int furthestPath = -1; - int furthestVisited = -1; - - // Find furthest common polygon. - for( int i = npath - 1; i >= 0; --i ) - { - bool found = false; - for( int j = nvisited - 1; j >= 0; --j ) - { - if( path[i] == visited[j] ) - { - furthestPath = i; - furthestVisited = j; - found = true; - } - } - if( found ) - break; - } - - // If no intersection found just return current path. - if( furthestPath == -1 || furthestVisited == -1 ) - return npath; - - // Concatenate paths. - - // Adjust beginning of the buffer to include the visited. - const int req = nvisited - furthestVisited; - const int orig = rcMin( furthestPath + 1, npath ); - int size = rcMax( 0, npath - orig ); - if( req + size > maxPath ) - size = maxPath - req; - if( size ) - memmove( path + req, path + orig, size * sizeof( dtPolyRef ) ); - - // Store visited - for( int i = 0; i < req; ++i ) - path[i] = visited[( nvisited - 1 ) - i]; - - return req + size; -} - -int Sapphire::NaviProvider::fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ) -{ - if( npath < 3 ) - return npath; - - // Get connected polygons - static const int maxNeis = 16; - dtPolyRef neis[maxNeis]; - int nneis = 0; - - const dtMeshTile* tile = 0; - const dtPoly* poly = 0; - if( dtStatusFailed( navQuery->getAttachedNavMesh()->getTileAndPolyByRef( path[0], &tile, &poly ) ) ) - return npath; - - for( unsigned int k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[k].next ) - { - const dtLink* link = &tile->links[k]; - if( link->ref != 0 ) - { - if( nneis < maxNeis ) - neis[nneis++] = link->ref; - } - } - - // If any of the neighbour polygons is within the next few polygons - // in the path, short cut to that polygon directly. - static const int maxLookAhead = 6; - int cut = 0; - for( int i = dtMin( maxLookAhead, npath ) - 1; i > 1 && cut == 0; i-- ) { - for( int j = 0; j < nneis; j++ ) - { - if( path[i] == neis[j] ) { - cut = i; - break; - } - } - } - if( cut > 1 ) - { - int offset = cut - 1; - npath -= offset; - for( int i = 1; i < npath; i++ ) - path[i] = path[i + offset]; - } - - return npath; -} - -bool Sapphire::NaviProvider::inRange( const float* v1, const float* v2, const float r, const float h ) -{ - const float dx = v2[0] - v1[0]; - const float dy = v2[1] - v1[1]; - const float dz = v2[2] - v1[2]; - return ( dx*dx + dz * dz ) < r*r && fabsf( dy ) < h; -} - -bool Sapphire::NaviProvider::getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, - const float minTargetDist, - const dtPolyRef* path, const int pathSize, - float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, - float* outPoints, int* outPointCount ) -{ - // Find steer target. - static const int MAX_STEER_POINTS = 3; - float steerPath[MAX_STEER_POINTS * 3]; - unsigned char steerPathFlags[MAX_STEER_POINTS]; - dtPolyRef steerPathPolys[MAX_STEER_POINTS]; - int nsteerPath = 0; - navQuery->findStraightPath( startPos, endPos, path, pathSize, - steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS ); - if( !nsteerPath ) - return false; - - if( outPoints && outPointCount ) - { - *outPointCount = nsteerPath; - for( int i = 0; i < nsteerPath; ++i ) - dtVcopy( &outPoints[i * 3], &steerPath[i * 3] ); - } - - - // Find vertex far enough to steer to. - int ns = 0; - while( ns < nsteerPath ) - { - // Stop at Off-Mesh link or when point is further than slop away. - if( ( steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) || - !inRange( &steerPath[ns * 3], startPos, minTargetDist, 1000.0f ) ) - break; - ns++; - } - // Failed to find good point to steer to. - if( ns >= nsteerPath ) - return false; - - dtVcopy( steerPos, &steerPath[ns * 3] ); - steerPos[1] = startPos[1]; - steerPosFlag = steerPathFlags[ns]; - steerPosRef = steerPathPolys[ns]; - - return true; -} - -std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::NaviProvider::findFollowPath( Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos ) -{ - if( !m_naviMesh || !m_naviMeshQuery ) - throw std::runtime_error( "No navimesh loaded" ); - - auto resultCoords = std::vector< Common::FFXIVARR_POSITION3 >(); - - dtPolyRef startRef, endRef = 0; - - float spos[3] = {startPos.x, startPos.y, startPos.z}; - float epos[3] = {endPos.x, endPos.y, endPos.z}; - - dtQueryFilter filter; - filter.setIncludeFlags( 0xffff ); - filter.setExcludeFlags( 0 ); - - m_naviMeshQuery->findNearestPoly( spos, m_polyFindRange, &filter, &startRef, 0 ); - m_naviMeshQuery->findNearestPoly( epos, m_polyFindRange, &filter, &endRef, 0 ); - - // Couldn't find any close polys to navigate from - if( !startRef || !endRef ) - return resultCoords; - - dtPolyRef polys[MAX_POLYS]; - int numPolys = 0; - - m_naviMeshQuery->findPath( startRef, endRef, spos, epos, &filter, polys, &numPolys, MAX_POLYS ); - - // Check if we got polys back for navigation - if( numPolys ) - { - // Iterate over the path to find smooth path on the detail mesh surface. - memcpy( polys, polys, sizeof( dtPolyRef )*numPolys ); - int npolys = numPolys; - - float iterPos[3], targetPos[3]; - m_naviMeshQuery->closestPointOnPoly( startRef, spos, iterPos, 0 ); - m_naviMeshQuery->closestPointOnPoly( polys[npolys - 1], epos, targetPos, 0 ); - - Logger::debug("IterPos: {0} {1} {2}; TargetPos: {3} {4} {5}", iterPos[0], iterPos[1], iterPos[2], targetPos[0], targetPos[1], targetPos[2]); - - static const float STEP_SIZE = 1.2f; - static const float SLOP = 0.15f; - - int numSmoothPath = 0; - float smoothPath[MAX_SMOOTH * 3]; - - dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); - numSmoothPath++; - - // Move towards target a small advancement at a time until target reached or - // when ran out of memory to store the path. - while( npolys && numSmoothPath < MAX_SMOOTH ) - { - // Find location to steer towards. - float steerPos[3]; - unsigned char steerPosFlag; - dtPolyRef steerPosRef; - - if( !getSteerTarget( m_naviMeshQuery, iterPos, targetPos, SLOP, - polys, npolys, steerPos, steerPosFlag, steerPosRef ) ) - break; - - bool endOfPath = ( steerPosFlag & DT_STRAIGHTPATH_END ) ? true : false; - bool offMeshConnection = ( steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) ? true : false; - - // Find movement delta. - float delta[3], len; - dtVsub( delta, steerPos, iterPos ); - len = dtMathSqrtf( dtVdot( delta, delta ) ); - // If the steer target is end of path or off-mesh link, do not move past the location. - if( ( endOfPath || offMeshConnection ) && len < STEP_SIZE ) - len = 1; - else - len = STEP_SIZE / len; - float moveTgt[3]; - dtVmad( moveTgt, iterPos, delta, len ); - - // Move - float result[3]; - dtPolyRef visited[16]; - int nvisited = 0; - m_naviMeshQuery->moveAlongSurface( polys[0], iterPos, moveTgt, &filter, - result, visited, &nvisited, 16 ); - - npolys = fixupCorridor( polys, npolys, MAX_POLYS, visited, nvisited ); - npolys = fixupShortcuts( polys, npolys, m_naviMeshQuery ); - - float h = 0; - m_naviMeshQuery->getPolyHeight( polys[0], result, &h ); - result[1] = h; - dtVcopy( iterPos, result ); - - // Handle end of path and off-mesh links when close enough. - if( endOfPath && inRange( iterPos, steerPos, SLOP, 1.0f ) ) - { - // Reached end of path. - dtVcopy( iterPos, targetPos ); - if( numSmoothPath < MAX_SMOOTH ) - { - dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); - numSmoothPath++; - } - break; - } - else if( offMeshConnection && inRange( iterPos, steerPos, SLOP, 1.0f ) ) - { - // Reached off-mesh connection. - float startPos[3], endPos[3]; - - // Advance the path up to and over the off-mesh connection. - dtPolyRef prevRef = 0, polyRef = polys[0]; - int npos = 0; - while( npos < npolys && polyRef != steerPosRef ) - { - prevRef = polyRef; - polyRef = polys[npos]; - npos++; - } - for( int i = npos; i < npolys; ++i ) - polys[i - npos] = polys[i]; - npolys -= npos; - - // Handle the connection. - dtStatus status = m_naviMesh->getOffMeshConnectionPolyEndPoints( prevRef, polyRef, startPos, endPos ); - if( dtStatusSucceed( status ) ) - { - if( numSmoothPath < MAX_SMOOTH ) - { - dtVcopy( &smoothPath[numSmoothPath * 3], startPos ); - numSmoothPath++; - // Hack to make the dotted path not visible during off-mesh connection. - if( numSmoothPath & 1 ) - { - dtVcopy( &smoothPath[numSmoothPath * 3], startPos ); - numSmoothPath++; - } - } - // Move position at the other side of the off-mesh link. - dtVcopy( iterPos, endPos ); - float eh = 0.0f; - m_naviMeshQuery->getPolyHeight( polys[0], iterPos, &eh ); - iterPos[1] = eh; - } - } - - // Store results. - if( numSmoothPath < MAX_SMOOTH ) - { - dtVcopy( &smoothPath[numSmoothPath * 3], iterPos ); - numSmoothPath++; - } - } - - for( int i = 0; i < numSmoothPath; i += 3 ) - { - resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[i], smoothPath[i + 1], smoothPath[i + 2] } ); - } - } - - return resultCoords; +#include "NaviProvider.h" + +#include +#include +#include +#include +#include + + +Sapphire::World::Navi::NaviProvider::NaviProvider( const std::string& internalName ) : + m_naviMesh( nullptr ), + m_naviMeshQuery( nullptr ), + m_internalName( internalName ) +{ + // Set defaults + m_polyFindRange[0] = 10; + m_polyFindRange[1] = 20; + m_polyFindRange[2] = 10; } -void Sapphire::NaviProvider::loadMesh( std::string path ) +bool Sapphire::World::Navi::NaviProvider::init() { - FILE* fp = fopen( path.c_str(), "rb" ); + auto meshesFolder = std::experimental::filesystem::path( "navi" ); + auto meshFolder = meshesFolder / std::experimental::filesystem::path( m_internalName ); + + if( std::experimental::filesystem::exists( meshFolder ) ) + { + auto baseMesh = meshFolder / std::experimental::filesystem::path( m_internalName + ".nav" ); + + loadMesh( baseMesh.string() ); + + initQuery(); + + return true; + } + + return false; +} + +bool Sapphire::World::Navi::NaviProvider::hasNaviMesh() const +{ + return m_naviMesh != nullptr; +} + +void Sapphire::World::Navi::NaviProvider::initQuery() +{ + if( m_naviMeshQuery ) + dtFreeNavMeshQuery( m_naviMeshQuery ); + + m_naviMeshQuery = dtAllocNavMeshQuery(); + m_naviMeshQuery->init( m_naviMesh, 2048 ); +} + +int32_t Sapphire::World::Navi::NaviProvider::fixupCorridor( dtPolyRef* path, const int32_t npath, const int32_t maxPath, + const dtPolyRef* visited, const int32_t nvisited ) +{ + int32_t furthestPath = -1; + int32_t furthestVisited = -1; + + // Find furthest common polygon. + for( int32_t i = npath - 1; i >= 0; --i ) + { + bool found = false; + for( int32_t j = nvisited - 1; j >= 0; --j ) + { + if( path[ i ] == visited[ j ] ) + { + furthestPath = i; + furthestVisited = j; + found = true; + } + } + if( found ) + break; + } + + // If no intersection found just return current path. + if( furthestPath == -1 || furthestVisited == -1 ) + return npath; + + // Concatenate paths. + + // Adjust beginning of the buffer to include the visited. + const int32_t req = nvisited - furthestVisited; + const int32_t orig = rcMin( furthestPath + 1, npath ); + int32_t size = rcMax( 0, npath - orig ); + if( req + size > maxPath ) + size = maxPath - req; + if( size ) + memmove( path + req, path + orig, size * sizeof( dtPolyRef ) ); + + // Store visited + for( int32_t i = 0; i < req; ++i ) + path[i] = visited[( nvisited - 1 ) - i]; + + return req + size; +} + +int32_t Sapphire::World::Navi::NaviProvider::fixupShortcuts( dtPolyRef* path, int32_t npath, dtNavMeshQuery* navQuery ) +{ + if( npath < 3 ) + return npath; + + // Get connected polygons + static const int32_t maxNeis = 16; + dtPolyRef neis[ maxNeis ]; + int32_t nneis = 0; + + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if( dtStatusFailed( navQuery->getAttachedNavMesh()->getTileAndPolyByRef( path[ 0 ], &tile, &poly ) ) ) + return npath; + + for( uint32_t k = poly->firstLink; k != DT_NULL_LINK; k = tile->links[ k ].next ) + { + const dtLink* link = &tile->links[ k ]; + if( link->ref != 0 ) + { + if( nneis < maxNeis ) + neis[ nneis++ ] = link->ref; + } + } + + // If any of the neighbour polygons is within the next few polygons + // in the path, short cut to that polygon directly. + static const int32_t maxLookAhead = 6; + int32_t cut = 0; + for( int32_t i = dtMin( maxLookAhead, npath ) - 1; i > 1 && cut == 0; i-- ) + { + for( int32_t j = 0; j < nneis; j++ ) + { + if( path[ i ] == neis[ j ] ) + { + cut = i; + break; + } + } + } + if( cut > 1 ) + { + int32_t offset = cut - 1; + npath -= offset; + for( int32_t i = 1; i < npath; i++ ) + path[ i ] = path[ i + offset ]; + } + + return npath; +} + +bool Sapphire::World::Navi::NaviProvider::inRange( const float* v1, const float* v2, const float r, const float h ) +{ + const float dx = v2[ 0 ] - v1[ 0 ]; + const float dy = v2[ 1 ] - v1[ 1 ]; + const float dz = v2[ 2 ] - v1[ 2 ]; + return ( dx * dx + dz * dz ) < r * r && fabsf( dy ) < h; +} + +bool Sapphire::World::Navi::NaviProvider::getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, + const float minTargetDist, const dtPolyRef* path, const int32_t pathSize, + float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, + float* outPoints, int32_t* outPointCount ) +{ + // Find steer target. + static const int32_t MAX_STEER_POINTS = 3; + float steerPath[ MAX_STEER_POINTS * 3 ]; + uint8_t steerPathFlags[ MAX_STEER_POINTS ]; + dtPolyRef steerPathPolys[ MAX_STEER_POINTS ]; + int32_t nsteerPath = 0; + navQuery->findStraightPath( startPos, endPos, path, pathSize, + steerPath, steerPathFlags, steerPathPolys, &nsteerPath, MAX_STEER_POINTS ); + if( !nsteerPath ) + return false; + + if( outPoints && outPointCount ) + { + *outPointCount = nsteerPath; + for( int32_t i = 0; i < nsteerPath; ++i ) + dtVcopy( &outPoints[ i * 3 ], &steerPath[ i * 3 ] ); + } + + // Find vertex far enough to steer to. + int32_t ns = 0; + while( ns < nsteerPath ) + { + // Stop at Off-Mesh link or when point is further than slop away. + if( ( steerPathFlags[ ns ] & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) || + !inRange( &steerPath[ ns * 3 ], startPos, minTargetDist, 1000.0f ) ) + break; + ns++; + } + // Failed to find good point to steer to. + if( ns >= nsteerPath ) + return false; + + dtVcopy( steerPos, &steerPath[ ns * 3 ] ); + steerPos[ 1 ] = startPos[ 1 ]; + steerPosFlag = steerPathFlags[ ns ]; + steerPosRef = steerPathPolys[ ns ]; + + return true; +} + +std::vector< Sapphire::Common::FFXIVARR_POSITION3 > + Sapphire::World::Navi::NaviProvider::findFollowPath( const Common::FFXIVARR_POSITION3& startPos, + const Common::FFXIVARR_POSITION3& endPos ) +{ + if( !m_naviMesh || !m_naviMeshQuery ) + throw std::runtime_error( "No navimesh loaded" ); + + auto resultCoords = std::vector< Common::FFXIVARR_POSITION3 >(); + + dtPolyRef startRef, endRef = 0; + + float spos[ 3 ] = { startPos.x, startPos.y, startPos.z }; + float epos[ 3 ] = { endPos.x, endPos.y, endPos.z }; + + dtQueryFilter filter; + filter.setIncludeFlags( 0xffff ); + filter.setExcludeFlags( 0 ); + + m_naviMeshQuery->findNearestPoly( spos, m_polyFindRange, &filter, &startRef, 0 ); + m_naviMeshQuery->findNearestPoly( epos, m_polyFindRange, &filter, &endRef, 0 ); + + // Couldn't find any close polys to navigate from + if( !startRef || !endRef ) + return resultCoords; + + dtPolyRef polys[ MAX_POLYS ]; + int32_t numPolys = 0; + + m_naviMeshQuery->findPath( startRef, endRef, spos, epos, &filter, polys, &numPolys, MAX_POLYS ); + + // Check if we got polys back for navigation + if( numPolys ) + { + // Iterate over the path to find smooth path on the detail mesh surface. + memcpy( polys, polys, sizeof( dtPolyRef )*numPolys ); + int32_t npolys = numPolys; + + float iterPos[3], targetPos[3]; + m_naviMeshQuery->closestPointOnPoly( startRef, spos, iterPos, 0 ); + m_naviMeshQuery->closestPointOnPoly( polys[ npolys - 1 ], epos, targetPos, 0 ); + + Logger::debug( "IterPos: {0} {1} {2}; TargetPos: {3} {4} {5}", + iterPos[ 0 ], iterPos[ 1 ], iterPos[ 2 ], + targetPos[ 0 ], targetPos[ 1 ], targetPos[ 2 ] ); + + static const float STEP_SIZE = 1.2f; + static const float SLOP = 0.15f; + + int32_t numSmoothPath = 0; + float smoothPath[ MAX_SMOOTH * 3 ]; + + dtVcopy( &smoothPath[ numSmoothPath * 3 ], iterPos ); + numSmoothPath++; + + // Move towards target a small advancement at a time until target reached or + // when ran out of memory to store the path. + while( npolys && numSmoothPath < MAX_SMOOTH ) + { + // Find location to steer towards. + float steerPos[ 3 ]; + uint8_t steerPosFlag; + dtPolyRef steerPosRef; + + if( !getSteerTarget( m_naviMeshQuery, iterPos, targetPos, SLOP, + polys, npolys, steerPos, steerPosFlag, steerPosRef ) ) + break; + + bool endOfPath = ( steerPosFlag & DT_STRAIGHTPATH_END ) ? true : false; + bool offMeshConnection = ( steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) ? true : false; + + // Find movement delta. + float delta[ 3 ], len; + dtVsub( delta, steerPos, iterPos ); + len = dtMathSqrtf( dtVdot( delta, delta ) ); + // If the steer target is end of path or off-mesh link, do not move past the location. + if( ( endOfPath || offMeshConnection ) && len < STEP_SIZE ) + len = 1; + else + len = STEP_SIZE / len; + float moveTgt[ 3 ]; + dtVmad( moveTgt, iterPos, delta, len ); + + // Move + float result[ 3 ]; + dtPolyRef visited[ 16 ]; + int32_t nvisited = 0; + m_naviMeshQuery->moveAlongSurface( polys[ 0 ], iterPos, moveTgt, &filter, + result, visited, &nvisited, 16 ); + + npolys = fixupCorridor( polys, npolys, MAX_POLYS, visited, nvisited ); + npolys = fixupShortcuts( polys, npolys, m_naviMeshQuery ); + + float h = 0; + m_naviMeshQuery->getPolyHeight( polys[0], result, &h ); + result[ 1 ] = h; + dtVcopy( iterPos, result ); + + // Handle end of path and off-mesh links when close enough. + if( endOfPath && inRange( iterPos, steerPos, SLOP, 1.0f ) ) + { + // Reached end of path. + dtVcopy( iterPos, targetPos ); + if( numSmoothPath < MAX_SMOOTH ) + { + dtVcopy( &smoothPath[ numSmoothPath * 3 ], iterPos ); + numSmoothPath++; + } + break; + } + else if( offMeshConnection && inRange( iterPos, steerPos, SLOP, 1.0f ) ) + { + // Reached off-mesh connection. + float startPos[ 3 ], endPos[ 3 ]; + + // Advance the path up to and over the off-mesh connection. + dtPolyRef prevRef = 0, polyRef = polys[ 0 ]; + int32_t npos = 0; + while( npos < npolys && polyRef != steerPosRef ) + { + prevRef = polyRef; + polyRef = polys[ npos ]; + npos++; + } + for( int32_t i = npos; i < npolys; ++i ) + polys[ i - npos ] = polys[ i ]; + npolys -= npos; + + // Handle the connection. + dtStatus status = m_naviMesh->getOffMeshConnectionPolyEndPoints( prevRef, polyRef, startPos, endPos ); + if( dtStatusSucceed( status ) ) + { + if( numSmoothPath < MAX_SMOOTH ) + { + dtVcopy( &smoothPath[ numSmoothPath * 3 ], startPos ); + numSmoothPath++; + // Hack to make the dotted path not visible during off-mesh connection. + if( numSmoothPath & 1 ) + { + dtVcopy( &smoothPath[ numSmoothPath * 3 ], startPos ); + numSmoothPath++; + } + } + // Move position at the other side of the off-mesh link. + dtVcopy( iterPos, endPos ); + float eh = 0.0f; + m_naviMeshQuery->getPolyHeight( polys[ 0 ], iterPos, &eh ); + iterPos[ 1 ] = eh; + } + } + + // Store results. + if( numSmoothPath < MAX_SMOOTH ) + { + dtVcopy( &smoothPath[ numSmoothPath * 3 ], iterPos ); + numSmoothPath++; + } + } + + for( int32_t i = 0; i < numSmoothPath; i += 3 ) + { + resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[ i ], smoothPath[ i + 1 ], smoothPath[ i + 2 ] } ); + } + } + + return resultCoords; +} + +void Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) +{ + FILE* fp = fopen( path.c_str(), "rb" ); if( !fp ) - throw std::runtime_error( "Could open navimesh file" ); - - // Read header. - NavMeshSetHeader header; - - size_t readLen = fread( &header, sizeof( NavMeshSetHeader ), 1, fp ); - if( readLen != 1 ) - { - fclose( fp ); - throw std::runtime_error( "Could not read NavMeshSetHeader" ); - } - - if( header.magic != NAVMESHSET_MAGIC ) - { - fclose( fp ); - throw std::runtime_error( "Not a NavMeshSet" ); - } - - if( header.version != NAVMESHSET_VERSION ) - { - fclose( fp ); - throw std::runtime_error( "Invalid NavMeshSet version" ); - } - - if( !m_naviMesh ) - { - m_naviMesh = dtAllocNavMesh(); - if( !m_naviMesh ) - { - fclose( fp ); - throw std::runtime_error( "Could not allocate dtNavMesh" ); - } - - dtStatus status = m_naviMesh->init( &header.params ); - if( dtStatusFailed( status ) ) - { - fclose( fp ); - throw std::runtime_error( "Could not initialize dtNavMesh" ); - } - } - - // Read tiles. - for( int i = 0; i < header.numTiles; ++i ) - { - NavMeshTileHeader tileHeader; - readLen = fread( &tileHeader, sizeof( tileHeader ), 1, fp ); - if( readLen != 1 ) - { - fclose( fp ); - throw std::runtime_error( "Could not read NavMeshTileHeader" ); - } - - if( !tileHeader.tileRef || !tileHeader.dataSize ) - break; - - unsigned char* data = (unsigned char*)dtAlloc( tileHeader.dataSize, DT_ALLOC_PERM ); - if( !data ) break; - memset( data, 0, tileHeader.dataSize ); - readLen = fread( data, tileHeader.dataSize, 1, fp ); - if( readLen != 1 ) - { - dtFree( data ); - fclose( fp ); - throw std::runtime_error( "Could not read tile data" ); - } - - m_naviMesh->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0 ); - } - + throw std::runtime_error( "Could open navimesh file" ); + + // Read header. + NavMeshSetHeader header; + + size_t readLen = fread( &header, sizeof( NavMeshSetHeader ), 1, fp ); + if( readLen != 1 ) + { + fclose( fp ); + throw std::runtime_error( "Could not read NavMeshSetHeader" ); + } + + if( header.magic != NAVMESHSET_MAGIC ) + { + fclose( fp ); + throw std::runtime_error( "Not a NavMeshSet" ); + } + + if( header.version != NAVMESHSET_VERSION ) + { + fclose( fp ); + throw std::runtime_error( "Invalid NavMeshSet version" ); + } + + if( !m_naviMesh ) + { + m_naviMesh = dtAllocNavMesh(); + if( !m_naviMesh ) + { + fclose( fp ); + throw std::runtime_error( "Could not allocate dtNavMesh" ); + } + + dtStatus status = m_naviMesh->init( &header.params ); + if( dtStatusFailed( status ) ) + { + fclose( fp ); + throw std::runtime_error( "Could not initialize dtNavMesh" ); + } + } + + // Read tiles. + for( int32_t i = 0; i < header.numTiles; ++i ) + { + NavMeshTileHeader tileHeader; + readLen = fread( &tileHeader, sizeof( tileHeader ), 1, fp ); + if( readLen != 1 ) + { + fclose( fp ); + throw std::runtime_error( "Could not read NavMeshTileHeader" ); + } + + if( !tileHeader.tileRef || !tileHeader.dataSize ) + break; + + uint8_t* data = reinterpret_cast< uint8_t* >( dtAlloc( tileHeader.dataSize, DT_ALLOC_PERM ) ); + if( !data ) + break; + memset( data, 0, tileHeader.dataSize ); + readLen = fread( data, tileHeader.dataSize, 1, fp ); + if( readLen != 1 ) + { + dtFree( data ); + fclose( fp ); + throw std::runtime_error( "Could not read tile data" ); + } + + m_naviMesh->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0 ); + } + fclose( fp ); -} \ No newline at end of file +} diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 3f54c502..41bd05b2 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -6,43 +6,43 @@ #include #include -namespace Sapphire +namespace Sapphire::World::Navi { class NaviProvider { - static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET'; - static const int NAVMESHSET_VERSION = 1; - - struct NavMeshSetHeader - { - int magic; - int version; - int numTiles; - dtNavMeshParams params; - }; - - struct NavMeshTileHeader - { - dtTileRef tileRef; - int dataSize; + static const int32_t NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET' + static const int32_t NAVMESHSET_VERSION = 1; + + struct NavMeshSetHeader + { + int32_t magic; + int32_t version; + int32_t numTiles; + dtNavMeshParams params; }; - static const int MAX_POLYS = 256; - static const int MAX_SMOOTH = 2048; + struct NavMeshTileHeader + { + dtTileRef tileRef; + int32_t dataSize; + }; + + static const int32_t MAX_POLYS = 256; + static const int32_t MAX_SMOOTH = 2048; public: - NaviProvider( const std::string internalName ); + NaviProvider( const std::string& internalName ); bool init(); - void loadMesh( std::string path ); + void loadMesh( const std::string& path ); void initQuery(); - void toDetourPos(const Common::FFXIVARR_POSITION3 position, float* out); - Sapphire::Common::FFXIVARR_POSITION3 toGamePos( float* pos ); + void toDetourPos( const Common::FFXIVARR_POSITION3& position, float* out ); + Common::FFXIVARR_POSITION3 toGamePos( float* pos ); - std::vector< Sapphire::Common::FFXIVARR_POSITION3 > findFollowPath(Common::FFXIVARR_POSITION3 startPos, Common::FFXIVARR_POSITION3 endPos); + std::vector< Common::FFXIVARR_POSITION3 > findFollowPath( const Common::FFXIVARR_POSITION3& startPos, const Common::FFXIVARR_POSITION3& endPos ); bool hasNaviMesh() const; @@ -52,13 +52,15 @@ namespace Sapphire dtNavMesh* m_naviMesh; dtNavMeshQuery* m_naviMeshQuery; - float m_polyFindRange[3]; + float m_polyFindRange[ 3 ]; private: - static int fixupCorridor( dtPolyRef* path, const int npath, const int maxPath, const dtPolyRef* visited, const int nvisited ); - static int fixupShortcuts( dtPolyRef* path, int npath, dtNavMeshQuery* navQuery ); + static int32_t fixupCorridor( dtPolyRef* path, int32_t npath, int32_t maxPath, const dtPolyRef* visited, int32_t nvisited ); + static int32_t fixupShortcuts( dtPolyRef* path, int32_t npath, dtNavMeshQuery* navQuery ); inline static bool inRange( const float* v1, const float* v2, const float r, const float h ); - static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, const float minTargetDist, const dtPolyRef* path, const int pathSize, float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef, float* outPoints = 0, int* outPointCount = 0 ); + static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, const float minTargetDist, + const dtPolyRef* path, const int32_t pathSize, float* steerPos, uint8_t& steerPosFlag, + dtPolyRef& steerPosRef, float* outPoints = 0, int32_t* outPointCount = 0 ); }; From dfdc249716c1a5ab3208e77f09c9eef93d50e65b Mon Sep 17 00:00:00 2001 From: NotAdam Date: Thu, 24 Jan 2019 22:28:53 +1100 Subject: [PATCH 21/50] cleanup some stupid type shit in naviprovider --- src/world/Navi/NaviProvider.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 2458779d..608c3927 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -107,7 +107,7 @@ int32_t Sapphire::World::Navi::NaviProvider::fixupShortcuts( dtPolyRef* path, in return npath; // Get connected polygons - static const int32_t maxNeis = 16; + const int32_t maxNeis = 16; dtPolyRef neis[ maxNeis ]; int32_t nneis = 0; @@ -128,7 +128,7 @@ int32_t Sapphire::World::Navi::NaviProvider::fixupShortcuts( dtPolyRef* path, in // If any of the neighbour polygons is within the next few polygons // in the path, short cut to that polygon directly. - static const int32_t maxLookAhead = 6; + const int32_t maxLookAhead = 6; int32_t cut = 0; for( int32_t i = dtMin( maxLookAhead, npath ) - 1; i > 1 && cut == 0; i-- ) { @@ -166,7 +166,7 @@ bool Sapphire::World::Navi::NaviProvider::getSteerTarget( dtNavMeshQuery* navQue float* outPoints, int32_t* outPointCount ) { // Find steer target. - static const int32_t MAX_STEER_POINTS = 3; + const int32_t MAX_STEER_POINTS = 3; float steerPath[ MAX_STEER_POINTS * 3 ]; uint8_t steerPathFlags[ MAX_STEER_POINTS ]; dtPolyRef steerPathPolys[ MAX_STEER_POINTS ]; @@ -250,8 +250,8 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > iterPos[ 0 ], iterPos[ 1 ], iterPos[ 2 ], targetPos[ 0 ], targetPos[ 1 ], targetPos[ 2 ] ); - static const float STEP_SIZE = 1.2f; - static const float SLOP = 0.15f; + const float STEP_SIZE = 1.2f; + const float SLOP = 0.15f; int32_t numSmoothPath = 0; float smoothPath[ MAX_SMOOTH * 3 ]; From 1c45e69628dd5d2d1482c92003c6cffef01336c3 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Thu, 24 Jan 2019 22:34:15 +1100 Subject: [PATCH 22/50] cleanup some unnecessary static members --- src/world/Navi/NaviProvider.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 41bd05b2..2b5987aa 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -12,8 +12,8 @@ namespace Sapphire::World::Navi class NaviProvider { - static const int32_t NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET' - static const int32_t NAVMESHSET_VERSION = 1; + const int32_t NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET' + const int32_t NAVMESHSET_VERSION = 1; struct NavMeshSetHeader { @@ -29,11 +29,11 @@ namespace Sapphire::World::Navi int32_t dataSize; }; - static const int32_t MAX_POLYS = 256; - static const int32_t MAX_SMOOTH = 2048; + const int32_t MAX_POLYS = 256; + const int32_t MAX_SMOOTH = 2048; public: - NaviProvider( const std::string& internalName ); + explicit NaviProvider( const std::string& internalName ); bool init(); void loadMesh( const std::string& path ); @@ -55,12 +55,12 @@ namespace Sapphire::World::Navi float m_polyFindRange[ 3 ]; private: - static int32_t fixupCorridor( dtPolyRef* path, int32_t npath, int32_t maxPath, const dtPolyRef* visited, int32_t nvisited ); - static int32_t fixupShortcuts( dtPolyRef* path, int32_t npath, dtNavMeshQuery* navQuery ); - inline static bool inRange( const float* v1, const float* v2, const float r, const float h ); - static bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, const float minTargetDist, - const dtPolyRef* path, const int32_t pathSize, float* steerPos, uint8_t& steerPosFlag, - dtPolyRef& steerPosRef, float* outPoints = 0, int32_t* outPointCount = 0 ); + int32_t fixupCorridor( dtPolyRef* path, int32_t npath, int32_t maxPath, const dtPolyRef* visited, int32_t nvisited ); + int32_t fixupShortcuts( dtPolyRef* path, int32_t npath, dtNavMeshQuery* navQuery ); + inline bool inRange( const float* v1, const float* v2, const float r, const float h ); + bool getSteerTarget( dtNavMeshQuery* navQuery, const float* startPos, const float* endPos, const float minTargetDist, + const dtPolyRef* path, const int32_t pathSize, float* steerPos, uint8_t& steerPosFlag, + dtPolyRef& steerPosRef, float* outPoints = 0, int32_t* outPointCount = 0 ); }; From 039a9662e2085b3372b0d61f89c9609cd0e8fcac Mon Sep 17 00:00:00 2001 From: mordred Date: Thu, 24 Jan 2019 13:15:30 +0100 Subject: [PATCH 23/50] Slightly simplified path calculation --- src/world/Actor/BNpc.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index d4c93678..1a9a3f0d 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -179,19 +179,14 @@ void Sapphire::Entity::BNpc::step() } // This is probably not a good way to do it but works fine for now - float rot = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ); - float newRot = PI - rot + ( PI / 2 ); - - face( stepPos ); float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI; auto x = ( cosf( angle ) * 1.1f ); - auto y = ( getPos().y + stepPos.y ) * 0.5f; // Get speed from somewhere else? + auto y = stepPos.y; auto z = ( sinf( angle ) * 1.1f ); - Common::FFXIVARR_POSITION3 newPos{ getPos().x + x, y, getPos().z + z }; - setPos( newPos ); - setRot( newRot ); + setPos( { getPos().x + x, y, getPos().z + z } ); + face( stepPos ); sendPositionUpdate(); } From 86f74da325129ab79e9209851c1a2104eb412d5c Mon Sep 17 00:00:00 2001 From: mordred Date: Thu, 24 Jan 2019 13:33:30 +0100 Subject: [PATCH 24/50] Build fix --- src/world/Navi/NaviProvider.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 2b5987aa..fab677c3 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -8,13 +8,14 @@ namespace Sapphire::World::Navi { + const int32_t MAX_POLYS = 256; + const int32_t MAX_SMOOTH = 2048; + + const int32_t NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET' + const int32_t NAVMESHSET_VERSION = 1; class NaviProvider { - - const int32_t NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; //'MSET' - const int32_t NAVMESHSET_VERSION = 1; - struct NavMeshSetHeader { int32_t magic; @@ -29,9 +30,6 @@ namespace Sapphire::World::Navi int32_t dataSize; }; - const int32_t MAX_POLYS = 256; - const int32_t MAX_SMOOTH = 2048; - public: explicit NaviProvider( const std::string& internalName ); From 25713edbc9222f2314949b724038a8562b7ca548 Mon Sep 17 00:00:00 2001 From: goaaats Date: Thu, 24 Jan 2019 22:24:45 +0100 Subject: [PATCH 25/50] Add navi mesh folder to world config --- src/world/Manager/NaviMgr.cpp | 2 +- src/world/Navi/NaviProvider.cpp | 16 +++++++++------- src/world/Navi/NaviProvider.h | 3 ++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp index 4bb04179..c6cc789c 100644 --- a/src/world/Manager/NaviMgr.cpp +++ b/src/world/Manager/NaviMgr.cpp @@ -10,7 +10,7 @@ Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& internalName ) { - auto provider = Navi::make_NaviProvider( internalName ); + auto provider = Navi::make_NaviProvider( internalName, m_pFw ); if( provider->init() ) { diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 608c3927..0a24a5bc 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -2,6 +2,8 @@ #include #include #include +#include + #include "NaviProvider.h" @@ -11,21 +13,21 @@ #include #include - -Sapphire::World::Navi::NaviProvider::NaviProvider( const std::string& internalName ) : +Sapphire::World::Navi::NaviProvider::NaviProvider( const std::string& internalName, FrameworkPtr pFw ) : m_naviMesh( nullptr ), m_naviMeshQuery( nullptr ), - m_internalName( internalName ) + m_internalName( internalName ), + m_pFw( pFw ) { // Set defaults - m_polyFindRange[0] = 10; - m_polyFindRange[1] = 20; - m_polyFindRange[2] = 10; + m_polyFindRange[ 0 ] = 10; + m_polyFindRange[ 1 ] = 20; + m_polyFindRange[ 2 ] = 10; } bool Sapphire::World::Navi::NaviProvider::init() { - auto meshesFolder = std::experimental::filesystem::path( "navi" ); + auto meshesFolder = std::experimental::filesystem::path( m_pFw->get< Sapphire::ConfigMgr >()->getValue< std::string >( "Navigation", "MeshPath", "navi" ) ); auto meshFolder = meshesFolder / std::experimental::filesystem::path( m_internalName ); if( std::experimental::filesystem::exists( meshFolder ) ) diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index fab677c3..6d7d0f0b 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -31,7 +31,7 @@ namespace Sapphire::World::Navi }; public: - explicit NaviProvider( const std::string& internalName ); + explicit NaviProvider( const std::string& internalName, FrameworkPtr pFw ); bool init(); void loadMesh( const std::string& path ); @@ -60,6 +60,7 @@ namespace Sapphire::World::Navi const dtPolyRef* path, const int32_t pathSize, float* steerPos, uint8_t& steerPosFlag, dtPolyRef& steerPosRef, float* outPoints = 0, int32_t* outPointCount = 0 ); + FrameworkPtr m_pFw; }; } From 4ce89df526e220323cf277e0e29aa824c50ffe65 Mon Sep 17 00:00:00 2001 From: goaaats Date: Thu, 24 Jan 2019 22:33:01 +0100 Subject: [PATCH 26/50] Add default to world.ini.config --- config/world.ini.default | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/world.ini.default b/config/world.ini.default index 0fd35e32..ec461c19 100644 --- a/config/world.ini.default +++ b/config/world.ini.default @@ -15,6 +15,9 @@ DisconnectTimeout = 20 ; Sent on login - each line must be shorter than 307 characters, split lines with ';' MotD = Welcome to Sapphire!;This is a very good server;You can change these messages by editing General.MotD in config/config.ini +[Navigation] +MeshPath = navi + [Housing] ; Set the default estate name. {0} will be replaced with the plot number DefaultEstateName = Estate ${0} \ No newline at end of file From dc520b730ba5c859319db390cfe2ecef4320d56d Mon Sep 17 00:00:00 2001 From: NotAdam Date: Fri, 25 Jan 2019 12:23:38 +1100 Subject: [PATCH 27/50] fix config reading, alter territory loading output slightly --- src/common/Config/ConfigDef.h | 5 +++++ src/world/Manager/TerritoryMgr.cpp | 6 +++--- src/world/Navi/NaviProvider.cpp | 6 ++++-- src/world/ServerMgr.cpp | 10 ++++++---- src/world/Territory/Zone.cpp | 4 ++-- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/common/Config/ConfigDef.h b/src/common/Config/ConfigDef.h index a6a111b8..587ae30f 100644 --- a/src/common/Config/ConfigDef.h +++ b/src/common/Config/ConfigDef.h @@ -56,6 +56,11 @@ namespace Sapphire::Common::Config bool hotSwap; } scripts; + struct Navigation + { + std::string meshPath; + } navigation; + std::string motd; }; diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index fcbb4223..a569530f 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -167,14 +167,14 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() auto pNaviMgr = framework()->get< Manager::NaviMgr >(); bool hasNaviMesh = pNaviMgr->setupTerritory( territoryInfo->name ); - Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", + Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5:<6}{6}", territoryTypeId, guid, territoryInfo->territoryIntendedUse, territoryInfo->name, ( isPrivateTerritory( territoryTypeId ) ? "PRIVATE" : "PUBLIC" ), - pPlaceName->name, - hasNaviMesh ? "NAVI" : ""); + hasNaviMesh ? "NAVI" : "", + pPlaceName->name ); auto pZone = make_Zone( territoryTypeId, guid, territoryInfo->name, pPlaceName->name, framework() ); pZone->init(); diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 0a24a5bc..eab3d6b7 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include "NaviProvider.h" @@ -27,7 +27,9 @@ Sapphire::World::Navi::NaviProvider::NaviProvider( const std::string& internalNa bool Sapphire::World::Navi::NaviProvider::init() { - auto meshesFolder = std::experimental::filesystem::path( m_pFw->get< Sapphire::ConfigMgr >()->getValue< std::string >( "Navigation", "MeshPath", "navi" ) ); + auto& cfg = m_pFw->get< Sapphire::World::ServerMgr >()->getConfig(); + + auto meshesFolder = std::experimental::filesystem::path( cfg.navigation.meshPath ); auto meshFolder = meshesFolder / std::experimental::filesystem::path( m_internalName ); if( std::experimental::filesystem::exists( meshFolder ) ) diff --git a/src/world/ServerMgr.cpp b/src/world/ServerMgr.cpp index be23f8c9..1b314584 100644 --- a/src/world/ServerMgr.cpp +++ b/src/world/ServerMgr.cpp @@ -96,6 +96,8 @@ bool Sapphire::World::ServerMgr::loadSettings( int32_t argc, char* argv[] ) m_config.scripts.path = pConfig->getValue< std::string >( "Scripts", "Path", "./compiledscripts/" ); m_config.scripts.cachePath = pConfig->getValue< std::string >( "Scripts", "CachePath", "./cache/" ); + m_config.navigation.meshPath = pConfig->getValue< std::string >( "Navigation", "MeshPath", "navi" ); + m_config.network.disconnectTimeout = pConfig->getValue< uint16_t >( "Network", "DisconnectTimeout", 20 ); m_config.network.listenIp = pConfig->getValue< std::string >( "Network", "ListenIp", "0.0.0.0" ); m_config.network.listenPort = pConfig->getValue< uint16_t >( "Network", "ListenPort", 54992 ); @@ -455,10 +457,10 @@ void Sapphire::World::ServerMgr::loadBNpcTemplates() auto look = res->getBlobVector( 12 ); auto models = res->getBlobVector( 13 ); - auto bnpcTemplate = std::make_shared< Entity::BNpcTemplate >( - id, bNPCBaseId, bNPCNameId, mainWeaponModel, secWeaponModel, - aggressionMode, enemyType, 0, pose, modelChara, displayFlags, - reinterpret_cast< uint32_t* >( &models[ 0 ] ), + auto bnpcTemplate = std::make_shared< Entity::BNpcTemplate >( + id, bNPCBaseId, bNPCNameId, mainWeaponModel, secWeaponModel, + aggressionMode, enemyType, 0, pose, modelChara, displayFlags, + reinterpret_cast< uint32_t* >( &models[ 0 ] ), reinterpret_cast< uint8_t* >( &look[ 0 ] ) ); m_bNpcTemplateMap[ name ] = bnpcTemplate; diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 466b552b..dc5559dd 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -760,7 +760,7 @@ bool Sapphire::Zone::loadSpawnGroups() m_spawnGroups.emplace_back( id, templateId, level, maxHp ); - Logger::debug( "id: {0}, template: {1}, level: {2}, maxHp: {3}", id, m_spawnGroups.back().getTemplateId(), level, maxHp ); + Logger::trace( "id: {0}, template: {1}, level: {2}, maxHp: {3}", id, m_spawnGroups.back().getTemplateId(), level, maxHp ); } res.reset(); @@ -783,7 +783,7 @@ bool Sapphire::Zone::loadSpawnGroups() group.getSpawnPointList().emplace_back( std::make_shared< Entity::SpawnPoint >( x, y, z, r, gimmickId ) ); - Logger::debug( "id: {0}, x: {1}, y: {2}, z: {3}, gimmickId: {4}", id, x, y, z, gimmickId ); + Logger::trace( "id: {0}, x: {1}, y: {2}, z: {3}, gimmickId: {4}", id, x, y, z, gimmickId ); } } return false; From 75ec397af92418b784840617ec2067330d45e32d Mon Sep 17 00:00:00 2001 From: NotAdam Date: Fri, 25 Jan 2019 12:34:21 +1100 Subject: [PATCH 28/50] bit of cleanup and consistent startup log formatting --- src/world/Manager/TerritoryMgr.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index a569530f..efd85073 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -167,7 +167,7 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() auto pNaviMgr = framework()->get< Manager::NaviMgr >(); bool hasNaviMesh = pNaviMgr->setupTerritory( territoryInfo->name ); - Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5:<6}{6}", + Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", territoryTypeId, guid, territoryInfo->territoryIntendedUse, @@ -199,7 +199,7 @@ bool Sapphire::World::Manager::TerritoryMgr::createHousingTerritories() auto territoryTypeId = territory.first; auto territoryInfo = territory.second; uint32_t wardNum; - uint32_t wardMaxNum = 1; + uint32_t wardMaxNum = 18; if( territoryInfo->name.empty() ) continue; @@ -213,7 +213,7 @@ bool Sapphire::World::Manager::TerritoryMgr::createHousingTerritories() { uint32_t guid = getNextInstanceId(); - Logger::info( "{0}\t{1}\t{2}\t{3:<10}\tHOUSING\t{4}#{5}", + Logger::info( "{0}\t{1}\t{2}\t{3:<10}\tHOUSING\t\t{4}#{5}", territoryTypeId, guid, territoryInfo->territoryIntendedUse, @@ -224,7 +224,6 @@ bool Sapphire::World::Manager::TerritoryMgr::createHousingTerritories() auto pHousingZone = make_HousingZone( wardNum, territoryTypeId, guid, territoryInfo->name, pPlaceName->name, framework() ); pHousingZone->init(); - wardMaxNum = 18; InstanceIdToZonePtrMap instanceMap; instanceMap[ guid ] = pHousingZone; From cad5636d631fadaee5f0c97dea2dee237c8c93cb Mon Sep 17 00:00:00 2001 From: Perize Date: Fri, 25 Jan 2019 15:21:09 +0900 Subject: [PATCH 29/50] ffxivmon --- src/common/Network/PacketDef/Ipcs.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 08edad4e..36a46dc5 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -229,6 +229,12 @@ namespace Sapphire::Network::Packets IPCTYPE_UNK_320 = 0x0253, // updated 4.5 IPCTYPE_UNK_322 = 0x0255, // updated 4.5 + + MahjongTileDiscard = 0x02C1, // Other player discards a tile, chi(1)/pon(2)/kan(4)/ron(8) flags etc.. + MahjongPlayersInfo = 0x02C2, // actor id, name, rating, stuff.. + MahjongOpenGui = 0x02BC, // players must be spawned beforehand + MahjongNextRound = 0x02BD, // initial hands, # of riichi(???), winds, honba #, scores, seats and stuff.. + MahjongTileTsumo = 0x02BE, // Other player tsumo(tile drawn) }; /** From e572acc0111aecec2da949646b5cf4505a97109e Mon Sep 17 00:00:00 2001 From: mordred Date: Fri, 25 Jan 2019 08:31:20 +0100 Subject: [PATCH 30/50] Changed navigation to use meshes named by bg path instead of teriinfo name# --- src/world/Manager/TerritoryMgr.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index efd85073..a6c0e452 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -165,7 +165,13 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() uint32_t guid = getNextInstanceId(); auto pNaviMgr = framework()->get< Manager::NaviMgr >(); - bool hasNaviMesh = pNaviMgr->setupTerritory( territoryInfo->name ); + std::string bgPath = territoryInfo->bg; + std::string bg; + auto findPos = bgPath.find_last_of( "/" ); + if( findPos != std::string::npos ) + bg = bgPath.substr( findPos + 1 ); + + bool hasNaviMesh = pNaviMgr->setupTerritory( bg ); Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", territoryTypeId, From 18f23dbc64f80b3dad6b0330d87e2a5a0eb8150d Mon Sep 17 00:00:00 2001 From: mordred Date: Fri, 25 Jan 2019 08:45:00 +0100 Subject: [PATCH 31/50] Navmeshes should now work again using bgpath --- src/world/Actor/BNpc.cpp | 2 +- src/world/Manager/NaviMgr.cpp | 18 ++++++++++++++---- src/world/Manager/TerritoryMgr.cpp | 7 +------ src/world/Territory/Zone.cpp | 6 ++++++ src/world/Territory/Zone.h | 3 +++ 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 1a9a3f0d..e5cb110e 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -205,7 +205,7 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) if( Util::getTimeMs() - m_naviLastUpdate > 500 ) { auto pNaviMgr = m_pFw->get< World::Manager::NaviMgr >(); - auto pNaviProvider = pNaviMgr->getNaviProvider( m_pCurrentZone->getInternalName() ); + auto pNaviProvider = pNaviMgr->getNaviProvider( m_pCurrentZone->getBgPath() ); if( !pNaviProvider ) { diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp index c6cc789c..3c56a74a 100644 --- a/src/world/Manager/NaviMgr.cpp +++ b/src/world/Manager/NaviMgr.cpp @@ -10,11 +10,16 @@ Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& internalName ) { - auto provider = Navi::make_NaviProvider( internalName, m_pFw ); + std::string bg; + auto findPos = internalName.find_last_of( "/" ); + if( findPos != std::string::npos ) + bg = internalName.substr( findPos + 1 ); + + auto provider = Navi::make_NaviProvider( bg, m_pFw ); if( provider->init() ) { - m_naviProviderTerritoryMap.insert( std::make_pair( internalName, provider ) ); + m_naviProviderTerritoryMap.insert( std::make_pair( bg, provider ) ); return true; } @@ -23,8 +28,13 @@ bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& inter Sapphire::World::Navi::NaviProviderPtr Sapphire::World::Manager::NaviMgr::getNaviProvider( const std::string& internalName ) { - if( m_naviProviderTerritoryMap.find( internalName ) != m_naviProviderTerritoryMap.end() ) - return m_naviProviderTerritoryMap[ internalName ]; + std::string bg; + auto findPos = internalName.find_last_of( "/" ); + if( findPos != std::string::npos ) + bg = internalName.substr( findPos + 1 ); + + if( m_naviProviderTerritoryMap.find( bg ) != m_naviProviderTerritoryMap.end() ) + return m_naviProviderTerritoryMap[ bg ]; return nullptr; } diff --git a/src/world/Manager/TerritoryMgr.cpp b/src/world/Manager/TerritoryMgr.cpp index a6c0e452..2c2edb83 100644 --- a/src/world/Manager/TerritoryMgr.cpp +++ b/src/world/Manager/TerritoryMgr.cpp @@ -166,12 +166,7 @@ bool Sapphire::World::Manager::TerritoryMgr::createDefaultTerritories() auto pNaviMgr = framework()->get< Manager::NaviMgr >(); std::string bgPath = territoryInfo->bg; - std::string bg; - auto findPos = bgPath.find_last_of( "/" ); - if( findPos != std::string::npos ) - bg = bgPath.substr( findPos + 1 ); - - bool hasNaviMesh = pNaviMgr->setupTerritory( bg ); + bool hasNaviMesh = pNaviMgr->setupTerritory( bgPath ); Logger::info( "{0}\t{1}\t{2}\t{3:<10}\t{4}\t{5}\t{6}", territoryTypeId, diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index dc5559dd..77c8c30d 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -79,6 +79,7 @@ Sapphire::Zone::Zone( uint16_t territoryTypeId, uint32_t guId, m_weatherOverride = Weather::None; m_territoryTypeInfo = pExdData->get< Sapphire::Data::TerritoryType >( territoryTypeId ); + m_bgPath = m_territoryTypeInfo->bg; loadWeatherRates(); loadSpawnGroups(); @@ -352,6 +353,11 @@ const std::string& Sapphire::Zone::getInternalName() const return m_internalName; } +const std::string& Sapphire::Zone::getBgPath() const +{ + return m_bgPath; +} + std::size_t Sapphire::Zone::getPopCount() const { return m_playerMap.size(); diff --git a/src/world/Territory/Zone.h b/src/world/Territory/Zone.h index 657b99c9..ee37ce0a 100644 --- a/src/world/Territory/Zone.h +++ b/src/world/Territory/Zone.h @@ -38,6 +38,7 @@ namespace Sapphire std::string m_placeName; std::string m_internalName; + std::string m_bgPath; std::unordered_map< int32_t, Entity::PlayerPtr > m_playerMap; std::unordered_map< int32_t, Entity::BNpcPtr > m_bNpcMap; @@ -135,6 +136,8 @@ namespace Sapphire const std::string& getInternalName() const; + const std::string& getBgPath() const; + std::size_t getPopCount() const; void loadWeatherRates(); From 4e03fd86dfd1ab3a37cb24bbc6365210f900d003 Mon Sep 17 00:00:00 2001 From: mordred Date: Fri, 25 Jan 2019 08:50:44 +0100 Subject: [PATCH 32/50] Renamed params for clearity --- src/world/Manager/NaviMgr.cpp | 12 ++++++------ src/world/Manager/NaviMgr.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp index 3c56a74a..5462af7f 100644 --- a/src/world/Manager/NaviMgr.cpp +++ b/src/world/Manager/NaviMgr.cpp @@ -8,12 +8,12 @@ Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : { } -bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& internalName ) +bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& bgPath ) { std::string bg; - auto findPos = internalName.find_last_of( "/" ); + auto findPos = bgPath.find_last_of( "/" ); if( findPos != std::string::npos ) - bg = internalName.substr( findPos + 1 ); + bg = bgPath.substr( findPos + 1 ); auto provider = Navi::make_NaviProvider( bg, m_pFw ); @@ -26,12 +26,12 @@ bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& inter return false; } -Sapphire::World::Navi::NaviProviderPtr Sapphire::World::Manager::NaviMgr::getNaviProvider( const std::string& internalName ) +Sapphire::World::Navi::NaviProviderPtr Sapphire::World::Manager::NaviMgr::getNaviProvider( const std::string& bgPath ) { std::string bg; - auto findPos = internalName.find_last_of( "/" ); + auto findPos = bgPath.find_last_of( "/" ); if( findPos != std::string::npos ) - bg = internalName.substr( findPos + 1 ); + bg = bgPath.substr( findPos + 1 ); if( m_naviProviderTerritoryMap.find( bg ) != m_naviProviderTerritoryMap.end() ) return m_naviProviderTerritoryMap[ bg ]; diff --git a/src/world/Manager/NaviMgr.h b/src/world/Manager/NaviMgr.h index 5ebd9879..5a55ab0c 100644 --- a/src/world/Manager/NaviMgr.h +++ b/src/world/Manager/NaviMgr.h @@ -16,8 +16,8 @@ namespace Sapphire::World::Manager NaviMgr( FrameworkPtr pFw ); virtual ~NaviMgr() = default; - bool setupTerritory( const std::string& internalName ); - Navi::NaviProviderPtr getNaviProvider( const std::string& internalName ); + bool setupTerritory( const std::string& bgPath ); + Navi::NaviProviderPtr getNaviProvider( const std::string& bgPath ); private: FrameworkPtr m_pFw; From babd47d7724d600f41a0f0a37ac0831499d75366 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Fri, 25 Jan 2019 19:00:21 +1100 Subject: [PATCH 33/50] add customisable log level to config --- config/global.ini.default | 2 ++ src/api/main.cpp | 2 ++ src/common/Config/ConfigDef.h | 2 ++ src/common/Config/ConfigMgr.cpp | 2 ++ src/common/Logging/Logger.cpp | 5 +++++ src/common/Logging/Logger.h | 1 + src/lobby/ServerLobby.cpp | 2 ++ src/world/ServerMgr.cpp | 2 ++ 8 files changed, 18 insertions(+) diff --git a/config/global.ini.default b/config/global.ini.default index 87c3b4cc..db3ec4a2 100644 --- a/config/global.ini.default +++ b/config/global.ini.default @@ -12,6 +12,8 @@ ServerSecret = default DataPath = C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack WorldID = 67 DefaultGMRank = 255 +LogLevel = 1 +LogFilter = 0 [Network] ; Values definining how Users and other servers will access - these have to be set to your public IP when running a public server diff --git a/src/api/main.cpp b/src/api/main.cpp index 5fd967a0..92d422b1 100644 --- a/src/api/main.cpp +++ b/src/api/main.cpp @@ -713,6 +713,8 @@ int main( int argc, char* argv[] ) if( !loadSettings( argc, argv ) ) throw std::exception(); + Logger::setLogLevel( m_config.global.general.logLevel ); + server.resource[ "^/ZoneName/([0-9]+)$" ][ "GET" ] = &getZoneName; server.resource[ "^/sapphire-api/lobby/createAccount" ][ "POST" ] = &createAccount; server.resource[ "^/sapphire-api/lobby/login" ][ "POST" ] = &login; diff --git a/src/common/Config/ConfigDef.h b/src/common/Config/ConfigDef.h index 587ae30f..3aca9cff 100644 --- a/src/common/Config/ConfigDef.h +++ b/src/common/Config/ConfigDef.h @@ -16,6 +16,8 @@ namespace Sapphire::Common::Config uint16_t worldID; uint8_t defaultGMRank; + uint8_t logLevel; + uint32_t logFilter; } general; struct Network diff --git a/src/common/Config/ConfigMgr.cpp b/src/common/Config/ConfigMgr.cpp index a1ad914b..a4fa1bdc 100644 --- a/src/common/Config/ConfigMgr.cpp +++ b/src/common/Config/ConfigMgr.cpp @@ -57,6 +57,8 @@ bool Sapphire::ConfigMgr::loadGlobalConfig( Common::Config::GlobalConfig& config config.general.serverSecret = getValue< std::string >( "General", "ServerSecret", "default" ); config.general.worldID = getValue< uint16_t >( "General", "WorldID", 67 ); config.general.defaultGMRank = getValue< uint8_t >( "General", "DefaultGMRank", 255 ); + config.general.logLevel = getValue< uint8_t >( "General", "LogLevel", 1 ); + config.general.logFilter = getValue< uint32_t >( "General", "LogFilter", 0 ); // network config.network.zoneHost = getValue< std::string >( "Network", "ZoneHost", "127.0.0.1" ); diff --git a/src/common/Logging/Logger.cpp b/src/common/Logging/Logger.cpp index dbf83f4f..a8c17bff 100644 --- a/src/common/Logging/Logger.cpp +++ b/src/common/Logging/Logger.cpp @@ -53,6 +53,11 @@ namespace Sapphire spdlog::flush_on( spdlog::level::critical ); } + void Logger::setLogLevel( uint8_t logLevel ) + { + spdlog::set_level( static_cast< spdlog::level::level_enum >( logLevel ) ); + } + void Logger::error( const std::string& text ) { spdlog::get( "logger" )->error( text ); diff --git a/src/common/Logging/Logger.h b/src/common/Logging/Logger.h index 5151eb1d..af0c9d0d 100644 --- a/src/common/Logging/Logger.h +++ b/src/common/Logging/Logger.h @@ -19,6 +19,7 @@ namespace Sapphire public: static void init( const std::string& logPath ); + static void setLogLevel( uint8_t logLevel ); // todo: this is a minor increase in build time because of fmtlib, but much less than including spdlog directly diff --git a/src/lobby/ServerLobby.cpp b/src/lobby/ServerLobby.cpp index d1530dcf..47d8a868 100644 --- a/src/lobby/ServerLobby.cpp +++ b/src/lobby/ServerLobby.cpp @@ -63,6 +63,8 @@ namespace Sapphire return; } + Logger::setLogLevel( m_config.global.general.logLevel ); + auto pFw = std::make_shared< Framework >(); Network::HivePtr hive( new Network::Hive() ); Network::addServerToHive< Network::GameConnection >( m_ip, m_port, hive, pFw ); diff --git a/src/world/ServerMgr.cpp b/src/world/ServerMgr.cpp index 1b314584..7149356d 100644 --- a/src/world/ServerMgr.cpp +++ b/src/world/ServerMgr.cpp @@ -129,6 +129,8 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] ) return; } + Logger::setLogLevel( m_config.global.general.logLevel ); + Logger::info( "Setting up generated EXD data" ); auto pExdData = std::make_shared< Data::ExdDataGenerated >(); auto dataPath = m_config.global.general.dataPath; From 0234800fce5d6fb54340c0c226bee81ea2a05fcd Mon Sep 17 00:00:00 2001 From: NotAdam Date: Fri, 25 Jan 2019 22:43:04 +1100 Subject: [PATCH 34/50] remove exceptions and handle error cases inside initMesh --- src/world/Navi/NaviProvider.cpp | 36 +++++++++++++++++++++++---------- src/world/Navi/NaviProvider.h | 2 +- src/world/Script/ScriptMgr.cpp | 2 +- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index eab3d6b7..7ad3f463 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -36,7 +36,8 @@ bool Sapphire::World::Navi::NaviProvider::init() { auto baseMesh = meshFolder / std::experimental::filesystem::path( m_internalName + ".nav" ); - loadMesh( baseMesh.string() ); + if( !loadMesh( baseMesh.string() ) ) + return false; initQuery(); @@ -376,11 +377,14 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > return resultCoords; } -void Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) +bool Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) { FILE* fp = fopen( path.c_str(), "rb" ); if( !fp ) - throw std::runtime_error( "Could open navimesh file" ); + { + Logger::error( "Couldn't open navimesh file: {0}", path ); + return false; + } // Read header. NavMeshSetHeader header; @@ -389,19 +393,22 @@ void Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) if( readLen != 1 ) { fclose( fp ); - throw std::runtime_error( "Could not read NavMeshSetHeader" ); + Logger::error( "Couldn't read NavMeshSetHeader for {0}", path ); + return false; } if( header.magic != NAVMESHSET_MAGIC ) { fclose( fp ); - throw std::runtime_error( "Not a NavMeshSet" ); + Logger::error( "'{0}' has an incorrect NavMeshSet header.", path ); + return false; } if( header.version != NAVMESHSET_VERSION ) { fclose( fp ); - throw std::runtime_error( "Invalid NavMeshSet version" ); + Logger::error( "'{0}' has an incorrect NavMeshSet version. Expected '{1}', got '{2}'", path, NAVMESHSET_VERSION, header.version ); + return false; } if( !m_naviMesh ) @@ -410,14 +417,16 @@ void Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) if( !m_naviMesh ) { fclose( fp ); - throw std::runtime_error( "Could not allocate dtNavMesh" ); + Logger::error( "Couldn't allocate dtNavMesh" ); + return false; } dtStatus status = m_naviMesh->init( &header.params ); if( dtStatusFailed( status ) ) { fclose( fp ); - throw std::runtime_error( "Could not initialize dtNavMesh" ); + Logger::error( "Couldn't initialise dtNavMesh" ); + return false; } } @@ -429,13 +438,14 @@ void Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) if( readLen != 1 ) { fclose( fp ); - throw std::runtime_error( "Could not read NavMeshTileHeader" ); + Logger::error( "Couldn't read NavMeshTileHeader from '{0}'", path ); + return false; } if( !tileHeader.tileRef || !tileHeader.dataSize ) break; - uint8_t* data = reinterpret_cast< uint8_t* >( dtAlloc( tileHeader.dataSize, DT_ALLOC_PERM ) ); + auto data = reinterpret_cast< uint8_t* >( dtAlloc( tileHeader.dataSize, DT_ALLOC_PERM ) ); if( !data ) break; memset( data, 0, tileHeader.dataSize ); @@ -444,11 +454,15 @@ void Sapphire::World::Navi::NaviProvider::loadMesh( const std::string& path ) { dtFree( data ); fclose( fp ); - throw std::runtime_error( "Could not read tile data" ); + + Logger::error( "Couldn't read tile data from '{0}'", path ); + return false; } m_naviMesh->addTile( data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0 ); } fclose( fp ); + + return true; } diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index 6d7d0f0b..ff833ca4 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -34,7 +34,7 @@ namespace Sapphire::World::Navi explicit NaviProvider( const std::string& internalName, FrameworkPtr pFw ); bool init(); - void loadMesh( const std::string& path ); + bool loadMesh( const std::string& path ); void initQuery(); void toDetourPos( const Common::FFXIVARR_POSITION3& position, float* out ); diff --git a/src/world/Script/ScriptMgr.cpp b/src/world/Script/ScriptMgr.cpp index c1cb2f69..d9021b9f 100644 --- a/src/world/Script/ScriptMgr.cpp +++ b/src/world/Script/ScriptMgr.cpp @@ -53,7 +53,7 @@ bool Sapphire::Scripting::ScriptMgr::init() if( !status ) { - Logger::error( "ScriptMgr: failed to load scripts, the server will not function correctly without scripts loaded." ); + Logger::error( "ScriptMgr: failed to load modules, the server will not function correctly without scripts loaded." ); return false; } From 19cf05c9c1f0defb7f122dc60cc36ed1ca2d1db0 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Fri, 25 Jan 2019 22:47:50 +1100 Subject: [PATCH 35/50] only create naviproviders if one doesn't exist for the map --- src/world/Manager/NaviMgr.cpp | 23 +++++++++++++++-------- src/world/Manager/NaviMgr.h | 2 ++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/world/Manager/NaviMgr.cpp b/src/world/Manager/NaviMgr.cpp index 5462af7f..c3490920 100644 --- a/src/world/Manager/NaviMgr.cpp +++ b/src/world/Manager/NaviMgr.cpp @@ -10,10 +10,11 @@ Sapphire::World::Manager::NaviMgr::NaviMgr( FrameworkPtr pFw ) : bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& bgPath ) { - std::string bg; - auto findPos = bgPath.find_last_of( "/" ); - if( findPos != std::string::npos ) - bg = bgPath.substr( findPos + 1 ); + std::string bg = getBgName( bgPath ); + + // check if a provider exists already + if( m_naviProviderTerritoryMap.find( bg ) != m_naviProviderTerritoryMap.end() ) + return true; auto provider = Navi::make_NaviProvider( bg, m_pFw ); @@ -28,13 +29,19 @@ bool Sapphire::World::Manager::NaviMgr::setupTerritory( const std::string& bgPat Sapphire::World::Navi::NaviProviderPtr Sapphire::World::Manager::NaviMgr::getNaviProvider( const std::string& bgPath ) { - std::string bg; - auto findPos = bgPath.find_last_of( "/" ); - if( findPos != std::string::npos ) - bg = bgPath.substr( findPos + 1 ); + std::string bg = getBgName( bgPath ); if( m_naviProviderTerritoryMap.find( bg ) != m_naviProviderTerritoryMap.end() ) return m_naviProviderTerritoryMap[ bg ]; return nullptr; } + +std::string Sapphire::World::Manager::NaviMgr::getBgName( const std::string& bgPath ) +{ + auto findPos = bgPath.find_last_of( "/" ); + if( findPos != std::string::npos ) + return bgPath.substr( findPos + 1 ); + + return ""; +} diff --git a/src/world/Manager/NaviMgr.h b/src/world/Manager/NaviMgr.h index 5a55ab0c..6aeac713 100644 --- a/src/world/Manager/NaviMgr.h +++ b/src/world/Manager/NaviMgr.h @@ -22,6 +22,8 @@ namespace Sapphire::World::Manager private: FrameworkPtr m_pFw; + std::string getBgName( const std::string& bgPath ); + std::unordered_map< std::string, Navi::NaviProviderPtr > m_naviProviderTerritoryMap; }; From 180c3fbdf0a7c68d56af6f4ee4fdced572a5d820 Mon Sep 17 00:00:00 2001 From: Perize Date: Fri, 25 Jan 2019 22:48:10 +0900 Subject: [PATCH 36/50] Mahjong opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A very professional, carefully written commit messagesâ„¢. --- src/common/Network/PacketDef/Ipcs.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 36a46dc5..afded198 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -229,12 +229,17 @@ namespace Sapphire::Network::Packets IPCTYPE_UNK_320 = 0x0253, // updated 4.5 IPCTYPE_UNK_322 = 0x0255, // updated 4.5 - - MahjongTileDiscard = 0x02C1, // Other player discards a tile, chi(1)/pon(2)/kan(4)/ron(8) flags etc.. - MahjongPlayersInfo = 0x02C2, // actor id, name, rating, stuff.. - MahjongOpenGui = 0x02BC, // players must be spawned beforehand - MahjongNextRound = 0x02BD, // initial hands, # of riichi(???), winds, honba #, scores, seats and stuff.. - MahjongTileTsumo = 0x02BE, // Other player tsumo(tile drawn) + /// Doman Mahjong ////////////////////////////////////// + MahjongOpenGui = 0x02BC, // only available in mahjong instance + MahjongNextRound = 0x02BD, // initial hands(baipai), # of riichi(wat), winds, honba, score and stuff + MahjongPlayerAction = 0x02BE, // tsumo(as in drawing a tile) called chi/pon/kan/riichi + MahjongEndRoundTsumo = 0x02BF, // called tsumo + MahjongEndRoundRon = 0x2C0, // called ron or double ron (waiting for action must be flagged from discard packet to call) + MahjongTileDiscard = 0x02C1, // giri (discarding a tile.) chi(1)/pon(2)/kan(4)/ron(8) flags etc.. + MahjongPlayersInfo = 0x02C2, // actor id, name, rating and stuff.. + // 2C3 and 2C4 are currently unknown + MahjongEndRoundDraw = 0x02C5, // self explanatory + MahjongEndGame = 0x02C6, // finished oorasu(all-last) round; shows a result screen. }; /** From 0c1cef8050a57325005404e46aaf6b16c4ac0b85 Mon Sep 17 00:00:00 2001 From: Mordred Date: Sat, 26 Jan 2019 00:06:24 +0100 Subject: [PATCH 37/50] Initial roaming tests, still very rough and ugly --- CMakeLists.txt | 1 - cmake/paths.cmake | 1 + src/world/Actor/BNpc.cpp | 68 ++++++++++++++++----------------- src/world/Actor/BNpc.h | 3 ++ src/world/Navi/NaviProvider.cpp | 63 ++++++++++++++++++++++++++++-- src/world/Navi/NaviProvider.h | 5 ++- 6 files changed, 99 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9623c60e..f6a3419f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,6 @@ add_custom_target( copy_runtime_files ALL # Dependencies and compiler settings # ###################################### include( "cmake/paths.cmake" ) -#include( "cmake/mysql.cmake" ) include( "cmake/compiler.cmake" ) include( "cmake/cotire.cmake" ) diff --git a/cmake/paths.cmake b/cmake/paths.cmake index 93e6008b..ab4ae84d 100644 --- a/cmake/paths.cmake +++ b/cmake/paths.cmake @@ -12,3 +12,4 @@ endif() # Create log folder file( MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin/log ) +file( MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin/navi ) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index e5cb110e..b5da6e5b 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -161,32 +161,25 @@ void Sapphire::Entity::BNpc::step() // No path to track return; - if( Util::distance( getPos().x, getPos().y, getPos().z, m_naviTarget.x, m_naviTarget.y, m_naviTarget.z ) <= 4 ) - { - // Reached target - m_naviLastPath.clear(); - return; - } - auto stepPos = m_naviLastPath[ m_naviPathStep ]; - if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 && m_naviPathStep < m_naviLastPath.size() - 1 ) + if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 && + m_naviPathStep < m_naviLastPath.size() - 1 ) { // Reached step in path m_naviPathStep++; - stepPos = m_naviLastPath[ m_naviPathStep ]; } // This is probably not a good way to do it but works fine for now float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI; - auto x = ( cosf( angle ) * 1.1f ); + auto x = ( cosf( angle ) * 1.7f ); auto y = stepPos.y; - auto z = ( sinf( angle ) * 1.1f ); + auto z = ( sinf( angle ) * 1.7f ); - setPos( { getPos().x + x, y, getPos().z + z } ); face( stepPos ); + setPos( { getPos().x + x, y, getPos().z + z } ); sendPositionUpdate(); } @@ -194,12 +187,11 @@ void Sapphire::Entity::BNpc::step() bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) { if( Util::distance( getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z ) <= 4 ) + { // Reached destination + m_naviLastPath.clear(); return true; - - if( m_naviTarget.x == pos.x && m_naviTarget.y == pos.y && m_naviTarget.z == pos.z ) - // Targets are the same - return false; + } // Check if we have to recalculate if( Util::getTimeMs() - m_naviLastUpdate > 500 ) @@ -224,29 +216,13 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) } else { - Logger::debug( "No path found from x{0} y{1} z{2} to x{3} y{4} z{5} in {6}", getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z, m_pCurrentZone->getInternalName() ); + Logger::debug( "No path found from x{0} y{1} z{2} to x{3} y{4} z{5} in {6}", + getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z, m_pCurrentZone->getInternalName() ); } } - /* - float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ); - float newRot = PI - rot + ( PI / 2 ); - face( pos ); - float angle = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ) + PI; - auto x = ( cosf( angle ) * 1.1f ); - auto y = ( getPos().y + pos.y ) * 0.5f; // fake value while there is no collision - auto z = ( sinf( angle ) * 1.1f ); - - Common::FFXIVARR_POSITION3 newPos{ getPos().x + x, y, getPos().z + z }; - setPos( newPos ); - - Common::FFXIVARR_POSITION3 tmpPos{ getPos().x + x, y, getPos().z + z }; - setPos( tmpPos ); - setRot( newRot ); - - sendPositionUpdate(); - */ + step(); return false; } @@ -377,6 +353,7 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) const uint8_t minActorDistance = 4; const uint8_t aggroRange = 8; const uint8_t maxDistanceToOrigin = 40; + const uint32_t roamTick = 20; switch( m_state ) { @@ -391,8 +368,28 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) } break; + case BNpcState::Roaming: + { + if( moveTo( m_roamPos ) ) + { + m_lastRoamTargetReached = Util::getTimeSeconds(); + m_state = BNpcState::Idle; + } + + // checkaggro + } + break; + case BNpcState::Idle: { + if( Util::getTimeSeconds() - m_lastRoamTargetReached > roamTick ) + { + auto pNaviMgr = m_pFw->get< World::Manager::NaviMgr >(); + auto pNaviProvider = pNaviMgr->getNaviProvider( m_pCurrentZone->getBgPath() ); + m_roamPos = pNaviProvider->findRandomPositionInCircle( m_spawnPos, 5 ); + m_state = BNpcState::Roaming; + } + // passive mobs should ignore players unless aggro'd if( m_aggressionMode == 1 ) return; @@ -465,7 +462,6 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) } } - step(); } void Sapphire::Entity::BNpc::onActionHostile( Sapphire::Entity::CharaPtr pSource ) diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index 171e5890..bd5e4d9a 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -24,6 +24,7 @@ namespace Sapphire::Entity Idle, Combat, Retreat, + Roaming, JustDied, Dead, }; @@ -103,8 +104,10 @@ namespace Sapphire::Entity uint8_t m_level; uint32_t m_timeOfDeath; + uint32_t m_lastRoamTargetReached; Common::FFXIVARR_POSITION3 m_spawnPos; + Common::FFXIVARR_POSITION3 m_roamPos; BNpcState m_state; std::set< std::shared_ptr< HateListEntry > > m_hateList; diff --git a/src/world/Navi/NaviProvider.cpp b/src/world/Navi/NaviProvider.cpp index 7ad3f463..f6ce1537 100644 --- a/src/world/Navi/NaviProvider.cpp +++ b/src/world/Navi/NaviProvider.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "NaviProvider.h" @@ -210,6 +211,60 @@ bool Sapphire::World::Navi::NaviProvider::getSteerTarget( dtNavMeshQuery* navQue return true; } +static float frand() +{ + return ( float ) rand() / (float)RAND_MAX; +} + + +Sapphire::Common::FFXIVARR_POSITION3 + Sapphire::World::Navi::NaviProvider::findRandomPositionInCircle( const Sapphire::Common::FFXIVARR_POSITION3& startPos, + float maxRadius ) +{ + dtStatus status; + + float spos[ 3 ] = { startPos.x, startPos.y, startPos.z }; + + float polyPickExt[ 3 ]; + polyPickExt[ 0 ] = 30; + polyPickExt[ 1 ] = 60; + polyPickExt[ 2 ] = 30; + + float randomPt[ 3 ]; + float snearest[ 3 ]; + + dtQueryFilter filter; + filter.setIncludeFlags( 0xffff ); + filter.setExcludeFlags( 0 ); + + dtPolyRef startRef; + dtPolyRef randomRef; + + status = m_naviMeshQuery->findNearestPoly( spos, polyPickExt, &filter, &startRef, snearest ); + + if( dtStatusFailed( status ) ) + { + return {}; + } + + if( !m_naviMesh->isValidPolyRef( startRef ) ) + { + return {}; + } + + auto pRNGMgr = m_pFw->get< World::Manager::RNGMgr >(); + auto rng = pRNGMgr->getRandGenerator< float >( 0.f, 1.f ); + status = m_naviMeshQuery->findRandomPointAroundCircle( startRef, spos, maxRadius, &filter, frand, + &randomRef, randomPt); + + if( dtStatusFailed( status ) ) + { + return {}; + } + + return { randomPt[ 0 ], randomPt[ 1 ], randomPt[ 2 ] }; +} + std::vector< Sapphire::Common::FFXIVARR_POSITION3 > Sapphire::World::Navi::NaviProvider::findFollowPath( const Common::FFXIVARR_POSITION3& startPos, const Common::FFXIVARR_POSITION3& endPos ) @@ -251,9 +306,9 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > m_naviMeshQuery->closestPointOnPoly( startRef, spos, iterPos, 0 ); m_naviMeshQuery->closestPointOnPoly( polys[ npolys - 1 ], epos, targetPos, 0 ); - Logger::debug( "IterPos: {0} {1} {2}; TargetPos: {3} {4} {5}", - iterPos[ 0 ], iterPos[ 1 ], iterPos[ 2 ], - targetPos[ 0 ], targetPos[ 1 ], targetPos[ 2 ] ); + //Logger::debug( "IterPos: {0} {1} {2}; TargetPos: {3} {4} {5}", + // iterPos[ 0 ], iterPos[ 1 ], iterPos[ 2 ], + // targetPos[ 0 ], targetPos[ 1 ], targetPos[ 2 ] ); const float STEP_SIZE = 1.2f; const float SLOP = 0.15f; @@ -370,7 +425,7 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 > for( int32_t i = 0; i < numSmoothPath; i += 3 ) { - resultCoords.push_back( Common::FFXIVARR_POSITION3{ smoothPath[ i ], smoothPath[ i + 1 ], smoothPath[ i + 2 ] } ); + resultCoords.emplace_back( Common::FFXIVARR_POSITION3{ smoothPath[ i ], smoothPath[ i + 1 ], smoothPath[ i + 2 ] } ); } } diff --git a/src/world/Navi/NaviProvider.h b/src/world/Navi/NaviProvider.h index ff833ca4..e69b6671 100644 --- a/src/world/Navi/NaviProvider.h +++ b/src/world/Navi/NaviProvider.h @@ -40,7 +40,10 @@ namespace Sapphire::World::Navi void toDetourPos( const Common::FFXIVARR_POSITION3& position, float* out ); Common::FFXIVARR_POSITION3 toGamePos( float* pos ); - std::vector< Common::FFXIVARR_POSITION3 > findFollowPath( const Common::FFXIVARR_POSITION3& startPos, const Common::FFXIVARR_POSITION3& endPos ); + std::vector< Common::FFXIVARR_POSITION3 > findFollowPath( const Common::FFXIVARR_POSITION3& startPos, + const Common::FFXIVARR_POSITION3& endPos ); + Common::FFXIVARR_POSITION3 findRandomPositionInCircle( const Sapphire::Common::FFXIVARR_POSITION3& startPos, + float maxRadius ); bool hasNaviMesh() const; From f8d4b4f9c0234328a029a87bfdcabf53a97042a6 Mon Sep 17 00:00:00 2001 From: Mordred Date: Sat, 26 Jan 2019 00:12:54 +0100 Subject: [PATCH 38/50] Do not crash on zones without navi data --- src/world/Actor/BNpc.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index b5da6e5b..da046a07 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -386,6 +386,13 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) { auto pNaviMgr = m_pFw->get< World::Manager::NaviMgr >(); auto pNaviProvider = pNaviMgr->getNaviProvider( m_pCurrentZone->getBgPath() ); + + if( !pNaviProvider ) + { + m_lastRoamTargetReached = Util::getTimeSeconds(); + break; + } + m_roamPos = pNaviProvider->findRandomPositionInCircle( m_spawnPos, 5 ); m_state = BNpcState::Roaming; } From b1bbfe3acd04406eddbbee71fc54b617574c6406 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 11:39:25 +1100 Subject: [PATCH 39/50] set correct bnpc animation when aggro'd/passive --- src/common/Network/PacketDef/Zone/ServerZoneDef.h | 2 +- src/world/Actor/BNpc.cpp | 13 ++++++++++--- src/world/Network/PacketWrappers/MoveActorPacket.h | 8 ++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index ef70853c..65de5804 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -666,7 +666,7 @@ struct FFXIVIpcActorMove : { /* 0000 */ uint8_t rotation; /* 0001 */ uint8_t unknown_1; - /* 0002 */ uint8_t unknown_2; + /* 0002 */ uint8_t animationType; /* 0003 */ uint8_t unknown_3; /* 0004 */ uint16_t unknown_4; /* 0006 */ uint16_t posX; diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index da046a07..2179e591 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -174,9 +174,9 @@ void Sapphire::Entity::BNpc::step() // This is probably not a good way to do it but works fine for now float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI; - auto x = ( cosf( angle ) * 1.7f ); + auto x = ( cosf( angle ) * .5f ); auto y = stepPos.y; - auto z = ( sinf( angle ) * 1.7f ); + auto z = ( sinf( angle ) * .5f ); face( stepPos ); setPos( { getPos().x + x, y, getPos().z + z } ); @@ -229,7 +229,14 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos ) void Sapphire::Entity::BNpc::sendPositionUpdate() { - auto movePacket = std::make_shared< MoveActorPacket >( *getAsChara(), 0x3A, 0, 0, 0x5A ); + uint8_t unk1 = 0x3a; + uint8_t animationType = 2; + + if( m_state == BNpcState::Combat ) + animationType = 0; + + + auto movePacket = std::make_shared< MoveActorPacket >( *getAsChara(), unk1, animationType, 0, 0x5A ); sendToInRangeSet( movePacket ); } diff --git a/src/world/Network/PacketWrappers/MoveActorPacket.h b/src/world/Network/PacketWrappers/MoveActorPacket.h index a3e09ddb..f4a4f589 100644 --- a/src/world/Network/PacketWrappers/MoveActorPacket.h +++ b/src/world/Network/PacketWrappers/MoveActorPacket.h @@ -19,19 +19,19 @@ namespace Sapphire::Network::Packets::Server public ZoneChannelPacket< FFXIVIpcActorMove > { public: - MoveActorPacket( Entity::Chara& actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) : + MoveActorPacket( Entity::Chara& actor, uint8_t unk1, uint8_t animationType, uint8_t unk3, uint16_t unk4 ) : ZoneChannelPacket< FFXIVIpcActorMove >( actor.getId(), actor.getId() ) { - initialize( actor, unk1, unk2, unk3, unk4 ); + initialize( actor, unk1, animationType, unk3, unk4 ); }; private: - void initialize( Entity::Chara& actor, uint8_t unk1, uint8_t unk2, uint8_t unk3, uint16_t unk4 ) + void initialize( Entity::Chara& actor, uint8_t unk1, uint8_t animationType, uint8_t unk3, uint16_t unk4 ) { m_data.rotation = Util::floatToUInt8Rot( actor.getRot() ); m_data.unknown_1 = unk1; - m_data.unknown_2 = unk2; + m_data.animationType = animationType; m_data.unknown_3 = unk3; m_data.unknown_4 = unk4; m_data.posX = Util::floatToUInt16( actor.getPos().x ); From b9520a6c3e448bf88b0d3a9aa4e9673641afff65 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 13:40:02 +1100 Subject: [PATCH 40/50] heal npcs when retreating, set hp to max when retreat finishes, cleanup --- src/common/Common.h | 1 + src/world/Actor/BNpc.cpp | 32 ++++++++++++++++++- src/world/Actor/Chara.cpp | 19 ++++++----- src/world/Actor/Chara.h | 2 +- src/world/Actor/Player.cpp | 4 +-- src/world/Actor/Player.h | 2 +- .../Network/Handlers/GMCommandHandlers.cpp | 9 ++++-- 7 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/common/Common.h b/src/common/Common.h index 6dfc5e70..1735d54b 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -615,6 +615,7 @@ namespace Sapphire::Common InvincibilityNone, InvincibilityRefill, InvincibilityStayAlive, + InvincibilityIgnoreDamage, }; enum PlayerStateFlag : uint8_t diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 2179e591..53b15876 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -370,8 +370,39 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) case BNpcState::Retreat: { + setInvincibilityType( InvincibilityType::InvincibilityIgnoreDamage ); + + // slowly restore hp every tick + + if( std::difftime( currTime, m_lastTickTime ) > 3000 ) + { + m_lastTickTime = currTime; + + if( m_hp < getMaxHp() ) + { + auto addHp = static_cast< uint32_t >( getMaxHp() * 0.1f + 1 ); + + if( m_hp + addHp < getMaxHp() ) + m_hp += addHp; + else + m_hp = getMaxHp(); + } + + sendStatusUpdate(); + } + if( moveTo( m_spawnPos ) ) + { + setInvincibilityType( InvincibilityType::InvincibilityNone ); + + // retail doesn't seem to roam straight after retreating + // todo: perhaps requires more investigation? + m_lastRoamTargetReached = Util::getTimeSeconds(); + + setHp( getMaxHp() ); + m_state = BNpcState::Idle; + } } break; @@ -475,7 +506,6 @@ void Sapphire::Entity::BNpc::update( int64_t currTime ) } } } - } void Sapphire::Entity::BNpc::onActionHostile( Sapphire::Entity::CharaPtr pSource ) diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 066a6355..6e2272cc 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -151,35 +151,35 @@ uint32_t Sapphire::Entity::Chara::getMaxMp() const void Sapphire::Entity::Chara::resetHp() { m_hp = getMaxHp(); - sendStatusUpdate( true ); + sendStatusUpdate(); } /*! \return reset mp to current max mp */ void Sapphire::Entity::Chara::resetMp() { m_mp = getMaxMp(); - sendStatusUpdate( true ); + sendStatusUpdate(); } /*! \param hp amount to set ( caps to maxHp ) */ void Sapphire::Entity::Chara::setHp( uint32_t hp ) { m_hp = hp < getMaxHp() ? hp : getMaxHp(); - sendStatusUpdate( true ); + sendStatusUpdate(); } /*! \param mp amount to set ( caps to maxMp ) */ void Sapphire::Entity::Chara::setMp( uint32_t mp ) { m_mp = mp < getMaxMp() ? mp : getMaxMp(); - sendStatusUpdate( true ); + sendStatusUpdate(); } /*! \param gp amount to set*/ void Sapphire::Entity::Chara::setGp( uint32_t gp ) { m_gp = gp; - sendStatusUpdate( true ); + sendStatusUpdate(); } /*! \param type invincibility type to set */ @@ -325,12 +325,14 @@ void Sapphire::Entity::Chara::takeDamage( uint32_t damage ) case InvincibilityStayAlive: setHp( 0 ); break; + case InvincibilityIgnoreDamage: + break; } } else m_hp -= damage; - sendStatusUpdate( false ); + sendStatusUpdate(); } /*! @@ -349,7 +351,7 @@ void Sapphire::Entity::Chara::heal( uint32_t amount ) else m_hp += amount; - sendStatusUpdate( false ); + sendStatusUpdate(); } /*! @@ -359,7 +361,7 @@ so players can have their own version and we can abolish the param. \param true if the update should also be sent to the actor ( player ) himself */ -void Sapphire::Entity::Chara::sendStatusUpdate( bool toSelf ) +void Sapphire::Entity::Chara::sendStatusUpdate() { FFXIVPacketBasePtr packet = std::make_shared< UpdateHpMpTpPacket >( *this ); sendToInRangeSet( packet ); @@ -391,6 +393,7 @@ void Sapphire::Entity::Chara::autoAttack( CharaPtr pTarget ) uint64_t tick = Util::getTimeMs(); + // todo: this needs to use the auto attack delay for the equipped weapon if( ( tick - m_lastAttack ) > 2500 ) { pTarget->onActionHostile( getAsChara() ); diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 94de7845..1adf919b 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -229,7 +229,7 @@ namespace Sapphire::Entity virtual uint8_t getLevel() const; - virtual void sendStatusUpdate( bool toSelf = true ); + virtual void sendStatusUpdate(); virtual void takeDamage( uint32_t damage ); diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index 9af7e25e..db5e56fb 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -739,7 +739,7 @@ void Sapphire::Entity::Player::gainLevel() } -void Sapphire::Entity::Player::sendStatusUpdate( bool toSelf ) +void Sapphire::Entity::Player::sendStatusUpdate() { sendToInRangeSet( std::make_shared< UpdateHpMpTpPacket >( *this ), true ); } @@ -810,7 +810,7 @@ void Sapphire::Entity::Player::setClassJob( Common::ClassJob classJob ) sendToInRangeSet( makeActorControl142( getId(), ClassJobChange, 0x04 ), true ); - sendStatusUpdate( true ); + sendStatusUpdate(); } void Sapphire::Entity::Player::setLevel( uint8_t level ) diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 3c4382da..02e1c282 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -707,7 +707,7 @@ namespace Sapphire::Entity void sendStateFlags(); /*! send status update */ - void sendStatusUpdate( bool toSelf = true ) override; + void sendStatusUpdate() override; /*! send the entire inventory sequence */ void sendInventory(); diff --git a/src/world/Network/Handlers/GMCommandHandlers.cpp b/src/world/Network/Handlers/GMCommandHandlers.cpp index 8291d24d..1526789b 100644 --- a/src/world/Network/Handlers/GMCommandHandlers.cpp +++ b/src/world/Network/Handlers/GMCommandHandlers.cpp @@ -275,8 +275,13 @@ void Sapphire::Network::GameConnection::gm1Handler( FrameworkPtr pFw, } case GmCommand::Hp: { - targetPlayer->setHp( param1 ); - player.sendNotice( "Hp for {0} was set to {1}", targetPlayer->getName(), param1 ); + auto chara = targetActor->getAsChara(); + if( chara ) + { + chara->setHp( param1 ); + player.sendNotice( "Hp for {0} was set to {1}", chara->getName(), param1 ); + } + break; } case GmCommand::Mp: From 29be5e124948be9063a3fab55f362c20f84a4c20 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 13:40:22 +1100 Subject: [PATCH 41/50] only run bnpc update in active cells --- src/world/Territory/Zone.cpp | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/world/Territory/Zone.cpp b/src/world/Territory/Zone.cpp index 77c8c30d..e7007fff 100644 --- a/src/world/Territory/Zone.cpp +++ b/src/world/Territory/Zone.cpp @@ -405,8 +405,29 @@ void Sapphire::Zone::updateBNpcs( int64_t tickCount ) removeActor( pBNpc ); break; } + } - pBNpc->update( tickCount ); + for( uint32_t x = 0; x < _sizeX; x++ ) + { + for( uint32_t y = 0; y < _sizeY; ++y ) + { + auto cell = getCellPtr( x, y ); + if( !cell ) + continue; + + // todo: this is a pretty shit because we will visit the same cells multiple times over + // ideally we run a pass every tick and cache active cells during that initial pass over every cell + // that way we don't have an expensive lookup for every actor + + if( !isCellActive( x, y ) ) + continue; + + for( const auto& actor : cell->m_actors ) + { + if( actor->isBattleNpc() ) + actor->getAsBNpc()->update( tickCount ); + } + } } } From 0915406bd36b8f016fcd7da3e05d6b5c8b0be2d7 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 14:59:04 +1100 Subject: [PATCH 42/50] style fix --- src/world/Actor/BNpc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index bd5e4d9a..cce71450 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -88,7 +88,7 @@ namespace Sapphire::Entity void onDeath() override; uint32_t getTimeOfDeath() const; - void setTimeOfDeath( uint32_t timeOfDeath); + void setTimeOfDeath( uint32_t timeOfDeath ); private: uint32_t m_bNpcBaseId; From d4b70cf1b6100aa8c52eb6fea7653f76247fbdfa Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 15:19:54 +1100 Subject: [PATCH 43/50] fix pcb_reader not exporting terrain in composite zone objects --- src/tools/pcb_reader/main.cpp | 9 ++++----- src/tools/pcb_reader/obj_exporter.h | 12 ++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index bb90af0f..60befa41 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -32,7 +32,7 @@ // garbage to ignore models bool noObj = false; -std::string gamePath( "C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack" ); +std::string gamePath( "/mnt/c/Program Files (x86)/Steam/steamapps/common/FINAL FANTASY XIV Online/game/sqpack" ); std::unordered_map< uint16_t, std::string > zoneNameMap; uint32_t zoneId; @@ -325,6 +325,7 @@ int main( int argc, char* argv[] ) buildModelEntry( pPcbFile, exportedTerrainGroup, fileName, zoneName ); } exportMgr.exportGroup( zoneName, exportedTerrainGroup, ( ExportFileType )exportFileType ); + exportedZone.groups.emplace( zoneName, exportedTerrainGroup ); for( const auto& lgb : lgbList ) { @@ -393,10 +394,10 @@ int main( int argc, char* argv[] ) } } exportMgr.exportGroup( zoneName, exportedGroup, ( ExportFileType )exportFileType ); - //exportedZone.groups.emplace( group.name, exportedGroup ); + exportedZone.groups.emplace( group.name, exportedGroup ); } } - //exportMgr.exportZone( exportedZone, ( ExportFileType )exportFileType ); + exportMgr.exportZone( exportedZone, ( ExportFileType )exportFileType ); printf( "Exported %s in %lu seconds \n", @@ -416,8 +417,6 @@ int main( int argc, char* argv[] ) printf( "Finished all tasks in %lu seconds\n", std::chrono::duration_cast< std::chrono::seconds >( std::chrono::high_resolution_clock::now() - startTime ).count() ); - getchar(); - delete eData; delete data1; diff --git a/src/tools/pcb_reader/obj_exporter.h b/src/tools/pcb_reader/obj_exporter.h index e1a35747..f5c1361b 100644 --- a/src/tools/pcb_reader/obj_exporter.h +++ b/src/tools/pcb_reader/obj_exporter.h @@ -20,14 +20,14 @@ public: auto start = std::chrono::high_resolution_clock::now(); - auto dir = currPath + "/" + zone.name + "/"; - auto fileName = dir + "/" + zone.name + ".obj"; + auto dir = currPath + "/pcb_export/" + zone.name + "/"; + auto fileName = dir + zone.name + ".obj"; std::error_code e; if( !std::experimental::filesystem::exists( dir, e ) ) { - if( !std::experimental::filesystem::create_directory( dir, e ) ) + if( !std::experimental::filesystem::create_directories( dir, e ) ) { printf( "Unable to create directory '%s'", ( dir ).c_str() ); return; @@ -61,13 +61,13 @@ public: auto start = std::chrono::high_resolution_clock::now(); - auto dir = currPath + "/" + zoneName + "/"; - auto fileName = dir + "/" + group.name + ".obj"; + auto dir = currPath + "/pcb_export/" + zoneName + "/groups/"; + auto fileName = dir + group.name + ".obj"; std::error_code e; if( !std::experimental::filesystem::exists( dir, e ) ) { - if( !std::experimental::filesystem::create_directory( dir, e ) ) + if( !std::experimental::filesystem::create_directories( dir, e ) ) { printf( "Unable to create directory '%s'", ( dir ).c_str() ); return; From f1fe40ffe5092d5ed4fa0a2c544a240e8441fad7 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 16:01:35 +1100 Subject: [PATCH 44/50] fix race condition where navmesh export would start before object is exported --- src/tools/pcb_reader/CMakeLists.txt | 5 +- src/tools/pcb_reader/exportmgr.h | 22 +- src/tools/pcb_reader/main.cpp | 2 +- .../pcb_reader/nav/TiledNavmeshGenerator.h | 159 +++++++++ .../pcb_reader/nav/ext/ChunkyTriMesh.cpp | 315 ++++++++++++++++++ src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h | 59 ++++ .../pcb_reader/nav/ext/MeshLoaderObj.cpp | 245 ++++++++++++++ src/tools/pcb_reader/nav/ext/MeshLoaderObj.h | 56 ++++ src/tools/pcb_reader/navmesh_exporter.h | 32 +- 9 files changed, 862 insertions(+), 33 deletions(-) create mode 100644 src/tools/pcb_reader/nav/TiledNavmeshGenerator.h create mode 100644 src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp create mode 100644 src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h create mode 100644 src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp create mode 100644 src/tools/pcb_reader/nav/ext/MeshLoaderObj.h diff --git a/src/tools/pcb_reader/CMakeLists.txt b/src/tools/pcb_reader/CMakeLists.txt index 96b4af4e..4381ace7 100644 --- a/src/tools/pcb_reader/CMakeLists.txt +++ b/src/tools/pcb_reader/CMakeLists.txt @@ -3,7 +3,10 @@ cmake_policy(SET CMP0015 NEW) project(Tool_pcb_reader2) file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*") -file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*") +file(GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + *.c* + nav/*.c* + nav/ext/*.c*) add_executable(pcb_reader2 ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES}) diff --git a/src/tools/pcb_reader/exportmgr.h b/src/tools/pcb_reader/exportmgr.h index 656e15f7..c448b5d2 100644 --- a/src/tools/pcb_reader/exportmgr.h +++ b/src/tools/pcb_reader/exportmgr.h @@ -20,14 +20,14 @@ public: void exportZone(const ExportedZone& zone, ExportFileType exportFileTypes) { - if( exportFileTypes & ExportFileType::WavefrontObj ) + m_threadpool.queue( [zone, exportFileTypes]() { - m_threadpool.queue( [zone](){ ObjExporter::exportZone( zone ); } ); - } - if( exportFileTypes & ExportFileType::Navmesh ) - { - m_threadpool.queue( [zone](){ NavmeshExporter::exportZone( zone ); } ); - } + if( exportFileTypes & ExportFileType::WavefrontObj ) + ObjExporter::exportZone( zone ); + + if( exportFileTypes & ExportFileType::Navmesh ) + NavmeshExporter::exportZone( zone ); + } ); } void exportGroup( const std::string& zoneName, const ExportedGroup& group, ExportFileType exportFileTypes ) @@ -36,10 +36,10 @@ public: { m_threadpool.queue( [zoneName, group](){ ObjExporter::exportGroup( zoneName, group ); } ); } - if( exportFileTypes & ExportFileType::Navmesh ) - { - m_threadpool.queue( [zoneName, group](){ NavmeshExporter::exportGroup( zoneName, group ); } ); - } +// if( exportFileTypes & ExportFileType::Navmesh ) +// { +// m_threadpool.queue( [zoneName, group](){ NavmeshExporter::exportGroup( zoneName, group ); } ); +// } } void waitForTasks() diff --git a/src/tools/pcb_reader/main.cpp b/src/tools/pcb_reader/main.cpp index 60befa41..ee3c9a5c 100644 --- a/src/tools/pcb_reader/main.cpp +++ b/src/tools/pcb_reader/main.cpp @@ -140,7 +140,7 @@ int main( int argc, char* argv[] ) } catch( std::exception& e ) { - printf( "Unable to initialise EXD! Usage: pcb_reader \"path/to/FINAL FANTASY XIV - A REALM REBORN/game/sqpack\" [--no-obj, --dump-all, --navmesh]" ); + printf( "Unable to initialise EXD!\n Usage: pcb_reader \"path/to/FINAL FANTASY XIV - A REALM REBORN/game/sqpack\" [--no-obj, --dump-all, --navmesh]\n" ); return -1; } ExportMgr exportMgr; diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h new file mode 100644 index 00000000..93d3051e --- /dev/null +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h @@ -0,0 +1,159 @@ +#ifndef SAPPHIRE_TILEDNAVMESHGENERATOR_H +#define SAPPHIRE_TILEDNAVMESHGENERATOR_H + +#include +#include +#include + +#include "ext/MeshLoaderObj.h" +#include "ext/ChunkyTriMesh.h" + +#include "recastnavigation/Detour/Include/DetourNavMesh.h" +#include "recastnavigation/Detour/Include/DetourNavMeshQuery.h" +#include "recastnavigation/Recast/Include/Recast.h" + +namespace fs = std::experimental::filesystem; + +class TiledNavmeshGenerator +{ +private: + rcMeshLoaderObj* m_mesh; + rcChunkyTriMesh* m_chunkyMesh; + + dtNavMesh* m_navMesh; + dtNavMeshQuery* m_navQuery; + + + float m_meshBMin[ 3 ]; + float m_meshBMax[ 3 ]; + + float m_tileSize = 160.f; + float m_cellSize = 0.2f; + + int m_maxTiles = 0; + int m_maxPolysPerTile = 0; + + inline unsigned int nextPow2( uint32_t v ) + { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; + } + + inline unsigned int ilog2( uint32_t v ) + { + uint32_t r; + uint32_t shift; + r = (v > 0xffff) << 4; v >>= r; + shift = (v > 0xff) << 3; v >>= shift; r |= shift; + shift = (v > 0xf) << 2; v >>= shift; r |= shift; + shift = (v > 0x3) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; + } + + +public: + explicit TiledNavmeshGenerator( const std::string& path ) + { + if( !fs::exists( path ) ) + throw std::runtime_error( "what" ); + + printf( "[Navmesh] loading obj: %s\n", path.c_str() ); + + m_mesh = new rcMeshLoaderObj; + assert( m_mesh ); + + if( !m_mesh->load( path ) ) + { + printf( "[Navmesh] Failed to allocate rcMeshLoaderObj\n" ); + return; + } + + rcCalcBounds( m_mesh->getVerts(), m_mesh->getVertCount(), m_meshBMin, m_meshBMax ); + + m_chunkyMesh = new rcChunkyTriMesh; + assert( m_chunkyMesh ); + + if( !rcCreateChunkyTriMesh( m_mesh->getVerts(), m_mesh->getTris(), m_mesh->getTriCount(), 256, m_chunkyMesh ) ) + { + printf( "[Navmesh] buildTiledNavigation: Failed to build chunky mesh.\n" ); + return; + } + + printf( "[Navmesh] loaded obj, verts: %i tris: %i\n", m_mesh->getVertCount(), m_mesh->getTriCount() ); + + // todo: load some bullshit settings from exd + + int gw = 0, gh = 0; + rcCalcGridSize( m_meshBMin, m_meshBMax, m_cellSize, &gw, &gh ); + + auto ts = static_cast< uint32_t >( m_tileSize ); + const uint32_t tw = (gw + ts-1) / ts; + const uint32_t th = (gh + ts-1) / ts; + + printf( "[Navmesh] Tiles %d x %d\n", tw, th ); + + int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 14); + if (tileBits > 14) tileBits = 14; + int polyBits = 22 - tileBits; + m_maxTiles = 1 << tileBits; + m_maxPolysPerTile = 1 << polyBits; + + printf( "[Navmesh] Max Tiles: %d\tMax Polys: %d\n", m_maxTiles, m_maxPolysPerTile ); + } + + bool buildNavmesh() + { + assert( m_mesh ); + + m_navMesh = dtAllocNavMesh(); + if( !m_navMesh ) + { + printf( "[Navmesh] buildTiledNavigation: Could not allocate navmesh.\n" ); + return false; + } + + dtNavMeshParams params{}; + rcVcopy( params.orig, m_meshBMin ); + params.tileWidth = m_tileSize * m_cellSize; + params.tileHeight = m_tileSize * m_cellSize; + params.maxTiles = m_maxTiles; + params.maxPolys = m_maxPolysPerTile; + + dtStatus status; + + status = m_navMesh->init( ¶ms ); + if( dtStatusFailed( status ) ) + { + printf( "[Navigation] buildTiledNavigation: Could not init navmesh.\n" ); + return false; + } + + m_navQuery = dtAllocNavMeshQuery(); + assert( m_navQuery ); + + status = m_navQuery->init( m_navMesh, 2048 ); + if( dtStatusFailed( status ) ) + { + printf( "[Navigation] buildTiledNavigation: Could not init Detour navmesh query\n" ); + return false; + } + + } + + ~TiledNavmeshGenerator() + { + delete m_mesh; + delete m_chunkyMesh; + } + +}; + + +#endif //SAPPHIRE_TILEDNAVMESHGENERATOR_H diff --git a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp new file mode 100644 index 00000000..2991d4ff --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp @@ -0,0 +1,315 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// 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 "ChunkyTriMesh.h" +#include +#include +#include + +struct BoundsItem +{ + float bmin[2]; + float bmax[2]; + int i; +}; + +static int compareItemX(const void* va, const void* vb) +{ + const BoundsItem* a = (const BoundsItem*)va; + const BoundsItem* b = (const BoundsItem*)vb; + if (a->bmin[0] < b->bmin[0]) + return -1; + if (a->bmin[0] > b->bmin[0]) + return 1; + return 0; +} + +static int compareItemY(const void* va, const void* vb) +{ + const BoundsItem* a = (const BoundsItem*)va; + const BoundsItem* b = (const BoundsItem*)vb; + if (a->bmin[1] < b->bmin[1]) + return -1; + if (a->bmin[1] > b->bmin[1]) + return 1; + return 0; +} + +static void calcExtends(const BoundsItem* items, const int /*nitems*/, + const int imin, const int imax, + float* bmin, float* bmax) +{ + bmin[0] = items[imin].bmin[0]; + bmin[1] = items[imin].bmin[1]; + + bmax[0] = items[imin].bmax[0]; + bmax[1] = items[imin].bmax[1]; + + for (int i = imin+1; i < imax; ++i) + { + const BoundsItem& it = items[i]; + if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0]; + if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1]; + + if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0]; + if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1]; + } +} + +inline int longestAxis(float x, float y) +{ + return y > x ? 1 : 0; +} + +static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk, + int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes, + int& curTri, int* outTris, const int* inTris) +{ + int inum = imax - imin; + int icur = curNode; + + if (curNode > maxNodes) + return; + + rcChunkyTriMeshNode& node = nodes[curNode++]; + + if (inum <= trisPerChunk) + { + // Leaf + calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); + + // Copy triangles. + node.i = curTri; + node.n = inum; + + for (int i = imin; i < imax; ++i) + { + const int* src = &inTris[items[i].i*3]; + int* dst = &outTris[curTri*3]; + curTri++; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + } + else + { + // Split + calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); + + int axis = longestAxis(node.bmax[0] - node.bmin[0], + node.bmax[1] - node.bmin[1]); + + if (axis == 0) + { + // Sort along x-axis + qsort(items+imin, static_cast(inum), sizeof(BoundsItem), compareItemX); + } + else if (axis == 1) + { + // Sort along y-axis + qsort(items+imin, static_cast(inum), sizeof(BoundsItem), compareItemY); + } + + int isplit = imin+inum/2; + + // Left + subdivide(items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris); + // Right + subdivide(items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris); + + int iescape = curNode - icur; + // Negative index means escape. + node.i = -iescape; + } +} + +bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris, + int trisPerChunk, rcChunkyTriMesh* cm) +{ + int nchunks = (ntris + trisPerChunk-1) / trisPerChunk; + + cm->nodes = new rcChunkyTriMeshNode[nchunks*4]; + if (!cm->nodes) + return false; + + cm->tris = new int[ntris*3]; + if (!cm->tris) + return false; + + cm->ntris = ntris; + + // Build tree + BoundsItem* items = new BoundsItem[ntris]; + if (!items) + return false; + + for (int i = 0; i < ntris; i++) + { + const int* t = &tris[i*3]; + BoundsItem& it = items[i]; + it.i = i; + // Calc triangle XZ bounds. + it.bmin[0] = it.bmax[0] = verts[t[0]*3+0]; + it.bmin[1] = it.bmax[1] = verts[t[0]*3+2]; + for (int j = 1; j < 3; ++j) + { + const float* v = &verts[t[j]*3]; + if (v[0] < it.bmin[0]) it.bmin[0] = v[0]; + if (v[2] < it.bmin[1]) it.bmin[1] = v[2]; + + if (v[0] > it.bmax[0]) it.bmax[0] = v[0]; + if (v[2] > it.bmax[1]) it.bmax[1] = v[2]; + } + } + + int curTri = 0; + int curNode = 0; + subdivide(items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks*4, curTri, cm->tris, tris); + + delete [] items; + + cm->nnodes = curNode; + + // Calc max tris per node. + cm->maxTrisPerChunk = 0; + for (int i = 0; i < cm->nnodes; ++i) + { + rcChunkyTriMeshNode& node = cm->nodes[i]; + const bool isLeaf = node.i >= 0; + if (!isLeaf) continue; + if (node.n > cm->maxTrisPerChunk) + cm->maxTrisPerChunk = node.n; + } + + return true; +} + + +inline bool checkOverlapRect(const float amin[2], const float amax[2], + const float bmin[2], const float bmax[2]) +{ + bool overlap = true; + overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; + overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; + return overlap; +} + +int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, + float bmin[2], float bmax[2], + int* ids, const int maxIds) +{ + // Traverse tree + int i = 0; + int n = 0; + while (i < cm->nnodes) + { + const rcChunkyTriMeshNode* node = &cm->nodes[i]; + const bool overlap = checkOverlapRect(bmin, bmax, node->bmin, node->bmax); + const bool isLeafNode = node->i >= 0; + + if (isLeafNode && overlap) + { + if (n < maxIds) + { + ids[n] = i; + n++; + } + } + + if (overlap || isLeafNode) + i++; + else + { + const int escapeIndex = -node->i; + i += escapeIndex; + } + } + + return n; +} + + + +static bool checkOverlapSegment(const float p[2], const float q[2], + const float bmin[2], const float bmax[2]) +{ + static const float EPSILON = 1e-6f; + + float tmin = 0; + float tmax = 1; + float d[2]; + d[0] = q[0] - p[0]; + d[1] = q[1] - p[1]; + + for (int i = 0; i < 2; i++) + { + if (fabsf(d[i]) < EPSILON) + { + // Ray is parallel to slab. No hit if origin not within slab + if (p[i] < bmin[i] || p[i] > bmax[i]) + return false; + } + else + { + // Compute intersection t value of ray with near and far plane of slab + float ood = 1.0f / d[i]; + float t1 = (bmin[i] - p[i]) * ood; + float t2 = (bmax[i] - p[i]) * ood; + if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; } + if (t1 > tmin) tmin = t1; + if (t2 < tmax) tmax = t2; + if (tmin > tmax) return false; + } + } + return true; +} + +int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, + float p[2], float q[2], + int* ids, const int maxIds) +{ + // Traverse tree + int i = 0; + int n = 0; + while (i < cm->nnodes) + { + const rcChunkyTriMeshNode* node = &cm->nodes[i]; + const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax); + const bool isLeafNode = node->i >= 0; + + if (isLeafNode && overlap) + { + if (n < maxIds) + { + ids[n] = i; + n++; + } + } + + if (overlap || isLeafNode) + i++; + else + { + const int escapeIndex = -node->i; + i += escapeIndex; + } + } + + return n; +} diff --git a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h new file mode 100644 index 00000000..65849799 --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h @@ -0,0 +1,59 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// 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 CHUNKYTRIMESH_H +#define CHUNKYTRIMESH_H + +struct rcChunkyTriMeshNode +{ + float bmin[2]; + float bmax[2]; + int i; + int n; +}; + +struct rcChunkyTriMesh +{ + inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {}; + inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; } + + rcChunkyTriMeshNode* nodes; + int nnodes; + int* tris; + int ntris; + int maxTrisPerChunk; + +private: + // Explicitly disabled copy constructor and copy assignment operator. + rcChunkyTriMesh(const rcChunkyTriMesh&); + rcChunkyTriMesh& operator=(const rcChunkyTriMesh&); +}; + +/// Creates partitioned triangle mesh (AABB tree), +/// where each node contains at max trisPerChunk triangles. +bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris, + int trisPerChunk, rcChunkyTriMesh* cm); + +/// Returns the chunk indices which overlap the input rectable. +int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds); + +/// Returns the chunk indices which overlap the input segment. +int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds); + + +#endif // CHUNKYTRIMESH_H diff --git a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp new file mode 100644 index 00000000..19047485 --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp @@ -0,0 +1,245 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// 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 "MeshLoaderObj.h" +#include +#include +#include +#define _USE_MATH_DEFINES +#include + +rcMeshLoaderObj::rcMeshLoaderObj() : + m_scale(1.0f), + m_verts(0), + m_tris(0), + m_normals(0), + m_vertCount(0), + m_triCount(0) +{ +} + +rcMeshLoaderObj::~rcMeshLoaderObj() +{ + delete [] m_verts; + delete [] m_normals; + delete [] m_tris; +} + +void rcMeshLoaderObj::addVertex(float x, float y, float z, int& cap) +{ + if (m_vertCount+1 > cap) + { + cap = !cap ? 8 : cap*2; + float* nv = new float[cap*3]; + if (m_vertCount) + memcpy(nv, m_verts, m_vertCount*3*sizeof(float)); + delete [] m_verts; + m_verts = nv; + } + float* dst = &m_verts[m_vertCount*3]; + *dst++ = x*m_scale; + *dst++ = y*m_scale; + *dst++ = z*m_scale; + m_vertCount++; +} + +void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap) +{ + if (m_triCount+1 > cap) + { + cap = !cap ? 8 : cap*2; + int* nv = new int[cap*3]; + if (m_triCount) + memcpy(nv, m_tris, m_triCount*3*sizeof(int)); + delete [] m_tris; + m_tris = nv; + } + int* dst = &m_tris[m_triCount*3]; + *dst++ = a; + *dst++ = b; + *dst++ = c; + m_triCount++; +} + +static char* parseRow(char* buf, char* bufEnd, char* row, int len) +{ + bool start = true; + bool done = false; + int n = 0; + while (!done && buf < bufEnd) + { + char c = *buf; + buf++; + // multirow + switch (c) + { + case '\\': + break; + case '\n': + if (start) break; + done = true; + break; + case '\r': + break; + case '\t': + case ' ': + if (start) break; + // else falls through + default: + start = false; + row[n++] = c; + if (n >= len-1) + done = true; + break; + } + } + row[n] = '\0'; + return buf; +} + +static int parseFace(char* row, int* data, int n, int vcnt) +{ + int j = 0; + while (*row != '\0') + { + // Skip initial white space + while (*row != '\0' && (*row == ' ' || *row == '\t')) + row++; + char* s = row; + // Find vertex delimiter and terminated the string there for conversion. + while (*row != '\0' && *row != ' ' && *row != '\t') + { + if (*row == '/') *row = '\0'; + row++; + } + if (*s == '\0') + continue; + int vi = atoi(s); + data[j++] = vi < 0 ? vi+vcnt : vi-1; + if (j >= n) return j; + } + return j; +} + +bool rcMeshLoaderObj::load(const std::string& filename) +{ + char* buf = 0; + FILE* fp = fopen(filename.c_str(), "rb"); + if (!fp) + return false; + if (fseek(fp, 0, SEEK_END) != 0) + { + fclose(fp); + return false; + } + long bufSize = ftell(fp); + if (bufSize < 0) + { + fclose(fp); + return false; + } + if (fseek(fp, 0, SEEK_SET) != 0) + { + fclose(fp); + return false; + } + buf = new char[bufSize]; + if (!buf) + { + fclose(fp); + return false; + } + size_t readLen = fread(buf, bufSize, 1, fp); + fclose(fp); + + if (readLen != 1) + { + delete[] buf; + return false; + } + + char* src = buf; + char* srcEnd = buf + bufSize; + char row[512]; + int face[32]; + float x,y,z; + int nv; + int vcap = 0; + int tcap = 0; + + while (src < srcEnd) + { + // Parse one row + row[0] = '\0'; + src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char)); + // Skip comments + if (row[0] == '#') continue; + if (row[0] == 'v' && row[1] != 'n' && row[1] != 't') + { + // Vertex pos + sscanf(row+1, "%f %f %f", &x, &y, &z); + addVertex(x, y, z, vcap); + } + if (row[0] == 'f') + { + // Faces + nv = parseFace(row+1, face, 32, m_vertCount); + for (int i = 2; i < nv; ++i) + { + const int a = face[0]; + const int b = face[i-1]; + const int c = face[i]; + if (a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount) + continue; + addTriangle(a, b, c, tcap); + } + } + } + + delete [] buf; + + // Calculate normals. + m_normals = new float[m_triCount*3]; + for (int i = 0; i < m_triCount*3; i += 3) + { + const float* v0 = &m_verts[m_tris[i]*3]; + const float* v1 = &m_verts[m_tris[i+1]*3]; + const float* v2 = &m_verts[m_tris[i+2]*3]; + float e0[3], e1[3]; + for (int j = 0; j < 3; ++j) + { + e0[j] = v1[j] - v0[j]; + e1[j] = v2[j] - v0[j]; + } + float* n = &m_normals[i]; + n[0] = e0[1]*e1[2] - e0[2]*e1[1]; + n[1] = e0[2]*e1[0] - e0[0]*e1[2]; + n[2] = e0[0]*e1[1] - e0[1]*e1[0]; + float d = sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); + if (d > 0) + { + d = 1.0f/d; + n[0] *= d; + n[1] *= d; + n[2] *= d; + } + } + + m_filename = filename; + return true; +} diff --git a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h new file mode 100644 index 00000000..075c04b3 --- /dev/null +++ b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h @@ -0,0 +1,56 @@ +// +// Copyright (c) 2009-2010 Mikko Mononen memon@inside.org +// +// 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 MESHLOADER_OBJ +#define MESHLOADER_OBJ + +#include + +class rcMeshLoaderObj +{ +public: + rcMeshLoaderObj(); + ~rcMeshLoaderObj(); + + bool load(const std::string& fileName); + + const float* getVerts() const { return m_verts; } + const float* getNormals() const { return m_normals; } + const int* getTris() const { return m_tris; } + int getVertCount() const { return m_vertCount; } + int getTriCount() const { return m_triCount; } + const std::string& getFileName() const { return m_filename; } + +private: + // Explicitly disabled copy constructor and copy assignment operator. + rcMeshLoaderObj(const rcMeshLoaderObj&); + rcMeshLoaderObj& operator=(const rcMeshLoaderObj&); + + void addVertex(float x, float y, float z, int& cap); + void addTriangle(int a, int b, int c, int& cap); + + std::string m_filename; + float m_scale; + float* m_verts; + int* m_tris; + float* m_normals; + int m_vertCount; + int m_triCount; +}; + +#endif // MESHLOADER_OBJ diff --git a/src/tools/pcb_reader/navmesh_exporter.h b/src/tools/pcb_reader/navmesh_exporter.h index c3d4d0c6..ec06a0cd 100644 --- a/src/tools/pcb_reader/navmesh_exporter.h +++ b/src/tools/pcb_reader/navmesh_exporter.h @@ -9,17 +9,8 @@ #include #include "exporter.h" -/* -#include -#include -#include -#include -#include -#include -#include -#include -#include -*/ +#include "nav/TiledNavmeshGenerator.h" + class NavmeshExporter { public: @@ -27,7 +18,16 @@ public: { auto start = std::chrono::high_resolution_clock::now(); - auto fileName = zone.name + ".obj"; + auto dir = std::experimental::filesystem::current_path().string() + "/pcb_export/" + zone.name + "/"; + auto fileName = dir + zone.name + ".obj"; + + TiledNavmeshGenerator gen( fileName ); + + if( !gen.buildNavmesh() ) + { + printf( "[Navmesh] Failed to build navmesh for '%s'\n", zone.name.c_str() ); + return; + } auto end = std::chrono::high_resolution_clock::now(); printf( "[Navmesh] Finished exporting %s in %lu ms\n", @@ -37,15 +37,7 @@ public: static void exportGroup( const std::string& zoneName, const ExportedGroup& group ) { - auto start = std::chrono::high_resolution_clock::now(); - auto fileName = zoneName + "_" + group.name + ".obj"; - - auto end = std::chrono::high_resolution_clock::now(); - - printf( "[Navmesh] Finished exporting %s in %lu ms\n", - fileName.c_str(), - std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } private: /*/ From b5fd8d8c3fdf4900d57595221341b3745ff06018 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 18:45:37 +1100 Subject: [PATCH 45/50] add headless navmesh exporting --- .../pcb_reader/nav/TiledNavmeshGenerator.cpp | 572 ++++++++++++++++++ .../pcb_reader/nav/TiledNavmeshGenerator.h | 203 +++---- src/tools/pcb_reader/navmesh_exporter.h | 347 +---------- src/tools/pcb_reader/obj_exporter.h | 8 +- 4 files changed, 674 insertions(+), 456 deletions(-) create mode 100644 src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp new file mode 100644 index 00000000..6aedfa70 --- /dev/null +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp @@ -0,0 +1,572 @@ +#include "TiledNavmeshGenerator.h" + +#include +#include + +#include + +namespace fs = std::experimental::filesystem; + + +inline unsigned int nextPow2( uint32_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +inline unsigned int ilog2( uint32_t v ) +{ + uint32_t r; + uint32_t shift; + r = (v > 0xffff) << 4; v >>= r; + shift = (v > 0xff) << 3; v >>= shift; r |= shift; + shift = (v > 0xf) << 2; v >>= shift; r |= shift; + shift = (v > 0x3) << 1; v >>= shift; r |= shift; + r |= (v >> 1); + return r; +} + +bool TiledNavmeshGenerator::init( const std::string& path ) +{ + if( !fs::exists( path ) ) + throw std::runtime_error( "what" ); + + // ignore logging/bullshit/etc + m_ctx = new rcContext( false ); + + printf( "[Navmesh] loading obj: %s\n", path.substr( path.find( "pcb_export" ) - 1 ).c_str() ); + + m_mesh = new rcMeshLoaderObj; + assert( m_mesh ); + + if( !m_mesh->load( path ) ) + { + printf( "[Navmesh] Failed to allocate rcMeshLoaderObj\n" ); + return false; + } + + rcCalcBounds( m_mesh->getVerts(), m_mesh->getVertCount(), m_meshBMin, m_meshBMax ); + + m_chunkyMesh = new rcChunkyTriMesh; + assert( m_chunkyMesh ); + + if( !rcCreateChunkyTriMesh( m_mesh->getVerts(), m_mesh->getTris(), m_mesh->getTriCount(), 256, m_chunkyMesh ) ) + { + printf( "[Navmesh] buildTiledNavigation: Failed to build chunky mesh.\n" ); + return false; + } + + // todo: load some bullshit settings from exd + + int gw = 0, gh = 0; + rcCalcGridSize( m_meshBMin, m_meshBMax, m_cellSize, &gw, &gh ); + + auto ts = static_cast< uint32_t >( m_tileSize ); + const uint32_t tw = (gw + ts-1) / ts; + const uint32_t th = (gh + ts-1) / ts; + + printf( "[Navmesh] - Tiles %d x %d\n", tw, th ); + + int tileBits = rcMin( ( int ) ilog2( nextPow2( tw * th ) ), 14 ); + if ( tileBits > 14 ) tileBits = 14; + int polyBits = 22 - tileBits; + m_maxTiles = 1 << tileBits; + m_maxPolysPerTile = 1 << polyBits; + + printf( "[Navmesh] - %.1fK verts, %.1fK tris\n", m_mesh->getVertCount() / 1000.0f, m_mesh->getTriCount() / 1000.0f ); + + return true; +} + +TiledNavmeshGenerator::~TiledNavmeshGenerator() +{ + delete m_mesh; + delete m_chunkyMesh; +} + +void TiledNavmeshGenerator::saveNavmesh( const std::string& name ) +{ + assert( m_navMesh ); + + // fuck this gay earth + auto mesh = const_cast< const dtNavMesh* >( m_navMesh ); + + auto dir = fs::current_path().string() + "/pcb_export/" + name + "/"; + auto fileName = dir + name + ".nav"; + + fs::create_directories( dir ); + + FILE* fp = fopen( fileName.c_str(), "wb" ); + if( !fp ) + return; + + // Store header. + NavMeshSetHeader header; + header.magic = NAVMESHSET_MAGIC; + header.version = NAVMESHSET_VERSION; + header.numTiles = 0; + for( int i = 0; i < mesh->getMaxTiles(); ++i ) + { + auto tile = mesh->getTile( i ); + if( !tile || !tile->header || !tile->dataSize ) + continue; + + header.numTiles++; + } + + memcpy( &header.params, mesh->getParams(), sizeof( dtNavMeshParams ) ); + fwrite( &header, sizeof( NavMeshSetHeader ), 1, fp ); + + // Store tiles. + for( int i = 0; i < mesh->getMaxTiles(); ++i ) + { + auto tile = mesh->getTile( i ); + if( !tile || !tile->header || !tile->dataSize ) + continue; + + NavMeshTileHeader tileHeader; + tileHeader.tileRef = mesh->getTileRef( tile ); + tileHeader.dataSize = tile->dataSize; + fwrite( &tileHeader, sizeof( tileHeader ), 1, fp ); + + fwrite( tile->data, tile->dataSize, 1, fp ); + } + + fclose( fp ); + + auto pos = fileName.find( "pcb_export" ); + fileName = fileName.substr( pos - 1 ); + + printf( "[Navmesh] Saved navmesh to '%s'\n", fileName.c_str() ); +} + +bool TiledNavmeshGenerator::buildNavmesh() +{ + assert( m_mesh ); + + m_navMesh = dtAllocNavMesh(); + if( !m_navMesh ) + { + printf( "[Navmesh] buildTiledNavigation: Could not allocate navmesh.\n" ); + return false; + } + + dtNavMeshParams params{}; + rcVcopy( params.orig, m_meshBMin ); + params.tileWidth = m_tileSize * m_cellSize; + params.tileHeight = m_tileSize * m_cellSize; + params.maxTiles = m_maxTiles; + params.maxPolys = m_maxPolysPerTile; + + dtStatus status; + + status = m_navMesh->init( ¶ms ); + if( dtStatusFailed( status ) ) + { + printf( "[Navmesh] buildTiledNavigation: Could not init navmesh.\n" ); + return false; + } + + m_navQuery = dtAllocNavMeshQuery(); + assert( m_navQuery ); + + status = m_navQuery->init( m_navMesh, 2048 ); + if( dtStatusFailed( status ) ) + { + printf( "[Navmesh] buildTiledNavigation: Could not init Detour navmesh query\n" ); + return false; + } + + // todo: duplicated from above, we can probably cache all this and only do it once + int gw = 0, gh = 0; + rcCalcGridSize( m_meshBMin, m_meshBMax, m_cellSize, &gw, &gh ); + auto ts = static_cast< uint32_t >( m_tileSize ); + const int tw = ( gw + ts - 1 ) / ts; + const int th = ( gh + ts - 1 ) / ts; + const float tcs = m_tileSize * m_cellSize; + + for( int y = 0; y < th; y++ ) + { + for( int x = 0; x < tw; x++ ) + { + m_lastBuiltTileBmin[ 0 ] = m_meshBMin[ 0 ] + x * tcs; + m_lastBuiltTileBmin[ 1 ] = m_meshBMin[ 1 ]; + m_lastBuiltTileBmin[ 2 ] = m_meshBMin[ 2 ] + y * tcs; + + m_lastBuiltTileBmax[ 0 ] = m_meshBMin[ 0 ] + ( x + 1 ) * tcs; + m_lastBuiltTileBmax[ 1 ] = m_meshBMax[ 1 ]; + m_lastBuiltTileBmax[ 2 ] = m_meshBMin[ 2 ] + ( y + 1 ) * tcs; + + int dataSize = 0; + + unsigned char* data = buildTileMesh( x, y, m_lastBuiltTileBmin, m_lastBuiltTileBmax, dataSize ); + if( data ) + { + // Remove any previous data (navmesh owns and deletes the data). + m_navMesh->removeTile( m_navMesh->getTileRefAt( x, y, 0 ), nullptr, nullptr ); + + // Let the navmesh own the data. + status = m_navMesh->addTile( data, dataSize, DT_TILE_FREE_DATA, 0, nullptr ); + + if( dtStatusFailed( status ) ) + { + dtFree( data ); + } + } + } + } + + return true; +} + + +unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, const float* bmin, const float* bmax, + int& dataSize ) +{ + const float* verts = m_mesh->getVerts(); + const int nverts = m_mesh->getVertCount(); + const int ntris = m_mesh->getTriCount(); + + // Init build configuration from GUI + memset( &m_cfg, 0, sizeof( m_cfg ) ); + m_cfg.cs = m_cellSize; + m_cfg.ch = m_cellHeight; + m_cfg.walkableSlopeAngle = m_agentMaxSlope; + m_cfg.walkableHeight = static_cast< int >( ceilf( m_agentHeight / m_cfg.ch ) ); + m_cfg.walkableClimb = static_cast< int >( floorf( m_agentMaxClimb / m_cfg.ch ) ); + m_cfg.walkableRadius = static_cast< int >( ceilf( m_agentRadius / m_cfg.cs ) ); + m_cfg.maxEdgeLen = static_cast< int >( m_edgeMaxLen / m_cellSize ); + m_cfg.maxSimplificationError = m_edgeMaxError; + m_cfg.minRegionArea = static_cast< int >( rcSqr( m_regionMinSize ) ); // Note: area = size*size + m_cfg.mergeRegionArea = static_cast< int >( rcSqr( m_regionMergeSize ) ); // Note: area = size*size + m_cfg.maxVertsPerPoly = static_cast< int >( m_vertsPerPoly ); + m_cfg.tileSize = static_cast< int >( m_tileSize ); + m_cfg.borderSize = m_cfg.walkableRadius + 3; // Reserve enough padding. + m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2; + m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2; + m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist; + m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; + + // Expand the heighfield bounding box by border size to find the extents of geometry we need to build this tile. + // + // This is done in order to make sure that the navmesh tiles connect correctly at the borders, + // and the obstacles close to the border work correctly with the dilation process. + // No polygons (or contours) will be created on the border area. + // + // IMPORTANT! + // + // :''''''''': + // : +-----+ : + // : | | : + // : | |<--- tile to build + // : | | : + // : +-----+ :<-- geometry needed + // :.........: + // + // You should use this bounding box to query your input geometry. + // + // For example if you build a navmesh for terrain, and want the navmesh tiles to match the terrain tile size + // you will need to pass in data from neighbour terrain tiles too! In a simple case, just pass in all the 8 neighbours, + // or use the bounding box below to only pass in a sliver of each of the 8 neighbours. + rcVcopy( m_cfg.bmin, bmin ); + rcVcopy( m_cfg.bmax, bmax ); + m_cfg.bmin[ 0 ] -= m_cfg.borderSize * m_cfg.cs; + m_cfg.bmin[ 2 ] -= m_cfg.borderSize * m_cfg.cs; + m_cfg.bmax[ 0 ] += m_cfg.borderSize * m_cfg.cs; + m_cfg.bmax[ 2 ] += m_cfg.borderSize * m_cfg.cs; + + m_solid = rcAllocHeightfield(); + if( !m_solid ) + { + printf( "[Navmesh] buildNavigation: Out of memory 'solid'.\n" ); + return nullptr; + } + + if( !rcCreateHeightfield( m_ctx, *m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch ) ) + { + printf( "[Navmesh] buildNavigation: Could not create solid heightfield.\n" ); + return nullptr; + } + + // Allocate array that can hold triangle flags. + // If you have multiple meshes you need to process, allocate + // and array which can hold the max number of triangles you need to process. + m_triareas = new unsigned char[ m_chunkyMesh->maxTrisPerChunk ]; + if( !m_triareas ) + { + printf( "[Navmesh] buildNavigation: Out of memory 'm_triareas' (%d).\n", m_chunkyMesh->maxTrisPerChunk ); + return nullptr; + } + + float tbmin[ 2 ]; + float tbmax[ 2 ]; + tbmin[ 0 ] = m_cfg.bmin[ 0 ]; + tbmin[ 1 ] = m_cfg.bmin[ 2 ]; + tbmax[ 0 ] = m_cfg.bmax[ 0 ]; + tbmax[ 1 ] = m_cfg.bmax[ 2 ]; + + int cid[ 512 ];// TODO: Make grow when returning too many items. + const int ncid = rcGetChunksOverlappingRect( m_chunkyMesh, tbmin, tbmax, cid, 512 ); + + if( !ncid ) + return nullptr; + + m_tileTriCount = 0; + + for (int i = 0; i < ncid; ++i) + { + const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[ cid[ i ] ]; + const int* ctris = &m_chunkyMesh->tris[ node.i * 3 ]; + const int nctris = node.n; + + m_tileTriCount += nctris; + + memset( m_triareas, 0, nctris * sizeof( unsigned char ) ); + rcMarkWalkableTriangles( m_ctx, m_cfg.walkableSlopeAngle, verts, nverts, ctris, nctris, m_triareas ); + if( !rcRasterizeTriangles( m_ctx, verts, nverts, ctris, m_triareas, nctris, *m_solid, m_cfg.walkableClimb ) ) + return nullptr; + } + + delete [] m_triareas; + m_triareas = nullptr; + + // Once all geometry is rasterized, we do initial pass of filtering to + // remove unwanted overhangs caused by the conservative rasterization + // as well as filter spans where the character cannot possibly stand. + rcFilterLowHangingWalkableObstacles( m_ctx, m_cfg.walkableClimb, *m_solid ); + rcFilterLedgeSpans( m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid ); + rcFilterWalkableLowHeightSpans( m_ctx, m_cfg.walkableHeight, *m_solid ); + + // Compact the heightfield so that it is faster to handle from now on. + // This will result more cache coherent data as well as the neighbours + // between walkable cells will be calculated. + m_chf = rcAllocCompactHeightfield(); + if( !m_chf ) + { + printf( "[Navmesh] buildNavigation: Out of memory 'chf'." ); + return nullptr; + } + if( !rcBuildCompactHeightfield( m_ctx, m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid, *m_chf ) ) + { + printf( "[Navmesh] buildNavigation: Could not build compact data." ); + return nullptr; + } + + rcFreeHeightField(m_solid); + m_solid = nullptr; + + // Erode the walkable area by agent radius. + if( !rcErodeWalkableArea( m_ctx, m_cfg.walkableRadius, *m_chf ) ) + { + printf( "[Navmesh] buildNavigation: Could not erode." ); + return nullptr; + } + + // (Optional) Mark areas. +// const ConvexVolume* vols = m_mesh->getConvexVolumes(); +// for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) +// rcMarkConvexPolyArea(m_ctx, vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf); + + // Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas. + // There are 3 martitioning methods, each with some pros and cons: + // 1) Watershed partitioning + // - the classic Recast partitioning + // - creates the nicest tessellation + // - usually slowest + // - partitions the heightfield into nice regions without holes or overlaps + // - the are some corner cases where this method creates produces holes and overlaps + // - holes may appear when a small obstacles is close to large open area (triangulation can handle this) + // - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail + // * generally the best choice if you precompute the nacmesh, use this if you have large open areas + // 2) Monotone partioning + // - fastest + // - partitions the heightfield into regions without holes and overlaps (guaranteed) + // - creates long thin polygons, which sometimes causes paths with detours + // * use this if you want fast navmesh generation + // 3) Layer partitoining + // - quite fast + // - partitions the heighfield into non-overlapping regions + // - relies on the triangulation code to cope with holes (thus slower than monotone partitioning) + // - produces better triangles than monotone partitioning + // - does not have the corner cases of watershed partitioning + // - can be slow and create a bit ugly tessellation (still better than monotone) + // if you have large open areas with small obstacles (not a problem if you use tiles) + // * good choice to use for tiled navmesh with medium and small sized tiles + + if( m_partitionType == SAMPLE_PARTITION_WATERSHED ) + { + // Prepare for region partitioning, by calculating distance field along the walkable surface. + if( !rcBuildDistanceField( m_ctx, *m_chf ) ) + { + printf( "[Navmesh] buildNavigation: Could not build distance field." ); + return nullptr; + } + + // Partition the walkable surface into simple regions without holes. + if( !rcBuildRegions( m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea ) ) + { + printf( "[Navmesh] buildNavigation: Could not build watershed regions." ); + return nullptr; + } + } + else if( m_partitionType == SAMPLE_PARTITION_MONOTONE ) + { + // Partition the walkable surface into simple regions without holes. + // Monotone partitioning does not need distancefield. + if( !rcBuildRegionsMonotone( m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea, m_cfg.mergeRegionArea ) ) + { + printf( "[Navmesh] buildNavigation: Could not build monotone regions." ); + return nullptr; + } + } + else // SAMPLE_PARTITION_LAYERS + { + // Partition the walkable surface into simple regions without holes. + if( !rcBuildLayerRegions( m_ctx, *m_chf, m_cfg.borderSize, m_cfg.minRegionArea ) ) + { + printf( "[Navmesh] buildNavigation: Could not build layer regions." ); + return nullptr; + } + } + + // Create contours. + m_cset = rcAllocContourSet(); + if( !m_cset ) + { + printf( "[Navmesh] buildNavigation: Out of memory 'cset'." ); + return nullptr; + } + if( !rcBuildContours( m_ctx, *m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset ) ) + { + printf( "[Navmesh] buildNavigation: Could not create contours." ); + return nullptr; + } + + if( m_cset->nconts == 0 ) + { + return nullptr; + } + + // Build polygon navmesh from the contours. + m_pmesh = rcAllocPolyMesh(); + if( !m_pmesh ) + { + printf( "[Navmesh] buildNavigation: Out of memory 'pmesh'." ); + return nullptr; + } + if( !rcBuildPolyMesh( m_ctx, *m_cset, m_cfg.maxVertsPerPoly, *m_pmesh ) ) + { + printf( "[Navmesh] buildNavigation: Could not triangulate contours." ); + return nullptr; + } + + // Build detail mesh. + m_dmesh = rcAllocPolyMeshDetail(); + if( !m_dmesh ) + { + printf( "[Navmesh] buildNavigation: Out of memory 'dmesh'." ); + return nullptr; + } + + if ( !rcBuildPolyMeshDetail( m_ctx, *m_pmesh, *m_chf, + m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, + *m_dmesh ) ) + { + printf( "[Navmesh] buildNavigation: Could build polymesh detail." ); + return nullptr; + } + + rcFreeCompactHeightfield( m_chf ); + rcFreeContourSet( m_cset ); + m_chf = nullptr; + m_cset = nullptr; + + unsigned char* navData = 0; + int navDataSize = 0; + if( m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON ) + { + if( m_pmesh->nverts >= 0xffff ) + { + // The vertex indices are ushorts, and cannot point to more than 0xffff vertices. + printf( "[Navmesh] Too many vertices per tile %d (max: %d).", m_pmesh->nverts, 0xffff ); + return nullptr; + } + + // Update poly flags from areas. + for (int i = 0; i < m_pmesh->npolys; ++i) + { + if (m_pmesh->areas[ i ] == RC_WALKABLE_AREA) + m_pmesh->areas[ i ] = SAMPLE_POLYAREA_GROUND; + + if (m_pmesh->areas[ i ] == SAMPLE_POLYAREA_GROUND || + m_pmesh->areas[ i ] == SAMPLE_POLYAREA_GRASS || + m_pmesh->areas[ i ] == SAMPLE_POLYAREA_ROAD) + { + m_pmesh->flags[ i ] = SAMPLE_POLYFLAGS_WALK; + } + else if (m_pmesh->areas[ i ] == SAMPLE_POLYAREA_WATER) + { + m_pmesh->flags[ i ] = SAMPLE_POLYFLAGS_SWIM; + } + else if (m_pmesh->areas[ i ] == SAMPLE_POLYAREA_DOOR) + { + m_pmesh->flags[ i ] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR; + } + } + + dtNavMeshCreateParams params; + memset( ¶ms, 0, sizeof( params ) ); + params.verts = m_pmesh->verts; + params.vertCount = m_pmesh->nverts; + params.polys = m_pmesh->polys; + params.polyAreas = m_pmesh->areas; + params.polyFlags = m_pmesh->flags; + params.polyCount = m_pmesh->npolys; + params.nvp = m_pmesh->nvp; + params.detailMeshes = m_dmesh->meshes; + params.detailVerts = m_dmesh->verts; + params.detailVertsCount = m_dmesh->nverts; + params.detailTris = m_dmesh->tris; + params.detailTriCount = m_dmesh->ntris; + + params.offMeshConVerts = nullptr; + params.offMeshConRad = nullptr; + params.offMeshConDir = nullptr; + params.offMeshConAreas = nullptr; + params.offMeshConFlags = nullptr; + params.offMeshConUserID = nullptr; + params.offMeshConCount = 0; + + params.walkableHeight = m_agentHeight; + params.walkableRadius = m_agentRadius; + params.walkableClimb = m_agentMaxClimb; + params.tileX = tx; + params.tileY = ty; + params.tileLayer = 0; + rcVcopy( params.bmin, m_pmesh->bmin ); + rcVcopy( params.bmax, m_pmesh->bmax ); + params.cs = m_cfg.cs; + params.ch = m_cfg.ch; + params.buildBvTree = true; + + if( !dtCreateNavMeshData( ¶ms, &navData, &navDataSize ) ) + { + printf( "[Navmesh] Could not build Detour navmesh." ); + return nullptr; + } + } + + rcFreePolyMesh( m_pmesh ); + rcFreePolyMeshDetail( m_dmesh ); + m_pmesh = nullptr; + m_dmesh = nullptr; + + dataSize = navDataSize; + return navData; +} diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h index 93d3051e..05b4744d 100644 --- a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include "ext/MeshLoaderObj.h" #include "ext/ChunkyTriMesh.h" @@ -12,147 +12,112 @@ #include "recastnavigation/Detour/Include/DetourNavMeshQuery.h" #include "recastnavigation/Recast/Include/Recast.h" -namespace fs = std::experimental::filesystem; - class TiledNavmeshGenerator { +public: + enum SamplePartitionType + { + SAMPLE_PARTITION_WATERSHED, + SAMPLE_PARTITION_MONOTONE, + SAMPLE_PARTITION_LAYERS, + }; + + enum SamplePolyAreas + { + SAMPLE_POLYAREA_GROUND, + SAMPLE_POLYAREA_WATER, + SAMPLE_POLYAREA_ROAD, + SAMPLE_POLYAREA_DOOR, + SAMPLE_POLYAREA_GRASS, + SAMPLE_POLYAREA_JUMP, + }; + enum SamplePolyFlags + { + SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road) + SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water). + SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors. + SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump. + SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon + SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities. + }; + + static const int NAVMESHSET_MAGIC = 'M'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'MSET'; + static const int NAVMESHSET_VERSION = 1; + + struct NavMeshSetHeader + { + int magic; + int version; + int numTiles; + dtNavMeshParams params; + }; + + struct NavMeshTileHeader + { + dtTileRef tileRef; + int dataSize; + }; + + + TiledNavmeshGenerator() = default; + ~TiledNavmeshGenerator(); + + bool init( const std::string& path ); + unsigned char* buildTileMesh( const int tx, const int ty, const float* bmin, const float* bmax, int& dataSize ); + bool buildNavmesh(); + void saveNavmesh( const std::string& name ); + private: + rcConfig m_cfg; + rcMeshLoaderObj* m_mesh; rcChunkyTriMesh* m_chunkyMesh; + rcContext* m_ctx; dtNavMesh* m_navMesh; dtNavMeshQuery* m_navQuery; + rcHeightfield* m_solid; + rcContourSet* m_cset; + rcPolyMesh* m_pmesh; + rcPolyMeshDetail* m_dmesh; + rcCompactHeightfield* m_chf; - float m_meshBMin[ 3 ]; - float m_meshBMax[ 3 ]; - - float m_tileSize = 160.f; - float m_cellSize = 0.2f; + unsigned char* m_triareas; int m_maxTiles = 0; int m_maxPolysPerTile = 0; - inline unsigned int nextPow2( uint32_t v ) - { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; - } + int m_tileTriCount = 0; - inline unsigned int ilog2( uint32_t v ) - { - uint32_t r; - uint32_t shift; - r = (v > 0xffff) << 4; v >>= r; - shift = (v > 0xff) << 3; v >>= shift; r |= shift; - shift = (v > 0xf) << 2; v >>= shift; r |= shift; - shift = (v > 0x3) << 1; v >>= shift; r |= shift; - r |= (v >> 1); - return r; - } + int m_partitionType = SamplePartitionType::SAMPLE_PARTITION_WATERSHED; + // options + float m_meshBMin[ 3 ]; + float m_meshBMax[ 3 ]; -public: - explicit TiledNavmeshGenerator( const std::string& path ) - { - if( !fs::exists( path ) ) - throw std::runtime_error( "what" ); + float m_lastBuiltTileBmin[3]; + float m_lastBuiltTileBmax[3]; - printf( "[Navmesh] loading obj: %s\n", path.c_str() ); + float m_tileSize = 160.f; + float m_cellSize = 0.2f; + float m_cellHeight = 0.2f; - m_mesh = new rcMeshLoaderObj; - assert( m_mesh ); + float m_agentMaxSlope = 56.f; + float m_agentHeight = 2.f; + float m_agentMaxClimb = 0.6f; + float m_agentRadius = 0.5f; - if( !m_mesh->load( path ) ) - { - printf( "[Navmesh] Failed to allocate rcMeshLoaderObj\n" ); - return; - } + float m_edgeMaxLen = 12.f; + float m_edgeMaxError = 1.4f; - rcCalcBounds( m_mesh->getVerts(), m_mesh->getVertCount(), m_meshBMin, m_meshBMax ); + float m_regionMinSize = 8.f; + float m_regionMergeSize = 20.f; - m_chunkyMesh = new rcChunkyTriMesh; - assert( m_chunkyMesh ); - - if( !rcCreateChunkyTriMesh( m_mesh->getVerts(), m_mesh->getTris(), m_mesh->getTriCount(), 256, m_chunkyMesh ) ) - { - printf( "[Navmesh] buildTiledNavigation: Failed to build chunky mesh.\n" ); - return; - } - - printf( "[Navmesh] loaded obj, verts: %i tris: %i\n", m_mesh->getVertCount(), m_mesh->getTriCount() ); - - // todo: load some bullshit settings from exd - - int gw = 0, gh = 0; - rcCalcGridSize( m_meshBMin, m_meshBMax, m_cellSize, &gw, &gh ); - - auto ts = static_cast< uint32_t >( m_tileSize ); - const uint32_t tw = (gw + ts-1) / ts; - const uint32_t th = (gh + ts-1) / ts; - - printf( "[Navmesh] Tiles %d x %d\n", tw, th ); - - int tileBits = rcMin((int)ilog2(nextPow2(tw*th)), 14); - if (tileBits > 14) tileBits = 14; - int polyBits = 22 - tileBits; - m_maxTiles = 1 << tileBits; - m_maxPolysPerTile = 1 << polyBits; - - printf( "[Navmesh] Max Tiles: %d\tMax Polys: %d\n", m_maxTiles, m_maxPolysPerTile ); - } - - bool buildNavmesh() - { - assert( m_mesh ); - - m_navMesh = dtAllocNavMesh(); - if( !m_navMesh ) - { - printf( "[Navmesh] buildTiledNavigation: Could not allocate navmesh.\n" ); - return false; - } - - dtNavMeshParams params{}; - rcVcopy( params.orig, m_meshBMin ); - params.tileWidth = m_tileSize * m_cellSize; - params.tileHeight = m_tileSize * m_cellSize; - params.maxTiles = m_maxTiles; - params.maxPolys = m_maxPolysPerTile; - - dtStatus status; - - status = m_navMesh->init( ¶ms ); - if( dtStatusFailed( status ) ) - { - printf( "[Navigation] buildTiledNavigation: Could not init navmesh.\n" ); - return false; - } - - m_navQuery = dtAllocNavMeshQuery(); - assert( m_navQuery ); - - status = m_navQuery->init( m_navMesh, 2048 ); - if( dtStatusFailed( status ) ) - { - printf( "[Navigation] buildTiledNavigation: Could not init Detour navmesh query\n" ); - return false; - } - - } - - ~TiledNavmeshGenerator() - { - delete m_mesh; - delete m_chunkyMesh; - } + float m_vertsPerPoly = 6.f; + float m_detailSampleDist = 6.f; + float m_detailSampleMaxError = 1.f; }; diff --git a/src/tools/pcb_reader/navmesh_exporter.h b/src/tools/pcb_reader/navmesh_exporter.h index ec06a0cd..6c4865c8 100644 --- a/src/tools/pcb_reader/navmesh_exporter.h +++ b/src/tools/pcb_reader/navmesh_exporter.h @@ -11,6 +11,10 @@ #include "exporter.h" #include "nav/TiledNavmeshGenerator.h" +#include + +namespace fs = std::experimental::filesystem; + class NavmeshExporter { public: @@ -18,10 +22,16 @@ public: { auto start = std::chrono::high_resolution_clock::now(); - auto dir = std::experimental::filesystem::current_path().string() + "/pcb_export/" + zone.name + "/"; + auto dir = fs::current_path().string() + "/pcb_export/" + zone.name + "/"; auto fileName = dir + zone.name + ".obj"; - TiledNavmeshGenerator gen( fileName ); + TiledNavmeshGenerator gen; + + if( !gen.init( fileName ) ) + { + printf( "[Navmesh] failed to init TiledNavmeshGenerator for file '%s'\n", fileName.c_str() ); + return; + } if( !gen.buildNavmesh() ) { @@ -29,6 +39,8 @@ public: return; } + gen.saveNavmesh( zone.name ); + auto end = std::chrono::high_resolution_clock::now(); printf( "[Navmesh] Finished exporting %s in %lu ms\n", fileName.c_str(), @@ -39,336 +51,5 @@ public: { } -private: - /*/ - static unsigned char* buildTileMesh( const ExportedGroup& group, int tx, int ty ) - { - unsigned char* navData; - rcConfig cfg; - cfg.ch = 0.2f; - cfg.cs = 0.2f; - cfg.walkableHeight = 2.f; - cfg.walkableRadius = 0.5; - cfg.walkableClimb = 0.6; - cfg.walkableSlopeAngle = 58.f; - cfg.minRegionArea = 8.0f; - cfg.mergeRegionArea = 20.f; - cfg.maxEdgeLen = 12.f; - cfg.maxSimplificationError = 1.4f; - cfg.maxVertsPerPoly = 6.f; - cfg.detailSampleDist = 6.f; - cfg.detailSampleMaxError = 1.f; - cfg.tileSize = 160.f; - - cfg.walkableHeight = (int)ceilf( cfg.walkableHeight / cfg.ch ); - cfg.walkableClimb = (int)floorf( cfg.walkableClimb / cfg.ch ); - cfg.walkableRadius = (int)ceilf( cfg.walkableRadius / cfg.cs ); - cfg.maxEdgeLen = (int)( cfg.maxEdgeLen / cfg.cs ); - cfg.minRegionArea = (int)rcSqr( cfg.minRegionArea ); // Note: area = size*size - cfg.mergeRegionArea = (int)rcSqr( cfg.mergeRegionArea ); // Note: area = size*size - cfg.borderSize = cfg.walkableRadius + 3; // Reserve enough padding. - cfg.width = cfg.tileSize + cfg.borderSize*2; - cfg.height = cfg.tileSize + cfg.borderSize*2; - cfg.detailSampleDist = cfg.detailSampleDist < 0.9f ? 0 : cfg.cs * cfg.detailSampleDist; - cfg.detailSampleMaxError = cfg.ch * cfg.detailSampleMaxError; - - rcContext ctx; - auto hf = rcAllocHeightfield(); - auto chf = rcAllocCompactHeightfield(); - auto cs = rcAllocContourSet(); - auto pmesh = rcAllocPolyMesh(); - auto pdetailmesh = rcAllocPolyMeshDetail(); - - std::vector< float > verts; - std::vector< int > indices; - - int i = 0; - int numIndices = 0; - for( const auto& model : group.models ) - { - for( const auto& mesh : model.second.meshes ) - { - auto size = mesh.verts.size(); - rcCalcBounds( mesh.verts.data(), size / 3, &cfg.bmin[0], &cfg.bmax[0] ); - verts.reserve( verts.size() + size ); - memcpy( &verts[i], mesh.verts.data(), size ); - i += size; - - size = mesh.indices.size(); - indices.reserve( indices.size() + size ); - for( auto j = 0; j < mesh.indices.size(); j += 3 ) - { - indices[j] = mesh.indices[j] + numIndices; - indices[j + 1] = mesh.indices[j + 1] + numIndices; - indices[j + 2] = mesh.indices[j + 2] + numIndices; - } - numIndices += size; - } - } - - auto chunkyMesh = new rcChunkyTriMesh; - rcCreateChunkyTriMesh( &verts[0], &indices[0], verts.size() / 3, 256, chunkyMesh ); - if( !rcCreateHeightfield( &ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch ) ) - { - - } - float tbmin[2], tbmax[2]; - tbmin[0] = cfg.bmin[0]; - tbmin[1] = cfg.bmin[2]; - tbmax[0] = cfg.bmax[0]; - tbmax[1] = cfg.bmax[2]; - int cid[512];// TODO: Make grow when returning too many items. - const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512); - if (!ncid) - return 0; - - auto tileTriCount = 0; - auto triareas = new unsigned char[chunkyMesh->maxTrisPerChunk]; - for (int i = 0; i < ncid; ++i) - { - const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]]; - const int* ctris = &chunkyMesh->tris[node.i*3]; - const int nctris = node.n; - - tileTriCount += nctris; - - memset(triareas, 0, nctris*sizeof(unsigned char)); - rcMarkWalkableTriangles(&ctx, cfg.walkableSlopeAngle, - &verts[0], verts.size() / 3, ctris, nctris, triareas); - - if (!rcRasterizeTriangles(&ctx, &verts[0], verts.size() / 3, ctris, triareas, nctris, *hf, cfg.walkableClimb)) - return 0; - } - - { - delete [] triareas; - triareas = 0; - } - - // Once all geometry is rasterized, we do initial pass of filtering to - // remove unwanted overhangs caused by the conservative rasterization - // as well as filter spans where the character cannot possibly stand. - - rcFilterLowHangingWalkableObstacles(&ctx, cfg.walkableClimb, *hf); - - rcFilterLedgeSpans(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf); - rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf); - - // Compact the heightfield so that it is faster to handle from now on. - // This will result more cache coherent data as well as the neighbours - // between walkable cells will be calculated. - chf = rcAllocCompactHeightfield(); - if (!chf) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'."); - return 0; - } - if (!rcBuildCompactHeightfield(&ctx, cfg.walkableHeight, cfg.walkableClimb, *hf, *chf)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could not build compact data."); - return 0; - } - - - { - rcFreeHeightField(hf); - hf = 0; - } - - // Erode the walkable area by agent radius. - if (!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could not erode."); - return 0; - } - - - // Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas. - // There are 3 martitioning methods, each with some pros and cons: - // 1) Watershed partitioning - // - the classic Recast partitioning - // - creates the nicest tessellation - // - usually slowest - // - partitions the heightfield into nice regions without holes or overlaps - // - the are some corner cases where this method creates produces holes and overlaps - // - holes may appear when a small obstacles is close to large open area (triangulation can handle this) - // - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail - // * generally the best choice if you precompute the nacmesh, use this if you have large open areas - // 2) Monotone partioning - // - fastest - // - partitions the heightfield into regions without holes and overlaps (guaranteed) - // - creates long thin polygons, which sometimes causes paths with detours - // * use this if you want fast navmesh generation - // 3) Layer partitoining - // - quite fast - // - partitions the heighfield into non-overlapping regions - // - relies on the triangulation code to cope with holes (thus slower than monotone partitioning) - // - produces better triangles than monotone partitioning - // - does not have the corner cases of watershed partitioning - // - can be slow and create a bit ugly tessellation (still better than monotone) - // if you have large open areas with small obstacles (not a problem if you use tiles) - // * good choice to use for tiled navmesh with medium and small sized tiles - - //if (m_partitionType == SAMPLE_PARTITION_WATERSHED) - { - // Prepare for region partitioning, by calculating distance field along the walkable surface. - if (!rcBuildDistanceField(&ctx, *chf)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could not build distance field."); - return 0; - } - - // Partition the walkable surface into simple regions without holes. - if (!rcBuildRegions(&ctx, *chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could not build watershed regions."); - return 0; - } - } - //else if (m_partitionType == SAMPLE_PARTITION_MONOTONE) - //{ - // // Partition the walkable surface into simple regions without holes. - // // Monotone partitioning does not need distancefield. - // if (!rcBuildRegionsMonotone(&ctx, *chf, cfg.borderSize, cfg.minRegionArea, cfg.mergeRegionArea)) - // { - // ctx.log(RC_LOG_ERROR, "buildNavigation: Could not build monotone regions."); - // return 0; - // } - //} - //else // SAMPLE_PARTITION_LAYERS - //{ - // // Partition the walkable surface into simple regions without holes. - // if (!rcBuildLayerRegions(&ctx, *chf, cfg.borderSize, cfg.minRegionArea)) - // { - // ctx.log(RC_LOG_ERROR, "buildNavigation: Could not build layer regions."); - // return 0; - // } - //} - - // Create contours. - cs = rcAllocContourSet(); - if (!cs) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Out of memory 'cset'."); - return 0; - } - if (!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cs)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could not create contours."); - return 0; - } - - if (cs->nconts == 0) - { - return 0; - } - - // Build polygon navmesh from the contours. - pmesh = rcAllocPolyMesh(); - if (!pmesh) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Out of memory 'pmesh'."); - return 0; - } - if (!rcBuildPolyMesh(&ctx, *cs, cfg.maxVertsPerPoly, *pmesh)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could not triangulate contours."); - return 0; - } - - // Build detail mesh. - pdetailmesh = rcAllocPolyMeshDetail(); - if (!pdetailmesh) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Out of memory 'dmesh'."); - return 0; - } - - if (!rcBuildPolyMeshDetail(&ctx, *pmesh, *chf, - cfg.detailSampleDist, cfg.detailSampleMaxError, - *pdetailmesh)) - { - ctx.log(RC_LOG_ERROR, "buildNavigation: Could build polymesh detail."); - return 0; - } - - { - rcFreeCompactHeightfield(chf); - chf = 0; - rcFreeContourSet(cs); - cs = 0; - } - - unsigned char* navData = 0; - int navDataSize = 0; - if (cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) - { - if (pmesh->nverts >= 0xffff) - { - // The vertex indices are ushorts, and cannot point to more than 0xffff vertices. - ctx.log(RC_LOG_ERROR, "Too many vertices per tile %d (max: %d).", pmesh->nverts, 0xffff); - return 0; - } - - // Update poly flags from areas. - for (int i = 0; i < pmesh->npolys; ++i) - { - //pmesh->flags[i] = sampleAreaToFlags(pmesh->areas[i]); - } - - dtNavMeshCreateParams params; - memset(¶ms, 0, sizeof(params)); - params.verts = pmesh->verts; - params.vertCount = pmesh->nverts; - params.polys = pmesh->polys; - params.polyAreas = pmesh->areas; - params.polyFlags = pmesh->flags; - params.polyCount = pmesh->npolys; - params.nvp = pmesh->nvp; - params.detailMeshes = pdetailmesh->meshes; - params.detailVerts = pdetailmesh->verts; - params.detailVertsCount = pdetailmesh->nverts; - params.detailTris = pdetailmesh->tris; - params.detailTriCount = pdetailmesh->ntris; - params.offMeshConVerts = 0; - params.offMeshConRad = 0; - params.offMeshConDir = 0; - params.offMeshConAreas = 0; - params.offMeshConFlags = 0; - params.offMeshConUserID = 0; - params.offMeshConCount = 0; - params.walkableHeight = cfg.walkableHeight; - params.walkableRadius = cfg.walkableRadius; - params.walkableClimb = cfg.walkableClimb; - params.tileX = 0; - params.tileY = 0; - params.tileLayer = 0; - rcVcopy(params.bmin, pmesh->bmin); - rcVcopy(params.bmax, pmesh->bmax); - params.cs = cfg.cs; - params.ch = cfg.ch; - params.buildBvTree = true; - - if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) - { - ctx.log(RC_LOG_ERROR, "Could not build Detour navmesh."); - return 0; - } - } - auto tileMemUsage = navDataSize/1024.0f; - - ctx.stopTimer(RC_TIMER_TOTAL); - - // Show performance stats. - //duLogBuildTimes(*&ctx, ctx.getAccumulatedTime(RC_TIMER_TOTAL)); - ctx.log(RC_LOG_PROGRESS, ">> Polymesh: %d vertices %d polygons", pmesh->nverts, pmesh->npolys); - - auto tileBuildTime = ctx.getAccumulatedTime(RC_TIMER_TOTAL)/1000.0f; - - auto dataSize = navDataSize; - return navData; - - } - //*/ }; #endif // !OBJ_EXPORTER_H diff --git a/src/tools/pcb_reader/obj_exporter.h b/src/tools/pcb_reader/obj_exporter.h index f5c1361b..17e9fb51 100644 --- a/src/tools/pcb_reader/obj_exporter.h +++ b/src/tools/pcb_reader/obj_exporter.h @@ -51,8 +51,8 @@ public: auto end = std::chrono::high_resolution_clock::now(); printf( "[Obj] Finished exporting %s in %lu ms\n", - fileName.c_str(), - std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); + fileName.substr( fileName.find( "pcb_export" ) - 1 ).c_str(), + std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } static void exportGroup( const std::string& zoneName, const ExportedGroup& group ) @@ -88,8 +88,8 @@ public: auto end = std::chrono::high_resolution_clock::now(); printf( "[Obj] Finished exporting %s in %lu ms\n", - fileName.c_str(), - std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); + fileName.substr( fileName.find( "pcb_export" ) - 1 ).c_str(), + std::chrono::duration_cast< std::chrono::milliseconds >( end - start ).count() ); } private: static void exportGroup( const ExportedGroup& group, std::ofstream& of, int& indicesOffset, int& modelCount ) From df3ea49f1a3acc42e1c67ee6ac8bb6632e79c522 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 18:47:59 +1100 Subject: [PATCH 46/50] move comment to correct location --- src/tools/pcb_reader/nav/TiledNavmeshGenerator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h index 05b4744d..e72ee4b5 100644 --- a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h @@ -92,13 +92,13 @@ private: int m_partitionType = SamplePartitionType::SAMPLE_PARTITION_WATERSHED; - // options float m_meshBMin[ 3 ]; float m_meshBMax[ 3 ]; float m_lastBuiltTileBmin[3]; float m_lastBuiltTileBmax[3]; + // options float m_tileSize = 160.f; float m_cellSize = 0.2f; float m_cellHeight = 0.2f; From e5f612bc43a49b3f3f8147123d4e597f011dca1e Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 19:41:57 +1100 Subject: [PATCH 47/50] fix style --- src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp | 4 ++-- src/tools/pcb_reader/nav/TiledNavmeshGenerator.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp index 6aedfa70..0a347532 100644 --- a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp @@ -319,7 +319,7 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, m_tileTriCount = 0; - for (int i = 0; i < ncid; ++i) + for( int i = 0; i < ncid; ++i ) { const rcChunkyTriMeshNode& node = m_chunkyMesh->nodes[ cid[ i ] ]; const int* ctris = &m_chunkyMesh->tris[ node.i * 3 ]; @@ -358,7 +358,7 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, return nullptr; } - rcFreeHeightField(m_solid); + rcFreeHeightField( m_solid ); m_solid = nullptr; // Erode the walkable area by agent radius. diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h index e72ee4b5..64785c23 100644 --- a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.h @@ -95,8 +95,8 @@ private: float m_meshBMin[ 3 ]; float m_meshBMax[ 3 ]; - float m_lastBuiltTileBmin[3]; - float m_lastBuiltTileBmax[3]; + float m_lastBuiltTileBmin[ 3 ]; + float m_lastBuiltTileBmax[ 3 ]; // options float m_tileSize = 160.f; @@ -108,16 +108,16 @@ private: float m_agentMaxClimb = 0.6f; float m_agentRadius = 0.5f; - float m_edgeMaxLen = 12.f; - float m_edgeMaxError = 1.4f; - float m_regionMinSize = 8.f; float m_regionMergeSize = 20.f; + float m_edgeMaxLen = 12.f; + float m_edgeMaxError = 1.4f; float m_vertsPerPoly = 6.f; float m_detailSampleDist = 6.f; float m_detailSampleMaxError = 1.f; + }; From 29edf879247a6fdd0e8229141ac31ec4c5486f37 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 19:43:42 +1100 Subject: [PATCH 48/50] fix more style --- .../pcb_reader/nav/ext/ChunkyTriMesh.cpp | 516 +++++++++--------- src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h | 45 +- .../pcb_reader/nav/ext/MeshLoaderObj.cpp | 197 +++---- src/tools/pcb_reader/nav/ext/MeshLoaderObj.h | 48 +- 4 files changed, 432 insertions(+), 374 deletions(-) diff --git a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp index 2991d4ff..7b5ef0d6 100644 --- a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp +++ b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.cpp @@ -23,293 +23,309 @@ struct BoundsItem { - float bmin[2]; - float bmax[2]; - int i; + float bmin[ 2 ]; + float bmax[ 2 ]; + int i; }; -static int compareItemX(const void* va, const void* vb) +static int compareItemX( const void* va, const void* vb ) { - const BoundsItem* a = (const BoundsItem*)va; - const BoundsItem* b = (const BoundsItem*)vb; - if (a->bmin[0] < b->bmin[0]) - return -1; - if (a->bmin[0] > b->bmin[0]) - return 1; - return 0; + const BoundsItem* a = ( const BoundsItem* ) va; + const BoundsItem* b = ( const BoundsItem* ) vb; + if( a->bmin[ 0 ] < b->bmin[ 0 ] ) + return -1; + if( a->bmin[ 0 ] > b->bmin[ 0 ] ) + return 1; + return 0; } -static int compareItemY(const void* va, const void* vb) +static int compareItemY( const void* va, const void* vb ) { - const BoundsItem* a = (const BoundsItem*)va; - const BoundsItem* b = (const BoundsItem*)vb; - if (a->bmin[1] < b->bmin[1]) - return -1; - if (a->bmin[1] > b->bmin[1]) - return 1; - return 0; + const BoundsItem* a = ( const BoundsItem* ) va; + const BoundsItem* b = ( const BoundsItem* ) vb; + if( a->bmin[ 1 ] < b->bmin[ 1 ] ) + return -1; + if( a->bmin[ 1 ] > b->bmin[ 1 ] ) + return 1; + return 0; } -static void calcExtends(const BoundsItem* items, const int /*nitems*/, - const int imin, const int imax, - float* bmin, float* bmax) +static void calcExtends( const BoundsItem* items, const int /*nitems*/, + const int imin, const int imax, + float* bmin, float* bmax ) { - bmin[0] = items[imin].bmin[0]; - bmin[1] = items[imin].bmin[1]; - - bmax[0] = items[imin].bmax[0]; - bmax[1] = items[imin].bmax[1]; - - for (int i = imin+1; i < imax; ++i) - { - const BoundsItem& it = items[i]; - if (it.bmin[0] < bmin[0]) bmin[0] = it.bmin[0]; - if (it.bmin[1] < bmin[1]) bmin[1] = it.bmin[1]; - - if (it.bmax[0] > bmax[0]) bmax[0] = it.bmax[0]; - if (it.bmax[1] > bmax[1]) bmax[1] = it.bmax[1]; - } + bmin[ 0 ] = items[ imin ].bmin[ 0 ]; + bmin[ 1 ] = items[ imin ].bmin[ 1 ]; + + bmax[ 0 ] = items[ imin ].bmax[ 0 ]; + bmax[ 1 ] = items[ imin ].bmax[ 1 ]; + + for( int i = imin + 1; i < imax; ++i ) + { + const BoundsItem& it = items[ i ]; + if( it.bmin[ 0 ] < bmin[ 0 ] ) + bmin[ 0 ] = it.bmin[ 0 ]; + if( it.bmin[ 1 ] < bmin[ 1 ] ) + bmin[ 1 ] = it.bmin[ 1 ]; + + if( it.bmax[ 0 ] > bmax[ 0 ] ) + bmax[ 0 ] = it.bmax[ 0 ]; + if( it.bmax[ 1 ] > bmax[ 1 ] ) + bmax[ 1 ] = it.bmax[ 1 ]; + } } -inline int longestAxis(float x, float y) +inline int longestAxis( float x, float y ) { - return y > x ? 1 : 0; + return y > x ? 1 : 0; } -static void subdivide(BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk, - int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes, - int& curTri, int* outTris, const int* inTris) +static void subdivide( BoundsItem* items, int nitems, int imin, int imax, int trisPerChunk, + int& curNode, rcChunkyTriMeshNode* nodes, const int maxNodes, + int& curTri, int* outTris, const int* inTris ) { - int inum = imax - imin; - int icur = curNode; - - if (curNode > maxNodes) - return; + int inum = imax - imin; + int icur = curNode; - rcChunkyTriMeshNode& node = nodes[curNode++]; - - if (inum <= trisPerChunk) - { - // Leaf - calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); - - // Copy triangles. - node.i = curTri; - node.n = inum; - - for (int i = imin; i < imax; ++i) - { - const int* src = &inTris[items[i].i*3]; - int* dst = &outTris[curTri*3]; - curTri++; - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - } - } - else - { - // Split - calcExtends(items, nitems, imin, imax, node.bmin, node.bmax); - - int axis = longestAxis(node.bmax[0] - node.bmin[0], - node.bmax[1] - node.bmin[1]); - - if (axis == 0) - { - // Sort along x-axis - qsort(items+imin, static_cast(inum), sizeof(BoundsItem), compareItemX); - } - else if (axis == 1) - { - // Sort along y-axis - qsort(items+imin, static_cast(inum), sizeof(BoundsItem), compareItemY); - } - - int isplit = imin+inum/2; - - // Left - subdivide(items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris); - // Right - subdivide(items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris); - - int iescape = curNode - icur; - // Negative index means escape. - node.i = -iescape; - } + if( curNode > maxNodes ) + return; + + rcChunkyTriMeshNode& node = nodes[ curNode++ ]; + + if( inum <= trisPerChunk ) + { + // Leaf + calcExtends( items, nitems, imin, imax, node.bmin, node.bmax ); + + // Copy triangles. + node.i = curTri; + node.n = inum; + + for( int i = imin; i < imax; ++i ) + { + const int* src = &inTris[ items[ i ].i * 3 ]; + int* dst = &outTris[ curTri * 3 ]; + curTri++; + dst[ 0 ] = src[ 0 ]; + dst[ 1 ] = src[ 1 ]; + dst[ 2 ] = src[ 2 ]; + } + } + else + { + // Split + calcExtends( items, nitems, imin, imax, node.bmin, node.bmax ); + + int axis = longestAxis( node.bmax[ 0 ] - node.bmin[ 0 ], + node.bmax[ 1 ] - node.bmin[ 1 ] ); + + if( axis == 0 ) + { + // Sort along x-axis + qsort( items + imin, static_cast(inum), sizeof( BoundsItem ), compareItemX ); + } + else if( axis == 1 ) + { + // Sort along y-axis + qsort( items + imin, static_cast(inum), sizeof( BoundsItem ), compareItemY ); + } + + int isplit = imin + inum / 2; + + // Left + subdivide( items, nitems, imin, isplit, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris ); + // Right + subdivide( items, nitems, isplit, imax, trisPerChunk, curNode, nodes, maxNodes, curTri, outTris, inTris ); + + int iescape = curNode - icur; + // Negative index means escape. + node.i = -iescape; + } } -bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris, - int trisPerChunk, rcChunkyTriMesh* cm) +bool rcCreateChunkyTriMesh( const float* verts, const int* tris, int ntris, + int trisPerChunk, rcChunkyTriMesh* cm ) { - int nchunks = (ntris + trisPerChunk-1) / trisPerChunk; + int nchunks = ( ntris + trisPerChunk - 1 ) / trisPerChunk; - cm->nodes = new rcChunkyTriMeshNode[nchunks*4]; - if (!cm->nodes) - return false; - - cm->tris = new int[ntris*3]; - if (!cm->tris) - return false; - - cm->ntris = ntris; + cm->nodes = new rcChunkyTriMeshNode[nchunks * 4]; + if( !cm->nodes ) + return false; - // Build tree - BoundsItem* items = new BoundsItem[ntris]; - if (!items) - return false; + cm->tris = new int[ntris * 3]; + if( !cm->tris ) + return false; - for (int i = 0; i < ntris; i++) - { - const int* t = &tris[i*3]; - BoundsItem& it = items[i]; - it.i = i; - // Calc triangle XZ bounds. - it.bmin[0] = it.bmax[0] = verts[t[0]*3+0]; - it.bmin[1] = it.bmax[1] = verts[t[0]*3+2]; - for (int j = 1; j < 3; ++j) - { - const float* v = &verts[t[j]*3]; - if (v[0] < it.bmin[0]) it.bmin[0] = v[0]; - if (v[2] < it.bmin[1]) it.bmin[1] = v[2]; + cm->ntris = ntris; - if (v[0] > it.bmax[0]) it.bmax[0] = v[0]; - if (v[2] > it.bmax[1]) it.bmax[1] = v[2]; - } - } + // Build tree + BoundsItem* items = new BoundsItem[ntris]; + if( !items ) + return false; - int curTri = 0; - int curNode = 0; - subdivide(items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks*4, curTri, cm->tris, tris); - - delete [] items; - - cm->nnodes = curNode; - - // Calc max tris per node. - cm->maxTrisPerChunk = 0; - for (int i = 0; i < cm->nnodes; ++i) - { - rcChunkyTriMeshNode& node = cm->nodes[i]; - const bool isLeaf = node.i >= 0; - if (!isLeaf) continue; - if (node.n > cm->maxTrisPerChunk) - cm->maxTrisPerChunk = node.n; - } - - return true; + for( int i = 0; i < ntris; i++ ) + { + const int* t = &tris[ i * 3 ]; + BoundsItem& it = items[ i ]; + it.i = i; + // Calc triangle XZ bounds. + it.bmin[ 0 ] = it.bmax[ 0 ] = verts[ t[ 0 ] * 3 + 0 ]; + it.bmin[ 1 ] = it.bmax[ 1 ] = verts[ t[ 0 ] * 3 + 2 ]; + for( int j = 1; j < 3; ++j ) + { + const float* v = &verts[ t[ j ] * 3 ]; + if( v[ 0 ] < it.bmin[ 0 ] ) + it.bmin[ 0 ] = v[ 0 ]; + if( v[ 2 ] < it.bmin[ 1 ] ) + it.bmin[ 1 ] = v[ 2 ]; + + if( v[ 0 ] > it.bmax[ 0 ] ) + it.bmax[ 0 ] = v[ 0 ]; + if( v[ 2 ] > it.bmax[ 1 ] ) + it.bmax[ 1 ] = v[ 2 ]; + } + } + + int curTri = 0; + int curNode = 0; + subdivide( items, ntris, 0, ntris, trisPerChunk, curNode, cm->nodes, nchunks * 4, curTri, cm->tris, tris ); + + delete[] items; + + cm->nnodes = curNode; + + // Calc max tris per node. + cm->maxTrisPerChunk = 0; + for( int i = 0; i < cm->nnodes; ++i ) + { + rcChunkyTriMeshNode& node = cm->nodes[ i ]; + const bool isLeaf = node.i >= 0; + if( !isLeaf ) + continue; + if( node.n > cm->maxTrisPerChunk ) + cm->maxTrisPerChunk = node.n; + } + + return true; } -inline bool checkOverlapRect(const float amin[2], const float amax[2], - const float bmin[2], const float bmax[2]) +inline bool checkOverlapRect( const float amin[2], const float amax[2], + const float bmin[2], const float bmax[2] ) { - bool overlap = true; - overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap; - overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap; - return overlap; + bool overlap = true; + overlap = ( amin[ 0 ] > bmax[ 0 ] || amax[ 0 ] < bmin[ 0 ] ) ? false : overlap; + overlap = ( amin[ 1 ] > bmax[ 1 ] || amax[ 1 ] < bmin[ 1 ] ) ? false : overlap; + return overlap; } -int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, - float bmin[2], float bmax[2], - int* ids, const int maxIds) +int rcGetChunksOverlappingRect( const rcChunkyTriMesh* cm, + float bmin[2], float bmax[2], + int* ids, const int maxIds ) { - // Traverse tree - int i = 0; - int n = 0; - while (i < cm->nnodes) - { - const rcChunkyTriMeshNode* node = &cm->nodes[i]; - const bool overlap = checkOverlapRect(bmin, bmax, node->bmin, node->bmax); - const bool isLeafNode = node->i >= 0; - - if (isLeafNode && overlap) - { - if (n < maxIds) - { - ids[n] = i; - n++; - } - } - - if (overlap || isLeafNode) - i++; - else - { - const int escapeIndex = -node->i; - i += escapeIndex; - } - } - - return n; + // Traverse tree + int i = 0; + int n = 0; + while( i < cm->nnodes ) + { + const rcChunkyTriMeshNode* node = &cm->nodes[ i ]; + const bool overlap = checkOverlapRect( bmin, bmax, node->bmin, node->bmax ); + const bool isLeafNode = node->i >= 0; + + if( isLeafNode && overlap ) + { + if( n < maxIds ) + { + ids[ n ] = i; + n++; + } + } + + if( overlap || isLeafNode ) + i++; + else + { + const int escapeIndex = -node->i; + i += escapeIndex; + } + } + + return n; } - -static bool checkOverlapSegment(const float p[2], const float q[2], - const float bmin[2], const float bmax[2]) +static bool checkOverlapSegment( const float p[2], const float q[2], + const float bmin[2], const float bmax[2] ) { - static const float EPSILON = 1e-6f; + static const float EPSILON = 1e-6f; - float tmin = 0; - float tmax = 1; - float d[2]; - d[0] = q[0] - p[0]; - d[1] = q[1] - p[1]; - - for (int i = 0; i < 2; i++) - { - if (fabsf(d[i]) < EPSILON) - { - // Ray is parallel to slab. No hit if origin not within slab - if (p[i] < bmin[i] || p[i] > bmax[i]) - return false; - } - else - { - // Compute intersection t value of ray with near and far plane of slab - float ood = 1.0f / d[i]; - float t1 = (bmin[i] - p[i]) * ood; - float t2 = (bmax[i] - p[i]) * ood; - if (t1 > t2) { float tmp = t1; t1 = t2; t2 = tmp; } - if (t1 > tmin) tmin = t1; - if (t2 < tmax) tmax = t2; - if (tmin > tmax) return false; - } - } - return true; + float tmin = 0; + float tmax = 1; + float d[2]; + d[ 0 ] = q[ 0 ] - p[ 0 ]; + d[ 1 ] = q[ 1 ] - p[ 1 ]; + + for( int i = 0; i < 2; i++ ) + { + if( fabsf( d[ i ] ) < EPSILON ) + { + // Ray is parallel to slab. No hit if origin not within slab + if( p[ i ] < bmin[ i ] || p[ i ] > bmax[ i ] ) + return false; + } + else + { + // Compute intersection t value of ray with near and far plane of slab + float ood = 1.0f / d[ i ]; + float t1 = ( bmin[ i ] - p[ i ] ) * ood; + float t2 = ( bmax[ i ] - p[ i ] ) * ood; + if( t1 > t2 ) + { + float tmp = t1; + t1 = t2; + t2 = tmp; + } + if( t1 > tmin ) + tmin = t1; + if( t2 < tmax ) + tmax = t2; + if( tmin > tmax ) + return false; + } + } + return true; } -int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, - float p[2], float q[2], - int* ids, const int maxIds) +int rcGetChunksOverlappingSegment( const rcChunkyTriMesh* cm, + float p[2], float q[2], + int* ids, const int maxIds ) { - // Traverse tree - int i = 0; - int n = 0; - while (i < cm->nnodes) - { - const rcChunkyTriMeshNode* node = &cm->nodes[i]; - const bool overlap = checkOverlapSegment(p, q, node->bmin, node->bmax); - const bool isLeafNode = node->i >= 0; - - if (isLeafNode && overlap) - { - if (n < maxIds) - { - ids[n] = i; - n++; - } - } - - if (overlap || isLeafNode) - i++; - else - { - const int escapeIndex = -node->i; - i += escapeIndex; - } - } - - return n; + // Traverse tree + int i = 0; + int n = 0; + while( i < cm->nnodes ) + { + const rcChunkyTriMeshNode* node = &cm->nodes[ i ]; + const bool overlap = checkOverlapSegment( p, q, node->bmin, node->bmax ); + const bool isLeafNode = node->i >= 0; + + if( isLeafNode && overlap ) + { + if( n < maxIds ) + { + ids[ n ] = i; + n++; + } + } + + if( overlap || isLeafNode ) + i++; + else + { + const int escapeIndex = -node->i; + i += escapeIndex; + } + } + + return n; } diff --git a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h index 65849799..24eb5890 100644 --- a/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h +++ b/src/tools/pcb_reader/nav/ext/ChunkyTriMesh.h @@ -21,39 +21,48 @@ struct rcChunkyTriMeshNode { - float bmin[2]; - float bmax[2]; - int i; - int n; + float bmin[ 2 ]; + float bmax[ 2 ]; + int i; + int n; }; struct rcChunkyTriMesh { - inline rcChunkyTriMesh() : nodes(0), nnodes(0), tris(0), ntris(0), maxTrisPerChunk(0) {}; - inline ~rcChunkyTriMesh() { delete [] nodes; delete [] tris; } + inline rcChunkyTriMesh() : + nodes( 0 ), nnodes( 0 ), tris( 0 ), ntris( 0 ), maxTrisPerChunk( 0 ) + { + }; - rcChunkyTriMeshNode* nodes; - int nnodes; - int* tris; - int ntris; - int maxTrisPerChunk; + inline ~rcChunkyTriMesh() + { + delete[] nodes; + delete[] tris; + } + + rcChunkyTriMeshNode* nodes; + int nnodes; + int* tris; + int ntris; + int maxTrisPerChunk; private: - // Explicitly disabled copy constructor and copy assignment operator. - rcChunkyTriMesh(const rcChunkyTriMesh&); - rcChunkyTriMesh& operator=(const rcChunkyTriMesh&); + // Explicitly disabled copy constructor and copy assignment operator. + rcChunkyTriMesh( const rcChunkyTriMesh& ); + + rcChunkyTriMesh& operator=( const rcChunkyTriMesh& ); }; /// Creates partitioned triangle mesh (AABB tree), /// where each node contains at max trisPerChunk triangles. -bool rcCreateChunkyTriMesh(const float* verts, const int* tris, int ntris, - int trisPerChunk, rcChunkyTriMesh* cm); +bool rcCreateChunkyTriMesh( const float* verts, const int* tris, int ntris, + int trisPerChunk, rcChunkyTriMesh* cm ); /// Returns the chunk indices which overlap the input rectable. -int rcGetChunksOverlappingRect(const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds); +int rcGetChunksOverlappingRect( const rcChunkyTriMesh* cm, float bmin[2], float bmax[2], int* ids, const int maxIds ); /// Returns the chunk indices which overlap the input segment. -int rcGetChunksOverlappingSegment(const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds); +int rcGetChunksOverlappingSegment( const rcChunkyTriMesh* cm, float p[2], float q[2], int* ids, const int maxIds ); #endif // CHUNKYTRIMESH_H diff --git a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp index 19047485..08c9c7f1 100644 --- a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp +++ b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.cpp @@ -20,154 +20,160 @@ #include #include #include + #define _USE_MATH_DEFINES + #include rcMeshLoaderObj::rcMeshLoaderObj() : - m_scale(1.0f), - m_verts(0), - m_tris(0), - m_normals(0), - m_vertCount(0), - m_triCount(0) + m_scale( 1.0f ), + m_verts( 0 ), + m_tris( 0 ), + m_normals( 0 ), + m_vertCount( 0 ), + m_triCount( 0 ) { } rcMeshLoaderObj::~rcMeshLoaderObj() { - delete [] m_verts; - delete [] m_normals; - delete [] m_tris; + delete[] m_verts; + delete[] m_normals; + delete[] m_tris; } -void rcMeshLoaderObj::addVertex(float x, float y, float z, int& cap) +void rcMeshLoaderObj::addVertex( float x, float y, float z, int& cap ) { - if (m_vertCount+1 > cap) + if( m_vertCount + 1 > cap ) { - cap = !cap ? 8 : cap*2; - float* nv = new float[cap*3]; - if (m_vertCount) - memcpy(nv, m_verts, m_vertCount*3*sizeof(float)); - delete [] m_verts; + cap = !cap ? 8 : cap * 2; + float* nv = new float[cap * 3]; + if( m_vertCount ) + memcpy( nv, m_verts, m_vertCount * 3 * sizeof( float ) ); + delete[] m_verts; m_verts = nv; } - float* dst = &m_verts[m_vertCount*3]; - *dst++ = x*m_scale; - *dst++ = y*m_scale; - *dst++ = z*m_scale; + float* dst = &m_verts[ m_vertCount * 3 ]; + *dst++ = x * m_scale; + *dst++ = y * m_scale; + *dst++ = z * m_scale; m_vertCount++; } -void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap) +void rcMeshLoaderObj::addTriangle( int a, int b, int c, int& cap ) { - if (m_triCount+1 > cap) + if( m_triCount + 1 > cap ) { - cap = !cap ? 8 : cap*2; - int* nv = new int[cap*3]; - if (m_triCount) - memcpy(nv, m_tris, m_triCount*3*sizeof(int)); - delete [] m_tris; + cap = !cap ? 8 : cap * 2; + int* nv = new int[cap * 3]; + if( m_triCount ) + memcpy( nv, m_tris, m_triCount * 3 * sizeof( int ) ); + delete[] m_tris; m_tris = nv; } - int* dst = &m_tris[m_triCount*3]; + int* dst = &m_tris[ m_triCount * 3 ]; *dst++ = a; *dst++ = b; *dst++ = c; m_triCount++; } -static char* parseRow(char* buf, char* bufEnd, char* row, int len) +static char* parseRow( char* buf, char* bufEnd, char* row, int len ) { bool start = true; bool done = false; int n = 0; - while (!done && buf < bufEnd) + while( !done && buf < bufEnd ) { char c = *buf; buf++; // multirow - switch (c) + switch( c ) { case '\\': break; case '\n': - if (start) break; + if( start ) + break; done = true; break; case '\r': break; case '\t': case ' ': - if (start) break; + if( start ) + break; // else falls through default: start = false; - row[n++] = c; - if (n >= len-1) + row[ n++ ] = c; + if( n >= len - 1 ) done = true; break; } } - row[n] = '\0'; + row[ n ] = '\0'; return buf; } -static int parseFace(char* row, int* data, int n, int vcnt) +static int parseFace( char* row, int* data, int n, int vcnt ) { int j = 0; - while (*row != '\0') + while( *row != '\0' ) { // Skip initial white space - while (*row != '\0' && (*row == ' ' || *row == '\t')) + while( *row != '\0' && ( *row == ' ' || *row == '\t' ) ) row++; char* s = row; // Find vertex delimiter and terminated the string there for conversion. - while (*row != '\0' && *row != ' ' && *row != '\t') + while( *row != '\0' && *row != ' ' && *row != '\t' ) { - if (*row == '/') *row = '\0'; + if( *row == '/' ) + *row = '\0'; row++; } - if (*s == '\0') + if( *s == '\0' ) continue; - int vi = atoi(s); - data[j++] = vi < 0 ? vi+vcnt : vi-1; - if (j >= n) return j; + int vi = atoi( s ); + data[ j++ ] = vi < 0 ? vi + vcnt : vi - 1; + if( j >= n ) + return j; } return j; } -bool rcMeshLoaderObj::load(const std::string& filename) +bool rcMeshLoaderObj::load( const std::string& filename ) { char* buf = 0; - FILE* fp = fopen(filename.c_str(), "rb"); - if (!fp) + FILE* fp = fopen( filename.c_str(), "rb" ); + if( !fp ) return false; - if (fseek(fp, 0, SEEK_END) != 0) + if( fseek( fp, 0, SEEK_END ) != 0 ) { - fclose(fp); + fclose( fp ); return false; } - long bufSize = ftell(fp); - if (bufSize < 0) + long bufSize = ftell( fp ); + if( bufSize < 0 ) { - fclose(fp); + fclose( fp ); return false; } - if (fseek(fp, 0, SEEK_SET) != 0) + if( fseek( fp, 0, SEEK_SET ) != 0 ) { - fclose(fp); + fclose( fp ); return false; } buf = new char[bufSize]; - if (!buf) + if( !buf ) { - fclose(fp); + fclose( fp ); return false; } - size_t readLen = fread(buf, bufSize, 1, fp); - fclose(fp); + size_t readLen = fread( buf, bufSize, 1, fp ); + fclose( fp ); - if (readLen != 1) + if( readLen != 1 ) { delete[] buf; return false; @@ -177,66 +183,67 @@ bool rcMeshLoaderObj::load(const std::string& filename) char* srcEnd = buf + bufSize; char row[512]; int face[32]; - float x,y,z; + float x, y, z; int nv; int vcap = 0; int tcap = 0; - while (src < srcEnd) + while( src < srcEnd ) { // Parse one row - row[0] = '\0'; - src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char)); + row[ 0 ] = '\0'; + src = parseRow( src, srcEnd, row, sizeof( row ) / sizeof( char ) ); // Skip comments - if (row[0] == '#') continue; - if (row[0] == 'v' && row[1] != 'n' && row[1] != 't') + if( row[ 0 ] == '#' ) + continue; + if( row[ 0 ] == 'v' && row[ 1 ] != 'n' && row[ 1 ] != 't' ) { // Vertex pos - sscanf(row+1, "%f %f %f", &x, &y, &z); - addVertex(x, y, z, vcap); + sscanf( row + 1, "%f %f %f", &x, &y, &z ); + addVertex( x, y, z, vcap ); } - if (row[0] == 'f') + if( row[ 0 ] == 'f' ) { // Faces - nv = parseFace(row+1, face, 32, m_vertCount); - for (int i = 2; i < nv; ++i) + nv = parseFace( row + 1, face, 32, m_vertCount ); + for( int i = 2; i < nv; ++i ) { - const int a = face[0]; - const int b = face[i-1]; - const int c = face[i]; - if (a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount) + const int a = face[ 0 ]; + const int b = face[ i - 1 ]; + const int c = face[ i ]; + if( a < 0 || a >= m_vertCount || b < 0 || b >= m_vertCount || c < 0 || c >= m_vertCount ) continue; - addTriangle(a, b, c, tcap); + addTriangle( a, b, c, tcap ); } } } - delete [] buf; + delete[] buf; // Calculate normals. - m_normals = new float[m_triCount*3]; - for (int i = 0; i < m_triCount*3; i += 3) + m_normals = new float[m_triCount * 3]; + for( int i = 0; i < m_triCount * 3; i += 3 ) { - const float* v0 = &m_verts[m_tris[i]*3]; - const float* v1 = &m_verts[m_tris[i+1]*3]; - const float* v2 = &m_verts[m_tris[i+2]*3]; + const float* v0 = &m_verts[ m_tris[ i ] * 3 ]; + const float* v1 = &m_verts[ m_tris[ i + 1 ] * 3 ]; + const float* v2 = &m_verts[ m_tris[ i + 2 ] * 3 ]; float e0[3], e1[3]; - for (int j = 0; j < 3; ++j) + for( int j = 0; j < 3; ++j ) { - e0[j] = v1[j] - v0[j]; - e1[j] = v2[j] - v0[j]; + e0[ j ] = v1[ j ] - v0[ j ]; + e1[ j ] = v2[ j ] - v0[ j ]; } - float* n = &m_normals[i]; - n[0] = e0[1]*e1[2] - e0[2]*e1[1]; - n[1] = e0[2]*e1[0] - e0[0]*e1[2]; - n[2] = e0[0]*e1[1] - e0[1]*e1[0]; - float d = sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); - if (d > 0) + float* n = &m_normals[ i ]; + n[ 0 ] = e0[ 1 ] * e1[ 2 ] - e0[ 2 ] * e1[ 1 ]; + n[ 1 ] = e0[ 2 ] * e1[ 0 ] - e0[ 0 ] * e1[ 2 ]; + n[ 2 ] = e0[ 0 ] * e1[ 1 ] - e0[ 1 ] * e1[ 0 ]; + float d = sqrtf( n[ 0 ] * n[ 0 ] + n[ 1 ] * n[ 1 ] + n[ 2 ] * n[ 2 ] ); + if( d > 0 ) { - d = 1.0f/d; - n[0] *= d; - n[1] *= d; - n[2] *= d; + d = 1.0f / d; + n[ 0 ] *= d; + n[ 1 ] *= d; + n[ 2 ] *= d; } } diff --git a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h index 075c04b3..1b3f9c56 100644 --- a/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h +++ b/src/tools/pcb_reader/nav/ext/MeshLoaderObj.h @@ -25,24 +25,50 @@ class rcMeshLoaderObj { public: rcMeshLoaderObj(); + ~rcMeshLoaderObj(); - bool load(const std::string& fileName); + bool load( const std::string& fileName ); - const float* getVerts() const { return m_verts; } - const float* getNormals() const { return m_normals; } - const int* getTris() const { return m_tris; } - int getVertCount() const { return m_vertCount; } - int getTriCount() const { return m_triCount; } - const std::string& getFileName() const { return m_filename; } + const float* getVerts() const + { + return m_verts; + } + + const float* getNormals() const + { + return m_normals; + } + + const int* getTris() const + { + return m_tris; + } + + int getVertCount() const + { + return m_vertCount; + } + + int getTriCount() const + { + return m_triCount; + } + + const std::string& getFileName() const + { + return m_filename; + } private: // Explicitly disabled copy constructor and copy assignment operator. - rcMeshLoaderObj(const rcMeshLoaderObj&); - rcMeshLoaderObj& operator=(const rcMeshLoaderObj&); + rcMeshLoaderObj( const rcMeshLoaderObj& ); - void addVertex(float x, float y, float z, int& cap); - void addTriangle(int a, int b, int c, int& cap); + rcMeshLoaderObj& operator=( const rcMeshLoaderObj& ); + + void addVertex( float x, float y, float z, int& cap ); + + void addTriangle( int a, int b, int c, int& cap ); std::string m_filename; float m_scale; From 6ae66c1edb267fdfb12395e78d0ca256d752c508 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 19:45:42 +1100 Subject: [PATCH 49/50] style is good now probably --- .../pcb_reader/nav/TiledNavmeshGenerator.cpp | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp index 0a347532..5b3be0ab 100644 --- a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp @@ -68,13 +68,14 @@ bool TiledNavmeshGenerator::init( const std::string& path ) rcCalcGridSize( m_meshBMin, m_meshBMax, m_cellSize, &gw, &gh ); auto ts = static_cast< uint32_t >( m_tileSize ); - const uint32_t tw = (gw + ts-1) / ts; - const uint32_t th = (gh + ts-1) / ts; + const uint32_t tw = ( gw + ts - 1 ) / ts; + const uint32_t th = ( gh + ts - 1 ) / ts; printf( "[Navmesh] - Tiles %d x %d\n", tw, th ); int tileBits = rcMin( ( int ) ilog2( nextPow2( tw * th ) ), 14 ); - if ( tileBits > 14 ) tileBits = 14; + if( tileBits > 14 ) + tileBits = 14; int polyBits = 22 - tileBits; m_maxTiles = 1 << tileBits; m_maxPolysPerTile = 1 << polyBits; @@ -248,8 +249,8 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, m_cfg.maxVertsPerPoly = static_cast< int >( m_vertsPerPoly ); m_cfg.tileSize = static_cast< int >( m_tileSize ); m_cfg.borderSize = m_cfg.walkableRadius + 3; // Reserve enough padding. - m_cfg.width = m_cfg.tileSize + m_cfg.borderSize*2; - m_cfg.height = m_cfg.tileSize + m_cfg.borderSize*2; + m_cfg.width = m_cfg.tileSize + m_cfg.borderSize * 2; + m_cfg.height = m_cfg.tileSize + m_cfg.borderSize * 2; m_cfg.detailSampleDist = m_detailSampleDist < 0.9f ? 0 : m_cellSize * m_detailSampleDist; m_cfg.detailSampleMaxError = m_cellHeight * m_detailSampleMaxError; @@ -311,7 +312,7 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, tbmax[ 0 ] = m_cfg.bmax[ 0 ]; tbmax[ 1 ] = m_cfg.bmax[ 2 ]; - int cid[ 512 ];// TODO: Make grow when returning too many items. + int cid[512];// TODO: Make grow when returning too many items. const int ncid = rcGetChunksOverlappingRect( m_chunkyMesh, tbmin, tbmax, cid, 512 ); if( !ncid ) @@ -333,7 +334,7 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, return nullptr; } - delete [] m_triareas; + delete[] m_triareas; m_triareas = nullptr; // Once all geometry is rasterized, we do initial pass of filtering to @@ -474,9 +475,9 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, return nullptr; } - if ( !rcBuildPolyMeshDetail( m_ctx, *m_pmesh, *m_chf, - m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, - *m_dmesh ) ) + if( !rcBuildPolyMeshDetail( m_ctx, *m_pmesh, *m_chf, + m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, + *m_dmesh ) ) { printf( "[Navmesh] buildNavigation: Could build polymesh detail." ); return nullptr; @@ -499,22 +500,22 @@ unsigned char* TiledNavmeshGenerator::buildTileMesh( const int tx, const int ty, } // Update poly flags from areas. - for (int i = 0; i < m_pmesh->npolys; ++i) + for( int i = 0; i < m_pmesh->npolys; ++i ) { - if (m_pmesh->areas[ i ] == RC_WALKABLE_AREA) + if( m_pmesh->areas[ i ] == RC_WALKABLE_AREA ) m_pmesh->areas[ i ] = SAMPLE_POLYAREA_GROUND; - if (m_pmesh->areas[ i ] == SAMPLE_POLYAREA_GROUND || + if( m_pmesh->areas[ i ] == SAMPLE_POLYAREA_GROUND || m_pmesh->areas[ i ] == SAMPLE_POLYAREA_GRASS || - m_pmesh->areas[ i ] == SAMPLE_POLYAREA_ROAD) + m_pmesh->areas[ i ] == SAMPLE_POLYAREA_ROAD ) { m_pmesh->flags[ i ] = SAMPLE_POLYFLAGS_WALK; } - else if (m_pmesh->areas[ i ] == SAMPLE_POLYAREA_WATER) + else if( m_pmesh->areas[ i ] == SAMPLE_POLYAREA_WATER ) { m_pmesh->flags[ i ] = SAMPLE_POLYFLAGS_SWIM; } - else if (m_pmesh->areas[ i ] == SAMPLE_POLYAREA_DOOR) + else if( m_pmesh->areas[ i ] == SAMPLE_POLYAREA_DOOR ) { m_pmesh->flags[ i ] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR; } From 68f9e7eae08fc499ea6607cd6f236f8bcff0715e Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sat, 26 Jan 2019 20:14:14 +1100 Subject: [PATCH 50/50] cleanup navmesh and navquery --- src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp index 5b3be0ab..3fa19668 100644 --- a/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp +++ b/src/tools/pcb_reader/nav/TiledNavmeshGenerator.cpp @@ -89,6 +89,11 @@ TiledNavmeshGenerator::~TiledNavmeshGenerator() { delete m_mesh; delete m_chunkyMesh; + + delete m_ctx; + + dtFreeNavMesh( m_navMesh ); + dtFreeNavMeshQuery( m_navQuery ); } void TiledNavmeshGenerator::saveNavmesh( const std::string& name )