From 9ee219ea42403466559b11da883f8ed5de45f2ec Mon Sep 17 00:00:00 2001 From: Jett <55758076+Jettford@users.noreply.github.com> Date: Tue, 2 Aug 2022 06:30:19 +0100 Subject: [PATCH] Move Navmesh code away from dPhysics (#701) --- CMakeLists.txt | 38 +-- dGame/dComponents/BaseCombatAIComponent.cpp | 2 +- dGame/dComponents/MovementAIComponent.cpp | 8 +- dGame/dComponents/PetComponent.cpp | 6 +- dGame/dUtilities/SlashCommandHandler.cpp | 2 +- dNavigation/CMakeLists.txt | 4 + dNavigation/DetourExtensions.h | 25 ++ dNavigation/dNavMesh.cpp | 228 +++++++++++++++++ dNavigation/dNavMesh.h | 38 +++ dPhysics/dpWorld.cpp | 269 ++------------------ dPhysics/dpWorld.h | 46 +--- dScripts/BaseEnemyMech.cpp | 2 +- dWorldServer/CMakeLists.txt | 4 +- 13 files changed, 349 insertions(+), 323 deletions(-) create mode 100644 dNavigation/CMakeLists.txt create mode 100644 dNavigation/DetourExtensions.h create mode 100644 dNavigation/dNavMesh.cpp create mode 100644 dNavigation/dNavMesh.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 65df9a35..cdc73d69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ string(REPLACE "\n" ";" variables ${variables}) foreach(variable ${variables}) # If the string contains a #, skip it if(NOT "${variable}" MATCHES "#") - + # Split the variable into name and value string(REPLACE "=" ";" variable ${variable}) @@ -43,7 +43,7 @@ set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT message(STATUS "Version: ${PROJECT_VERSION}") # Compiler flags: -# Disabled deprecated warnings as the MySQL includes have deprecated code in them. +# Disabled deprecated warnings as the MySQL includes have deprecated code in them. # Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent. # Disabled no-register # Disabled unknown pragmas because Linux doesn't understand Windows pragmas. @@ -118,17 +118,18 @@ endforeach() set(INCLUDED_DIRECTORIES "dCommon" "dChatFilter" - "dGame" - "dGame/dBehaviors" - "dGame/dComponents" - "dGame/dGameMessages" - "dGame/dInventory" - "dGame/dMission" - "dGame/dEntity" - "dGame/dUtilities" - "dPhysics" - "dZoneManager" - "dDatabase" + "dGame" + "dGame/dBehaviors" + "dGame/dComponents" + "dGame/dGameMessages" + "dGame/dInventory" + "dGame/dMission" + "dGame/dEntity" + "dGame/dUtilities" + "dPhysics" + "dNavigation" + "dZoneManager" + "dDatabase" "dDatabase/Tables" "dNet" "dScripts" @@ -192,7 +193,7 @@ file( file( GLOB HEADERS_DGAME LIST_DIRECTORIES false - ${PROJECT_SOURCE_DIR}/dGame/Entity.h + ${PROJECT_SOURCE_DIR}/dGame/Entity.h ${PROJECT_SOURCE_DIR}/dGame/dGameMessages/GameMessages.h ${PROJECT_SOURCE_DIR}/dGame/EntityManager.h ${PROJECT_SOURCE_DIR}/dScripts/CppScripts.h @@ -206,6 +207,7 @@ add_subdirectory(dNet) add_subdirectory(dScripts) # Add for dGame to use add_subdirectory(dGame) add_subdirectory(dZoneManager) +add_subdirectory(dNavigation) add_subdirectory(dPhysics) # Create a list of common libraries shared between all binaries @@ -230,18 +232,18 @@ add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the ot # Add our precompiled headers target_precompile_headers( - dGame PRIVATE + dGame PRIVATE ${HEADERS_DGAME} ) target_precompile_headers( - dZoneManager PRIVATE + dZoneManager PRIVATE ${HEADERS_DZONEMANAGER} ) # Need to specify to use the CXX compiler language here or else we get errors including . target_precompile_headers( - dDatabase PRIVATE + dDatabase PRIVATE "$<$:${HEADERS_DDATABASE}>" ) @@ -253,4 +255,4 @@ target_precompile_headers( target_precompile_headers( tinyxml2 PRIVATE "$<$:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>" -) \ No newline at end of file +) diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 2ac2cf04..c035a807 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -654,7 +654,7 @@ void BaseCombatAIComponent::Wander() { auto destination = m_StartPosition + delta; if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetHeightAtPoint(destination); + destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); } if (Vector3::DistanceSquared(destination, m_MovementAI->GetCurrentPosition()) < 2 * 2) { diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index 9c76b286..43231abf 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -196,7 +196,7 @@ NiPoint3 MovementAIComponent::ApproximateLocation() const { NiPoint3 approximation = NiPoint3(x, y, z); if (dpWorld::Instance().IsLoaded()) { - approximation.y = dpWorld::Instance().GetHeightAtPoint(approximation); + approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation); } return approximation; @@ -208,7 +208,7 @@ bool MovementAIComponent::Warp(const NiPoint3& point) { NiPoint3 destination = point; if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetHeightAtPoint(point); + destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); if (std::abs(destination.y - point.y) > 3) { return false; @@ -387,7 +387,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) { std::vector computedPath; if (dpWorld::Instance().IsLoaded()) { - computedPath = dpWorld::Instance().GetPath(GetCurrentPosition(), value, m_Info.wanderSpeed); + computedPath = dpWorld::Instance().GetNavMesh()->GetPath(GetCurrentPosition(), value, m_Info.wanderSpeed); } else { // Than take 10 points between the current position and the destination and make that the path @@ -416,7 +416,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) { // Simply path for (auto point : computedPath) { if (dpWorld::Instance().IsLoaded()) { - point.y = dpWorld::Instance().GetHeightAtPoint(point); + point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); } m_CurrentPath.push_back(point); diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 42aceaf3..7e637ab7 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -267,14 +267,14 @@ void PetComponent::OnUse(Entity* originator) { if (dpWorld::Instance().IsLoaded()) { NiPoint3 attempt = petPosition + forward * interactionDistance; - float y = dpWorld::Instance().GetHeightAtPoint(attempt); + float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); attempt = originatorPosition + forward * interactionDistance; - y = dpWorld::Instance().GetHeightAtPoint(attempt); + y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); interactionDistance -= 0.5f; } @@ -819,7 +819,7 @@ void PetComponent::Wander() { auto destination = m_StartPosition + delta; if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetHeightAtPoint(destination); + destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); } if (Vector3::DistanceSquared(destination, m_MovementAI->GetCurrentPosition()) < 2 * 2) { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 16cdddbb..6ef1632b 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -733,7 +733,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit auto control = static_cast(entity->GetComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS)); if (!control) return; - float y = dpWorld::Instance().GetHeightAtPoint(control->GetPosition()); + float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(control->GetPosition()); std::u16string msg = u"Navmesh height: " + (GeneralUtils::to_u16string(y)); ChatPackets::SendSystemMessage(sysAddr, msg); } diff --git a/dNavigation/CMakeLists.txt b/dNavigation/CMakeLists.txt new file mode 100644 index 00000000..beb498e3 --- /dev/null +++ b/dNavigation/CMakeLists.txt @@ -0,0 +1,4 @@ +set(DNAVIGATION_SOURCES "dNavMesh.cpp") + +add_library(dNavigation STATIC ${DNAVIGATION_SOURCES}) +target_link_libraries(dNavigation detour recast) diff --git a/dNavigation/DetourExtensions.h b/dNavigation/DetourExtensions.h new file mode 100644 index 00000000..55d3e29c --- /dev/null +++ b/dNavigation/DetourExtensions.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Recast.h" +#include "DetourCommon.h" +#include "DetourNavMesh.h" +#include "DetourNavMeshBuilder.h" +#include "DetourNavMeshQuery.h" + +static const int NAVMESHSET_MAGIC = 'M' << 24 | 'S' << 16 | 'E' << 8 | 'T'; // char[4] of '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 int MAX_POLYS = 256; +static const int MAX_SMOOTH = 2048; diff --git a/dNavigation/dNavMesh.cpp b/dNavigation/dNavMesh.cpp new file mode 100644 index 00000000..da471882 --- /dev/null +++ b/dNavigation/dNavMesh.cpp @@ -0,0 +1,228 @@ +#include "dNavMesh.h" + +#include "Game.h" +#include "dLogger.h" +#include "dPlatforms.h" +#include "NiPoint3.h" +#include "BinaryIO.h" + +dNavMesh::dNavMesh(uint32_t zoneId) { + m_ZoneId = zoneId; + + this->LoadNavmesh(); + + if (m_NavMesh) { + m_NavQuery = dtAllocNavMeshQuery(); + m_NavQuery->init(m_NavMesh, 2048); + + Game::logger->Log("dNavMesh", "Navmesh loaded successfully!"); + } else { + Game::logger->Log("dNavMesh", "Navmesh loading failed (This may be intended)."); + } +} + +dNavMesh::~dNavMesh() { + // Clean up Recast information + + rcFreeHeightField(m_Solid); + rcFreeCompactHeightfield(m_CHF); + rcFreeContourSet(m_CSet); + rcFreePolyMesh(m_PMesh); + rcFreePolyMeshDetail(m_PMDMesh); + dtFreeNavMesh(m_NavMesh); + dtFreeNavMeshQuery(m_NavQuery); + + if (m_Ctx) delete m_Ctx; + if (m_Triareas) delete[] m_Triareas; +} + + +void dNavMesh::LoadNavmesh() { + + std::string path = "./res/maps/navmeshes/" + std::to_string(m_ZoneId) + ".bin"; + + if (!BinaryIO::DoesFileExist(path)) { + return; + } + + FILE* fp; + +#ifdef _WIN32 + fopen_s(&fp, path.c_str(), "rb"); +#elif __APPLE__ + // macOS has 64bit file IO by default + fp = fopen(path.c_str(), "rb"); +#else + fp = fopen64(path.c_str(), "rb"); +#endif + + if (!fp) { + return; + } + + // Read header. + NavMeshSetHeader header; + size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp); + if (readLen != 1) { + fclose(fp); + return; + } + + if (header.magic != NAVMESHSET_MAGIC) { + fclose(fp); + return; + } + + if (header.version != NAVMESHSET_VERSION) { + fclose(fp); + return; + } + + dtNavMesh* mesh = dtAllocNavMesh(); + if (!mesh) { + fclose(fp); + return; + } + + dtStatus status = mesh->init(&header.params); + if (dtStatusFailed(status)) { + fclose(fp); + return; + } + + // Read tiles. + for (int i = 0; i < header.numTiles; ++i) { + NavMeshTileHeader tileHeader; + readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp); + if (readLen != 1) return; + + 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) return; + + mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); + } + + fclose(fp); + + m_NavMesh = mesh; +} + +float dNavMesh::GetHeightAtPoint(const NiPoint3& location) { + if (m_NavMesh == nullptr) { + return location.y; + } + + float toReturn = 0.0f; + float pos[3]; + pos[0] = location.x; + pos[1] = location.y; + pos[2] = location.z; + + dtPolyRef nearestRef = 0; + float polyPickExt[3] = { 32.0f, 32.0f, 32.0f }; + dtQueryFilter filter{}; + + m_NavQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, 0); + m_NavQuery->getPolyHeight(nearestRef, pos, &toReturn); + + if (toReturn == 0.0f) { + toReturn = location.y; + } + + return toReturn; +} + +std::vector dNavMesh::GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed) { + std::vector path; + + // Allows for non-navmesh maps (like new custom maps) to have "basic" enemies. + if (m_NavMesh == nullptr) { + // How many points to generate between start/end? + // Note: not actually 100% accurate due to rounding, but worst case it causes them to go a tiny bit faster + // than their speed value would normally allow at the end. + int numPoints = startPos.Distance(startPos, endPos) / speed; + + path.push_back(startPos); //insert the start pos + + // Linearly interpolate between these two points: + for (int i = 0; i < numPoints; i++) { + NiPoint3 newPoint{ startPos }; + + newPoint.x += speed; + newPoint.y = newPoint.y + (((endPos.y - startPos.y) / (endPos.x - startPos.x)) * (newPoint.x - startPos.x)); + + path.push_back(newPoint); + } + + path.push_back(endPos); //finally insert our end pos + + return path; + } + + float sPos[3]; + float ePos[3]; + sPos[0] = startPos.x; + sPos[1] = startPos.y; + sPos[2] = startPos.z; + + ePos[0] = endPos.x; + ePos[1] = endPos.y; + ePos[2] = endPos.z; + + dtStatus pathFindStatus; + dtPolyRef startRef; + dtPolyRef endRef; + float polyPickExt[3] = { 32.0f, 32.0f, 32.0f }; + dtQueryFilter filter{}; + + //Find our start poly + m_NavQuery->findNearestPoly(sPos, polyPickExt, &filter, &startRef, 0); + + //Find our end poly + m_NavQuery->findNearestPoly(ePos, polyPickExt, &filter, &endRef, 0); + + pathFindStatus = DT_FAILURE; + int m_nstraightPath = 0; + int m_npolys = 0; + dtPolyRef m_polys[MAX_POLYS]; + float m_straightPath[MAX_POLYS * 3]; + unsigned char m_straightPathFlags[MAX_POLYS]; + dtPolyRef m_straightPathPolys[MAX_POLYS]; + int m_straightPathOptions = 0; + + if (startRef && endRef) { + m_NavQuery->findPath(startRef, endRef, sPos, ePos, &filter, m_polys, &m_npolys, MAX_POLYS); + + if (m_npolys) { + // In case of partial path, make sure the end point is clamped to the last polygon. + float epos[3]; + dtVcopy(epos, ePos); + + if (m_polys[m_npolys - 1] != endRef) { + m_NavQuery->closestPointOnPoly(m_polys[m_npolys - 1], ePos, epos, 0); + } + + m_NavQuery->findStraightPath(sPos, epos, m_polys, m_npolys, + m_straightPath, m_straightPathFlags, + m_straightPathPolys, &m_nstraightPath, MAX_POLYS, m_straightPathOptions); + + // At this point we have our path. Copy it to the path store + int nIndex = 0; + for (int nVert = 0; nVert < m_nstraightPath; nVert++) { + NiPoint3 newPoint{ m_straightPath[nIndex++], m_straightPath[nIndex++], m_straightPath[nIndex++] }; + path.push_back(newPoint); + } + } + } else { + m_npolys = 0; + m_nstraightPath = 0; + } + + return path; +} diff --git a/dNavigation/dNavMesh.h b/dNavigation/dNavMesh.h new file mode 100644 index 00000000..09e9c895 --- /dev/null +++ b/dNavigation/dNavMesh.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "DetourExtensions.h" + +class NiPoint3; + +class dNavMesh { +public: + dNavMesh(uint32_t zoneId); + ~dNavMesh(); + + float GetHeightAtPoint(const NiPoint3& location); + std::vector GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f); +private: + void LoadNavmesh(); + + uint32_t m_ZoneId; + + uint8_t* m_Triareas = nullptr; + rcHeightfield* m_Solid = nullptr; + rcCompactHeightfield* m_CHF = nullptr; + rcContourSet* m_CSet = nullptr; + rcPolyMesh* m_PMesh = nullptr; + rcConfig m_Config; + rcPolyMeshDetail* m_PMDMesh = nullptr; + + class InputGeom* m_Geometry = nullptr; + class dtNavMesh* m_NavMesh = nullptr; + class dtNavMeshQuery* m_NavQuery = nullptr; + uint8_t m_NavMeshDrawFlags; + rcContext* m_Ctx = nullptr; +}; diff --git a/dPhysics/dpWorld.cpp b/dPhysics/dpWorld.cpp index 666171c0..0a7e0df0 100644 --- a/dPhysics/dpWorld.cpp +++ b/dPhysics/dpWorld.cpp @@ -21,12 +21,9 @@ void dpWorld::Initialize(unsigned int zoneID) { m_Grid = new dpGrid(phys_sp_tilecount, phys_sp_tilesize); } - Game::logger->Log("dpWorld", "Physics world initialized!"); + m_NavMesh = new dNavMesh(zoneID); - if (ShouldLoadNavmesh(zoneID)) { - if (LoadNavmeshByZoneID(zoneID)) Game::logger->Log("dpWorld", "Loaded navmesh!"); - else Game::logger->Log("dpWorld", "Error(s) occurred during navmesh load."); - } + Game::logger->Log("dpWorld", "Physics world initialized!"); } dpWorld::~dpWorld() { @@ -35,7 +32,9 @@ dpWorld::~dpWorld() { m_Grid = nullptr; } - RecastCleanup(); + m_NavMesh->~dNavMesh(); + delete m_NavMesh; + m_NavMesh = nullptr; } void dpWorld::StepWorld(float deltaTime) { @@ -98,256 +97,20 @@ void dpWorld::RemoveEntity(dpEntity* entity) { } } -void dpWorld::RecastCleanup() { - if (m_triareas) delete[] m_triareas; - m_triareas = 0; - - rcFreeHeightField(m_solid); - m_solid = 0; - rcFreeCompactHeightfield(m_chf); - m_chf = 0; - rcFreeContourSet(m_cset); - m_cset = 0; - rcFreePolyMesh(m_pmesh); - m_pmesh = 0; - rcFreePolyMeshDetail(m_dmesh); - m_dmesh = 0; - dtFreeNavMesh(m_navMesh); - m_navMesh = 0; - - dtFreeNavMeshQuery(m_navQuery); - m_navQuery = 0; - - if (m_ctx) delete m_ctx; -} - -bool dpWorld::LoadNavmeshByZoneID(unsigned int zoneID) { - std::string path = "./res/maps/navmeshes/" + std::to_string(zoneID) + ".bin"; - m_navMesh = LoadNavmesh(path.c_str()); - - if (m_navMesh) { m_navQuery = dtAllocNavMeshQuery(); m_navQuery->init(m_navMesh, 2048); } else return false; - - return true; -} - -dtNavMesh* dpWorld::LoadNavmesh(const char* path) { - FILE* fp; - -#ifdef _WIN32 - fopen_s(&fp, path, "rb"); -#elif __APPLE__ - // macOS has 64bit file IO by default - fp = fopen(path, "rb"); -#else - fp = fopen64(path, "rb"); -#endif - - if (!fp) { - return 0; - } - - // Read header. - NavMeshSetHeader header; - size_t readLen = fread(&header, sizeof(NavMeshSetHeader), 1, fp); - if (readLen != 1) { - fclose(fp); - return 0; - } - - if (header.magic != NAVMESHSET_MAGIC) { - fclose(fp); - return 0; - } - - if (header.version != NAVMESHSET_VERSION) { - fclose(fp); - return 0; - } - - dtNavMesh* mesh = dtAllocNavMesh(); - if (!mesh) { - fclose(fp); - return 0; - } - - dtStatus status = mesh->init(&header.params); - if (dtStatusFailed(status)) { - fclose(fp); - return 0; - } - - // Read tiles. - for (int i = 0; i < header.numTiles; ++i) { - NavMeshTileHeader tileHeader; - readLen = fread(&tileHeader, sizeof(tileHeader), 1, fp); - if (readLen != 1) - return 0; - - 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) - return 0; - - mesh->addTile(data, tileHeader.dataSize, DT_TILE_FREE_DATA, tileHeader.tileRef, 0); - } - - fclose(fp); - - return mesh; -} - -bool dpWorld::ShouldLoadNavmesh(unsigned int zoneID) { - return true; //We use default paths now. Might re-tool this function later. - - //TODO: Add to this list as the navmesh folder grows. - switch (zoneID) { - case 1100: - case 1150: - case 1151: - case 1200: - case 1201: - case 1300: - case 1400: - case 1603: - return true; - } - - return false; -} - bool dpWorld::ShouldUseSP(unsigned int zoneID) { - //TODO: Add to this list as needed. Only large maps should be added as tiling likely makes little difference on small maps. + // TODO: Add to this list as needed. + // Only large maps should be added as tiling likely makes little difference on small maps. + switch (zoneID) { - case 1100: //Avant Gardens - case 1200: //Nimbus Station - case 1300: //Gnarled Forest - case 1400: //Forbidden Valley - case 1800: //Crux Prime - case 1900: //Nexus Tower - case 2000: //Ninjago - return true; + case 1100: // Avant Gardens + case 1200: // Nimbus Station + case 1300: // Gnarled Forest + case 1400: // Forbidden Valley + case 1800: // Crux Prime + case 1900: // Nexus Tower + case 2000: // Ninjago + return true; } return false; } - -float dpWorld::GetHeightAtPoint(const NiPoint3& location) { - if (m_navMesh == nullptr) { - return location.y; - } - - float toReturn = 0.0f; - float pos[3]; - pos[0] = location.x; - pos[1] = location.y; - pos[2] = location.z; - - dtPolyRef nearestRef = 0; - float polyPickExt[3] = { 32.0f, 32.0f, 32.0f }; - dtQueryFilter filter{}; - - m_navQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, 0); - m_navQuery->getPolyHeight(nearestRef, pos, &toReturn); - - if (toReturn == 0.0f) { - toReturn = location.y; - } - - return toReturn; -} - -std::vector dpWorld::GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed) { - std::vector path; - - //allows for non-navmesh maps (like new custom maps) to have "basic" enemies. - if (m_navMesh == nullptr) { - //how many points to generate between start/end? - //note: not actually 100% accurate due to rounding, but worst case it causes them to go a tiny bit faster - //than their speed value would normally allow at the end. - int numPoints = startPos.Distance(startPos, endPos) / speed; - - path.push_back(startPos); //insert the start pos - - //Linearly interpolate between these two points: - for (int i = 0; i < numPoints; i++) { - NiPoint3 newPoint{ startPos }; - - newPoint.x += speed; - newPoint.y = newPoint.y + (((endPos.y - startPos.y) / (endPos.x - startPos.x)) * (newPoint.x - startPos.x)); - - path.push_back(newPoint); - } - - path.push_back(endPos); //finally insert our end pos - - return path; - } - - float sPos[3]; - float ePos[3]; - sPos[0] = startPos.x; - sPos[1] = startPos.y; - sPos[2] = startPos.z; - - ePos[0] = endPos.x; - ePos[1] = endPos.y; - ePos[2] = endPos.z; - - dtStatus pathFindStatus; - dtPolyRef startRef; - dtPolyRef endRef; - float polyPickExt[3] = { 32.0f, 32.0f, 32.0f }; - dtQueryFilter filter{}; - - //Find our start poly - m_navQuery->findNearestPoly(sPos, polyPickExt, &filter, &startRef, 0); - - //Find our end poly - m_navQuery->findNearestPoly(ePos, polyPickExt, &filter, &endRef, 0); - - pathFindStatus = DT_FAILURE; - int m_nstraightPath = 0; - int m_npolys = 0; - dtPolyRef m_polys[MAX_POLYS]; - float m_straightPath[MAX_POLYS * 3]; - unsigned char m_straightPathFlags[MAX_POLYS]; - dtPolyRef m_straightPathPolys[MAX_POLYS]; - int m_straightPathOptions = 0; - - if (startRef && endRef) { - m_navQuery->findPath(startRef, endRef, sPos, ePos, &filter, m_polys, &m_npolys, MAX_POLYS); - - if (m_npolys) { - // In case of partial path, make sure the end point is clamped to the last polygon. - float epos[3]; - dtVcopy(epos, ePos); - if (m_polys[m_npolys - 1] != endRef) - m_navQuery->closestPointOnPoly(m_polys[m_npolys - 1], ePos, epos, 0); - - m_navQuery->findStraightPath(sPos, epos, m_polys, m_npolys, - m_straightPath, m_straightPathFlags, - m_straightPathPolys, &m_nstraightPath, MAX_POLYS, m_straightPathOptions); - - // At this point we have our path. Copy it to the path store - int nIndex = 0; - for (int nVert = 0; nVert < m_nstraightPath; nVert++) { - /*m_PathStore[nPathSlot].PosX[nVert] = StraightPath[nIndex++]; - m_PathStore[nPathSlot].PosY[nVert] = StraightPath[nIndex++]; - m_PathStore[nPathSlot].PosZ[nVert] = StraightPath[nIndex++];*/ - - NiPoint3 newPoint{ m_straightPath[nIndex++], m_straightPath[nIndex++], m_straightPath[nIndex++] }; - path.push_back(newPoint); - } - } - } else { - m_npolys = 0; - m_nstraightPath = 0; - } - - return path; -} diff --git a/dPhysics/dpWorld.h b/dPhysics/dpWorld.h index 5362803f..9f13f7a9 100644 --- a/dPhysics/dpWorld.h +++ b/dPhysics/dpWorld.h @@ -1,6 +1,6 @@ #pragma once + #include "Singleton.h" -#include //Navmesh includes: #include "Recast.h" @@ -11,23 +11,7 @@ #include #include -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 int MAX_POLYS = 256; -static const int MAX_SMOOTH = 2048; +#include "dNavMesh.h" class NiPoint3; class dpEntity; @@ -38,22 +22,17 @@ public: void Initialize(unsigned int zoneID); ~dpWorld(); - void RecastCleanup(); - bool LoadNavmeshByZoneID(unsigned int zoneID); - dtNavMesh* LoadNavmesh(const char* path); - bool ShouldLoadNavmesh(unsigned int zoneID); bool ShouldUseSP(unsigned int zoneID); - - float GetHeightAtPoint(const NiPoint3& location); - std::vector GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f); - bool IsLoaded() const { return m_navMesh != nullptr; } + bool IsLoaded() const { return m_NavMesh != nullptr; } void StepWorld(float deltaTime); void AddEntity(dpEntity* entity); void RemoveEntity(dpEntity* entity); + dNavMesh* GetNavMesh() { return m_NavMesh; } + private: dpGrid* m_Grid; bool phys_spatial_partitioning = 1; @@ -63,18 +42,5 @@ private: std::vector m_StaticEntities; std::vector m_DynamicEntites; - //Navmesh stuffs: - unsigned char* m_triareas; - rcHeightfield* m_solid; - rcCompactHeightfield* m_chf; - rcContourSet* m_cset; - rcPolyMesh* m_pmesh; - rcConfig m_cfg; - rcPolyMeshDetail* m_dmesh; - - class InputGeom* m_geom; - class dtNavMesh* m_navMesh; - class dtNavMeshQuery* m_navQuery; - unsigned char m_navMeshDrawFlags; - rcContext* m_ctx; + dNavMesh* m_NavMesh; }; diff --git a/dScripts/BaseEnemyMech.cpp b/dScripts/BaseEnemyMech.cpp index 32dc38dd..8017be2c 100644 --- a/dScripts/BaseEnemyMech.cpp +++ b/dScripts/BaseEnemyMech.cpp @@ -17,7 +17,7 @@ void BaseEnemyMech::OnDie(Entity* self, Entity* killer) { ControllablePhysicsComponent* controlPhys = static_cast(self->GetComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS)); if (!controlPhys) return; - NiPoint3 newLoc = { controlPhys->GetPosition().x, dpWorld::Instance().GetHeightAtPoint(controlPhys->GetPosition()), controlPhys->GetPosition().z }; + NiPoint3 newLoc = { controlPhys->GetPosition().x, dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(controlPhys->GetPosition()), controlPhys->GetPosition().z }; EntityInfo info = EntityInfo(); std::vector cfg; diff --git a/dWorldServer/CMakeLists.txt b/dWorldServer/CMakeLists.txt index 3df808a2..05338960 100644 --- a/dWorldServer/CMakeLists.txt +++ b/dWorldServer/CMakeLists.txt @@ -1,6 +1,6 @@ set(DWORLDSERVER_SOURCES "ObjectIDManager.cpp" "PerformanceManager.cpp" "WorldServer.cpp") - + add_executable(WorldServer ${DWORLDSERVER_SOURCES}) -target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics detour recast tinyxml2) +target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics detour recast tinyxml2 dNavigation)