diff options
Diffstat (limited to 'src')
51 files changed, 1278 insertions, 1568 deletions
diff --git a/src/server/collision/Management/MMapFactory.cpp b/src/server/collision/Management/MMapFactory.cpp index 6aa71d77ed8..28a1caf14ba 100644 --- a/src/server/collision/Management/MMapFactory.cpp +++ b/src/server/collision/Management/MMapFactory.cpp @@ -25,14 +25,14 @@ namespace MMAP { // ######################## MMapFactory ######################## // our global singleton copy - MMapManager* g_MMapManager = NULL; + MMapManager* _manager = NULL; - MMapManager* MMapFactory::createOrGetMMapManager() + MMapManager* MMapFactory::CreateOrGetMMapManager() { - if (g_MMapManager == NULL) - g_MMapManager = new MMapManager(); + if (_manager == NULL) + _manager = new MMapManager(); - return g_MMapManager; + return _manager; } bool MMapFactory::IsPathfindingEnabled(uint32 mapId) @@ -41,12 +41,12 @@ namespace MMAP && !DisableMgr::IsDisabledFor(DISABLE_TYPE_MMAP, mapId, NULL, MMAP_DISABLE_PATHFINDING); } - void MMapFactory::clear() + void MMapFactory::Clear() { - if (g_MMapManager) + if (_manager) { - delete g_MMapManager; - g_MMapManager = NULL; + delete _manager; + _manager = NULL; } } }
\ No newline at end of file diff --git a/src/server/collision/Management/MMapFactory.h b/src/server/collision/Management/MMapFactory.h index 038d44a941c..c7e3283d22d 100644 --- a/src/server/collision/Management/MMapFactory.h +++ b/src/server/collision/Management/MMapFactory.h @@ -20,10 +20,6 @@ #define _MMAP_FACTORY_H #include "MMapManager.h" -#include "UnorderedMap.h" -#include "DetourAlloc.h" -#include "DetourNavMesh.h" -#include "DetourNavMeshQuery.h" namespace MMAP { @@ -40,8 +36,8 @@ namespace MMAP class MMapFactory { public: - static MMapManager* createOrGetMMapManager(); - static void clear(); + static MMapManager* CreateOrGetMMapManager(); + static void Clear(); static bool IsPathfindingEnabled(uint32 mapId); }; } diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp index 4e6b3bb6d10..d3bfe17aaa4 100644 --- a/src/server/collision/Management/MMapManager.cpp +++ b/src/server/collision/Management/MMapManager.cpp @@ -22,32 +22,29 @@ namespace MMAP { - // ######################## MMapManager ######################## MMapManager::~MMapManager() { - for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i) + for (MMapDataSet::iterator i = _loadedMaps.begin(); i != _loadedMaps.end(); ++i) delete i->second; - - // by now we should not have maps loaded - // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost! } - bool MMapManager::loadMapData(uint32 mapId) + bool MMapManager::LoadMap(uint32 mapId) { - // we already have this map loaded? - if (loadedMMaps.find(mapId) != loadedMMaps.end()) + // Do not load a map twice. + if (_loadedMaps.find(mapId) != _loadedMaps.end()) return true; // load and init dtNavMesh - read parameters from file - uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1; - char *fileName = new char[pathLen]; - snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId); + std::string basePath = sWorld->GetDataPath(); + uint32 pathLen = basePath.length() + strlen("mmaps/%03i.mmap") + 1; + char* fileName = new char[pathLen]; + snprintf(fileName, pathLen, (basePath + "mmaps/%03i.mmap").c_str(), mapId); FILE* file = fopen(fileName, "rb"); if (!file) { - TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName); - delete [] fileName; + TC_LOG_DEBUG("maps", "MMAP::LoadMap: Error: Could not open mmap file '%s'", fileName); + delete[] fileName; return false; } @@ -56,80 +53,75 @@ namespace MMAP fclose(file); if (count != 1) { - TC_LOG_DEBUG("maps", "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName); - delete [] fileName; + TC_LOG_DEBUG("maps", "MMAP::LoadMap: Error: Could not read params from file '%s'", fileName); + delete[] fileName; return false; } dtNavMesh* mesh = dtAllocNavMesh(); - ASSERT(mesh); if (dtStatusFailed(mesh->init(¶ms))) { dtFreeNavMesh(mesh); - TC_LOG_ERROR("maps", "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName); - delete [] fileName; + TC_LOG_ERROR("maps", "MMAP::LoadMap: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName); + delete[] fileName; return false; } - delete [] fileName; + delete[] fileName; - TC_LOG_INFO("maps", "MMAP:loadMapData: Loaded %03i.mmap", mapId); + TC_LOG_INFO("maps", "MMAP::LoadMap: Loaded %03i.mmap", mapId); // store inside our map list - MMapData* mmap_data = new MMapData(mesh); - mmap_data->mmapLoadedTiles.clear(); + MMapData* mmapData = new MMapData(mesh); + mmapData->_loadedTiles.clear(); - loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data)); + _loadedMaps.insert(std::pair<uint32, MMapData*>(mapId, mmapData)); return true; } - uint32 MMapManager::packTileID(int32 x, int32 y) - { - return uint32(x << 16 | y); - } - - bool MMapManager::loadMap(const std::string& /*basePath*/, uint32 mapId, int32 x, int32 y) + bool MMapManager::LoadMapTile(uint32 mapId, int32 x, int32 y) { // make sure the mmap is loaded and ready to load tiles - if (!loadMapData(mapId)) + if (!LoadMap(mapId)) return false; // get this mmap data - MMapData* mmap = loadedMMaps[mapId]; + MMapData* mmap = _loadedMaps[mapId]; ASSERT(mmap->navMesh); - // check if we already have this tile loaded - uint32 packedGridPos = packTileID(x, y); - if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end()) + // Check if we already have this tile loaded + uint32 pos = PackTileId(x, y); + if (mmap->_loadedTiles.find(pos) != mmap->_loadedTiles.end()) return false; - // load this tile :: mmaps/MMMXXYY.mmtile - uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1; - char *fileName = new char[pathLen]; + std::string basePath = sWorld->GetDataPath(); + uint32 pathLen = basePath.length() + strlen("mmaps/%03i%02i%02i.mmtile") + 1; + char* fileName = new char[pathLen]; - snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y); + snprintf(fileName, pathLen, (basePath + "mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y); FILE* file = fopen(fileName, "rb"); if (!file) { - TC_LOG_DEBUG("maps", "MMAP:loadMap: Could not open mmtile file '%s'", fileName); - delete [] fileName; + TC_LOG_DEBUG("maps", "MMAP::LoadMapTile: Could not open mmtile file '%s'", fileName); + delete[] fileName; return false; } - delete [] fileName; + + delete[] fileName; // read header MmapTileHeader fileHeader; if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC) { - TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP::LoadMapTile: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y); fclose(file); return false; } if (fileHeader.mmapVersion != MMAP_VERSION) { - TC_LOG_ERROR("maps", "MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i", + TC_LOG_ERROR("maps", "MMAP::LoadMapTile: %03u%02i%02i.mmtile was built with generator v%i, expected v%i", mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION); fclose(file); return false; @@ -139,9 +131,9 @@ namespace MMAP ASSERT(data); size_t result = fread(data, fileHeader.size, 1, file); - if (!result) + if (result != 1) { - TC_LOG_ERROR("maps", "MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP::LoadMapTile: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y); fclose(file); return false; } @@ -154,14 +146,14 @@ namespace MMAP // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed if (dtStatusSucceed(mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))) { - mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef)); - ++loadedTiles; - TC_LOG_INFO("maps", "MMAP:loadMap: Loaded mmtile %03i[%02i, %02i] into %03i[%02i, %02i]", mapId, x, y, mapId, header->x, header->y); + mmap->_loadedTiles.insert(std::pair<uint32, dtTileRef>(pos, tileRef)); + ++_loadedTiles; + TC_LOG_INFO("maps", "MMAP::LoadMapTile: Loaded mmtile %03i[%02i, %02i] into %03i[%02i, %02i]", mapId, x, y, mapId, header->x, header->y); return true; } else { - TC_LOG_ERROR("maps", "MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP::LoadMapTile: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y); dtFree(data); return false; } @@ -169,28 +161,26 @@ namespace MMAP return false; } - bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y) + bool MMapManager::UnloadMapTile(uint32 mapId, int32 x, int32 y) { - // check if we have this map loaded - if (loadedMMaps.find(mapId) == loadedMMaps.end()) + // Do not attempt to remove tiles from a not-loaded map + if (_loadedMaps.find(mapId) == _loadedMaps.end()) { - // file may not exist, therefore not loaded - TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y); + TC_LOG_DEBUG("maps", "MMAP::UnloadMapTile: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y); return false; } - MMapData* mmap = loadedMMaps[mapId]; + MMapData* mmap = _loadedMaps[mapId]; // check if we have this tile loaded - uint32 packedGridPos = packTileID(x, y); - if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end()) + uint32 pos = PackTileId(x, y); + if (mmap->_loadedTiles.find(pos) == mmap->_loadedTiles.end()) { - // file may not exist, therefore not loaded - TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y); + TC_LOG_DEBUG("maps", "MMAP::UnloadMapTile: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y); return false; } - dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos]; + dtTileRef tileRef = mmap->_loadedTiles[pos]; // unload, and mark as non loaded if (dtStatusFailed(mmap->navMesh->removeTile(tileRef, NULL, NULL))) @@ -198,107 +188,106 @@ namespace MMAP // this is technically a memory leak // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used // we cannot recover from this error - assert out - TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP::UnloadMapTile: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y); ASSERT(false); } else { - mmap->mmapLoadedTiles.erase(packedGridPos); - --loadedTiles; - TC_LOG_INFO("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); + mmap->_loadedTiles.erase(pos); + --_loadedTiles; + TC_LOG_INFO("maps", "MMAP::UnloadMapTile: Unloaded mmtile [%02i, %02i] from %03i", x, y, mapId); return true; } return false; } - bool MMapManager::unloadMap(uint32 mapId) + bool MMapManager::UnloadMap(uint32 mapId) { - if (loadedMMaps.find(mapId) == loadedMMaps.end()) + if (_loadedMaps.find(mapId) == _loadedMaps.end()) { // file may not exist, therefore not loaded - TC_LOG_DEBUG("maps", "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId); + TC_LOG_DEBUG("maps", "MMAP::UnloadMap: Asked to unload not loaded navmesh map %03u", mapId); return false; } // unload all tiles from given map - MMapData* mmap = loadedMMaps[mapId]; - for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i) + MMapData* mmap = _loadedMaps[mapId]; + for (MMapTileSet::iterator i = mmap->_loadedTiles.begin(); i != mmap->_loadedTiles.end(); ++i) { uint32 x = (i->first >> 16); uint32 y = (i->first & 0x0000FFFF); if (dtStatusFailed(mmap->navMesh->removeTile(i->second, NULL, NULL))) - TC_LOG_ERROR("maps", "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y); + TC_LOG_ERROR("maps", "MMAP::UnloadMap: Could not unload %03u%02u%02u.mmtile from navmesh", mapId, x, y); else { - --loadedTiles; - TC_LOG_INFO("maps", "MMAP:unloadMap: Unloaded mmtile %03i[%02i, %02i] from %03i", mapId, x, y, mapId); + --_loadedTiles; + TC_LOG_INFO("maps", "MMAP::UnloadMap: Unloaded mmtile [%02u, %02u] from %03u", mapId, x, y, mapId); } } delete mmap; - loadedMMaps.erase(mapId); - TC_LOG_INFO("maps", "MMAP:unloadMap: Unloaded %03i.mmap", mapId); + _loadedMaps.erase(mapId); + TC_LOG_INFO("maps", "MMAP::UnloadMap: Unloaded %03u.mmap", mapId); return true; } - bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId) + bool MMapManager::UnloadMapInstance(uint32 mapId, uint32 instanceId) { // check if we have this map loaded - if (loadedMMaps.find(mapId) == loadedMMaps.end()) + if (_loadedMaps.find(mapId) == _loadedMaps.end()) { // file may not exist, therefore not loaded - TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId); + TC_LOG_DEBUG("maps", "MMAP::UnloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId); return false; } - MMapData* mmap = loadedMMaps[mapId]; - if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end()) + MMapData* mmap = _loadedMaps[mapId]; + if (mmap->_navMeshQueries.find(instanceId) == mmap->_navMeshQueries.end()) { - TC_LOG_DEBUG("maps", "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId); + TC_LOG_DEBUG("maps", "MMAP::UnloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId); return false; } - dtNavMeshQuery* query = mmap->navMeshQueries[instanceId]; + dtNavMeshQuery* query = mmap->_navMeshQueries[instanceId]; dtFreeNavMeshQuery(query); - mmap->navMeshQueries.erase(instanceId); - TC_LOG_INFO("maps", "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId); + mmap->_navMeshQueries.erase(instanceId); + TC_LOG_INFO("maps", "MMAP::UnloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId); return true; } dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId) { - if (loadedMMaps.find(mapId) == loadedMMaps.end()) + if (_loadedMaps.find(mapId) == _loadedMaps.end()) return NULL; - return loadedMMaps[mapId]->navMesh; + return _loadedMaps[mapId]->navMesh; } dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId) { - if (loadedMMaps.find(mapId) == loadedMMaps.end()) + if (_loadedMaps.find(mapId) == _loadedMaps.end()) return NULL; - MMapData* mmap = loadedMMaps[mapId]; - if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end()) + MMapData* mmap = _loadedMaps[mapId]; + if (mmap->_navMeshQueries.find(instanceId) == mmap->_navMeshQueries.end()) { // allocate mesh query dtNavMeshQuery* query = dtAllocNavMeshQuery(); - ASSERT(query); - if (dtStatusFailed(query->init(mmap->navMesh, 1024))) + if (dtStatusFailed(query->init(mmap->navMesh, 2048))) { dtFreeNavMeshQuery(query); - TC_LOG_ERROR("maps", "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId); + TC_LOG_ERROR("maps", "MMAP::GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId); return NULL; } TC_LOG_INFO("maps", "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId); - mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query)); + mmap->_navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query)); } - return mmap->navMeshQueries[instanceId]; + return mmap->_navMeshQueries[instanceId]; } } diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h index 9a04d638805..9d90d1bb2af 100644 --- a/src/server/collision/Management/MMapManager.h +++ b/src/server/collision/Management/MMapManager.h @@ -36,7 +36,7 @@ namespace MMAP MMapData(dtNavMesh* mesh) : navMesh(mesh) { } ~MMapData() { - for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i) + for (NavMeshQuerySet::iterator i = _navMeshQueries.begin(); i != _navMeshQueries.end(); ++i) dtFreeNavMeshQuery(i->second); if (navMesh) @@ -46,8 +46,8 @@ namespace MMAP dtNavMesh* navMesh; // we have to use single dtNavMeshQuery for every instance, since those are not thread safe - NavMeshQuerySet navMeshQueries; // instanceId to query - MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile] + NavMeshQuerySet _navMeshQueries; // instanceId to query + MMapTileSet _loadedTiles; // maps [map grid coords] to [dtTile] }; @@ -58,26 +58,27 @@ namespace MMAP class MMapManager { public: - MMapManager() : loadedTiles(0) { } + MMapManager() : _loadedTiles(0) {} + ~MMapManager(); - bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y); - bool unloadMap(uint32 mapId, int32 x, int32 y); - bool unloadMap(uint32 mapId); - bool unloadMapInstance(uint32 mapId, uint32 instanceId); + bool LoadMapTile(uint32 mapId, int32 x, int32 y); + bool UnloadMapTile(uint32 mapId, int32 x, int32 y); + bool UnloadMap(uint32 mapId); + bool UnloadMapInstance(uint32 mapId, uint32 instanceId); // the returned [dtNavMeshQuery const*] is NOT threadsafe dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId); dtNavMesh const* GetNavMesh(uint32 mapId); - uint32 getLoadedTilesCount() const { return loadedTiles; } - uint32 getLoadedMapsCount() const { return loadedMMaps.size(); } + uint32 GetLoadedTilesCount() const { return _loadedTiles; } + uint32 GetLoadedMapsCount() const { return _loadedMaps.size(); } private: - bool loadMapData(uint32 mapId); - uint32 packTileID(int32 x, int32 y); + bool LoadMap(uint32 mapId); + uint32 PackTileId(int32 x, int32 y) { return uint32(x << 16 | y); } - MMapDataSet loadedMMaps; - uint32 loadedTiles; + MMapDataSet _loadedMaps; + uint32 _loadedTiles; }; } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 3723fc4ab45..9e4ef152ed3 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -66,7 +66,9 @@ Map::~Map() if (!m_scriptSchedule.empty()) sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size()); - MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId); + MMAP::MMapManager* manager = MMAP::MMapFactory::CreateOrGetMMapManager(); + manager->UnloadMapInstance(GetId(), i_InstanceId); // Delete the dtNavMeshQuery + manager->UnloadMap(GetId()); // Unload the loaded tiles and delete the dtNavMesh } bool Map::ExistMap(uint32 mapid, int gx, int gy) @@ -119,12 +121,16 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy) void Map::LoadMMap(int gx, int gy) { - bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy); + bool mmapLoadResult = false; + if (GetEntry()->Instanceable()) + mmapLoadResult = MMAP::MMapFactory::CreateOrGetMMapManager()->LoadMapTile(GetId(), 0, 0); // Ignore the tile entry for instances, as they only have 1 tile. + else + mmapLoadResult = MMAP::MMapFactory::CreateOrGetMMapManager()->LoadMapTile(GetId(), gx, gy); if (mmapLoadResult) - TC_LOG_INFO("maps", "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); + TC_LOG_INFO("maps", "MMAP loaded name: %s, id: %d, x: %d, y: %d", GetMapName(), GetId(), gx, gy); else - TC_LOG_INFO("maps", "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); + TC_LOG_INFO("maps", "Could not load MMAP name: %s, id: %d, x: %d, y: %d", GetMapName(), GetId(), gx, gy); } void Map::LoadVMap(int gx, int gy) @@ -226,9 +232,9 @@ i_gridExpiry(expiry), i_scriptLock(false) { m_parentMap = (_parent ? _parent : this); - for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx) + for (unsigned int idx = 0; idx < MAX_NUMBER_OF_GRIDS; ++idx) { - for (unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j) + for (unsigned int j = 0; j < MAX_NUMBER_OF_GRIDS; ++j) { //z code GridMaps[idx][j] =NULL; @@ -1336,7 +1342,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) delete GridMaps[gx][gy]; } VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy); - MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy); + MMAP::MMapFactory::CreateOrGetMMapManager()->UnloadMapTile(GetId(), gx, gy); } else ((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy)); diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp index 667f94fb53a..d191293ecaf 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -260,7 +260,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD)) { VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId()); - MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId()); + MMAP::MMapFactory::CreateOrGetMMapManager()->UnloadMap(itr->second->GetId()); // in that case, unload grids of the base map, too // so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded Map::UnloadAll(); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index cb5156b2b64..20f8efe5dfa 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -3542,7 +3542,7 @@ enum PartyResult }; const uint32 MMAP_MAGIC = 0x4d4d4150; // 'MMAP' -#define MMAP_VERSION 4 +#define MMAP_VERSION 5 struct MmapTileHeader { diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp index ef24b112253..63b1cf283a4 100755 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp @@ -86,7 +86,6 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, uint32 diff) unit->MovePositionToFirstCollision(pos, dest, 0.0f); PathGenerator path(unit); - path.SetPathLengthLimit(30.0f); bool result = path.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ); if (!result || (path.GetPathType() & PATHFIND_NOPATH)) { diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index f78411fc547..1c65499fe50 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -44,7 +44,6 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T* owner) _getPoint(owner, x, y, z); PathGenerator path(owner); - path.SetPathLengthLimit(30.0f); bool result = path.CalculatePath(x, y, z); if (!result || (path.GetPathType() & PATHFIND_NOPATH)) { diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index 91ad6d2b676..3e32be56fb0 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -1,4 +1,5 @@ /* + * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify @@ -26,21 +27,19 @@ #include "DetourCommon.h" #include "DetourNavMeshQuery.h" +float PathGenerator::MinWallDistance = 2.5f; + ////////////////// PathGenerator ////////////////// PathGenerator::PathGenerator(const Unit* owner) : - _polyLength(0), _type(PATHFIND_BLANK), _useStraightPath(false), - _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH), - _endPosition(G3D::Vector3::zero()), _sourceUnit(owner), _navMesh(NULL), - _navMeshQuery(NULL) + _type(PATHFIND_BLANK), _endPosition(G3D::Vector3::zero()), + _sourceUnit(owner), _navMesh(NULL), _navMeshQuery(NULL) { - memset(_pathPolyRefs, 0, sizeof(_pathPolyRefs)); - - TC_LOG_DEBUG("maps", "++ PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow()); + TC_LOG_DEBUG("maps", "PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow()); uint32 mapId = _sourceUnit->GetMapId(); if (MMAP::MMapFactory::IsPathfindingEnabled(mapId)) { - MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager(); + MMAP::MMapManager* mmap = MMAP::MMapFactory::CreateOrGetMMapManager(); _navMesh = mmap->GetNavMesh(mapId); _navMeshQuery = mmap->GetNavMeshQuery(mapId, _sourceUnit->GetInstanceId()); } @@ -50,7 +49,7 @@ PathGenerator::PathGenerator(const Unit* owner) : PathGenerator::~PathGenerator() { - TC_LOG_DEBUG("maps", "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow()); + TC_LOG_DEBUG("maps", "PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow()); } bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest) @@ -67,463 +66,90 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo G3D::Vector3 start(x, y, z); SetStartPosition(start); - _forceDestination = forceDest; - - TC_LOG_DEBUG("maps", "++ PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow()); + TC_LOG_DEBUG("maps", "PathGenerator::CalculatePath() for %u \n", _sourceUnit->GetGUIDLow()); // make sure navMesh works - we can run on map w/o mmap // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?) - if (!_navMesh || !_navMeshQuery || _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) || - !HaveTile(start) || !HaveTile(dest)) + if (!_navMesh || !_navMeshQuery || _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING)) { - BuildShortcut(); _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return true; } UpdateFilter(); - BuildPolyPath(start, dest); - return true; -} - -dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* point, float* distance) const -{ - if (!polyPath || !polyPathSize) - return INVALID_POLYREF; + float startPos[3]; + startPos[0] = -y; + startPos[1] = z; + startPos[2] = -x; - dtPolyRef nearestPoly = INVALID_POLYREF; - float minDist2d = FLT_MAX; - float minDist3d = 0.0f; + float endPos[3]; + endPos[0] = -destY; + endPos[1] = destZ; + endPos[2] = -destX; - for (uint32 i = 0; i < polyPathSize; ++i) - { - float closestPoint[VERTEX_SIZE]; - if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint))) - continue; - - float d = dtVdist2DSqr(point, closestPoint); - if (d < minDist2d) - { - minDist2d = d; - nearestPoly = polyPath[i]; - minDist3d = dtVdistSqr(point, closestPoint); - } - - if (minDist2d < 1.0f) // shortcut out - close enough for us - break; - } + float polyPickExt[3]; + polyPickExt[0] = 2.5f; + polyPickExt[1] = 2.5f; + polyPickExt[2] = 2.5f; - if (distance) - *distance = dtSqrt(minDist3d); - - return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF; -} - -dtPolyRef PathGenerator::GetPolyByLocation(float const* point, float* distance) const -{ - // first we check the current path - // if the current path doesn't contain the current poly, - // we need to use the expensive navMesh.findNearestPoly - dtPolyRef polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, distance); - if (polyRef != INVALID_POLYREF) - return polyRef; - - // we don't have it in our old path - // try to get it by findNearestPoly() - // first try with low search box - float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area - float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f}; - if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) - { - *distance = dtVdist(closestPoint, point); - return polyRef; - } - - // still nothing .. - // try with bigger search box - // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly) - extents[1] = 50.0f; - - if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) - { - *distance = dtVdist(closestPoint, point); - return polyRef; - } - - return INVALID_POLYREF; -} + // + dtPolyRef startRef; + dtPolyRef endRef; -void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos) -{ - // *** getting start/end poly logic *** - - float distToStartPoly, distToEndPoly; - float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x}; - float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x}; - - dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly); - dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly); - - // we have a hole in our mesh - // make shortcut path and mark it as NOPATH ( with flying and swimming exception ) - // its up to caller how he will use this info - if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); - BuildShortcut(); - bool path = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly(); - - bool waterPath = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanSwim(); - if (waterPath) - { - // Check both start and end points, if they're both in water, then we can *safely* let the creature move - for (uint32 i = 0; i < _pathPoints.size(); ++i) - { - ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL); - // One of the points is not in the water, cancel movement. - if (status == LIQUID_MAP_NO_WATER) - { - waterPath = false; - break; - } - } - } - - _type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; - return; - } - - // we may need a better number here - bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); - if (farFromPoly) - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); - - bool buildShotrcut = false; - if (_sourceUnit->GetTypeId() == TYPEID_UNIT) - { - Creature* owner = (Creature*)_sourceUnit; - - G3D::Vector3 const& p = (distToStartPoly > 7.0f) ? startPos : endPos; - if (_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z)) - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: underWater case\n"); - if (owner->CanSwim()) - buildShotrcut = true; - } - else - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: flying case\n"); - if (owner->CanFly()) - buildShotrcut = true; - } - } - - if (buildShotrcut) - { - BuildShortcut(); - _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); - return; - } - else - { - float closestPoint[VERTEX_SIZE]; - // we may want to use closestPointOnPolyBoundary instead - if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint))) - { - dtVcopy(endPoint, closestPoint); - SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1])); - } - - _type = PATHFIND_INCOMPLETE; - } - } - - // *** poly path generating logic *** - - // start and end are on same polygon - // just need to move in straight line - if (startPoly == endPoly) - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPoly == endPoly)\n"); - - BuildShortcut(); - - _pathPolyRefs[0] = startPoly; - _polyLength = 1; - - _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: path type %d\n", _type); - return; - } - - // look for startPoly/endPoly in current path - /// @todo we can merge it with getPathPolyByPosition() loop - bool startPolyFound = false; - bool endPolyFound = false; - uint32 pathStartIndex = 0; - uint32 pathEndIndex = 0; - - if (_polyLength) - { - for (; pathStartIndex < _polyLength; ++pathStartIndex) - { - // here to carch few bugs - ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF); - - if (_pathPolyRefs[pathStartIndex] == startPoly) - { - startPolyFound = true; - break; - } - } - - for (pathEndIndex = _polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) - if (_pathPolyRefs[pathEndIndex] == endPoly) - { - endPolyFound = true; - break; - } - } - - if (startPolyFound && endPolyFound) - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); - - // we moved along the path and the target did not move out of our old poly-path - // our path is a simple subpath case, we have all the data we need - // just "cut" it out - - _polyLength = pathEndIndex - pathStartIndex + 1; - memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef)); - } - else if (startPolyFound && !endPolyFound) - { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); - - // we are moving on the old path but target moved out - // so we have atleast part of poly-path ready - - _polyLength -= pathStartIndex; - - // try to adjust the suffix of the path instead of recalculating entire length - // at given interval the target cannot get too far from its last location - // thus we have less poly to cover - // sub-path of optimal path is optimal - - // take ~80% of the original length - /// @todo play with the values here - uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f); - memmove(_pathPolyRefs, _pathPolyRefs+pathStartIndex, prefixPolyLength * sizeof(dtPolyRef)); - - dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength-1]; - - // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data - float suffixEndPoint[VERTEX_SIZE]; - if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))) - { - // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that - // try to recover by using prev polyref - --prefixPolyLength; - suffixStartPoly = _pathPolyRefs[prefixPolyLength-1]; - if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))) - { - // suffixStartPoly is still invalid, error state - BuildShortcut(); - _type = PATHFIND_NOPATH; - return; - } - } - - // generate suffix - uint32 suffixPolyLength = 0; - dtStatus dtResult = _navMeshQuery->findPath( - suffixStartPoly, // start polygon - endPoly, // end polygon - suffixEndPoint, // start position - endPoint, // end position - &_filter, // polygon search filter - _pathPolyRefs + prefixPolyLength - 1, // [out] path - (int*)&suffixPolyLength, - MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path - - if (!suffixPolyLength || dtStatusFailed(dtResult)) - { - // this is probably an error state, but we'll leave it - // and hopefully recover on the next Update - // we still need to copy our preffix - TC_LOG_ERROR("maps", "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow()); - } + float nearestPt[3]; - TC_LOG_DEBUG("maps", "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength); + _navMeshQuery->findNearestPoly(startPos, polyPickExt, &_filter, &startRef, nearestPt); + _navMeshQuery->findNearestPoly(endPos, polyPickExt, &_filter, &endRef, nearestPt); - // new path = prefix + suffix - overlap - _polyLength = prefixPolyLength + suffixPolyLength - 1; - } - else + if (!startRef || !endRef) { - TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); - - // either we have no path at all -> first run - // or something went really wrong -> we aren't moving along the path to the target - // just generate new path - - // free and invalidate old path data - Clear(); - - dtStatus dtResult = _navMeshQuery->findPath( - startPoly, // start polygon - endPoly, // end polygon - startPoint, // start position - endPoint, // end position - &_filter, // polygon search filter - _pathPolyRefs, // [out] path - (int*)&_polyLength, - MAX_PATH_LENGTH); // max number of polygons in output path - - if (!_polyLength || dtStatusFailed(dtResult)) - { - // only happens if we passed bad data to findPath(), or navmesh is messed up - TC_LOG_ERROR("maps", "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow()); - BuildShortcut(); - _type = PATHFIND_NOPATH; - return; - } + TC_LOG_DEBUG("maps", "PathGenerator::CalculatePath() for %u no polygons found for start and end locations\n", _sourceUnit->GetGUIDLow()); + _type = PATHFIND_NOPATH; + return false; } - // by now we know what type of path we can get - if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE)) - _type = PATHFIND_NORMAL; - else - _type = PATHFIND_INCOMPLETE; - - // generate the point-path out of our up-to-date poly-path - BuildPointPath(startPoint, endPoint); -} - -void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoint) -{ - float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE]; - uint32 pointCount = 0; - dtStatus dtResult = DT_FAILURE; - if (_useStraightPath) - { - dtResult = _navMeshQuery->findStraightPath( - startPoint, // start position - endPoint, // end position - _pathPolyRefs, // current path - _polyLength, // lenth of current path - pathPoints, // [out] path corner points - NULL, // [out] flags - NULL, // [out] shortened path - (int*)&pointCount, - _pointPathLimit); // maximum number of points/polygons to use - } - else - { - dtResult = FindSmoothPath( - startPoint, // start position - endPoint, // end position - _pathPolyRefs, // current path - _polyLength, // length of current path - pathPoints, // [out] path corner points - (int*)&pointCount, - _pointPathLimit); // maximum number of points - } + int hops; + dtPolyRef* hopBuffer = new dtPolyRef[8192]; + dtStatus status = _navMeshQuery->findPath(startRef, endRef, startPos, endPos, &_filter, hopBuffer, &hops, 8192); - if (pointCount < 2 || dtStatusFailed(dtResult)) + if (!dtStatusSucceed(status)) { - // only happens if pass bad data to findStraightPath or navmesh is broken - // single point paths can be generated here - /// @todo check the exact cases - TC_LOG_DEBUG("maps", "++ PathGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount); - BuildShortcut(); + TC_LOG_DEBUG("maps", "PathGenerator::CalculatePath() for %u no path found for start and end locations\n", _sourceUnit->GetGUIDLow()); _type = PATHFIND_NOPATH; - return; - } - else if (pointCount == _pointPathLimit) - { - TC_LOG_DEBUG("maps", "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d\n", pointCount, _pointPathLimit); - BuildShortcut(); - _type = PATHFIND_SHORT; - return; + return false; } - _pathPoints.resize(pointCount); - for (uint32 i = 0; i < pointCount; ++i) - _pathPoints[i] = G3D::Vector3(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1]); + int resultHopCount; + float* straightPath = new float[2048 * 3]; + unsigned char* pathFlags = new unsigned char[2048]; + dtPolyRef* pathRefs = new dtPolyRef[2048]; - NormalizePath(); - - // first point is always our current location - we need the next one - SetActualEndPosition(_pathPoints[pointCount-1]); - - // force the given destination, if needed - if (_forceDestination && - (!(_type & PATHFIND_NORMAL) || !InRange(GetEndPosition(), GetActualEndPosition(), 1.0f, 1.0f))) + status = _navMeshQuery->findStraightPath(startPos, endPos, hopBuffer, hops, straightPath, pathFlags, pathRefs, &resultHopCount, 2048); + if (!dtStatusSucceed(status)) { - // we may want to keep partial subpath - if (Dist3DSqr(GetActualEndPosition(), GetEndPosition()) < 0.3f * Dist3DSqr(GetStartPosition(), GetEndPosition())) - { - SetActualEndPosition(GetEndPosition()); - _pathPoints[_pathPoints.size()-1] = GetEndPosition(); - } - else - { - SetActualEndPosition(GetEndPosition()); - BuildShortcut(); - } - - _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); + TC_LOG_DEBUG("maps", "PathGenerator::CalculatePath() for %u no straight path found for start and end locations\n", _sourceUnit->GetGUIDLow()); + _type = PATHFIND_NOPATH; + return false; } - TC_LOG_DEBUG("maps", "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", _type, pointCount, _polyLength); -} - -void PathGenerator::NormalizePath() -{ - for (uint32 i = 0; i < _pathPoints.size(); ++i) - _sourceUnit->UpdateAllowedPositionZ(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z); -} - -void PathGenerator::BuildShortcut() -{ - TC_LOG_DEBUG("maps", "++ BuildShortcut :: making shortcut\n"); - - Clear(); - - // make two point path, our curr pos is the start, and dest is the end - _pathPoints.resize(2); + SmoothPath(polyPickExt, resultHopCount, straightPath); // Separate the path from the walls - // set start and a default next position - _pathPoints[0] = GetStartPosition(); - _pathPoints[1] = GetActualEndPosition(); + for (uint32 i = 0; i < resultHopCount; ++i) + _pathPoints.push_back(G3D::Vector3(-straightPath[i * 3 + 2], -straightPath[i * 3 + 0], straightPath[i * 3 + 1])); - NormalizePath(); - - _type = PATHFIND_SHORTCUT; + return true; } void PathGenerator::CreateFilter() { - uint16 includeFlags = 0; + uint16 includeFlags = POLY_FLAG_WALK | POLY_FLAG_SWIM; uint16 excludeFlags = 0; - if (_sourceUnit->GetTypeId() == TYPEID_UNIT) - { - Creature* creature = (Creature*)_sourceUnit; - if (creature->CanWalk()) - includeFlags |= NAV_GROUND; // walk - - // creatures don't take environmental damage - if (creature->CanSwim()) - includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim - } - else // assume Player + if (_sourceUnit->GetTypeId() == TYPEID_UNIT && !_sourceUnit->ToCreature()->CanSwim()) { - // perfect support not possible, just stay 'safe' - includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA | NAV_SLIME); + includeFlags = POLY_FLAG_WALK; + excludeFlags = POLY_FLAG_SWIM; } _filter.setIncludeFlags(includeFlags); @@ -534,275 +160,130 @@ void PathGenerator::CreateFilter() void PathGenerator::UpdateFilter() { - // allow creatures to cheat and use different movement types if they are moved - // forcefully into terrain they can't normally move in - if (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater()) - { - uint16 includedFlags = _filter.getIncludeFlags(); - includedFlags |= GetNavTerrain(_sourceUnit->GetPositionX(), - _sourceUnit->GetPositionY(), - _sourceUnit->GetPositionZ()); - _filter.setIncludeFlags(includedFlags); - } } -NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z) +float PathGenerator::GetTriangleArea(float* verts, int nv) { - LiquidData data; - ZLiquidStatus liquidStatus = _sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data); - if (liquidStatus == LIQUID_MAP_NO_WATER) - return NAV_GROUND; - - switch (data.type_flags) - { - case MAP_LIQUID_TYPE_WATER: - case MAP_LIQUID_TYPE_OCEAN: - return NAV_WATER; - case MAP_LIQUID_TYPE_MAGMA: - return NAV_MAGMA; - case MAP_LIQUID_TYPE_SLIME: - return NAV_SLIME; - default: - return NAV_GROUND; - } + float area = 0; + for (int i = 0; i < nv - 1; i++) + area += verts[i * 3] * verts[i * 3 + 5] - verts[i * 3 + 3] * verts[i * 3 + 2]; + area += verts[(nv - 1) * 3] * verts[2] - verts[0] * verts[(nv - 1) * 3 + 2]; + return area * 0.5f; } -bool PathGenerator::HaveTile(const G3D::Vector3& p) const +bool PathGenerator::PointInPoly(float* pos, float* verts, int nv, float err) { - int tx = -1, ty = -1; - float point[VERTEX_SIZE] = {p.y, p.z, p.x}; - - _navMesh->calcTileLoc(point, &tx, &ty); - - /// Workaround - /// For some reason, often the tx and ty variables wont get a valid value - /// Use this check to prevent getting negative tile coords and crashing on getTileAt - if (tx < 0 || ty < 0) - return false; - - return (_navMesh->getTileAt(tx, ty, 0) != NULL); -} - -uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited) -{ - int32 furthestPath = -1; - int32 furthestVisited = -1; - - // Find furthest common polygon. - for (int32 i = npath-1; i >= 0; --i) + // Poly area + float area = abs(PathGenerator::GetTriangleArea(verts, nv)); + + // Calculate each area of the triangles + float testTri[9]; + memcpy(testTri, pos, sizeof(float) * 3); + float area1 = 0; + for(int i = 0; i < nv - 1; ++i) { - bool found = false; - for (int32 j = nvisited-1; j >= 0; --j) - { - if (path[i] == visited[j]) - { - furthestPath = i; - furthestVisited = j; - found = true; - } - } - if (found) - break; + memcpy(&testTri[3], &verts[i * 3], sizeof(float) * 3); + memcpy(&testTri[6], &verts[i * 3 + 3], sizeof(float) * 3); + area1 += abs(PathGenerator::GetTriangleArea(testTri, 3)); + if (area1 - err > area) + return false; } - // If no intersection found just return current path. - if (furthestPath == -1 || furthestVisited == -1) - return npath; - - // Concatenate paths. + // Last one + memcpy(&testTri[3], verts, sizeof(float) * 3); + memcpy(&testTri[6], &verts[nv * 3 - 3] , sizeof(float) * 3); + area1 += abs(PathGenerator::GetTriangleArea(testTri, 3)); - // Adjust beginning of the buffer to include the visited. - uint32 req = nvisited - furthestVisited; - uint32 orig = uint32(furthestPath + 1) < npath ? furthestPath + 1 : npath; - uint32 size = npath > orig ? npath - orig : 0; - if (req + size > maxPath) - size = maxPath-req; + return abs(area1 - area) < err; +} - if (size) - memmove(path + req, path + orig, size * sizeof(dtPolyRef)); +float PathGenerator::DistanceToWall(float* polyPickExt, float* pos, float* hitPos, float* hitNormal) +{ + float distanceToWall = 0; + dtPolyRef ref; - // Store visited - for (uint32 i = 0; i < req; ++i) - path[i] = visited[(nvisited - 1) - i]; + dtStatus status = _navMeshQuery->findNearestPoly(pos, polyPickExt, &_filter, &ref, 0); - return req+size; -} + if (!dtStatusSucceed(status) || ref == 0) + return -1; -bool PathGenerator::GetSteerTarget(float const* startPos, float const* endPos, - float minTargetDist, dtPolyRef const* path, uint32 pathSize, - float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef) -{ - // Find steer target. - static const uint32 MAX_STEER_POINTS = 3; - float steerPath[MAX_STEER_POINTS*VERTEX_SIZE]; - unsigned char steerPathFlags[MAX_STEER_POINTS]; - dtPolyRef steerPathPolys[MAX_STEER_POINTS]; - uint32 nsteerPath = 0; - dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize, - steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS); - if (!nsteerPath || dtStatusFailed(dtResult)) - return false; + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if (dtStatusFailed(_navMesh->getTileAndPolyByRef(ref, &tile, &poly))) + return -1; - // Find vertex far enough to steer to. - uint32 ns = 0; - while (ns < nsteerPath) + // Collect vertices. + float verts[DT_VERTS_PER_POLYGON * 3]; + int nv = 0; + for (unsigned char i = 0; i < poly->vertCount; ++i) { - // Stop at Off-Mesh link or when point is further than slop away. - if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) || - !InRangeYZX(&steerPath[ns*VERTEX_SIZE], startPos, minTargetDist, 1000.0f)) - break; - ns++; + dtVcopy(&verts[nv * 3], &tile->verts[poly->verts[i] * 3]); + nv++; } - // Failed to find good point to steer to. - if (ns >= nsteerPath) - return false; - dtVcopy(steerPos, &steerPath[ns*VERTEX_SIZE]); - steerPos[1] = startPos[1]; // keep Z value - steerPosFlag = steerPathFlags[ns]; - steerPosRef = steerPathPolys[ns]; + bool inside = PathGenerator::PointInPoly(pos, verts, nv, 0.05f); + if (!inside) + return -1; - return true; + if (!dtStatusSucceed(_navMeshQuery->findDistanceToWall(ref, pos, 100.0f, &_filter, &distanceToWall, hitPos, hitNormal))) + return -1; + + return distanceToWall; } -dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPos, - dtPolyRef const* polyPath, uint32 polyPathSize, - float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize) +void PathGenerator::SmoothPath(float* polyPickExt, int pathLength, float*& straightPath) { - *smoothPathSize = 0; - uint32 nsmoothPath = 0; - - dtPolyRef polys[MAX_PATH_LENGTH]; - memcpy(polys, polyPath, sizeof(dtPolyRef)*polyPathSize); - uint32 npolys = polyPathSize; - - float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE]; - if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))) - return DT_FAILURE; - - if (dtStatusFailed(_navMeshQuery->closestPointOnPolyBoundary(polys[npolys-1], endPos, targetPos))) - return DT_FAILURE; - - dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos); - nsmoothPath++; - - // Move towards target a small advancement at a time until target reached or - // when ran out of memory to store the path. - while (npolys && nsmoothPath < maxSmoothPathSize) + float hitPos[3]; + float hitNormal[3]; + float testPos[3]; + float distanceToWall = 0; + float up[]= { 0, 1, 0 }; + float origDis = 0; + + for (int i = 1; i < pathLength - 1; ++i) { - // Find location to steer towards. - float steerPos[VERTEX_SIZE]; - unsigned char steerPosFlag; - dtPolyRef steerPosRef = INVALID_POLYREF; - - if (!GetSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef)) - break; - - bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END); - bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION); - - // Find movement delta. - float delta[VERTEX_SIZE]; - dtVsub(delta, steerPos, iterPos); - float len = dtSqrt(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 < SMOOTH_PATH_STEP_SIZE) - len = 1.0f; - else - len = SMOOTH_PATH_STEP_SIZE / len; - - float moveTgt[VERTEX_SIZE]; - dtVmad(moveTgt, iterPos, delta, len); - - // Move - float result[VERTEX_SIZE]; - const static uint32 MAX_VISIT_POLY = 16; - dtPolyRef visited[MAX_VISIT_POLY]; - - uint32 nvisited = 0; - _navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY); - npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited); - - _navMeshQuery->getPolyHeight(polys[0], result, &result[1]); - result[1] += 0.5f; - dtVcopy(iterPos, result); - - // Handle end of path and off-mesh links when close enough. - if (endOfPath && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f)) - { - // Reached end of path. - dtVcopy(iterPos, targetPos); - if (nsmoothPath < maxSmoothPathSize) - { - dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos); - nsmoothPath++; - } - break; - } - else if (offMeshConnection && InRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f)) - { - // Advance the path up to and over the off-mesh connection. - dtPolyRef prevRef = INVALID_POLYREF; - dtPolyRef polyRef = polys[0]; - uint32 npos = 0; - while (npos < npolys && polyRef != steerPosRef) - { - prevRef = polyRef; - polyRef = polys[npos]; - npos++; - } - - for (uint32 i = npos; i < npolys; ++i) - polys[i-npos] = polys[i]; - - npolys -= npos; + dtPolyRef pt; + float* curPoi = &straightPath[i * 3]; + distanceToWall = DistanceToWall(polyPickExt, curPoi, hitPos, hitNormal); - // Handle the connection. - float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE]; - if (dtStatusSucceed(_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos))) + if (distanceToWall < PathGenerator::MinWallDistance && distanceToWall >= 0) + { + float vec[3]; + dtVsub(vec, &straightPath[i * 3 - 3], &straightPath[i * 3]); + // If distanceToWall is 0 means the point is in the edge, so we can't get the hitpos. + if (distanceToWall == 0) { - if (nsmoothPath < maxSmoothPathSize) + // Test the left side + dtVcross(testPos, vec, up); + dtVadd(testPos, testPos, curPoi); + float ft = PathGenerator::MinWallDistance / dtVdist(testPos, curPoi); + dtVlerp(testPos, curPoi, testPos, ft); + distanceToWall = DistanceToWall(polyPickExt, testPos, hitPos, hitNormal); + if (abs(PathGenerator::MinWallDistance - distanceToWall) > 0.1f) { - dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], startPos); - nsmoothPath++; + // Test the right side + dtVcross(testPos, up, vec); + dtVadd(testPos, testPos, curPoi); + ft = PathGenerator::MinWallDistance / dtVdist(testPos, curPoi); + dtVlerp(testPos, curPoi, testPos, ft); + distanceToWall = DistanceToWall(polyPickExt, testPos, hitPos, hitNormal); } - // Move position at the other side of the off-mesh link. - dtVcopy(iterPos, endPos); - _navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]); - iterPos[1] += 0.5f; + + // If the test point is better than the orig point, replace it. + if (abs(distanceToWall - PathGenerator::MinWallDistance) < 0.1f) + dtVcopy(curPoi, testPos); } - } + else + { + // We get the hitpos with a ray + float ft = PathGenerator::MinWallDistance / distanceToWall; + dtVlerp(testPos, hitPos, curPoi, ft); + distanceToWall = DistanceToWall(polyPickExt, testPos, hitPos, hitNormal); - // Store results. - if (nsmoothPath < maxSmoothPathSize) - { - dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos); - nsmoothPath++; + if (abs(distanceToWall - PathGenerator::MinWallDistance) < 0.1f) + dtVcopy(curPoi, testPos); + } } } - - *smoothPathSize = nsmoothPath; - - // this is most likely a loop - return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE; -} - -bool PathGenerator::InRangeYZX(const float* v1, const float* v2, float r, float h) const -{ - const float dx = v2[0] - v1[0]; - const float dy = v2[1] - v1[1]; // elevation - const float dz = v2[2] - v1[2]; - return (dx * dx + dz * dz) < r * r && fabsf(dy) < h; -} - -bool PathGenerator::InRange(G3D::Vector3 const& p1, G3D::Vector3 const& p2, float r, float h) const -{ - G3D::Vector3 d = p1 - p2; - return (d.x * d.x + d.y * d.y) < r * r && fabsf(d.z) < h; -} - -float PathGenerator::Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) const -{ - return (p1 - p2).squaredLength(); } diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h index ac66b7cec57..075d6dabc9f 100644 --- a/src/server/game/Movement/PathGenerator.h +++ b/src/server/game/Movement/PathGenerator.h @@ -26,18 +26,6 @@ class Unit; -// 74*4.0f=296y number_of_points*interval = max_path_len -// this is way more than actual evade range -// I think we can safely cut those down even more -#define MAX_PATH_LENGTH 74 -#define MAX_POINT_PATH_LENGTH 74 - -#define SMOOTH_PATH_STEP_SIZE 4.0f -#define SMOOTH_PATH_SLOP 0.3f - -#define VERTEX_SIZE 3 -#define INVALID_POLYREF 0 - enum PathType { PATHFIND_BLANK = 0x00, // path not built yet @@ -49,6 +37,12 @@ enum PathType PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length }; +enum PolyFlag +{ + POLY_FLAG_WALK = 1, + POLY_FLAG_SWIM = 2 +}; + class PathGenerator { public: @@ -59,10 +53,6 @@ class PathGenerator // return: true if new path was calculated, false otherwise (no change needed) bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false); - // option setters - use optional - void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; } - void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); } - // result getters G3D::Vector3 const& GetStartPosition() const { return _startPosition; } G3D::Vector3 const& GetEndPosition() const { return _endPosition; } @@ -71,19 +61,13 @@ class PathGenerator Movement::PointsArray const& GetPath() const { return _pathPoints; } PathType GetPathType() const { return _type; } + + static float MinWallDistance; private: - - dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references - uint32 _polyLength; // number of polygons in the path - Movement::PointsArray _pathPoints; // our actual (x,y,z) path to the target PathType _type; // tells what kind of path this is - bool _useStraightPath; // type of path will be generated - bool _forceDestination; // when set, we will always arrive at given point - uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH) - G3D::Vector3 _startPosition; // {x, y, z} of current location G3D::Vector3 _endPosition; // {x, y, z} of the destination G3D::Vector3 _actualEndPosition; // {x, y, z} of the closest possible point to given destination @@ -97,37 +81,16 @@ class PathGenerator void SetStartPosition(G3D::Vector3 const& point) { _startPosition = point; } void SetEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; _endPosition = point; } void SetActualEndPosition(G3D::Vector3 const& point) { _actualEndPosition = point; } - void NormalizePath(); - - void Clear() - { - _polyLength = 0; - _pathPoints.clear(); - } - bool InRange(G3D::Vector3 const& p1, G3D::Vector3 const& p2, float r, float h) const; - float Dist3DSqr(G3D::Vector3 const& p1, G3D::Vector3 const& p2) const; - bool InRangeYZX(float const* v1, float const* v2, float r, float h) const; + // Path smoothing + void SmoothPath(float* polyPickExt, int pathLength, float*& straightPath); + float DistanceToWall(float* polyPickExt, float* pos, float* hitPos, float* hitNormal); + // dtPointInPolygon will return false when the point is too close to the edge, so we rewrite the test function. + static bool PointInPoly(float* pos, float* verts, int nv, float err); + static float GetTriangleArea(float* verts, int nv); - dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const; - dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const; - bool HaveTile(G3D::Vector3 const& p) const; - - void BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos); - void BuildPointPath(float const* startPoint, float const* endPoint); - void BuildShortcut(); - - NavTerrain GetNavTerrain(float x, float y, float z); void CreateFilter(); void UpdateFilter(); - - // smooth path aux functions - uint32 FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited); - bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos, - unsigned char& steerPosFlag, dtPolyRef& steerPosRef); - dtStatus FindSmoothPath(float const* startPos, float const* endPos, - dtPolyRef const* polyPath, uint32 polyPathSize, - float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize); }; #endif diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index c969c97abf1..200564e1f8d 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5177,7 +5177,6 @@ SpellCastResult Spell::CheckCast(bool strict) target->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); target->GetFirstCollisionPosition(pos, CONTACT_DISTANCE, target->GetRelativeAngle(m_caster)); - m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f); bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize()); if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT) return SPELL_FAILED_OUT_OF_RANGE; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 7e9482317d1..e0f3d83ae77 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -149,7 +149,7 @@ World::~World() delete command; VMAP::VMapFactory::clear(); - MMAP::MMapFactory::clear(); + MMAP::MMapFactory::Clear(); /// @todo free addSessQueue } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 3eba91cdab5..ce3f62e38dd 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -187,7 +187,7 @@ public: uint32 haveMap = Map::ExistMap(mapId, gridX, gridY) ? 1 : 0; uint32 haveVMap = Map::ExistVMap(mapId, gridX, gridY) ? 1 : 0; - uint32 haveMMap = (MMAP::MMapFactory::IsPathfindingEnabled(mapId) && MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) ? 1 : 0; + uint32 haveMMap = (MMAP::MMapFactory::IsPathfindingEnabled(mapId) && MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) ? 1 : 0; if (haveVMap) { diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp index 690988ebac1..88e364878cc 100644 --- a/src/server/scripts/Commands/cs_mmaps.cpp +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -30,11 +30,13 @@ #include "PointMovementGenerator.h" #include "PathGenerator.h" #include "MMapFactory.h" +#include "DetourCommon.h" #include "Map.h" #include "TargetedMovementGenerator.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" +#include "MMapManager.h" class mmaps_commandscript : public CommandScript { @@ -61,9 +63,137 @@ public: return commandTable; } + static float Fix_GetXZArea(float* verts, int nv) + { + float area = 0; + for(int i=0; i<nv-1; i++) + area+=(verts[i*3]*verts[i*3+5]-verts[i*3+3]*verts[i*3+2]); + area += (verts[(nv-1)*3]*verts[2] - verts[0]*verts[(nv-1)*3+2]); + return area*0.5f; + } + + + //dtPointInPolygon will return false when the point is too close to the edge,so we rewite the test function. + static bool Fix_PointIsInPoly(float* pos,float* verts,int nv,float err) + { + //poly area + float area = abs(Fix_GetXZArea(verts,nv)); + + //calculate each area of triangles + float TestTri[9]; + memcpy(TestTri,pos,sizeof(float)*3); + float area1 = 0; + for(int i=0;i<nv-1;++i) + { + memcpy(&TestTri[3],&verts[i*3],sizeof(float)*3); + memcpy(&TestTri[6],&verts[i*3+3],sizeof(float)*3); + area1+= abs(Fix_GetXZArea(TestTri,3)); + if(area1-err>area) + return false; + } + + //last one + memcpy(&TestTri[3],verts,sizeof(float)*3); + memcpy(&TestTri[6],&verts[nv*3-3],sizeof(float)*3); + area1+= abs(Fix_GetXZArea(TestTri,3)); + + return abs(area1-area)<err; + } + + + static float DistanceToWall(dtNavMeshQuery* navQuery, dtNavMesh* navMesh, float* polyPickExt, dtQueryFilter& filter, float* pos,float* hitPos,float* hitNormal) + { + float distanceToWall=0; + dtPolyRef ref; + if(dtStatusSucceed(navQuery->findNearestPoly(pos, polyPickExt, &filter, &ref, 0))==false || ref ==0) + return -1; + + const dtMeshTile* tile = 0; + const dtPoly* poly = 0; + if (dtStatusFailed(navMesh->getTileAndPolyByRef(ref, &tile, &poly))) + return -1; + + // Collect vertices. + float verts[DT_VERTS_PER_POLYGON*3]; + int nv = 0; + for (int i = 0; i < (int)poly->vertCount; ++i) + { + dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]); + nv++; + } + + bool inside = Fix_PointIsInPoly(pos, verts, nv,0.05f); + if(inside == false) + return -1; + + if(dtStatusSucceed(navQuery->findDistanceToWall(ref, pos, 100.0f, &filter, &distanceToWall, hitPos, hitNormal))==false) + return -1; + + return distanceToWall; + } + + #define MIN_WALL_DISTANCE 1.5f //set this value bigger to make the path point far way from wall + + //Try to fix the path, + static void FixPath(dtNavMesh* navMesh, dtNavMeshQuery* navQuery, float* polyPickExt, dtQueryFilter& filter, int pathLength, float*& straightPath) + { + float hitPos[3]; + float hitNormal[3]; + float TestPos[3]; + float distanceToWall=0; + float up[3]={0,1,0}; + float origDis = 0; + + for(int i=1;i<pathLength-1;++i) + { + dtPolyRef pt; + float* pCurPoi=&straightPath[i*3]; + distanceToWall = DistanceToWall(navQuery, navMesh, polyPickExt, filter, pCurPoi,hitPos,hitNormal); + + if(distanceToWall<MIN_WALL_DISTANCE && distanceToWall>=0) + { + float vec[3]; + dtVsub(vec,&straightPath[i*3-3],&straightPath[i*3]); + //distanceToWall is 0 means the point is in the edge.so we can't get the hitpos. + if(distanceToWall == 0) + { + //test left side + dtVcross(TestPos,vec,up); + dtVadd(TestPos,TestPos,pCurPoi); + float ft = MIN_WALL_DISTANCE/dtVdist(TestPos,pCurPoi); + dtVlerp(TestPos,pCurPoi,TestPos,ft); + distanceToWall = DistanceToWall(navQuery, navMesh, polyPickExt, filter,TestPos,hitPos,hitNormal); + if(abs(MIN_WALL_DISTANCE - distanceToWall)>0.1f) + { + //test right side + dtVcross(TestPos,up,vec); + dtVadd(TestPos,TestPos,pCurPoi); + ft = MIN_WALL_DISTANCE/dtVdist(TestPos,pCurPoi); + dtVlerp(TestPos,pCurPoi,TestPos,ft); + distanceToWall = DistanceToWall(navQuery, navMesh, polyPickExt, filter,TestPos,hitPos,hitNormal); + } + + //if test point is better than the orig point,replace it. + if(abs(distanceToWall-MIN_WALL_DISTANCE)<0.1f) + dtVcopy(pCurPoi,TestPos); + } + else + { + //ok,we get the hitpos,just make a ray + float ft = MIN_WALL_DISTANCE/distanceToWall; + dtVlerp(TestPos,hitPos,pCurPoi,ft); + distanceToWall = DistanceToWall(navQuery, navMesh, polyPickExt, filter, TestPos,hitPos,hitNormal); + + if(abs(distanceToWall-MIN_WALL_DISTANCE)<0.1f) + dtVcopy(pCurPoi,TestPos); + } + } + } + } + static bool HandleMmapPathCommand(ChatHandler* handler, char const* args) { - if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) + if (!MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) { handler->PSendSysMessage("NavMesh not loaded for current map."); return true; @@ -91,10 +221,10 @@ public: player->GetPosition(x, y, z); // path - PathGenerator path(target); + /*PathGenerator path(target); path.SetUseStraightPath(useStraightPath); bool result = path.CalculatePath(x, y, z); - + Movement::PointsArray const& pointPath = path.GetPath(); handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str()); handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : "SmoothPath"); @@ -107,13 +237,64 @@ public: handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z); handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z); handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z); + */ + float m_spos[3]; + m_spos[0] = -y; + m_spos[1] = z; + m_spos[2] = -x; + + // + float m_epos[3]; + m_epos[0] = -target->GetPositionY(); + m_epos[1] = target->GetPositionZ(); + m_epos[2] = -target->GetPositionX(); + + // + dtQueryFilter m_filter; + m_filter.setIncludeFlags(3); + m_filter.setExcludeFlags(2); + + // + float m_polyPickExt[3]; + m_polyPickExt[0] = 2.5f; + m_polyPickExt[1] = 2.5f; + m_polyPickExt[2] = 2.5f; + + // + dtPolyRef m_startRef; + dtPolyRef m_endRef; + + const dtNavMesh* navMesh = MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMesh(player->GetMapId()); + const dtNavMeshQuery* navMeshQuery = MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMeshQuery(player->GetMapId(), handler->GetSession()->GetPlayer()->GetInstanceId()); + + float nearestPt[3]; + + navMeshQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, nearestPt); + navMeshQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, nearestPt); + + if ( !m_startRef || !m_endRef ) + { + std::cerr << "Could not find any nearby poly's (" << m_startRef << "," << m_endRef << ")" << std::endl; + return 0; + } + + int hops; + dtPolyRef* hopBuffer = new dtPolyRef[8192]; + dtStatus status = navMeshQuery->findPath(m_startRef, m_endRef, m_spos, m_epos, &m_filter, hopBuffer, &hops, 8192); + + int resultHopCount; + float* straightPath = new float[2048*3]; + unsigned char* pathFlags = new unsigned char[2048]; + dtPolyRef* pathRefs = new dtPolyRef[2048]; + + status = navMeshQuery->findStraightPath(m_spos, m_epos, hopBuffer, hops, straightPath, pathFlags, pathRefs, &resultHopCount, 2048); + FixPath(const_cast<dtNavMesh*>(navMesh), const_cast<dtNavMeshQuery*>(navMeshQuery), m_polyPickExt, m_filter, resultHopCount, straightPath); + for (uint32 i = 0; i < resultHopCount; ++i) + player->SummonCreature(VISUAL_WAYPOINT, -straightPath[i * 3 + 2], -straightPath[i * 3 + 0], straightPath[i * 3 + 1], 0, TEMPSUMMON_TIMED_DESPAWN, 9000); if (!player->IsGameMaster()) handler->PSendSysMessage("Enable GM mode to see the path points."); - for (uint32 i = 0; i < pointPath.size(); ++i) - player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000); - return true; } @@ -131,8 +312,8 @@ public: handler->PSendSysMessage("gridloc [%i, %i]", gx, gy); // calculate navmesh tile location - dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); - dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId()); + dtNavMesh const* navmesh = MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); + dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId()); if (!navmesh || !navmeshquery) { handler->PSendSysMessage("NavMesh not loaded for current map."); @@ -142,8 +323,8 @@ public: float const* min = navmesh->getParams()->orig; float x, y, z; player->GetPosition(x, y, z); - float location[VERTEX_SIZE] = {y, z, x}; - float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; + float location[] = {y, z, x}; + float extents[] = {3.0f, 5.0f, 3.0f}; int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS); int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS); @@ -152,14 +333,14 @@ public: // navmesh poly -> navmesh tile location dtQueryFilter filter = dtQueryFilter(); - dtPolyRef polyRef = INVALID_POLYREF; + dtPolyRef polyRef = 0; if (dtStatusFailed(navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, NULL))) { handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)"); return true; } - if (polyRef == INVALID_POLYREF) + if (polyRef == 0) handler->PSendSysMessage("Dt [??, ??] (invalid poly, probably no tile loaded)"); else { @@ -183,8 +364,8 @@ public: static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/) { uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId(); - dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid); - dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId()); + dtNavMesh const* navmesh = MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMesh(mapid); + dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::CreateOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId()); if (!navmesh || !navmeshquery) { handler->PSendSysMessage("NavMesh not loaded for current map."); @@ -211,8 +392,8 @@ public: handler->PSendSysMessage("mmap stats:"); handler->PSendSysMessage(" global mmap pathfinding is %sabled", MMAP::MMapFactory::IsPathfindingEnabled(mapId) ? "en" : "dis"); - MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager(); - handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount()); + MMAP::MMapManager* manager = MMAP::MMapFactory::CreateOrGetMMapManager(); + handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->GetLoadedMapsCount(), manager->GetLoadedTilesCount()); dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); if (!navmesh) diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 3adccf105de..f4a4aee92f6 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -11,7 +11,8 @@ add_subdirectory(map_extractor) add_subdirectory(vmap4_assembler) add_subdirectory(vmap4_extractor) -add_subdirectory(mmaps_generator) if (WITH_MESHEXTRACTOR) add_subdirectory(mesh_extractor) +else() + add_subdirectory(mmaps_generator) endif() diff --git a/src/tools/mesh_extractor/ADT.cpp b/src/tools/mesh_extractor/ADT.cpp index a32276f9856..39b600d21b7 100644 --- a/src/tools/mesh_extractor/ADT.cpp +++ b/src/tools/mesh_extractor/ADT.cpp @@ -25,7 +25,7 @@ ADT::ADT( std::string file, int x, int y ) : ObjectData(NULL), Data(NULL), HasOb { Data = new ChunkedData(file); ObjectData = new ChunkedData(file); - if (ObjectData->Stream) + if (ObjectData->_Stream) HasObjectData = true; else ObjectData = NULL; diff --git a/src/tools/mesh_extractor/CMakeLists.txt b/src/tools/mesh_extractor/CMakeLists.txt index 9ed8472051d..f5dbb0fd7ff 100644 --- a/src/tools/mesh_extractor/CMakeLists.txt +++ b/src/tools/mesh_extractor/CMakeLists.txt @@ -47,4 +47,4 @@ if( UNIX ) install(TARGETS MeshExtractor DESTINATION bin) elseif( WIN32 ) install(TARGETS MeshExtractor DESTINATION "${CMAKE_INSTALL_PREFIX}") -endif() +endif()
\ No newline at end of file diff --git a/src/tools/mesh_extractor/Cache.h b/src/tools/mesh_extractor/Cache.h index 017b4c53f72..499983c6eb0 100644 --- a/src/tools/mesh_extractor/Cache.h +++ b/src/tools/mesh_extractor/Cache.h @@ -31,24 +31,18 @@ class GenericCache public: GenericCache() {} - static const uint32 FlushLimit = 300; // We can't get too close to filling up all the memory, and we have to be wary of the maximum number of open streams. - - void Insert(K key, T* val) - { - ACE_GUARD(ACE_Thread_Mutex, g, mutex); - - if (_items.size() > FlushLimit) - Clear(); - _items[key] = val; - } - T* Get(K key) { ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); typename std::map<K, T*>::iterator itr = _items.find(key); if (itr != _items.end()) return itr->second; - return NULL; + else + { + T* t = new T(key); // Create the object + _items[key] = t; + return t; + } } void Clear() diff --git a/src/tools/mesh_extractor/Chunk.cpp b/src/tools/mesh_extractor/Chunk.cpp index 42eb9e14881..bc6d9b66b96 100644 --- a/src/tools/mesh_extractor/Chunk.cpp +++ b/src/tools/mesh_extractor/Chunk.cpp @@ -25,24 +25,24 @@ int32 Chunk::FindSubChunkOffset(std::string name) if (name.size() != 4) return -1; - FILE* stream = GetStream(); + Stream* stream = GetStream(); uint32 matched = 0; - while (uint32(ftell(stream)) < Utils::Size(stream)) + while (stream->GetPos() < stream->GetSize()) { - char b = 0; - if (fread(&b, sizeof(char), 1, stream) != 1 || b != name[matched]) + char b = stream->Read<char>(); + if (b != name[matched]) matched = 0; else ++matched; if (matched == 4) - return ftell(stream) - 4; + return stream->GetPos() - 4; } return -1; } -FILE* Chunk::GetStream() +Stream* Chunk::GetStream() { - fseek(Stream, Offset, SEEK_SET); - return Stream; + _Stream->Seek(Offset, SEEK_SET); + return _Stream; } diff --git a/src/tools/mesh_extractor/Chunk.h b/src/tools/mesh_extractor/Chunk.h index 0641eb1dc3c..95d06efe206 100644 --- a/src/tools/mesh_extractor/Chunk.h +++ b/src/tools/mesh_extractor/Chunk.h @@ -19,19 +19,21 @@ #define CHUNK_H #include "Define.h" #include <string> +#include "Stream.h" + class ChunkedData; class Chunk { public: - Chunk(const char* name, uint32 length, uint32 offset, FILE* stream) : Name(name), Length(length), Offset(offset), Stream(stream) {} + Chunk(const char* name, uint32 length, uint32 offset, Stream* stream) : Name(name), Length(length), Offset(offset), _Stream(stream) {} int32 FindSubChunkOffset(std::string name); - FILE* GetStream(); + Stream* GetStream(); std::string Name; uint32 Length; uint32 Offset; - FILE* Stream; + Stream* _Stream; }; #endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/ChunkedData.cpp b/src/tools/mesh_extractor/ChunkedData.cpp index 1330e4b47f9..00c528352aa 100644 --- a/src/tools/mesh_extractor/ChunkedData.cpp +++ b/src/tools/mesh_extractor/ChunkedData.cpp @@ -21,18 +21,18 @@ #include <string> -ChunkedData::ChunkedData( FILE* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) : -Stream(stream) +ChunkedData::ChunkedData(Stream* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) : +_Stream(stream) { - if (!Stream) + if (!_Stream) return; Load(maxLength, chunksHint); } ChunkedData::ChunkedData( const std::string& file, uint32 chunksHint /*= 300*/ ) { - Stream = MPQHandler->GetFile(file); - if (!Stream) + _Stream = MPQHandler->GetFile(file); + if (!_Stream) return; Load(0, chunksHint); } @@ -40,27 +40,26 @@ ChunkedData::ChunkedData( const std::string& file, uint32 chunksHint /*= 300*/ ) void ChunkedData::Load( uint32 maxLength, uint32 chunksHint ) { if (!maxLength) - maxLength = Utils::Size(Stream); + maxLength = _Stream->GetSize(); Chunks.reserve(chunksHint); - uint32 baseOffset = ftell(Stream); + uint32 baseOffset = _Stream->GetPos(); uint32 calcOffset = 0; - while ((calcOffset + baseOffset) < Utils::Size(Stream) && (calcOffset < maxLength)) + while ((calcOffset + baseOffset) < _Stream->GetSize() && (calcOffset < maxLength)) { char nameBytes[5]; - uint32 read = fread(&nameBytes, sizeof(char), 4, Stream); - nameBytes[read] = '\0'; + _Stream->Read(nameBytes, sizeof(char) * 4); + nameBytes[4] = '\0'; std::string name = std::string(nameBytes); - // Utils::Reverse(nameBytes); name = std::string(name.rbegin(), name.rend()); - uint32 length; - if (fread(&length, sizeof(uint32), 1, Stream) != 1) - continue; + + uint32 length = _Stream->Read<uint32>(); calcOffset += 8; - Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, Stream)); + + Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, _Stream)); calcOffset += length; // save an extra seek at the end - if ((calcOffset + baseOffset) < Utils::Size(Stream) && calcOffset < maxLength) - fseek(Stream, length, SEEK_CUR); + if ((calcOffset + baseOffset) < _Stream->GetSize() && calcOffset < maxLength) + _Stream->Seek(length, SEEK_CUR); } } @@ -86,6 +85,6 @@ ChunkedData::~ChunkedData() delete *itr; Chunks.clear(); - if (Stream) - fclose(Stream); + if (_Stream) + delete _Stream; } diff --git a/src/tools/mesh_extractor/ChunkedData.h b/src/tools/mesh_extractor/ChunkedData.h index 5befdf30dd1..349750b6c85 100644 --- a/src/tools/mesh_extractor/ChunkedData.h +++ b/src/tools/mesh_extractor/ChunkedData.h @@ -20,11 +20,12 @@ #include <vector> #include "Chunk.h" +#include "Stream.h" class ChunkedData { public: - ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300); + ChunkedData(Stream* stream, uint32 maxLength, uint32 chunksHint = 300); ChunkedData(const std::string &file, uint32 chunksHint = 300); ~ChunkedData(); @@ -33,6 +34,6 @@ public: void Load(uint32 maxLength, uint32 chunksHint); std::vector<Chunk*> Chunks; - FILE* Stream; + Stream* _Stream; }; #endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp index 841d7a608e4..8321a7924a1 100644 --- a/src/tools/mesh_extractor/ContinentBuilder.cpp +++ b/src/tools/mesh_extractor/ContinentBuilder.cpp @@ -16,72 +16,13 @@ */ #include "ContinentBuilder.h" -#include "TileBuilder.h" #include "WDT.h" #include "Utils.h" #include "DetourNavMesh.h" #include "Cache.h" -#include "ace/Task.h" #include "Recast.h" #include "DetourCommon.h" -class BuilderThread : public ACE_Task_Base -{ -private: - int X, Y, MapId; - std::string Continent; - dtNavMeshParams Params; - ContinentBuilder* cBuilder; -public: - BuilderThread(ContinentBuilder* _cBuilder, dtNavMeshParams& params) : Params(params), cBuilder(_cBuilder), Free(true) {} - - void SetData(int x, int y, int map, const std::string& cont) - { - X = x; - Y = y; - MapId = map; - Continent = cont; - } - - int svc() - { - Free = false; - printf("[%02i,%02i] Building tile\n", X, Y); - TileBuilder builder(cBuilder, Continent, X, Y, MapId); - char buff[100]; - sprintf(buff, "mmaps/%03u%02i%02i.mmtile", MapId, Y, X); - FILE* f = fopen(buff, "r"); - if (f) // Check if file already exists. - { - printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y); - fclose(f); - Free = true; - return 0; - } - uint8* nav = builder.BuildTiled(Params); - if (nav) - { - f = fopen(buff, "wb"); - if (!f) - { - printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff); - return 0; - } - MmapTileHeader header; - header.size = builder.DataSize; - fwrite(&header, sizeof(MmapTileHeader), 1, f); - fwrite(nav, sizeof(unsigned char), builder.DataSize, f); - fclose(f); - } - dtFree(nav); - printf("[%02i,%02i] Tile Built!\n", X, Y); - Free = true; - return 0; - } - - bool Free; -}; - void ContinentBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax) { // this is for elevation @@ -89,7 +30,7 @@ void ContinentBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, i rcCalcBounds(verts, vertCount, bmin, bmax); else { - bmin[1] = FLT_MIN; + bmin[1] = -FLT_MAX; bmax[1] = FLT_MAX; } @@ -128,8 +69,6 @@ void ContinentBuilder::Build() dtNavMeshParams params; - std::vector<BuilderThread*> Threads; - if (TileMap->IsGlobalModel) { printf("Map %s ( %u ) is a WMO. Building with 1 thread.\n", Continent.c_str(), MapId); @@ -159,6 +98,7 @@ void ContinentBuilder::Build() } MmapTileHeader mheader; + Utils::InitializeMmapTileHeader(mheader); mheader.size = builder->DataSize; fwrite(&mheader, sizeof(MmapTileHeader), 1, f); fwrite(nav, sizeof(unsigned char), builder->DataSize, f); @@ -178,36 +118,62 @@ void ContinentBuilder::Build() fwrite(¶ms, sizeof(dtNavMeshParams), 1, mmap); fclose(mmap); - for (uint32 i = 0; i < NumberOfThreads; ++i) - Threads.push_back(new BuilderThread(this, params)); + std::vector<BuilderThread*> _threads; + BuilderThreadPool* pool = NumberOfThreads > 0 ? new BuilderThreadPool() : NULL; + printf("Map %s ( %u ) has %u tiles. Building them with %u threads\n", Continent.c_str(), MapId, uint32(TileMap->TileTable.size()), NumberOfThreads); + for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr) + pool->Enqueue(new TileBuildRequest(this, Continent, itr->X, itr->Y, MapId, params)); + + for (uint32 i = 0; i < NumberOfThreads; ++i) + _threads.push_back(new BuilderThread(this, pool->Queue())); + + // Free memory + for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th) { - bool next = false; - while (!next) - { - for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th) - { - if ((*_th)->Free) - { - (*_th)->SetData(itr->X, itr->Y, MapId, Continent); - (*_th)->activate(); - next = true; - break; - } - } - // Wait for 20 seconds - ACE_OS::sleep(ACE_Time_Value (0, 20000)); - } + (*_th)->wait(); + delete *_th; } + + delete pool; } Cache->Clear(); +} - // Free memory - for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th) +int TileBuildRequest::call() +{ + printf("[%02i,%02i] Building tile\n", X, Y); + // Build the tile and return negative on error + TileBuilder tile(_builder, _continent, X, Y, _mapId); + char buff[100]; + sprintf(buff, "mmaps/%03u%02i%02i.mmtile", _mapId, Y, X); + FILE* f = fopen(buff, "r"); + if (f) // Check if file already exists. { - (*_th)->wait(); - delete *_th; + printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y); + fclose(f); + return 0; + } + uint8* nav = tile.BuildTiled(_params); + if (nav) + { + f = fopen(buff, "wb"); + if (!f) + { + printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff); + dtFree(nav); + return -1; + } + MmapTileHeader header; + Utils::InitializeMmapTileHeader(header); + header.size = tile.DataSize; + fwrite(&header, sizeof(MmapTileHeader), 1, f); + fwrite(nav, sizeof(unsigned char), tile.DataSize, f); + fclose(f); } + dtFree(nav); + printf("[%02i,%02i] Tile Built!\n", X, Y); + return 0; } diff --git a/src/tools/mesh_extractor/ContinentBuilder.h b/src/tools/mesh_extractor/ContinentBuilder.h index 5349b8e1ba5..ce1eef9b642 100644 --- a/src/tools/mesh_extractor/ContinentBuilder.h +++ b/src/tools/mesh_extractor/ContinentBuilder.h @@ -17,9 +17,15 @@ #ifndef CONT_BUILDER_H #define CONT_BUILDER_H + #include <string> #include "WDT.h" #include "Define.h" +#include "TileBuilder.h" + +#include <ace/Task.h> +#include <ace/Activation_Queue.h> +#include <ace/Method_Request.h> class ContinentBuilder { @@ -44,4 +50,61 @@ private: int tileXMax; int tileYMax; }; + +class TileBuildRequest : public ACE_Method_Request +{ +public: + TileBuildRequest(ContinentBuilder* builder, std::string& continent, uint32 x, uint32 y, uint32 mapId, dtNavMeshParams& params) : _builder(builder), _continent(continent), X(x), Y(y), _mapId(mapId), _params(params) { } + + virtual int call(); + +private: + uint32 _mapId; + ContinentBuilder* _builder; + std::string& _continent; + uint32 X; + uint32 Y; + dtNavMeshParams& _params; +}; + +class BuilderThreadPool +{ +public: + BuilderThreadPool() : _queue(new ACE_Activation_Queue()) {} + ~BuilderThreadPool() { _queue->queue()->close(); delete _queue; } + + void Enqueue(TileBuildRequest* request) + { + _queue->enqueue(request); + } + + ACE_Activation_Queue* Queue() { return _queue; } + +private: + ACE_Activation_Queue* _queue; +}; + +class BuilderThread : public ACE_Task_Base +{ +private: + ContinentBuilder* _builder; + ACE_Activation_Queue* _queue; +public: + BuilderThread(ContinentBuilder* builder, ACE_Activation_Queue* queue) : _builder(builder), _queue(queue) { activate(); } + + int svc() + { + /// @ Set a timeout for dequeue attempts (only used when the queue is empty) as it will never get populated after thread starts + ACE_Time_Value timeout(5); + ACE_Method_Request* request = NULL; + while ((request = _queue->dequeue(&timeout)) != NULL) + { + request->call(); + delete request; + request = NULL; + } + return 0; + } +}; + #endif diff --git a/src/tools/mesh_extractor/DBC.cpp b/src/tools/mesh_extractor/DBC.cpp index 9249e320563..1484864d7a3 100644 --- a/src/tools/mesh_extractor/DBC.cpp +++ b/src/tools/mesh_extractor/DBC.cpp @@ -19,19 +19,15 @@ #include "DBC.h" #include "Define.h" -DBC::DBC( FILE* stream ) : StringBlock(NULL), StringBlockSize(0), IsFaulty(true) +DBC::DBC(Stream* stream) : StringBlock(NULL), StringBlockSize(0), IsFaulty(true) { - char magic[5]; - uint32 count = 0; - count += fread(&magic, sizeof(char), 4, stream); - magic[4] = '\0'; - count += fread(&RecordCount, sizeof(uint32), 1, stream); + delete[] stream->Read(4); // Read the magic "WDBC" + + RecordCount = stream->Read<int>(); Records.reserve(RecordCount); - count += fread(&Fields, sizeof(uint32), 1, stream); - count += fread(&RecordSize, sizeof(uint32), 1, stream); - count += fread(&StringBlockSize, sizeof(uint32), 1, stream); - if (count != 8) - printf("DBC::DBC: Failed to read some data expected 8, read %u\n", count); + Fields = stream->Read<int>(); + RecordSize = stream->Read<int>(); + StringBlockSize = stream->Read<uint32>(); for (int i = 0; i < RecordCount; i++) { @@ -45,17 +41,18 @@ DBC::DBC( FILE* stream ) : StringBlock(NULL), StringBlockSize(0), IsFaulty(true) IsFaulty = true; break; } - uint32 tmp; - if (fread(&tmp, sizeof(uint32), 1, stream) != 1) - printf("DBC::DBC: Failed to read some data expected 1, read 0\n"); - rec->Values.push_back(tmp); + rec->Values.push_back(stream->Read<uint32>()); size += 4; } } - StringBlock = new uint8[StringBlockSize]; - count = fread(StringBlock, sizeof(uint8), StringBlockSize, stream); - if (count != StringBlockSize) - printf("DBC::DBC: Failed to read some data expected %u, read %u\n", StringBlockSize, count); + StringBlock = (uint8*)stream->Read(StringBlockSize); +} + +DBC::~DBC() +{ + delete[] StringBlock; + for (std::vector<Record*>::iterator itr = Records.begin(); itr != Records.end(); ++itr) + delete *itr; } std::string DBC::GetStringByOffset( int offset ) @@ -73,7 +70,7 @@ std::string DBC::GetStringByOffset( int offset ) strcpy(d, (const char*)(StringBlock + offset)); d[len] = '\0'; std::string val = std::string(d); - delete [] d; + delete[] d; return val; } diff --git a/src/tools/mesh_extractor/DBC.h b/src/tools/mesh_extractor/DBC.h index 0d3b85d8c78..61912cd456d 100644 --- a/src/tools/mesh_extractor/DBC.h +++ b/src/tools/mesh_extractor/DBC.h @@ -20,13 +20,15 @@ #include <vector> #include <string> #include "Define.h" +#include "Stream.h" class Record; class DBC { public: - DBC(FILE* stream); + DBC(Stream* stream); + ~DBC(); std::string GetStringByOffset(int offset); diff --git a/src/tools/mesh_extractor/DoodadHandler.cpp b/src/tools/mesh_extractor/DoodadHandler.cpp index 2e0743134d4..90f676ef769 100644 --- a/src/tools/mesh_extractor/DoodadHandler.cpp +++ b/src/tools/mesh_extractor/DoodadHandler.cpp @@ -34,20 +34,18 @@ DoodadHandler::DoodadHandler( ADT* adt ) : ReadDoodadPaths(mmid, mmdx); } -void DoodadHandler::ProcessInternal( MapChunk* mcnk ) +void DoodadHandler::ProcessInternal(MapChunk* mcnk) { if (!IsSane()) return; uint32 refCount = mcnk->Header.DoodadRefs; - FILE* stream = mcnk->Source->GetStream(); - fseek(stream, mcnk->Source->Offset + mcnk->Header.OffsetMCRF, SEEK_SET); + Stream* stream = mcnk->Source->GetStream(); + stream->Seek(mcnk->Source->Offset + mcnk->Header.OffsetMCRF, SEEK_SET); + for (uint32 i = 0; i < refCount; i++) { - int32 index; - int32 count; - if ((count = fread(&index, sizeof(int32), 1, stream)) != 1) - printf("DoodadHandler::ProcessInternal: Failed to read some data expected 1, read %d\n", count); + int32 index = stream->Read<int32>(); if (index < 0 || uint32(index) >= _definitions->size()) continue; DoodadDefinition doodad = (*_definitions)[index]; @@ -59,11 +57,6 @@ void DoodadHandler::ProcessInternal( MapChunk* mcnk ) std::string path = (*_paths)[doodad.MmidIndex]; Model* model = Cache->ModelCache.Get(path); - if (!model) - { - model = new Model(path); - Cache->ModelCache.Insert(path, model); - } if (!model->IsCollidable) continue; @@ -73,7 +66,7 @@ void DoodadHandler::ProcessInternal( MapChunk* mcnk ) InsertModelGeometry(doodad, model); } // Restore the stream position - fseek(stream, mcnk->Source->Offset, SEEK_SET); + stream->Seek(mcnk->Source->Offset, SEEK_SET); } void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk ) @@ -81,7 +74,7 @@ void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk ) int32 count = chunk->Length / 36; _definitions = new std::vector<DoodadDefinition>; _definitions->reserve(count); - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); for (int i = 0; i < count; i++) { DoodadDefinition def; @@ -97,14 +90,13 @@ void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data ) _paths->reserve(paths); for (int i = 0; i < paths; i++) { - FILE* idStream = id->GetStream(); - fseek(idStream, i * 4, SEEK_CUR); - uint32 offset; - if (fread(&offset, sizeof(uint32), 1, idStream) != 1) - printf("DoodadHandler::ReadDoodadPaths: Failed to read some data expected 1, read 0\n"); - FILE* dataStream = data->GetStream(); - fseek(dataStream, offset + data->Offset, SEEK_SET); - _paths->push_back(Utils::ReadString(dataStream)); + Stream* idStream = id->GetStream(); + idStream->Seek(i * 4, SEEK_CUR); + uint32 offset = idStream->Read<uint32>(); + + Stream* dataStream = data->GetStream(); + dataStream->Seek(offset + data->Offset, SEEK_SET); + _paths->push_back(dataStream->ReadString()); } } diff --git a/src/tools/mesh_extractor/DoodadHandler.h b/src/tools/mesh_extractor/DoodadHandler.h index c3a8d645016..b02fcc664d1 100644 --- a/src/tools/mesh_extractor/DoodadHandler.h +++ b/src/tools/mesh_extractor/DoodadHandler.h @@ -21,6 +21,7 @@ #include "Utils.h" #include "Chunk.h" #include "Model.h" +#include "Stream.h" #include <set> #include <vector> @@ -39,18 +40,14 @@ public: return Vector3(vec.z, vec.x, vec.y); } - void Read(FILE* stream) + void Read(Stream* stream) { - int count = 0; - - count += fread(&MmidIndex, sizeof(uint32), 1, stream); - count += fread(&UniqueId, sizeof(uint32), 1, stream); - Position = (Vector3::Read(stream)); + MmidIndex = stream->Read<uint32>(); + UniqueId = stream->Read<uint32>(); + Position = Vector3::Read(stream); Rotation = Vector3::Read(stream); - count += fread(&DecimalScale, sizeof(uint16), 1, stream); - count += fread(&Flags, sizeof(uint16), 1, stream); - if (count != 4) - printf("DoodadDefinition::Read: Failed to read some data expected 4, read %d\n", count); + DecimalScale = stream->Read<uint16>(); + Flags = stream->Read<uint16>(); } }; diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp index 0c7b7a492c5..a11dda85659 100644 --- a/src/tools/mesh_extractor/Geometry.cpp +++ b/src/tools/mesh_extractor/Geometry.cpp @@ -34,7 +34,7 @@ void Geometry::CalculateBoundingBox( float*& min, float*& max ) max = new float[3]; for (int i = 0; i < 3; ++i) { - max[i] = std::numeric_limits<float>::lowest(); + max[i] = -FLT_MAX; min[i] = std::numeric_limits<float>::max(); } @@ -60,7 +60,7 @@ void Geometry::CalculateBoundingBox( float*& min, float*& max ) void Geometry::CalculateMinMaxHeight( float& min, float& max ) { min = std::numeric_limits<float>::max(); - max = std::numeric_limits<float>::lowest(); + max = -FLT_MAX; for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr) { diff --git a/src/tools/mesh_extractor/LiquidHandler.cpp b/src/tools/mesh_extractor/LiquidHandler.cpp index eef36d8ea92..fa59d1bc7f0 100644 --- a/src/tools/mesh_extractor/LiquidHandler.cpp +++ b/src/tools/mesh_extractor/LiquidHandler.cpp @@ -32,7 +32,7 @@ void LiquidHandler::HandleNewLiquid() Vertices.reserve(1000); Triangles.reserve(1000); - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); H2OHeader header[256]; MCNKData.reserve(256); for (int i = 0; i < 256; i++) @@ -47,7 +47,7 @@ void LiquidHandler::HandleNewLiquid() MCNKData.push_back(MCNKLiquidData(NULL, H2ORenderMask())); continue; } - fseek(stream, chunk->Offset + h.OffsetInformation, SEEK_SET); + stream->Seek(chunk->Offset + h.OffsetInformation, SEEK_SET); H2OInformation information = H2OInformation::Read(stream); float** heights = new float*[9]; @@ -60,24 +60,22 @@ void LiquidHandler::HandleNewLiquid() H2ORenderMask renderMask; if (information.LiquidType != 2) { - fseek(stream, chunk->Offset + h.OffsetRender, SEEK_SET); + stream->Seek(chunk->Offset + h.OffsetRender, SEEK_SET); renderMask = H2ORenderMask::Read(stream); if ((Utils::IsAllZero(renderMask.Mask, 8) || (information.Width == 8 && information.Height == 8)) && information.OffsetMask2) { - fseek(stream, chunk->Offset + information.OffsetMask2, SEEK_SET); + stream->Seek(chunk->Offset + information.OffsetMask2, SEEK_SET); uint32 size = ceil(information.Width * information.Height / 8.0f); - uint8* altMask = new uint8[size]; - if (fread(altMask, sizeof(uint8), size, stream) == size) - for (uint32 mi = 0; mi < size; mi++) - renderMask.Mask[mi + information.OffsetY] |= altMask[mi]; + uint8* altMask = (uint8*)stream->Read(size); + for (uint32 mi = 0; mi < size; mi++) + renderMask.Mask[mi + information.OffsetY] |= altMask[mi]; delete[] altMask; } - fseek(stream, chunk->Offset + information.OffsetHeightmap, SEEK_SET); + stream->Seek(chunk->Offset + information.OffsetHeightmap, SEEK_SET); for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++) for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++) - if (fread(&heights[x][y], sizeof(float), 1, stream) != 1) - return; + heights[x][y] = stream->Read<float>(); } else { @@ -115,5 +113,10 @@ void LiquidHandler::HandleNewLiquid() Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1)); } } + + // At this stage, heights is no longer needed, so we deallocate it + for (int j = 0; j < 9; ++j) + delete[] heights[j]; + delete[] heights; } } diff --git a/src/tools/mesh_extractor/MPQ.cpp b/src/tools/mesh_extractor/MPQ.cpp index 5a2957cac0b..4a86ab0b4de 100644 --- a/src/tools/mesh_extractor/MPQ.cpp +++ b/src/tools/mesh_extractor/MPQ.cpp @@ -17,6 +17,7 @@ #include "MPQ.h" #include "MPQManager.h" +#include "Stream.h" #include <deque> #include <cstdio> @@ -125,15 +126,8 @@ void MPQFile::close() eof = true; } -FILE* MPQFile::GetFileStream() +Stream* MPQFile::GetFileStream() { - FILE* file = tmpfile(); - if (!file) - { - printf("Could not create temporary file. Please run as Administrator or root\n"); - exit(1); - } - fwrite(buffer, sizeof(char), size, file); - fseek(file, 0, SEEK_SET); - return file; + Stream* stream = new Stream(buffer, size); + return stream; } diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h index 17dbe74dcaa..c9b55545f1f 100644 --- a/src/tools/mesh_extractor/MPQ.h +++ b/src/tools/mesh_extractor/MPQ.h @@ -20,6 +20,7 @@ #include "libmpq/mpq.h" #include "Define.h" +#include "Stream.h" #include <string> #include <ctype.h> #include <vector> @@ -30,7 +31,7 @@ class MPQArchive { public: - mpq_archive_s *mpq_a; + mpq_archive_s* mpq_a; std::vector<std::string> Files; @@ -70,8 +71,8 @@ class MPQFile { //MPQHANDLE handle; bool eof; - char *buffer; - libmpq__off_t pointer,size; + char* buffer; + libmpq__off_t pointer, size; // disable copying MPQFile(const MPQFile& /*f*/) {} @@ -81,7 +82,7 @@ public: MPQFile(const char* filename); // filenames are not case sensitive ~MPQFile() { close(); } size_t Read(void* dest, size_t bytes); - FILE* GetFileStream(); + Stream* GetFileStream(); size_t getSize() { return size; } size_t getPos() { return pointer; } char* getBuffer() { return buffer; } diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp index 8954da6c154..82e4a2b5bff 100644 --- a/src/tools/mesh_extractor/MPQManager.cpp +++ b/src/tools/mesh_extractor/MPQManager.cpp @@ -19,6 +19,7 @@ #include "MPQ.h" #include "DBC.h" #include "Utils.h" +#include "Stream.h" #include <ace/Guard_T.h> char const* MPQManager::Files[] = { @@ -31,6 +32,12 @@ char const* MPQManager::Files[] = { "patch-3.MPQ" }; +char const* MPQManager::LocalePatchFiles[] = { + "Data/%s/patch-%s.MPQ", + "Data/%s/patch-%s-2.MPQ", + "Data/%s/patch-%s-3.MPQ" +}; + char const* MPQManager::Languages[] = { "enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" }; void MPQManager::Initialize() @@ -48,7 +55,6 @@ void MPQManager::Initialize() void MPQManager::InitializeDBC() { BaseLocale = -1; - std::string fileName; uint32 size = sizeof(Languages) / sizeof(char*); MPQArchive* _baseLocale = NULL; for (uint32 i = 0; i < size; ++i) @@ -58,20 +64,34 @@ void MPQManager::InitializeDBC() if (file) { if (BaseLocale == -1) - { BaseLocale = i; - _baseLocale = new MPQArchive(_fileName.c_str()); - fileName = _fileName; - LocaleFiles[i] = _baseLocale; + + // Load the base locale file + MPQArchive* arch = new MPQArchive(_fileName.c_str()); + LocaleFiles[i].push_front(arch); + + Archives.push_front(arch); // For lookup in GetFile + + // Load the locale patches + for (uint32 j = 0; j < sizeof(LocalePatchFiles) / sizeof(char*); ++j) + { + char patchName[100]; + sprintf(patchName, LocalePatchFiles[j], Languages[i], Languages[i]); + FILE* patch = fopen(patchName, "rb"); + if (file) + { + MPQArchive* archP = new MPQArchive(patchName); + LocaleFiles[i].push_front(archP); + Archives.push_front(archP); // For lookup in GetFile + fclose(patch); + } } - else - LocaleFiles[i] = new MPQArchive(_fileName.c_str()); AvailableLocales.insert(i); printf("Detected locale: %s\n", Languages[i]); } } - Archives.push_front(_baseLocale); + if (BaseLocale == -1) { printf("No locale data detected. Please make sure that the executable is in the same folder as your WoW installation.\n"); @@ -81,7 +101,7 @@ void MPQManager::InitializeDBC() printf("Using default locale: %s\n", Languages[BaseLocale]); } -FILE* MPQManager::GetFile(const std::string& path ) +Stream* MPQManager::GetFile(const std::string& path ) { ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); MPQFile file(path.c_str()); @@ -96,7 +116,40 @@ DBC* MPQManager::GetDBC(const std::string& name ) return new DBC(GetFile(path)); } -FILE* MPQManager::GetFileFrom(const std::string& path, MPQArchive* file ) +Stream* MPQManager::GetFileFromLocale( const std::string& path, uint32 locale ) +{ + ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); + std::deque<MPQArchive*> files = LocaleFiles[locale]; + Stream* ret = NULL; + for (std::deque<MPQArchive*>::iterator itr = files.begin(); itr != files.end(); ++itr) + { + mpq_archive* mpq_a = (*itr)->mpq_a; + + uint32_t filenum; + if(libmpq__file_number(mpq_a, path.c_str(), &filenum)) + continue; + libmpq__off_t transferred; + libmpq__off_t size = 0; + libmpq__file_unpacked_size(mpq_a, filenum, &size); + + // HACK: in patch.mpq some files don't want to open and give 1 for filesize + if (size <= 1) + continue; + + char* buffer = new char[size]; + + //libmpq_file_getdata + libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); + + ret = new Stream(buffer, size); + + delete[] buffer; + break; + } + return ret; +} + +Stream* MPQManager::GetFileFrom(const std::string& path, MPQArchive* file ) { ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); mpq_archive* mpq_a = file->mpq_a; @@ -119,14 +172,7 @@ FILE* MPQManager::GetFileFrom(const std::string& path, MPQArchive* file ) libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); // Pack the return into a FILE stream - FILE* ret = tmpfile(); - if (!ret) - { - printf("Could not create temporary file. Please run as Administrator or root\n"); - exit(1); - } - fwrite(buffer, sizeof(uint8), size, ret); - fseek(ret, 0, SEEK_SET); + Stream* ret = new Stream((char*)buffer, size); delete[] buffer; return ret; } diff --git a/src/tools/mesh_extractor/MPQManager.h b/src/tools/mesh_extractor/MPQManager.h index 6172237df49..32a60917c82 100644 --- a/src/tools/mesh_extractor/MPQManager.h +++ b/src/tools/mesh_extractor/MPQManager.h @@ -19,6 +19,7 @@ #define MPQ_MANAGER_H #include "MPQ.h" +#include "Stream.h" #include <ace/Synch.h> #include <set> #include <map> @@ -31,17 +32,20 @@ public: ~MPQManager() {} void Initialize(); - FILE* GetFile(const std::string& path); - FILE* GetFileFrom(const std::string& path, MPQArchive* file); + Stream* GetFile(const std::string& path); + Stream* GetFileFrom(const std::string& path, MPQArchive* file); + Stream* GetFileFromLocale(const std::string& path, uint32 locale); + DBC* GetDBC(const std::string& name); std::vector<std::string> GetAllFiles(std::string extension); std::deque<MPQArchive*> Archives; int32 BaseLocale; std::set<uint32> AvailableLocales; - std::map<uint32, MPQArchive*> LocaleFiles; + std::map<uint32, std::deque<MPQArchive*> > LocaleFiles; static char const* Files[]; + static char const* LocalePatchFiles[]; static char const* Languages[]; protected: void InitializeDBC(); diff --git a/src/tools/mesh_extractor/MapChunk.cpp b/src/tools/mesh_extractor/MapChunk.cpp index 4cd5afad3f1..f9d49762209 100644 --- a/src/tools/mesh_extractor/MapChunk.cpp +++ b/src/tools/mesh_extractor/MapChunk.cpp @@ -21,9 +21,9 @@ MapChunk::MapChunk( ADT* _adt, Chunk* chunk ) : Adt(_adt), Source(chunk) { - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); Header.Read(stream); - fseek(stream, chunk->Offset, SEEK_SET); + stream->Seek(chunk->Offset, SEEK_SET); Index = Header.IndexX + Header.IndexY * 16; GenerateVertices(stream); } @@ -64,9 +64,9 @@ void MapChunk::GenerateTriangles() } } -void MapChunk::GenerateVertices( FILE* stream ) +void MapChunk::GenerateVertices(Stream* stream) { - fseek(stream, Header.OffsetMCVT, SEEK_CUR); + stream->Seek(Header.OffsetMCVT, SEEK_CUR); Vertices.reserve(125); for (int j = 0; j < 17; j++) @@ -74,9 +74,7 @@ void MapChunk::GenerateVertices( FILE* stream ) int values = j % 2 ? 8 : 9; for (int i = 0; i < values; i++) { - float tmp; - if (fread(&tmp, sizeof(float), 1, stream) != 1) - printf("MapChunk::GenerateVertices: Failed to read some data expected 1, read 0\n"); + float tmp = stream->Read<float>(); Vector3 vert(Header.Position.x - (j * (Constants::UnitSize * 0.5f)), Header.Position.y - (i * Constants::UnitSize), Header.Position.z + tmp); if (values == 8) vert.y -= Constants::UnitSize * 0.5f; @@ -84,7 +82,7 @@ void MapChunk::GenerateVertices( FILE* stream ) } } // Restore stream position. - fseek(stream, Source->Offset, SEEK_SET); + stream->Seek(Source->Offset, SEEK_SET); } bool MapChunk::HasHole( uint32 map, int x, int y ) diff --git a/src/tools/mesh_extractor/MapChunk.h b/src/tools/mesh_extractor/MapChunk.h index 95bce9ffcd6..c5127b08002 100644 --- a/src/tools/mesh_extractor/MapChunk.h +++ b/src/tools/mesh_extractor/MapChunk.h @@ -29,7 +29,7 @@ public: MapChunk(ADT* _adt, Chunk* chunk); void GenerateTriangles(); - void GenerateVertices(FILE* stream); + void GenerateVertices(Stream* stream); static bool HasHole(uint32 map, int x, int y); ADT* Adt; Chunk* Source; diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp index e60e11b1db8..1f123a6452c 100644 --- a/src/tools/mesh_extractor/MeshExtractor.cpp +++ b/src/tools/mesh_extractor/MeshExtractor.cpp @@ -34,16 +34,41 @@ MPQManager* MPQHandler; CacheClass* Cache; +bool IgnoreMap(uint32 id) +{ + switch (id) + { + case 13: // test.wdt + case 25: // ScottTest.wdt + case 29: // Test.wdt + case 42: // Colin.wdt + case 169: // EmeraldDream.wdt (unused, and very large) + case 451: // development.wdt + case 573: // ExteriorTest.wdt + case 597: // CraigTest.wdt + case 605: // development_nonweighted.wdt + case 606: // QA_DVD.wdt + return true; + default: + break; + } + + return false; +} + void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads) { + std::string basePath = "mmaps/"; + Utils::CreateDir(basePath); + DBC* dbc = MPQHandler->GetDBC("Map"); printf("Map.dbc contains %u rows.\n", dbc->Records.size()); for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr) { uint32 mapId = (*itr)->Values[0]; - // Skip this map if a list of specific maps was provided and this one is not contained in it. - if (!mapIds.empty() && mapIds.find(mapId) == mapIds.end()) + // Skip this map if a list of specific maps was provided and this one is not contained in it, or if the map is in the ignore list. + if ((!mapIds.empty() && mapIds.find(mapId) == mapIds.end()) || IgnoreMap(mapId)) { if (Constants::Debug) printf("Map %u will not be built.\n", mapId); @@ -75,9 +100,12 @@ void ExtractDBCs() // Populate list of DBC files // We get the DBC names by going over the (guaranteed to exist) default locale files // Then we look in other locale files in case that they are available. - for (std::vector<std::string>::iterator itr = MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.begin(); itr != MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.end(); ++itr) - if (itr->rfind(".dbc") == itr->length() - extLen) // Check if the extension is ".dbc" - DBCFiles.insert(*itr); + for (std::map<uint32, std::deque<MPQArchive*> >::iterator itr = MPQHandler->LocaleFiles.begin(); itr != MPQHandler->LocaleFiles.end(); ++itr) + for (std::deque<MPQArchive*>::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2) + for (std::vector<std::string>::iterator itr3 = (*itr2)->Files.begin(); itr3 != (*itr2)->Files.end(); ++itr3) + if (itr3->rfind(".dbc") == itr3->length() - extLen) // Check if the extension is ".dbc" + if (DBCFiles.find(*itr3) == DBCFiles.end()) + DBCFiles.insert(*itr3); const size_t folderLen = strlen("DBFilesClient\\"); // Iterate over all available locales @@ -93,10 +121,10 @@ void ExtractDBCs() std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt"; // Extract the component file - Utils::SaveToDisk(MPQHandler->GetFileFrom(component, MPQHandler->LocaleFiles[*itr]), path + component); + Utils::SaveToDisk(MPQHandler->GetFileFromLocale(component, *itr), path + component); // Extract the DBC files for the given locale for (std::set<std::string>::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2) - Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + folderLen)); + Utils::SaveToDisk(MPQHandler->GetFileFromLocale(*itr2, *itr), path + (itr2->c_str() + folderLen)); } printf("DBC extraction finished!\n"); } @@ -363,7 +391,6 @@ void LoadTile(dtNavMesh*& navMesh, const char* tile) int main(int argc, char* argv[]) { - _setmaxstdio(2048); uint32 threads = 4, extractFlags = 0; std::set<uint32> mapIds; @@ -395,8 +422,8 @@ int main(int argc, char* argv[]) if (extractFlags & Constants::EXTRACT_FLAG_TEST) { - float start[] = { 16226.200195f, 16257.000000f, 13.202200f }; - float end[] = { 16245.725586f, 16382.465820f, 47.384956f }; + float start[] = { -1.37402868f, -21.7641087f, -20.1751060f }; + float end[] = { -22.756405f, -62.745014f, -21.371508f }; // float m_spos[3]; @@ -425,7 +452,7 @@ int main(int argc, char* argv[]) dtPolyRef m_startRef; dtPolyRef m_endRef; - FILE* mmap = fopen("mmaps/001.mmap", "rb"); + FILE* mmap = fopen("mmaps/389.mmap", "rb"); dtNavMeshParams params; int count = fread(¶ms, sizeof(dtNavMeshParams), 1, mmap); fclose(mmap); @@ -444,7 +471,7 @@ int main(int argc, char* argv[]) for (int j = 0; j <= 32; ++j) { char buff[100]; - sprintf(buff, "mmaps/001%02i%02i.mmtile", i, j); + sprintf(buff, "mmaps/389%02i%02i.mmtile", i, j); LoadTile(navMesh, buff); } } @@ -474,7 +501,7 @@ int main(int argc, char* argv[]) status = navMeshQuery->findStraightPath(m_spos, m_epos, hopBuffer, hops, straightPath, pathFlags, pathRefs, &resultHopCount, 2048); std::vector<Vector3> FinalPath; FinalPath.reserve(resultHopCount); - for (uint32 i = 0; i < resultHopCount; ++i) + for (int i = 0; i < resultHopCount; ++i) { Vector3 finalV = Utils::ToWoWCoords(Vector3(straightPath[i * 3 + 0], straightPath[i * 3 + 1], straightPath[i * 3 + 2])); FinalPath.push_back(finalV); diff --git a/src/tools/mesh_extractor/Model.cpp b/src/tools/mesh_extractor/Model.cpp index 2a1a80f41eb..f88f620720c 100644 --- a/src/tools/mesh_extractor/Model.cpp +++ b/src/tools/mesh_extractor/Model.cpp @@ -19,15 +19,15 @@ #include "MPQManager.h" #include "Utils.h" -Model::Model( std::string path ) : IsCollidable(false), IsBad(false) +Model::Model(std::string path) : IsCollidable(false), IsBad(false) { - Stream = MPQHandler->GetFile(Utils::FixModelPath(path)); - if (!Stream) + _Stream = MPQHandler->GetFile(Utils::FixModelPath(path)); + if (!_Stream) { IsBad = true; return; } - Header.Read(Stream); + Header.Read(_Stream); if (Header.OffsetBoundingNormals > 0 && Header.OffsetBoundingVertices > 0 && Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f) { @@ -40,17 +40,17 @@ Model::Model( std::string path ) : IsCollidable(false), IsBad(false) Model::~Model() { - if (Stream) - fclose(Stream); + if (_Stream) + delete _Stream; } void Model::ReadVertices() { - fseek(Stream, Header.OffsetBoundingVertices, SEEK_SET); + _Stream->Seek(Header.OffsetBoundingVertices, SEEK_SET); Vertices.reserve(Header.CountBoundingVertices); for (uint32 i = 0; i < Header.CountBoundingVertices; ++i) { - Vertices.push_back(Vector3::Read(Stream)); + Vertices.push_back(Vector3::Read(_Stream)); if (Constants::ToWoWCoords) Vertices[i] = Utils::ToWoWCoords(Vertices[i]); } @@ -58,27 +58,24 @@ void Model::ReadVertices() void Model::ReadBoundingTriangles() { - fseek(Stream, Header.OffsetBoundingTriangles, SEEK_SET); + _Stream->Seek(Header.OffsetBoundingTriangles, SEEK_SET); Triangles.reserve(Header.CountBoundingTriangles / 3); for (uint32 i = 0; i < Header.CountBoundingTriangles / 3; i++) { Triangle<uint16> tri; tri.Type = Constants::TRIANGLE_TYPE_DOODAD; - int count = 0; - count += fread(&tri.V0, sizeof(uint16), 1, Stream); - count += fread(&tri.V1, sizeof(uint16), 1, Stream); - count += fread(&tri.V2, sizeof(uint16), 1, Stream); - if (count != 3) - printf("Model::ReadBoundingTriangles: Error reading data, expected 3, read %d\n", count); + tri.V0 = _Stream->Read<uint16>(); + tri.V1 = _Stream->Read<uint16>(); + tri.V2 = _Stream->Read<uint16>(); Triangles.push_back(tri); } } void Model::ReadBoundingNormals() { - fseek(Stream, Header.OffsetBoundingNormals, SEEK_SET); + _Stream->Seek(Header.OffsetBoundingNormals, SEEK_SET); Normals.reserve(Header.CountBoundingNormals); for (uint32 i = 0; i < Header.CountBoundingNormals; i++) - Normals.push_back(Vector3::Read(Stream)); + Normals.push_back(Vector3::Read(_Stream)); } diff --git a/src/tools/mesh_extractor/Model.h b/src/tools/mesh_extractor/Model.h index a1f224729dd..9b460b1e2b6 100644 --- a/src/tools/mesh_extractor/Model.h +++ b/src/tools/mesh_extractor/Model.h @@ -19,6 +19,7 @@ #define MODEL_H #include <vector> #include "Utils.h" +#include "Stream.h" class Model { @@ -34,7 +35,7 @@ public: std::vector<Vector3> Normals; std::vector<Triangle<uint16> > Triangles; bool IsCollidable; - FILE* Stream; + Stream* _Stream; bool IsBad; }; #endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/Stream.cpp b/src/tools/mesh_extractor/Stream.cpp new file mode 100644 index 00000000000..f775d97d114 --- /dev/null +++ b/src/tools/mesh_extractor/Stream.cpp @@ -0,0 +1,47 @@ +#include "Stream.h" +#include <iostream> + +Stream::Stream(char* buffer, uint32 size) : _size(size), _position(0) +{ + _buffer = new char[size]; + memcpy(_buffer, buffer, size); // Initialize the buffer +} + +Stream::~Stream() +{ + delete[] _buffer; +} + +char* Stream::Read(uint32 size) +{ + char* buff = new char[size]; + memcpy(buff, &_buffer[_position], size); + _position += size; + return buff; +} + +void Stream::Seek(uint32 position, uint32 type) +{ + switch (type) + { + case SEEK_SET: + _position = position; + break; + case SEEK_CUR: + _position += position; + break; + } +} + +std::string Stream::ReadString() +{ + std::string str; + while (true) + { + char b = Read<char>(); + if (b == 0) + break; + str.push_back(b); + } + return str; +}
\ No newline at end of file diff --git a/src/tools/mesh_extractor/Stream.h b/src/tools/mesh_extractor/Stream.h new file mode 100644 index 00000000000..76d9511bbc4 --- /dev/null +++ b/src/tools/mesh_extractor/Stream.h @@ -0,0 +1,53 @@ +#ifndef STREAM_H +#define STREAM_H + +#include "Define.h" +#include <iostream> + +class Stream +{ +public: + Stream(char* buffer, uint32 size); + ~Stream(); + + template<typename T> + T Read() + { + T ret = *((T*)(&_buffer[_position])); + _position += sizeof(T); + return ret; + } + + template<typename T> + void Read(T* dest, uint32 size) + { + memcpy(dest, &_buffer[_position], size); + _position += size; + } + + char* Read(uint32 size); + std::string ReadString(); + + void Reset() + { + _position = 0; + } + + void Seek(uint32 position, uint32 type); + + uint32 GetSize() + { + return _size; + } + + uint32 GetPos() + { + return _position; + } + +private: + char* _buffer; + uint32 _size; + uint32 _position; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp index 7b70ccca44c..1a3a90f2168 100644 --- a/src/tools/mesh_extractor/TileBuilder.cpp +++ b/src/tools/mesh_extractor/TileBuilder.cpp @@ -59,13 +59,13 @@ TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, InstanceConfig.mergeRegionArea = 100; InstanceConfig.walkableSlopeAngle = 50.0f; InstanceConfig.detailSampleDist = 3.0f; - InstanceConfig.detailSampleMaxError = 1.5f; + InstanceConfig.detailSampleMaxError = 1.25f; InstanceConfig.walkableClimb = 1.0f / InstanceConfig.ch; InstanceConfig.walkableHeight = 2.1f / InstanceConfig.ch; InstanceConfig.walkableRadius = 0.6f / InstanceConfig.cs; InstanceConfig.maxEdgeLen = 8 * InstanceConfig.walkableRadius; InstanceConfig.maxVertsPerPoly = 6; - InstanceConfig.maxSimplificationError = 1.25f; + InstanceConfig.maxSimplificationError = 1.3f; InstanceConfig.borderSize = 0; Context = new rcContext; @@ -183,11 +183,11 @@ uint8* TileBuilder::BuildInstance( dtNavMeshParams& navMeshParams ) rcFreeHeightField(hf); rcFreeCompactHeightfield(chf); rcFreeContourSet(contours); - delete vertices; - delete triangles; - delete areas; - delete bmin; - delete bmax; + delete[] vertices; + delete[] triangles; + delete[] areas; + delete[] bmin; + delete[] bmax; if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount) { @@ -234,7 +234,8 @@ uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) CalculateTileBounds(bmin, bmax, navMeshParams); _Geometry->CalculateMinMaxHeight(bmin[1], bmax[1]); - // again, we load everything - wasteful but who cares + // This is commented out to reduce the size of the resulting files (and the time it takes to generate them), we shouldn't need to load 4 more ADTs each time. + /*// again, we load everything - wasteful but who cares for (int ty = Y - 1; ty <= Y + 1; ty++) { for (int tx = X - 1; tx <= X + 1; tx++) @@ -245,7 +246,7 @@ uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty), tx, ty); // If this condition is met, it means that this WDT does not contain the ADT - if (!_adt->Data->Stream) + if (!_adt->Data->_Stream) { delete _adt; continue; @@ -254,7 +255,7 @@ uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) _Geometry->AddAdt(_adt); delete _adt; } - } + }*/ OutputDebugVertices(); @@ -351,11 +352,11 @@ uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) rcFreeHeightField(hf); rcFreeCompactHeightfield(chf); rcFreeContourSet(contours); - delete vertices; - delete triangles; - delete areas; - delete bmin; - delete bmax; + delete[] vertices; + delete[] triangles; + delete[] areas; + delete[] bmin; + delete[] bmax; if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount) { diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp index f82eb6ed1c8..937de4b443a 100644 --- a/src/tools/mesh_extractor/Utils.cpp +++ b/src/tools/mesh_extractor/Utils.cpp @@ -18,6 +18,7 @@ #include "Utils.h" #include "WorldModelHandler.h" #include "Constants.h" +#include "Stream.h" #include <cstring> #include "G3D/Matrix4.h" #include "G3D/Quat.h" @@ -53,40 +54,9 @@ void Utils::CreateDir( const std::string& Path ) #endif } -void Utils::Reverse(char word[]) +void Utils::Reverse(std::string& str) { - int len = strlen(word); - for (int i = 0;i < len / 2; i++) - { - word[i] ^= word[len-i-1]; - word[len-i-1] ^= word[i]; - word[i] ^= word[len-i-1]; - } -} - -std::string Utils::ReadString( FILE* file ) -{ - std::string ret; - while (true) - { - char b; - if (fread(&b, sizeof(char), 1, file) != 1 || b == 0) - break; - ret.push_back(b); - } - return ret; -} - -uint32 Utils::Size( FILE* file ) -{ - // store the old position - uint32 offset = ftell(file); - // Get file size - fseek(file, 0, SEEK_END); - uint32 size = ftell(file); - // reset back to the old position - fseek(file, offset, SEEK_SET); - return size; + std::reverse(str.begin(), str.end()); } Vector3 Utils::ToRecast(const Vector3& val ) @@ -104,7 +74,7 @@ std::string Utils::FixModelPath(const std::string& path ) return Utils::GetPathBase(path) + ".M2"; } -Vector3 Utils::TransformDoodadVertex(const IDefinition& def, Vector3& vec, bool translate) +Vector3 Utils::TransformDoodadVertex(const IDefinition& def, Vector3 vec, bool translate) { // Sources of information: /// http://www.pxr.dk/wowdev/wiki/index.php?title=ADT/v18&oldid=3715 @@ -153,12 +123,9 @@ std::string Utils::GetPathBase(const std::string& path ) return path; } -Vector3 Vector3::Read( FILE* file ) +Vector3 Vector3::Read(Stream* file) { - Vector3 ret; - if (fread(&ret, sizeof(Vector3), 1, file) != 1) - printf("Vector3::Read: Failed to read some data expected 1, read 0\n"); - return ret; + return file->Read<Vector3>(); } Vector3 Utils::GetLiquidVert(const IDefinition& def, Vector3 basePosition, float height, int x, int y, bool translate) @@ -184,41 +151,36 @@ std::string Utils::Replace( std::string str, const std::string& oldStr, const st return str; } -void Utils::SaveToDisk( FILE* stream, const std::string& path ) +void Utils::SaveToDisk(Stream* stream, const std::string& path) { FILE* disk = fopen(path.c_str(), "wb"); if (!disk) { printf("SaveToDisk: Could not save file %s to disk, please verify that you have write permissions on that directory\n", path.c_str()); - fclose(stream); + delete stream; return; } - uint32 size = Utils::Size(stream); - uint8* data = new uint8[size]; - // Read the data to an array - size_t read = fread(data, size, 1, stream); - if (read != 1) - { - printf("SaveToDisk: Error reading from Stream while trying to save file %s to disk.\n", path.c_str()); - fclose(disk); - fclose(stream); - return; - } + uint32 size = stream->GetSize(); + stream->Reset(); // Reset the stream just in case + // Read the data to an array + char* data = stream->Read(size); + // And write it in the file size_t wrote = fwrite(data, size, 1, disk); if (wrote != 1) { printf("SaveToDisk: Error writing to the file while trying to save %s to disk.\n", path.c_str()); - fclose(stream); + delete[] data; + delete stream; fclose(disk); return; } // Close the filestream fclose(disk); - fclose(stream); + delete stream; // Free the used memory delete[] data; @@ -239,249 +201,193 @@ std::string Utils::GetExtension( std::string path ) return extension; } -void MapChunkHeader::Read(FILE* stream) -{ - int count = 0; - - count += fread(&Flags, sizeof(uint32), 1, stream); - count += fread(&IndexX, sizeof(uint32), 1, stream); - count += fread(&IndexY, sizeof(uint32), 1, stream); - count += fread(&Layers, sizeof(uint32), 1, stream); - count += fread(&DoodadRefs, sizeof(uint32), 1, stream); - count += fread(&OffsetMCVT, sizeof(uint32), 1, stream); - count += fread(&OffsetMCNR, sizeof(uint32), 1, stream); - count += fread(&OffsetMCLY, sizeof(uint32), 1, stream); - count += fread(&OffsetMCRF, sizeof(uint32), 1, stream); - count += fread(&OffsetMCAL, sizeof(uint32), 1, stream); - count += fread(&SizeMCAL, sizeof(uint32), 1, stream); - count += fread(&OffsetMCSH, sizeof(uint32), 1, stream); - count += fread(&SizeMCSH, sizeof(uint32), 1, stream); - count += fread(&AreaId, sizeof(uint32), 1, stream); - count += fread(&MapObjectRefs, sizeof(uint32), 1, stream); - count += fread(&Holes, sizeof(uint32), 1, stream); - LowQualityTextureMap = new uint32[4]; - count += fread(LowQualityTextureMap, sizeof(uint32), 4, stream); - count += fread(&PredTex, sizeof(uint32), 1, stream); - count += fread(&NumberEffectDoodad, sizeof(uint32), 1, stream); - count += fread(&OffsetMCSE, sizeof(uint32), 1, stream); - count += fread(&SoundEmitters, sizeof(uint32), 1, stream); - count += fread(&OffsetMCLQ, sizeof(uint32), 1, stream); - count += fread(&SizeMCLQ, sizeof(uint32), 1, stream); +void MapChunkHeader::Read(Stream* stream) +{ + Flags = stream->Read<uint32>(); + IndexX = stream->Read<uint32>(); + IndexY = stream->Read<uint32>(); + Layers = stream->Read<uint32>(); + DoodadRefs = stream->Read<uint32>(); + OffsetMCVT = stream->Read<uint32>(); + OffsetMCNR = stream->Read<uint32>(); + OffsetMCLY = stream->Read<uint32>(); + OffsetMCRF = stream->Read<uint32>(); + OffsetMCAL = stream->Read<uint32>(); + SizeMCAL = stream->Read<uint32>(); + OffsetMCSH = stream->Read<uint32>(); + SizeMCSH = stream->Read<uint32>(); + AreaId = stream->Read<uint32>(); + MapObjectRefs = stream->Read<uint32>(); + Holes = stream->Read<uint32>(); + stream->Read(LowQualityTextureMap, sizeof(uint32) * 4); + PredTex = stream->Read<uint32>(); + NumberEffectDoodad = stream->Read<uint32>(); + OffsetMCSE = stream->Read<uint32>(); + SoundEmitters = stream->Read<uint32>(); + OffsetMCLQ = stream->Read<uint32>(); + SizeMCLQ = stream->Read<uint32>(); Position = Vector3::Read(stream); - count += fread(&OffsetMCCV, sizeof(uint32), 1, stream); - - if (count != 27) - printf("MapChunkHeader::Read: Failed to read some data expected 27, read %d\n", count); + OffsetMCCV = stream->Read<uint32>(); } -void MHDR::Read(FILE* stream) +void MHDR::Read(Stream* stream) { - int count = 0; - - count += fread(&Flags, sizeof(uint32), 1, stream); - count += fread(&OffsetMCIN, sizeof(uint32), 1, stream); - count += fread(&OffsetMTEX, sizeof(uint32), 1, stream); - count += fread(&OffsetMMDX, sizeof(uint32), 1, stream); - count += fread(&OffsetMMID, sizeof(uint32), 1, stream); - count += fread(&OffsetMWMO, sizeof(uint32), 1, stream); - count += fread(&OffsetMWID, sizeof(uint32), 1, stream); - count += fread(&OffsetMDDF, sizeof(uint32), 1, stream); - count += fread(&OffsetMODF, sizeof(uint32), 1, stream); - count += fread(&OffsetMFBO, sizeof(uint32), 1, stream); - count += fread(&OffsetMH2O, sizeof(uint32), 1, stream); - count += fread(&OffsetMTFX, sizeof(uint32), 1, stream); - - if (count != 12) - printf("MHDR::Read: Failed to read some data expected 12, read %d\n", count); + Flags = stream->Read<uint32>(); + OffsetMCIN = stream->Read<uint32>(); + OffsetMTEX = stream->Read<uint32>(); + OffsetMMDX = stream->Read<uint32>(); + OffsetMMID = stream->Read<uint32>(); + OffsetMWMO = stream->Read<uint32>(); + OffsetMWID = stream->Read<uint32>(); + OffsetMDDF = stream->Read<uint32>(); + OffsetMODF = stream->Read<uint32>(); + OffsetMFBO = stream->Read<uint32>(); + OffsetMH2O = stream->Read<uint32>(); + OffsetMTFX = stream->Read<uint32>(); } -void ModelHeader::Read(FILE* stream) +void ModelHeader::Read(Stream* stream) { - int count = 0; - - count += fread(&Magic, sizeof(char), 4, stream); + stream->Read(Magic, 4); Magic[4] = '\0'; // null-terminate it. - count += fread(&Version, sizeof(uint32), 1, stream); - count += fread(&LengthModelName, sizeof(uint32), 1, stream); - count += fread(&OffsetName, sizeof(uint32), 1, stream); - count += fread(&ModelFlags, sizeof(uint32), 1, stream); - count += fread(&CountGlobalSequences, sizeof(uint32), 1, stream); - count += fread(&OffsetGlobalSequences, sizeof(uint32), 1, stream); - count += fread(&CountAnimations, sizeof(uint32), 1, stream); - count += fread(&OffsetAnimations, sizeof(uint32), 1, stream); - count += fread(&CountAnimationLookup, sizeof(uint32), 1, stream); - count += fread(&OffsetAnimationLookup, sizeof(uint32), 1, stream); - count += fread(&CountBones, sizeof(uint32), 1, stream); - count += fread(&OffsetBones, sizeof(uint32), 1, stream); - count += fread(&CountKeyBoneLookup, sizeof(uint32), 1, stream); - count += fread(&OffsetKeyBoneLookup, sizeof(uint32), 1, stream); - count += fread(&CountVertices, sizeof(uint32), 1, stream); - count += fread(&OffsetVertices, sizeof(uint32), 1, stream); - count += fread(&CountViews, sizeof(uint32), 1, stream); - count += fread(&CountColors, sizeof(uint32), 1, stream); - count += fread(&OffsetColors, sizeof(uint32), 1, stream); - count += fread(&CountTextures, sizeof(uint32), 1, stream); - count += fread(&OffsetTextures, sizeof(uint32), 1, stream); - count += fread(&CountTransparency, sizeof(uint32), 1, stream); - count += fread(&OffsetTransparency, sizeof(uint32), 1, stream); - count += fread(&CountUvAnimation, sizeof(uint32), 1, stream); - count += fread(&OffsetUvAnimation, sizeof(uint32), 1, stream); - count += fread(&CountTexReplace, sizeof(uint32), 1, stream); - count += fread(&OffsetTexReplace, sizeof(uint32), 1, stream); - count += fread(&CountRenderFlags, sizeof(uint32), 1, stream); - count += fread(&OffsetRenderFlags, sizeof(uint32), 1, stream); - count += fread(&CountBoneLookup, sizeof(uint32), 1, stream); - count += fread(&OffsetBoneLookup, sizeof(uint32), 1, stream); - count += fread(&CountTexLookup, sizeof(uint32), 1, stream); - count += fread(&OffsetTexLookup, sizeof(uint32), 1, stream); - count += fread(&CountTexUnits, sizeof(uint32), 1, stream); - count += fread(&OffsetTexUnits, sizeof(uint32), 1, stream); - count += fread(&CountTransLookup, sizeof(uint32), 1, stream); - count += fread(&OffsetTransLookup, sizeof(uint32), 1, stream); - count += fread(&CountUvAnimLookup, sizeof(uint32), 1, stream); - count += fread(&OffsetUvAnimLookup, sizeof(uint32), 1, stream); + Version = stream->Read<uint32>(); + LengthModelName = stream->Read<uint32>(); + OffsetName = stream->Read<uint32>(); + ModelFlags = stream->Read<uint32>(); + CountGlobalSequences = stream->Read<uint32>(); + OffsetGlobalSequences = stream->Read<uint32>(); + CountAnimations = stream->Read<uint32>(); + OffsetAnimations = stream->Read<uint32>(); + CountAnimationLookup = stream->Read<uint32>(); + OffsetAnimationLookup = stream->Read<uint32>(); + CountBones = stream->Read<uint32>(); + OffsetBones = stream->Read<uint32>(); + CountKeyBoneLookup = stream->Read<uint32>(); + OffsetKeyBoneLookup = stream->Read<uint32>(); + CountVertices = stream->Read<uint32>(); + OffsetVertices = stream->Read<uint32>(); + CountViews = stream->Read<uint32>(); + CountColors = stream->Read<uint32>(); + OffsetColors = stream->Read<uint32>(); + CountTextures = stream->Read<uint32>(); + OffsetTextures = stream->Read<uint32>(); + CountTransparency = stream->Read<uint32>(); + OffsetTransparency = stream->Read<uint32>(); + CountUvAnimation = stream->Read<uint32>(); + OffsetUvAnimation = stream->Read<uint32>(); + CountTexReplace = stream->Read<uint32>(); + OffsetTexReplace = stream->Read<uint32>(); + CountRenderFlags = stream->Read<uint32>(); + OffsetRenderFlags = stream->Read<uint32>(); + CountBoneLookup = stream->Read<uint32>(); + OffsetBoneLookup = stream->Read<uint32>(); + CountTexLookup = stream->Read<uint32>(); + OffsetTexLookup = stream->Read<uint32>(); + CountTexUnits = stream->Read<uint32>(); + OffsetTexUnits = stream->Read<uint32>(); + CountTransLookup = stream->Read<uint32>(); + OffsetTransLookup = stream->Read<uint32>(); + CountUvAnimLookup = stream->Read<uint32>(); + OffsetUvAnimLookup = stream->Read<uint32>(); VertexBox[0] = Vector3::Read(stream); VertexBox[1] = Vector3::Read(stream); - count += fread(&VertexRadius, sizeof(float), 1, stream); + VertexRadius = stream->Read<float>(); BoundingBox[0] = Vector3::Read(stream); BoundingBox[1] = Vector3::Read(stream); - count += fread(&BoundingRadius, sizeof(float), 1, stream); - count += fread(&CountBoundingTriangles, sizeof(uint32), 1, stream); - count += fread(&OffsetBoundingTriangles, sizeof(uint32), 1, stream); - count += fread(&CountBoundingVertices, sizeof(uint32), 1, stream); - count += fread(&OffsetBoundingVertices, sizeof(uint32), 1, stream); - count += fread(&CountBoundingNormals, sizeof(uint32), 1, stream); - count += fread(&OffsetBoundingNormals, sizeof(uint32), 1, stream); - - if (count != 51) - printf("ModelHeader::Read: Failed to read some data expected 51, read %d\n", count); - -} - -WorldModelHeader WorldModelHeader::Read(FILE* stream) -{ - WorldModelHeader ret; - int count = 0; - - count += fread(&ret.CountMaterials, sizeof(uint32), 1, stream); - count += fread(&ret.CountGroups, sizeof(uint32), 1, stream); - count += fread(&ret.CountPortals, sizeof(uint32), 1, stream); - count += fread(&ret.CountLights, sizeof(uint32), 1, stream); - count += fread(&ret.CountModels, sizeof(uint32), 1, stream); - count += fread(&ret.CountDoodads, sizeof(uint32), 1, stream); - count += fread(&ret.CountSets, sizeof(uint32), 1, stream); - count += fread(&ret.AmbientColorUnk, sizeof(uint32), 1, stream); - count += fread(&ret.WmoId, sizeof(uint32), 1, stream); - ret.BoundingBox[0] = Vector3::Read(stream); - ret.BoundingBox[1] = Vector3::Read(stream); - count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream); - - if (count != 10) - printf("WorldModelHeader::Read: Failed to read some data expected 10, read %d\n", count); - - return ret; + BoundingRadius = stream->Read<float>(); + CountBoundingTriangles = stream->Read<uint32>(); + OffsetBoundingTriangles = stream->Read<uint32>(); + CountBoundingVertices = stream->Read<uint32>(); + OffsetBoundingVertices = stream->Read<uint32>(); + CountBoundingNormals = stream->Read<uint32>(); + OffsetBoundingNormals = stream->Read<uint32>(); +} + +void WorldModelHeader::Read(Stream* stream) +{ + CountMaterials = stream->Read<uint32>(); + CountGroups = stream->Read<uint32>(); + CountPortals = stream->Read<uint32>(); + CountLights = stream->Read<uint32>(); + CountModels = stream->Read<uint32>(); + CountDoodads = stream->Read<uint32>(); + CountSets = stream->Read<uint32>(); + AmbientColorUnk = stream->Read<uint32>(); + WmoId = stream->Read<uint32>(); + BoundingBox[0] = Vector3::Read(stream); + BoundingBox[1] = Vector3::Read(stream); + LiquidTypeRelated = stream->Read<uint32>(); } -DoodadInstance DoodadInstance::Read(FILE* stream) +DoodadInstance DoodadInstance::Read(Stream* stream) { DoodadInstance ret; - int count = 0; - count += fread(&ret.FileOffset, sizeof(uint32), 1, stream); + ret.FileOffset = stream->Read<uint32>(); ret.Position = Vector3::Read(stream); - count += fread(&ret.QuatW, sizeof(float), 1, stream); - count += fread(&ret.QuatX, sizeof(float), 1, stream); - count += fread(&ret.QuatY, sizeof(float), 1, stream); - count += fread(&ret.QuatZ, sizeof(float), 1, stream); - count += fread(&ret.Scale, sizeof(float), 1, stream); - count += fread(&ret.LightColor, sizeof(uint32), 1, stream); - - if (count != 7) - printf("DoodadInstance::Read: Failed to read some data expected 7, read %d\n", count); - + ret.QuatW = stream->Read<float>(); + ret.QuatX = stream->Read<float>(); + ret.QuatY = stream->Read<float>(); + ret.QuatZ = stream->Read<float>(); + ret.Scale = stream->Read<float>(); + ret.LightColor = stream->Read<uint32>(); return ret; } -DoodadSet DoodadSet::Read(FILE* stream) +DoodadSet DoodadSet::Read(Stream* stream) { DoodadSet ret; - char name[21]; - int count = 0; - - count += fread(&name, sizeof(char), 20, stream); - name[20] = '\0'; - ret.Name = name; - count += fread(&ret.FirstInstanceIndex, sizeof(uint32), 1, stream); - count += fread(&ret.CountInstances, sizeof(uint32), 1, stream); - count += fread(&ret.UnknownZero, sizeof(uint32), 1, stream); - - if (count != 23) - printf("DoodadSet::Read: Failed to read some data expected 23, read %d\n", count); - + char* name = stream->Read(20); + ret.Name = std::string(name, 20); + delete[] name; + ret.FirstInstanceIndex = stream->Read<uint32>(); + ret.CountInstances = stream->Read<uint32>(); + ret.UnknownZero = stream->Read<uint32>(); + return ret; } -LiquidHeader LiquidHeader::Read(FILE* stream) +void LiquidHeader::Read(Stream* stream) { - LiquidHeader ret; - int count = 0; - count += fread(&ret.CountXVertices, sizeof(uint32), 1, stream); - count += fread(&ret.CountYVertices, sizeof(uint32), 1, stream); - count += fread(&ret.Width, sizeof(uint32), 1, stream); - count += fread(&ret.Height, sizeof(uint32), 1, stream); - ret.BaseLocation = Vector3::Read(stream); - count += fread(&ret.MaterialId, sizeof(uint16), 1, stream); - - if (count != 5) - printf("LiquidHeader::Read: Failed to read some data expected 5, read %d\n", count); - - return ret; + CountXVertices = stream->Read<uint32>(); + CountYVertices = stream->Read<uint32>(); + Width = stream->Read<uint32>(); + Height = stream->Read<uint32>(); + BaseLocation = Vector3::Read(stream); + MaterialId = stream->Read<uint16>(); } -LiquidData LiquidData::Read(FILE* stream, LiquidHeader& header) +void LiquidData::Read(Stream* stream, LiquidHeader& header) { - LiquidData ret; - ret.HeightMap = new float*[header.CountXVertices]; + CountXVertices = header.CountXVertices; + Width = header.Width; + + HeightMap = new float*[header.CountXVertices]; for (uint32 i = 0; i < header.CountXVertices; ++i) - ret.HeightMap[i] = new float[header.CountYVertices]; + HeightMap[i] = new float[header.CountYVertices]; - ret.RenderFlags = new uint8*[header.Width]; + RenderFlags = new uint8*[header.Width]; for (uint32 i = 0; i < header.Width; ++i) - ret.RenderFlags[i] = new uint8[header.Height]; + RenderFlags[i] = new uint8[header.Height]; for (uint32 y = 0; y < header.CountYVertices; y++) { for (uint32 x = 0; x < header.CountXVertices; x++) { - uint32 discard; - float tmp; - if (fread(&discard, sizeof(uint32), 1, stream) == 1 && - fread(&tmp, sizeof(float), 1, stream) == 1) - { - ret.HeightMap[x][y] = tmp; - } + stream->Read<uint32>(); // Dummy value + HeightMap[x][y] = stream->Read<float>(); } } for (uint32 y = 0; y < header.Height; y++) - { for (uint32 x = 0; x < header.Width; x++) - { - uint8 tmp = 0; - if (fread(&tmp, sizeof(uint8), 1, stream) == 1) - ret.RenderFlags[x][y] = tmp; - } - } - - return ret; + RenderFlags[x][y] = stream->Read<uint8>(); } -H2ORenderMask H2ORenderMask::Read(FILE* stream) +H2ORenderMask H2ORenderMask::Read(Stream* stream) { H2ORenderMask ret; - int32 count; - if ((count = fread(&ret.Mask, sizeof(uint8), 8, stream)) != 8) - printf("H2OHeader::Read: Failed to read some data expected 8, read %d\n", count); + stream->Read(ret.Mask, sizeof(uint8) * 8); return ret; } @@ -497,38 +403,31 @@ bool MCNKLiquidData::IsWater(int x, int y, float height) return false; } -H2OHeader H2OHeader::Read(FILE* stream) +H2OHeader H2OHeader::Read(Stream* stream) { H2OHeader ret; - int count = 0; - count += fread(&ret.OffsetInformation, sizeof(uint32), 1, stream); - count += fread(&ret.LayerCount, sizeof(uint32), 1, stream); - count += fread(&ret.OffsetRender, sizeof(uint32), 1, stream); - - if (count != 3) - printf("H2OHeader::Read: Failed to read some data expected 3, read %d\n", count); - + + ret.OffsetInformation = stream->Read<uint32>(); + ret.LayerCount = stream->Read<uint32>(); + ret.OffsetRender = stream->Read<uint32>(); + return ret; } -H2OInformation H2OInformation::Read(FILE* stream) +H2OInformation H2OInformation::Read(Stream* stream) { H2OInformation ret; - int count = 0; - count += fread(&ret.LiquidType, sizeof(uint16), 1, stream); - count += fread(&ret.Flags, sizeof(uint16), 1, stream); - count += fread(&ret.HeightLevel1, sizeof(float), 1, stream); - count += fread(&ret.HeightLevel2, sizeof(float), 1, stream); - count += fread(&ret.OffsetX, sizeof(uint8), 1, stream); - count += fread(&ret.OffsetY, sizeof(uint8), 1, stream); - count += fread(&ret.Width, sizeof(uint8), 1, stream); - count += fread(&ret.Height, sizeof(uint8), 1, stream); - count += fread(&ret.OffsetMask2, sizeof(uint32), 1, stream); - count += fread(&ret.OffsetHeightmap, sizeof(uint32), 1, stream); - - if (count != 10) - printf("H2OInformation::Read: Failed to read some data expected 10, read %d\n", count); - + ret.LiquidType = stream->Read<uint16>(); + ret.Flags = stream->Read<uint16>(); + ret.HeightLevel1 = stream->Read<float>(); + ret.HeightLevel2 = stream->Read<float>(); + ret.OffsetX = stream->Read<uint8>(); + ret.OffsetY = stream->Read<uint8>(); + ret.Width = stream->Read<uint8>(); + ret.Height = stream->Read<uint8>(); + ret.OffsetMask2 = stream->Read<uint32>(); + ret.OffsetHeightmap = stream->Read<uint32>(); + return ret; } @@ -541,24 +440,30 @@ char* Utils::GetPlainName(const char* FileName) return (char*)FileName; } -WMOGroupHeader WMOGroupHeader::Read( FILE* stream ) +WMOGroupHeader WMOGroupHeader::Read(Stream* stream) { WMOGroupHeader ret; - int count = 0; - count += fread(&ret.OffsetGroupName, sizeof(uint32), 1, stream); - count += fread(&ret.OffsetDescriptiveName, sizeof(uint32), 1, stream); - count += fread(&ret.Flags, sizeof(uint32), 1, stream); + ret.OffsetGroupName = stream->Read<uint32>(); + ret.OffsetDescriptiveName = stream->Read<uint32>(); + ret.Flags = stream->Read<uint32>(); ret.BoundingBox[0] = Vector3::Read(stream); ret.BoundingBox[1] = Vector3::Read(stream); - count += fread(&ret.OffsetPortals, sizeof(uint32), 1, stream); - count += fread(&ret.CountPortals, sizeof(uint32), 1, stream); - count += fread(&ret.CountBatches, sizeof(uint16), 4, stream); - count += fread(&ret.Fogs, sizeof(uint8), 4, stream); - count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream); - count += fread(&ret.WmoId, sizeof(uint32), 1, stream); - - if (count != 15) - printf("WMOGroupHeader::Read: Failed to read some data expected 15, read %d\n", count); - + ret.OffsetPortals = stream->Read<uint32>(); + ret.CountPortals = stream->Read<uint32>(); + stream->Read(ret.CountBatches, sizeof(uint16) * 4); + stream->Read(ret.Fogs, sizeof(uint8) * 4); + ret.LiquidTypeRelated = stream->Read<uint32>(); + ret.WmoId = stream->Read<uint32>(); + return ret; } + +void Utils::InitializeMmapTileHeader(MmapTileHeader& header) +{ + memset(&header, 0, sizeof(MmapTileHeader)); + header.mmapMagic = MMAP_MAGIC; + header.dtVersion = DT_NAVMESH_VERSION; + header.mmapVersion = MMAP_VERSION; + header.size = 0; + header.usesLiquids = true; +} diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h index 612282f3e8f..07a76282a45 100644 --- a/src/tools/mesh_extractor/Utils.h +++ b/src/tools/mesh_extractor/Utils.h @@ -27,6 +27,8 @@ #include "Define.h" #include "Constants.h" +#include "Stream.h" + #include <ace/Stack_Trace.h> struct WorldModelDefinition; @@ -59,7 +61,7 @@ struct Vector3 return Vector3(x * s, y * s, z * s); } - static Vector3 Read(FILE* file); + static Vector3 Read(Stream* file); }; struct TilePos @@ -100,7 +102,7 @@ public: uint32 AreaId; uint32 MapObjectRefs; uint32 Holes; - uint32* LowQualityTextureMap; + uint32 LowQualityTextureMap[4]; uint32 PredTex; uint32 NumberEffectDoodad; uint32 OffsetMCSE; @@ -110,7 +112,7 @@ public: Vector3 Position; uint32 OffsetMCCV; - void Read(FILE* stream); + void Read(Stream* stream); }; class MHDR @@ -130,7 +132,7 @@ public: uint32 OffsetMH2O; uint32 OffsetMTFX; - void Read(FILE* stream); + void Read(Stream* stream); }; class ModelHeader @@ -187,7 +189,7 @@ public: uint32 CountBoundingNormals; uint32 OffsetBoundingNormals; - void Read(FILE* stream); + void Read(Stream* stream); }; class WorldModelHeader @@ -206,7 +208,7 @@ public: Vector3 BoundingBox[2]; uint32 LiquidTypeRelated; - static WorldModelHeader Read(FILE* stream); + void Read(Stream* stream); }; class DoodadInstance @@ -223,7 +225,7 @@ public: float Scale; uint32 LightColor; - static DoodadInstance Read(FILE* stream); + static DoodadInstance Read(Stream* stream); }; class DoodadSet @@ -235,7 +237,7 @@ public: uint32 CountInstances; uint32 UnknownZero; - static DoodadSet Read(FILE* stream); + static DoodadSet Read(Stream* stream); }; class LiquidHeader @@ -249,22 +251,36 @@ public: Vector3 BaseLocation; uint16 MaterialId; - static LiquidHeader Read(FILE* stream); + void Read(Stream* stream); }; class LiquidData { public: LiquidData() {} + + ~LiquidData() + { + /*for (uint32 i = 0; i < CountXVertices; ++i) + delete[] HeightMap[i]; + delete[] HeightMap; + + for (uint32 i = 0; i < Width; ++i) + delete[] RenderFlags[i]; + delete[] RenderFlags;*/ + } + float** HeightMap; uint8** RenderFlags; + uint32 CountXVertices; + uint32 Width; bool ShouldRender(int x, int y) { return RenderFlags[x][y] != 0x0F; } - static LiquidData Read(FILE* stream, LiquidHeader& header); + void Read(Stream* stream, LiquidHeader& header); }; class H2ORenderMask @@ -278,7 +294,7 @@ public: return (Mask[y] >> x & 1) != 0; } - static H2ORenderMask Read(FILE* stream); + static H2ORenderMask Read(Stream* stream); }; class MCNKLiquidData @@ -301,7 +317,7 @@ public: uint32 LayerCount; uint32 OffsetRender; - static H2OHeader Read(FILE* stream); + static H2OHeader Read(Stream* stream); }; class H2OInformation @@ -319,7 +335,7 @@ public: uint32 OffsetMask2; uint32 OffsetHeightmap; - static H2OInformation Read(FILE* stream); + static H2OInformation Read(Stream* stream); }; class WMOGroupHeader @@ -338,7 +354,7 @@ public: uint32 LiquidTypeRelated; uint32 WmoId; - static WMOGroupHeader Read(FILE* stream); + static WMOGroupHeader Read(Stream* stream); }; // Dummy class to act as an interface. @@ -351,7 +367,7 @@ public: }; #define MMAP_MAGIC 0x4d4d4150 // 'MMAP' -#define MMAP_VERSION 3 +#define MMAP_VERSION 5 struct MmapTileHeader { @@ -359,18 +375,13 @@ struct MmapTileHeader uint32 dtVersion; uint32 mmapVersion; uint32 size; - bool usesLiquids; - - MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION), - mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {} + bool usesLiquids : 1; }; class Utils { public: - static void Reverse(char word[]); - static std::string ReadString(FILE* file); - static uint32 Size(FILE* file); + static void Reverse(std::string& str); static Vector3 ToRecast(const Vector3& val ); static std::string GetAdtPath(const std::string& world, int x, int y); static std::string FixModelPath(const std::string& path); @@ -394,14 +405,15 @@ public: return false; return true; } - static std::string Replace( std::string str, const std::string& oldStr, const std::string& newStr ); - static void CreateDir( const std::string& Path ); - static void SaveToDisk(FILE* stream, const std::string& path); - static Vector3 ToWoWCoords(const Vector3& vec ); - static std::string GetExtension( std::string path ); + static std::string Replace(std::string str, const std::string& oldStr, const std::string& newStr); + static void CreateDir(const std::string& Path); + static void SaveToDisk(Stream* stream, const std::string& path); + static Vector3 ToWoWCoords(const Vector3& vec); + static std::string GetExtension( std::string path); static char* GetPlainName(const char* FileName); - static Vector3 TransformDoodadVertex(const IDefinition& def, Vector3& vec, bool translate = true); - static Vector3 VectorTransform(const Vector3& vec, const G3D::Matrix4& matrix, bool normal = false ); - static Vector3 TransformWmoDoodad(const DoodadInstance& inst, const WorldModelDefinition& root, Vector3& vec, bool translate = true ); + static Vector3 TransformDoodadVertex(const IDefinition& def, Vector3 vec, bool translate = true); + static Vector3 VectorTransform(const Vector3& vec, const G3D::Matrix4& matrix, bool normal = false); + static Vector3 TransformWmoDoodad(const DoodadInstance& inst, const WorldModelDefinition& root, Vector3& vec, bool translate = true); + static void InitializeMmapTileHeader(MmapTileHeader& header); }; #endif diff --git a/src/tools/mesh_extractor/WDT.cpp b/src/tools/mesh_extractor/WDT.cpp index 07aec95365e..b22e9ff136b 100644 --- a/src/tools/mesh_extractor/WDT.cpp +++ b/src/tools/mesh_extractor/WDT.cpp @@ -20,6 +20,7 @@ #include "ChunkedData.h" #include "Utils.h" #include "WorldModelHandler.h" +#include "Cache.h" WDT::WDT(std::string file) : IsGlobalModel(false), IsValid(false), Model(NULL) { @@ -37,8 +38,8 @@ void WDT::ReadGlobalModel() IsGlobalModel = true; ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream()); - ModelFile = Utils::ReadString(fileChunk->GetStream()); - Model = new WorldModelRoot(ModelFile); + ModelFile = fileChunk->GetStream()->ReadString(); + Model = Cache->WorldModelCache.Get(ModelFile); } void WDT::ReadTileTable() @@ -47,20 +48,14 @@ void WDT::ReadTileTable() if (!chunk) return; IsValid = true; - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); for (int y = 0; y < 64; ++y) { for (int x = 0; x < 64; ++x) { const uint32 hasTileFlag = 0x1; - uint32 flags; - uint32 discard; - int count = 0; - count += fread(&flags, sizeof(uint32), 1, stream); - count += fread(&discard, sizeof(uint32), 1, stream); - - if (count != 2) - printf("WDT::ReadTileTable: Failed to read some data expected 2, read %d\n", count); + uint32 flags = stream->Read<uint32>(); + uint32 discard = stream->Read<uint32>(); if (flags & hasTileFlag) TileTable.push_back(TilePos(x, y)); diff --git a/src/tools/mesh_extractor/WorldModelGroup.cpp b/src/tools/mesh_extractor/WorldModelGroup.cpp index 6ea72d6edbe..283c51e24f6 100644 --- a/src/tools/mesh_extractor/WorldModelGroup.cpp +++ b/src/tools/mesh_extractor/WorldModelGroup.cpp @@ -20,14 +20,35 @@ #include "Chunk.h" #include "Utils.h" -WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false), HasLiquidData(false) +WorldModelGroup::WorldModelGroup(std::string path, int groupIndex) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false), HasLiquidData(false) { Data = new ChunkedData(path); - if (!Data->Stream) + if (!Data->_Stream) { IsBad = true; return; } + Load(path); +} + +WorldModelGroup::WorldModelGroup(Stream* stream, std::string path, int groupIndex) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false), HasLiquidData(false) +{ + Data = new ChunkedData(stream, stream->GetSize()); + Load(path); +} + +WorldModelGroup::~WorldModelGroup() +{ + /* + @ToDo: Uncomment this when emplace_back is properly used in void WorldModelRoot::ReadGroups() or replace with smart pointers + delete Data; + delete SubData; + delete[] MOBA; + */ +} + +void WorldModelGroup::Load(std::string& path) +{ Chunk* mainChunk = Data->GetChunkByName("MOGP"); int32 firstSub = mainChunk->FindSubChunkOffset("MOPY"); if (firstSub == -1) @@ -35,8 +56,8 @@ WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupInde Name = Utils::GetPlainName(path.c_str()); - FILE* stream = mainChunk->GetStream(); - fseek(stream, firstSub, SEEK_SET); + Stream* stream = mainChunk->GetStream(); + stream->Seek(firstSub, SEEK_SET); SubData = new ChunkedData(stream, mainChunk->Length - firstSub); ReadHeader(); @@ -57,7 +78,7 @@ void WorldModelGroup::ReadNormals() uint32 normalCount = chunk->Length / 12; ASSERT(normalCount == Vertices.size() && "normalCount is different than the Vertices count"); Normals.reserve(normalCount); - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); for (uint32 i = 0; i < normalCount; i++) Normals.push_back(Vector3::Read(stream)); } @@ -69,9 +90,9 @@ void WorldModelGroup::ReadLiquid() return; HasLiquidData = true; - FILE* stream = chunk->GetStream(); - LiquidDataHeader = LiquidHeader::Read(stream); - LiquidDataGeometry = LiquidData::Read(stream, LiquidDataHeader); + Stream* stream = chunk->GetStream(); + LiquidDataHeader.Read(stream); + LiquidDataGeometry.Read(stream, LiquidDataHeader); } void WorldModelGroup::ReadVertices() @@ -82,7 +103,7 @@ void WorldModelGroup::ReadVertices() uint32 verticeCount = chunk->Length / 12; Vertices.reserve(verticeCount); - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); for (uint32 i = 0; i < verticeCount; i++) Vertices.push_back(Vector3::Read(stream)); } @@ -95,19 +116,13 @@ void WorldModelGroup::ReadTriangles() uint32 triangleCount = chunk->Length / 6; ASSERT(triangleCount == TriangleFlags.size() && "triangleCount != TriangleFlags.size()"); - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); Triangles.reserve(triangleCount); for (uint32 i = 0; i < triangleCount; i++) { - uint16 v0; - uint16 v1; - uint16 v2; - int count = 0; - count += fread(&v0, sizeof(uint16), 1, stream); - count += fread(&v1, sizeof(uint16), 1, stream); - count += fread(&v2, sizeof(uint16), 1, stream); - if (count != 3) - printf("WorldModelGroup::ReadMaterials: Error reading data, expected 3, read %d\n", count); + uint16 v0 = stream->Read<uint16>(); + uint16 v1 = stream->Read<uint16>(); + uint16 v2 = stream->Read<uint16>(); Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_WMO, v0, v1, v2)); } @@ -119,20 +134,15 @@ void WorldModelGroup::ReadMaterials() if (!chunk) return; - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); uint32 triangleCount = chunk->Length / 2; TriangleFlags.reserve(triangleCount); TriangleMaterials.reserve(triangleCount); for (uint32 i = 0; i < triangleCount; i++) { - uint8 tmp; - if (fread(&tmp, sizeof(uint8), 1, stream) != 1) - printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n"); - TriangleFlags.push_back(tmp); + TriangleFlags.push_back(stream->Read<uint8>()); // Read again for material. - if (fread(&tmp, sizeof(uint8), 1, stream) != 1) - printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n"); - TriangleMaterials.push_back(tmp); + TriangleMaterials.push_back(stream->Read<uint8>()); } } @@ -142,7 +152,7 @@ void WorldModelGroup::ReadHeader() if (!chunk) return; - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); Header = WMOGroupHeader::Read(stream); } @@ -154,7 +164,5 @@ void WorldModelGroup::ReadBatches() MOBALength = chunk->Length / 2; MOBA = new uint16[MOBALength]; - uint32 count = (uint32)fread(MOBA, sizeof(uint16), MOBALength, chunk->GetStream()); - if (count != MOBALength) - printf("WorldModelGroup::ReadBatches: Error reading data, expected %u, read %u\n", MOBALength, count); + chunk->GetStream()->Read(MOBA, sizeof(uint16) * MOBALength); } diff --git a/src/tools/mesh_extractor/WorldModelGroup.h b/src/tools/mesh_extractor/WorldModelGroup.h index b3c2c2bd940..20d453ee028 100644 --- a/src/tools/mesh_extractor/WorldModelGroup.h +++ b/src/tools/mesh_extractor/WorldModelGroup.h @@ -24,6 +24,10 @@ class WorldModelGroup { public: WorldModelGroup(std::string path, int groupIndex); + WorldModelGroup(Stream* stream, std::string path, int groupIndex); + ~WorldModelGroup(); + void Load(std::string& path); + ChunkedData* Data; ChunkedData* SubData; int GroupIndex; diff --git a/src/tools/mesh_extractor/WorldModelHandler.cpp b/src/tools/mesh_extractor/WorldModelHandler.cpp index 7c0acbc38e4..e4238b1e321 100644 --- a/src/tools/mesh_extractor/WorldModelHandler.cpp +++ b/src/tools/mesh_extractor/WorldModelHandler.cpp @@ -21,27 +21,24 @@ #include "Cache.h" #include "Model.h" #include "Define.h" +#include "Stream.h" #include "G3D/Matrix4.h" #include "G3D/Quat.h" #include <cstdio> -WorldModelDefinition WorldModelDefinition::Read( FILE* file ) +WorldModelDefinition WorldModelDefinition::Read(Stream* file) { WorldModelDefinition ret; - int count = 0; - count += fread(&ret.MwidIndex, sizeof(uint32), 1, file); - count += fread(&ret.UniqueId, sizeof(uint32), 1, file); + ret.MwidIndex = file->Read<uint32>(); + ret.UniqueId = file->Read<uint32>(); ret.Position = Vector3::Read(file); ret.Rotation = Vector3::Read(file); ret.UpperExtents = Vector3::Read(file); ret.LowerExtents = Vector3::Read(file); - count += fread(&ret.Flags, sizeof(uint16), 1, file); - count += fread(&ret.DoodadSet, sizeof(uint16), 1, file); - uint32 discard; - count += fread(&discard, sizeof(uint32), 1, file); - - if (count != 5) - printf("WorldModelDefinition::Read: Error reading data, expected 5, read %d\n", count); + ret.Flags = file->Read<uint16>(); + ret.DoodadSet = file->Read<uint16>(); + file->Read<uint32>(); // Discarded + return ret; } @@ -58,14 +55,12 @@ void WorldModelHandler::ProcessInternal( MapChunk* mcnk ) return; uint32 refCount = mcnk->Header.MapObjectRefs; - FILE* stream = mcnk->Source->GetStream(); - fseek(stream, mcnk->Source->Offset + mcnk->Header.OffsetMCRF, SEEK_SET); + Stream* stream = mcnk->Source->GetStream(); + stream->Seek(mcnk->Source->Offset + mcnk->Header.OffsetMCRF, SEEK_SET); // Start looping at the last Doodad Ref index for (uint32 i = mcnk->Header.DoodadRefs; i < refCount; i++) { - int32 index; - if (fread(&index, sizeof(int32), 1, stream) != 1) - printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n"); + int32 index = stream->Read<int32>(); if (index < 0 || uint32(index) >= _definitions->size()) continue; @@ -81,11 +76,6 @@ void WorldModelHandler::ProcessInternal( MapChunk* mcnk ) std::string path = (*_paths)[wmo.MwidIndex]; WorldModelRoot* model = Cache->WorldModelCache.Get(path); - if (!model) - { - model = new WorldModelRoot(path); - Cache->WorldModelCache.Insert(path, model); - } Vertices.reserve(1000); Triangles.reserve(1000); @@ -93,7 +83,7 @@ void WorldModelHandler::ProcessInternal( MapChunk* mcnk ) InsertModelGeometry(Vertices, Triangles, wmo, model); } // Restore the stream position - fseek(stream, mcnk->Source->Offset, SEEK_SET); + stream->Seek(mcnk->Source->Offset, SEEK_SET); } void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, const WorldModelDefinition& def, WorldModelRoot* root, bool translate ) @@ -133,11 +123,6 @@ void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::v for (std::vector<DoodadInstance>::iterator instance = instances.begin(); instance != instances.end(); ++instance) { Model* model = Cache->ModelCache.Get(instance->File); - if (!model) - { - model = new Model(instance->File); - Cache->ModelCache.Insert(instance->File, model); - } if (!model->IsCollidable) continue; @@ -202,7 +187,7 @@ void WorldModelHandler::ReadDefinitions() uint32 definitionCount = chunk->Length / definitionSize; _definitions = new std::vector<WorldModelDefinition>; _definitions->reserve(definitionCount); - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); for (uint32 i = 0; i < definitionCount; i++) _definitions->push_back(WorldModelDefinition::Read(stream)); } @@ -219,14 +204,13 @@ void WorldModelHandler::ReadModelPaths() _paths->reserve(paths); for (uint32 i = 0; i < paths; i++) { - FILE* stream = mwid->GetStream(); - fseek(stream, i * 4, SEEK_CUR); - uint32 offset; - if (fread(&offset, sizeof(uint32), 1, stream) != 1) - printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n"); - FILE* dataStream = mwmo->GetStream(); - fseek(dataStream, offset + mwmo->Offset, SEEK_SET); - _paths->push_back(Utils::ReadString(dataStream)); + Stream* stream = mwid->GetStream(); + stream->Seek(i * 4, SEEK_CUR); + uint32 offset = stream->Read<uint32>(); + + Stream* dataStream = mwmo->GetStream(); + dataStream->Seek(offset + mwmo->Offset, SEEK_SET); + _paths->push_back(dataStream->ReadString()); } } diff --git a/src/tools/mesh_extractor/WorldModelHandler.h b/src/tools/mesh_extractor/WorldModelHandler.h index dd030e4521f..9e5b1d1a3f7 100644 --- a/src/tools/mesh_extractor/WorldModelHandler.h +++ b/src/tools/mesh_extractor/WorldModelHandler.h @@ -39,7 +39,7 @@ public: uint16 Flags; uint16 DoodadSet; - static WorldModelDefinition Read(FILE* file); + static WorldModelDefinition Read(Stream* file); }; class WorldModelHandler : public ObjectDataHandler diff --git a/src/tools/mesh_extractor/WorldModelRoot.cpp b/src/tools/mesh_extractor/WorldModelRoot.cpp index 4b6e4e189a4..a288713c646 100644 --- a/src/tools/mesh_extractor/WorldModelRoot.cpp +++ b/src/tools/mesh_extractor/WorldModelRoot.cpp @@ -18,6 +18,7 @@ #include "WorldModelRoot.h" #include "ChunkedData.h" #include "Utils.h" +#include "MPQManager.h" WorldModelRoot::WorldModelRoot( std::string path ) { @@ -42,9 +43,10 @@ void WorldModelRoot::ReadGroups() { char name[200]; sprintf(name, "%s_%03u.wmo", pathBase.c_str(), i); - WorldModelGroup group(name, i); - if (!group.IsBad) - Groups.push_back(group); + Stream* stream = MPQHandler->GetFile(name); + if (!stream) + continue; + Groups.emplace_back(WorldModelGroup(stream, name, i)); // @ToDo: Use the real signature of emplace_back with variadic templates once we make the full switch to C++11 (At least Visual Studio 2012) } } @@ -54,7 +56,7 @@ void WorldModelRoot::ReadDoodadSets() if (!chunk) return; - FILE* stream = chunk->GetStream(); + Stream* stream = chunk->GetStream(); ASSERT(chunk->Length / 32 == Header.CountSets && "chunk.Length / 32 == Header.CountSets"); DoodadSets.reserve(Header.CountSets); for (uint32 i = 0; i < Header.CountSets; i++) @@ -73,14 +75,14 @@ void WorldModelRoot::ReadDoodadInstances() DoodadInstances.reserve(countInstances); for (uint32 i = 0; i < countInstances; i++) { - FILE* stream = chunk->GetStream(); - fseek(stream, instanceSize * i, SEEK_CUR); + Stream* stream = chunk->GetStream(); + stream->Seek(instanceSize * i, SEEK_CUR); DoodadInstance instance = DoodadInstance::Read(stream); - FILE* nameStream = nameChunk->GetStream(); + Stream* nameStream = nameChunk->GetStream(); if (instance.FileOffset >= nameChunk->Length) continue; - fseek(nameStream, instance.FileOffset, SEEK_CUR); - instance.File = Utils::ReadString(nameStream); + nameStream->Seek(instance.FileOffset, SEEK_CUR); + instance.File = nameStream->ReadString(); DoodadInstances.push_back(instance); } } @@ -91,6 +93,6 @@ void WorldModelRoot::ReadHeader() if (!chunk) return; - FILE* stream = chunk->GetStream(); - Header = WorldModelHeader::Read(stream); + Stream* stream = chunk->GetStream(); + Header.Read(stream); } |