diff options
| author | Shauren <shauren.trinity@gmail.com> | 2013-10-07 14:30:37 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2013-10-07 14:30:37 +0200 |
| commit | fca4a5448ac476b942c330108a76032ae242711e (patch) | |
| tree | 81fe500a02353fa5079532fe53c21a4f3a269b82 /src/tools | |
| parent | 1b56bd1b6a860c325f3e4e76d94dc367429fbb16 (diff) | |
| parent | c8f525c76e162b5b546c91628a1457f9aef43699 (diff) | |
Merge branch 'master' of github.com:TrinityCore/TrinityCore into 4.3.4
Diffstat (limited to 'src/tools')
34 files changed, 644 insertions, 431 deletions
diff --git a/src/tools/mesh_extractor/ADT.cpp b/src/tools/mesh_extractor/ADT.cpp index c2ac19d5be0..79ece1213d4 100644 --- a/src/tools/mesh_extractor/ADT.cpp +++ b/src/tools/mesh_extractor/ADT.cpp @@ -3,11 +3,11 @@ #include "LiquidHandler.h" #include "WorldModelHandler.h" -ADT::ADT( std::string file ) : ObjectData(NULL), Data(NULL), HasObjectData(false), - _DoodadHandler(NULL), _WorldModelHandler(NULL), _LiquidHandler(NULL) +ADT::ADT( std::string file, int x, int y ) : ObjectData(NULL), Data(NULL), HasObjectData(false), + _DoodadHandler(NULL), _WorldModelHandler(NULL), _LiquidHandler(NULL), X(x), Y(y) { Data = new ChunkedData(file); - ObjectData = new ChunkedData(Utils::Replace(file, ".adt", "_obj0.adt")); + ObjectData = new ChunkedData(file); if (ObjectData->Stream) HasObjectData = true; else diff --git a/src/tools/mesh_extractor/ADT.h b/src/tools/mesh_extractor/ADT.h index 133596eb024..6f9973f7cde 100644 --- a/src/tools/mesh_extractor/ADT.h +++ b/src/tools/mesh_extractor/ADT.h @@ -10,7 +10,7 @@ class LiquidHandler; class ADT { public: - ADT(std::string file); + ADT(std::string file, int x, int y); ~ADT(); void Read(); @@ -25,5 +25,8 @@ public: DoodadHandler* _DoodadHandler; WorldModelHandler* _WorldModelHandler; LiquidHandler* _LiquidHandler; + + int X; + int Y; }; #endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/Cache.h b/src/tools/mesh_extractor/Cache.h index 60e3d8434cf..9b3e046fe1e 100644 --- a/src/tools/mesh_extractor/Cache.h +++ b/src/tools/mesh_extractor/Cache.h @@ -5,9 +5,8 @@ #include "Define.h" #include <ace/Guard_T.h> #include <ace/Synch.h> - -class WorldModelRoot; -class Model; +#include "WorldModelRoot.h" +#include "Model.h" template<class K, class T> class GenericCache @@ -15,7 +14,7 @@ class GenericCache public: GenericCache() {} - static const uint32 FlushLimit = 1000; + 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) { @@ -55,7 +54,8 @@ public: void Clear() { - + ModelCache.Clear(); + WorldModelCache.Clear(); } }; diff --git a/src/tools/mesh_extractor/ChunkedData.cpp b/src/tools/mesh_extractor/ChunkedData.cpp index e0db12a6be7..f273ef946dd 100644 --- a/src/tools/mesh_extractor/ChunkedData.cpp +++ b/src/tools/mesh_extractor/ChunkedData.cpp @@ -12,7 +12,7 @@ Stream(stream) Load(maxLength, chunksHint); } -ChunkedData::ChunkedData( std::string file, uint32 chunksHint /*= 300*/ ) +ChunkedData::ChunkedData( const std::string& file, uint32 chunksHint /*= 300*/ ) { Stream = MPQHandler->GetFile(file); if (!Stream) @@ -47,7 +47,7 @@ void ChunkedData::Load( uint32 maxLength, uint32 chunksHint ) } } -int ChunkedData::GetFirstIndex( std::string name ) +int ChunkedData::GetFirstIndex( const std::string& name ) { for (uint32 i = 0; i < Chunks.size(); ++i) if (Chunks[i]->Name == name) @@ -55,7 +55,7 @@ int ChunkedData::GetFirstIndex( std::string name ) return -1; } -Chunk* ChunkedData::GetChunkByName( std::string name ) +Chunk* ChunkedData::GetChunkByName( const std::string& name ) { for (uint32 i = 0; i < Chunks.size(); ++i) if (Chunks[i]->Name == name) diff --git a/src/tools/mesh_extractor/ChunkedData.h b/src/tools/mesh_extractor/ChunkedData.h index e23648c845e..1e1cb17749e 100644 --- a/src/tools/mesh_extractor/ChunkedData.h +++ b/src/tools/mesh_extractor/ChunkedData.h @@ -8,11 +8,11 @@ class ChunkedData { public: ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300); - ChunkedData(std::string file, uint32 chunksHint = 300); + ChunkedData(const std::string &file, uint32 chunksHint = 300); ~ChunkedData(); - int GetFirstIndex(std::string name); - Chunk* GetChunkByName(std::string name); + int GetFirstIndex(const std::string& name); + Chunk* GetChunkByName(const std::string& name); void Load(uint32 maxLength, uint32 chunksHint); std::vector<Chunk*> Chunks; diff --git a/src/tools/mesh_extractor/Constants.h b/src/tools/mesh_extractor/Constants.h index 02e2d25559f..f2d9e8af8f5 100644 --- a/src/tools/mesh_extractor/Constants.h +++ b/src/tools/mesh_extractor/Constants.h @@ -47,6 +47,7 @@ public: static const float PI; static const float MaxStandableHeight; static bool ToWoWCoords; + static bool Debug; static const char* VMAPMagic; static const float BaseUnitDim; static const int VertexPerMap; diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp index d6125bdd8e2..c90a6e527f7 100644 --- a/src/tools/mesh_extractor/ContinentBuilder.cpp +++ b/src/tools/mesh_extractor/ContinentBuilder.cpp @@ -6,18 +6,25 @@ #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; - bool debug; dtNavMeshParams Params; ContinentBuilder* cBuilder; public: - BuilderThread(ContinentBuilder* _cBuilder, bool deb, dtNavMeshParams& params) : debug(deb), Params(params), cBuilder(_cBuilder), Free(true) {} - void SetData(int x, int y, int map, std::string cont) { X = x; Y = y; MapId = map; Continent = cont; } + 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() { @@ -25,7 +32,7 @@ public: printf("[%02i,%02i] Building tile\n", X, Y); TileBuilder builder(cBuilder, Continent, X, Y, MapId); char buff[100]; - sprintf(buff, "mmaps/%03u%02u%02u.mmtile", MapId, Y, X); + sprintf(buff, "mmaps/%03u%02i%02i.mmtile", MapId, Y, X); FILE* f = fopen(buff, "r"); if (f) // Check if file already exists. { @@ -34,7 +41,7 @@ public: Free = true; return 0; } - uint8* nav = builder.Build(debug, Params); + uint8* nav = builder.BuildTiled(Params); if (nav) { f = fopen(buff, "wb"); @@ -50,7 +57,7 @@ public: fclose(f); } dtFree(nav); - printf("[%02u,%02u] Tile Built!\n", X, Y); + printf("[%02i,%02i] Tile Built!\n", X, Y); Free = true; return 0; } @@ -89,7 +96,7 @@ void ContinentBuilder::CalculateTileBounds() getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax); } -void ContinentBuilder::Build(bool debug) +void ContinentBuilder::Build() { char buff[50]; sprintf(buff, "mmaps/%03u.mmap", MapId); @@ -103,36 +110,81 @@ void ContinentBuilder::Build(bool debug) CalculateTileBounds(); dtNavMeshParams params; - params.maxPolys = 1 << STATIC_POLY_BITS; - params.maxTiles = TileMap->TileTable.size(); - rcVcopy(params.orig, bmin); - params.tileHeight = Constants::TileSize; - params.tileWidth = Constants::TileSize; - fwrite(¶ms, sizeof(dtNavMeshParams), 1, mmap); - fclose(mmap); + std::vector<BuilderThread*> Threads; - for (uint32 i = 0; i < NumberOfThreads; ++i) - Threads.push_back(new BuilderThread(this, debug, params)); - printf("Map %s ( %i ) has %u tiles. Building them with %i 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) + + if (TileMap->IsGlobalModel) + { + printf("Map %s ( %u ) is a WMO. Building with 1 thread.\n", Continent.c_str(), MapId); + + TileBuilder* builder = new TileBuilder(this, Continent, 0, 0, MapId); + builder->AddGeometry(TileMap->Model, TileMap->ModelDefinition); + uint8* nav = builder->BuildInstance(params); + if (nav) + { + // Set some params for the navmesh + dtMeshHeader* header = (dtMeshHeader*)nav; + dtVcopy(params.orig, header->bmin); + params.tileWidth = header->bmax[0] - header->bmin[0]; + params.tileHeight = header->bmax[2] - header->bmin[2]; + params.maxTiles = 1; + params.maxPolys = header->polyCount; + fwrite(¶ms, sizeof(dtNavMeshParams), 1, mmap); + fclose(mmap); + + char buff[100]; + sprintf(buff, "mmaps/%03u%02i%02i.mmtile", MapId, 0, 0); + FILE* 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; + } + + MmapTileHeader mheader; + mheader.size = builder->DataSize; + fwrite(&mheader, sizeof(MmapTileHeader), 1, f); + fwrite(nav, sizeof(unsigned char), builder->DataSize, f); + fclose(f); + } + + dtFree(nav); + delete builder; + } + else { - bool next = false; - while (!next) + params.maxPolys = 32768; + params.maxTiles = 4096; + rcVcopy(params.orig, Constants::Origin); + params.tileHeight = Constants::TileSize; + params.tileWidth = Constants::TileSize; + fwrite(¶ms, sizeof(dtNavMeshParams), 1, mmap); + fclose(mmap); + + for (uint32 i = 0; i < NumberOfThreads; ++i) + Threads.push_back(new BuilderThread(this, params)); + 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) { - for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th) + bool next = false; + while (!next) { - if ((*_th)->Free) + for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th) { - (*_th)->SetData(itr->X, itr->Y, MapId, Continent); - (*_th)->activate(); - next = true; - break; + 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)); } - // Wait for 20 seconds - ACE_OS::sleep(ACE_Time_Value (0, 20000)); } } + Cache->Clear(); // Free memory diff --git a/src/tools/mesh_extractor/ContinentBuilder.h b/src/tools/mesh_extractor/ContinentBuilder.h index b36ca125b9e..64d7be49aed 100644 --- a/src/tools/mesh_extractor/ContinentBuilder.h +++ b/src/tools/mesh_extractor/ContinentBuilder.h @@ -12,7 +12,7 @@ public: NumberOfThreads(tn), tileXMin(64), tileYMin(64), tileXMax(0), tileYMax(0) {} - void Build(bool debug); + void Build(); void getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax); void CalculateTileBounds(); float bmin[3]; diff --git a/src/tools/mesh_extractor/DoodadHandler.cpp b/src/tools/mesh_extractor/DoodadHandler.cpp index 56c2a7986f8..72c051500e1 100644 --- a/src/tools/mesh_extractor/DoodadHandler.cpp +++ b/src/tools/mesh_extractor/DoodadHandler.cpp @@ -4,10 +4,9 @@ #include "Model.h" #include "G3D/Matrix4.h" -DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL) +DoodadHandler::DoodadHandler( ADT* adt ) : + ObjectDataHandler(adt), _definitions(NULL), _paths(NULL) { - if (!adt->HasObjectData) - return; Chunk* mddf = adt->ObjectData->GetChunkByName("MDDF"); if (mddf) ReadDoodadDefinitions(mddf); @@ -18,19 +17,19 @@ DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions( ReadDoodadPaths(mmid, mmdx); } -void DoodadHandler::ProcessInternal( ChunkedData* subChunks ) +void DoodadHandler::ProcessInternal( MapChunk* mcnk ) { if (!IsSane()) return; - Chunk* doodadReferencesChunk = subChunks->GetChunkByName("MCRD"); - if (!doodadReferencesChunk) - return; - FILE* stream = doodadReferencesChunk->GetStream(); - uint32 refCount = doodadReferencesChunk->Length / 4; + + uint32 refCount = mcnk->Header.DoodadRefs; + FILE* stream = mcnk->Source->GetStream(); + fseek(stream, mcnk->Source->Offset + mcnk->Header.OffsetMCRF, SEEK_SET); for (uint32 i = 0; i < refCount; i++) { int32 index; - if (int count = fread(&index, sizeof(int32), 1, stream) != 1) + 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); if (index < 0 || uint32(index) >= _definitions->size()) continue; @@ -56,6 +55,8 @@ void DoodadHandler::ProcessInternal( ChunkedData* subChunks ) InsertModelGeometry(doodad, model); } + // Restore the stream position + fseek(stream, mcnk->Source->Offset, SEEK_SET); } void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk ) @@ -92,11 +93,10 @@ void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data ) void DoodadHandler::InsertModelGeometry(const DoodadDefinition& def, Model* model) { - G3D::Matrix4 transformation = Utils::GetTransformation(def); uint32 vertOffset = Vertices.size(); - + for (std::vector<Vector3>::iterator itr = model->Vertices.begin(); itr != model->Vertices.end(); ++itr) - Vertices.push_back(Utils::VectorTransform(*itr, transformation)); + Vertices.push_back(Utils::TransformDoodadVertex(def, *itr)); // Vertices have to be converted based on the information from the DoodadDefinition struct for (std::vector<Triangle<uint16> >::iterator itr = model->Triangles.begin(); itr != model->Triangles.end(); ++itr) Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_DOODAD, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset)); diff --git a/src/tools/mesh_extractor/DoodadHandler.h b/src/tools/mesh_extractor/DoodadHandler.h index 96aecbcce27..51377f32d01 100644 --- a/src/tools/mesh_extractor/DoodadHandler.h +++ b/src/tools/mesh_extractor/DoodadHandler.h @@ -17,13 +17,18 @@ public: virtual float Scale() const { return DecimalScale / 1024.0f; } + Vector3 FixCoords(Vector3& vec) + { + return Vector3(vec.z, vec.x, vec.y); + } + void Read(FILE* stream) { int count = 0; count += fread(&MmidIndex, sizeof(uint32), 1, stream); count += fread(&UniqueId, sizeof(uint32), 1, stream); - Position = Vector3::Read(stream); + Position = (Vector3::Read(stream)); Rotation = Vector3::Read(stream); count += fread(&DecimalScale, sizeof(uint16), 1, stream); count += fread(&Flags, sizeof(uint16), 1, stream); @@ -44,7 +49,7 @@ public: protected: - void ProcessInternal(ChunkedData* chunk); + void ProcessInternal(MapChunk* chunk); private: void ReadDoodadDefinitions(Chunk* chunk); diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp index 2fc470e8e9f..df828dcd573 100644 --- a/src/tools/mesh_extractor/Geometry.cpp +++ b/src/tools/mesh_extractor/Geometry.cpp @@ -3,6 +3,7 @@ #include "ADT.h" #include "WorldModelHandler.h" #include "DoodadHandler.h" +#include <limits.h> Geometry::Geometry() : Transform(false) { @@ -14,6 +15,11 @@ void Geometry::CalculateBoundingBox( float*& min, float*& max ) { min = new float[3]; max = new float[3]; + for (int i = 0; i < 3; ++i) + { + max[i] = std::numeric_limits<float>::lowest(); + min[i] = std::numeric_limits<float>::max(); + } for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr) { @@ -36,8 +42,8 @@ void Geometry::CalculateBoundingBox( float*& min, float*& max ) void Geometry::CalculateMinMaxHeight( float& min, float& max ) { - min = 0.0f; - max = 0.0f; + min = std::numeric_limits<float>::max(); + max = std::numeric_limits<float>::lowest(); 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 285ea1a5b74..33a661a9adf 100644 --- a/src/tools/mesh_extractor/LiquidHandler.cpp +++ b/src/tools/mesh_extractor/LiquidHandler.cpp @@ -34,7 +34,7 @@ void LiquidHandler::HandleNewLiquid() H2OInformation information = H2OInformation::Read(stream); float** heights = new float*[9]; - for (int j = 0; j < 9; ++i) + for (int j = 0; j < 9; ++j) { heights[j] = new float[9]; memset(heights[j], 0, sizeof(float) * 9); diff --git a/src/tools/mesh_extractor/MPQ.cpp b/src/tools/mesh_extractor/MPQ.cpp index 18a9eb0f0e3..896d7bc32ac 100644 --- a/src/tools/mesh_extractor/MPQ.cpp +++ b/src/tools/mesh_extractor/MPQ.cpp @@ -103,8 +103,7 @@ void MPQFile::seekRelative(int offset) void MPQFile::close() { - if (buffer) - delete[] buffer; + delete[] buffer; buffer = 0; eof = true; } @@ -112,6 +111,11 @@ void MPQFile::close() FILE* 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; diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h index 2f8b082f526..30e11741550 100644 --- a/src/tools/mesh_extractor/MPQ.h +++ b/src/tools/mesh_extractor/MPQ.h @@ -26,7 +26,8 @@ public: libmpq__off_t size, transferred; libmpq__file_unpacked_size(mpq_a, filenum, &size); - char *buffer = new char[size]; + char* buffer = new char[size + 1]; + buffer[size] = '\0'; libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp index 91b9c121c89..4d3ab808a2e 100644 --- a/src/tools/mesh_extractor/MPQManager.cpp +++ b/src/tools/mesh_extractor/MPQManager.cpp @@ -23,7 +23,7 @@ void MPQManager::Initialize() for (uint32 i = 0; i < size; ++i) { MPQArchive* arc = new MPQArchive(std::string("Data/" + std::string(Files[i])).c_str()); - Archives.push_front(arc); + Archives.push_front(arc); // MPQ files have to be transversed in reverse order to properly account for patched files printf("Opened %s\n", Files[i]); } } @@ -57,14 +57,14 @@ void MPQManager::InitializeDBC() Archives.push_front(_baseLocale); if (BaseLocale == -1) { - printf("No locale data detected\n"); + printf("No locale data detected. Please make sure that the executable is in the same folder as your WoW installation.\n"); ASSERT(false); } else printf("Using default locale: %s\n", Languages[BaseLocale]); } -FILE* MPQManager::GetFile( std::string path ) +FILE* MPQManager::GetFile(const std::string& path ) { ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); MPQFile file(path.c_str()); @@ -73,13 +73,13 @@ FILE* MPQManager::GetFile( std::string path ) return file.GetFileStream(); } -DBC* MPQManager::GetDBC( std::string name ) +DBC* MPQManager::GetDBC(const std::string& name ) { std::string path = "DBFilesClient\\" + name + ".dbc"; return new DBC(GetFile(path)); } -FILE* MPQManager::GetFileFrom( std::string path, MPQArchive* file ) +FILE* MPQManager::GetFileFrom(const std::string& path, MPQArchive* file ) { ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); mpq_archive* mpq_a = file->mpq_a; @@ -103,6 +103,13 @@ FILE* MPQManager::GetFileFrom( std::string path, MPQArchive* file ) // 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); + delete[] buffer; return ret; } diff --git a/src/tools/mesh_extractor/MPQManager.h b/src/tools/mesh_extractor/MPQManager.h index 2f49ad258a5..7f9d675c4d4 100644 --- a/src/tools/mesh_extractor/MPQManager.h +++ b/src/tools/mesh_extractor/MPQManager.h @@ -14,9 +14,9 @@ public: ~MPQManager() {} void Initialize(); - FILE* GetFile(std::string path); - FILE* GetFileFrom(std::string path, MPQArchive* file); - DBC* GetDBC(std::string name); + FILE* GetFile(const std::string& path); + FILE* GetFileFrom(const std::string& path, MPQArchive* file); + DBC* GetDBC(const std::string& name); std::vector<std::string> GetAllFiles(std::string extension); std::deque<MPQArchive*> Archives; diff --git a/src/tools/mesh_extractor/MapChunk.cpp b/src/tools/mesh_extractor/MapChunk.cpp index 8fe40773d43..789166d5c9b 100644 --- a/src/tools/mesh_extractor/MapChunk.cpp +++ b/src/tools/mesh_extractor/MapChunk.cpp @@ -66,6 +66,8 @@ void MapChunk::GenerateVertices( FILE* stream ) Vertices.push_back(vert); } } + // Restore stream position. + fseek(stream, Source->Offset, SEEK_SET); } bool MapChunk::HasHole( uint32 map, int x, int y ) diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp index e06f44c7125..0d9160a610b 100644 --- a/src/tools/mesh_extractor/MeshExtractor.cpp +++ b/src/tools/mesh_extractor/MeshExtractor.cpp @@ -10,45 +10,59 @@ #include "DetourNavMesh.h" #include "DetourNavMeshQuery.h" +#include <stdio.h> + #include <set> MPQManager* MPQHandler; CacheClass* Cache; -void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads, bool debug) +void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads) { 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()) + { + if (Constants::Debug) + printf("Map %u will not be built.\n", mapId); continue; + } std::string name = (*itr)->GetString(1); WDT wdt("World\\maps\\" + name + "\\" + name + ".wdt"); - if (!wdt.IsValid || wdt.IsGlobalModel) + if (!wdt.IsValid) + { + printf("Could not find WDT data for map %u (%s)\n", mapId, name.c_str()); continue; + } printf("Building %s MapId %u\n", name.c_str(), mapId); ContinentBuilder builder(name, mapId, &wdt, threads); - builder.Build(debug); + builder.Build(); } } void ExtractDBCs() { printf("Extracting DBCs\n"); - // Create the filesystem structure + // Create the file system structure std::string baseDBCPath = "dbc/"; Utils::CreateDir(baseDBCPath); - // Populate list of DBC files std::set<std::string> DBCFiles; + const size_t extLen = strlen(".dbc"); + // 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() - strlen(".dbc")) + if (itr->rfind(".dbc") == itr->length() - extLen) // Check if the extension is ".dbc" DBCFiles.insert(*itr); + const size_t folderLen = strlen("DBFilesClient\\"); // Iterate over all available locales for (std::set<uint32>::iterator itr = MPQHandler->AvailableLocales.begin(); itr != MPQHandler->AvailableLocales.end(); ++itr) { @@ -62,10 +76,10 @@ void ExtractDBCs() std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt"; // Extract the component file - Utils::SaveToDisk(MPQHandler->GetFile(component), path + component); + Utils::SaveToDisk(MPQHandler->GetFileFrom(component, MPQHandler->LocaleFiles[*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() + strlen("DBFilesClient\\"))); + Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + folderLen)); } printf("DBC extraction finished!\n"); } @@ -193,16 +207,18 @@ void ExtractGameobjectModels() fwrite(&model.Header.CountGroups, sizeof(uint32), 1, output); fwrite(&model.Header.WmoId, sizeof(uint32), 1, output); + const char grp[] = { 'G' , 'R' , 'P', ' ' }; for (std::vector<WorldModelGroup>::iterator itr2 = model.Groups.begin(); itr2 != model.Groups.end(); ++itr2) { - fwrite(&itr2->Header.Flags, sizeof(uint32), 1, output); - fwrite(&itr2->Header.WmoId, sizeof(uint32), 1, output); - fwrite(&itr2->Header.BoundingBox[0], sizeof(uint32), 1, output); - fwrite(&itr2->Header.BoundingBox[1], sizeof(uint32), 1, output); + const WMOGroupHeader& header = itr2->Header; + fwrite(&header.Flags, sizeof(uint32), 1, output); + fwrite(&header.WmoId, sizeof(uint32), 1, output); + fwrite(&header.BoundingBox[0], sizeof(uint32), 1, output); + fwrite(&header.BoundingBox[1], sizeof(uint32), 1, output); uint32 LiquidFlags = itr2->HasLiquidData ? 1 : 0; fwrite(&LiquidFlags, sizeof(uint32), 1, output); - fwrite("GRP ", sizeof(char), 4, output); + fwrite(grp, sizeof(char), sizeof(grp), output); uint32 k = 0; uint32 mobaBatch = itr2->MOBALength / 12; uint32* MobaEx = new uint32[mobaBatch*4]; @@ -216,7 +232,7 @@ void ExtractGameobjectModels() fwrite(MobaEx, 4, k, output); delete[] MobaEx; - // Note: still not finished + //@TODO: Finish this. } fclose(output); @@ -242,7 +258,7 @@ bool HandleArgs(int argc, char** argv, uint32& threads, std::set<uint32>& mapLis return false; threads = atoi(param); - printf("Using %i threads\n", threads); + printf("Using %u threads\n", threads); } else if (strcmp(argv[i], "--maps") == 0) { @@ -257,6 +273,8 @@ bool HandleArgs(int argc, char** argv, uint32& threads, std::set<uint32>& mapLis mapList.insert(atoi(token)); token = strtok(NULL, ","); } + + free(copy); printf("Extracting only provided list of maps (%u).\n", uint32(mapList.size())); } @@ -310,6 +328,8 @@ void PrintUsage() void LoadTile(dtNavMesh*& navMesh, const char* tile) { FILE* f = fopen(tile, "rb"); + if (!f) + return; MmapTileHeader header; if (fread(&header, sizeof(MmapTileHeader), 1, f) != 1) @@ -326,18 +346,19 @@ void LoadTile(dtNavMesh*& navMesh, const char* tile) int main(int argc, char* argv[]) { - if (!system("pause")) + _setmaxstdio(2048); + uint32 threads = 4, extractFlags = 0; + std::set<uint32> mapIds; + + if (!HandleArgs(argc, argv, threads, mapIds, Constants::Debug, extractFlags)) { - printf("main: Error in system call to pause\n"); + PrintUsage(); return -1; } - uint32 threads = 4, extractFlags = 0; - std::set<uint32> mapIds; - bool debug = false; - - if (!HandleArgs(argc, argv, threads, mapIds, debug, extractFlags)) + if (extractFlags == 0) { + printf("You must provide valid extract flags.\n"); PrintUsage(); return -1; } @@ -350,44 +371,44 @@ int main(int argc, char* argv[]) ExtractDBCs(); if (extractFlags & Constants::EXTRACT_FLAG_MMAPS) - ExtractMMaps(mapIds, threads, debug); + ExtractMMaps(mapIds, threads); if (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ExtractGameobjectModels(); if (extractFlags & Constants::EXTRACT_FLAG_TEST) { - float start[] = { 0.0f, 0.0f, 0.0f }; - float end[] = { 0.0f, 0.0f, 0.0f }; + float start[] = { 16226.200195f, 16257.000000f, 13.202200f }; + float end[] = { 16245.725586f, 16382.465820f, 47.384956f }; // float m_spos[3]; - m_spos[0] = -1.0f * start[1]; + m_spos[0] = -start[1]; m_spos[1] = start[2]; - m_spos[2] = -1.0f * start[0]; + m_spos[2] = -start[0]; // float m_epos[3]; - m_epos[0] = -1.0f * end[1]; + m_epos[0] = -end[1]; m_epos[1] = end[2]; - m_epos[2] = -1.0f * end[0]; + m_epos[2] = -end[0]; // dtQueryFilter m_filter; - m_filter.setIncludeFlags(0xffff) ; - m_filter.setExcludeFlags(0); + m_filter.setIncludeFlags(Constants::POLY_AREA_ROAD | Constants::POLY_AREA_TERRAIN); + m_filter.setExcludeFlags(Constants::POLY_AREA_WATER); // float m_polyPickExt[3]; - m_polyPickExt[0] = 2; - m_polyPickExt[1] = 4; - m_polyPickExt[2] = 2; + m_polyPickExt[0] = 2.5f; + m_polyPickExt[1] = 2.5f; + m_polyPickExt[2] = 2.5f; // dtPolyRef m_startRef; dtPolyRef m_endRef; - FILE* mmap = fopen(".mmap", "rb"); + FILE* mmap = fopen("mmaps/001.mmap", "rb"); dtNavMeshParams params; int count = fread(¶ms, sizeof(dtNavMeshParams), 1, mmap); fclose(mmap); @@ -401,13 +422,16 @@ int main(int argc, char* argv[]) dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery(); navMesh->init(¶ms); - LoadTile(navMesh, ".mmtile"); - LoadTile(navMesh, ".mmtile"); - LoadTile(navMesh, ".mmtile"); - LoadTile(navMesh, ".mmtile"); - LoadTile(navMesh, ".mmtile"); - LoadTile(navMesh, ".mmtile"); - + for (int i = 0; i <= 32; ++i) + { + for (int j = 0; j <= 32; ++j) + { + char buff[100]; + sprintf(buff, "mmaps/001%02i%02i.mmtile", i, j); + LoadTile(navMesh, buff); + } + } + navMeshQuery->init(navMesh, 2048); float nearestPt[3]; @@ -421,7 +445,24 @@ int main(int argc, char* argv[]) return 0; } - printf("Found!"); + 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); + std::vector<Vector3> FinalPath; + FinalPath.reserve(resultHopCount); + for (uint32 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); + printf("Point %f %f %f\n", finalV.x, finalV.y, finalV.z); + } } return 0; diff --git a/src/tools/mesh_extractor/Model.cpp b/src/tools/mesh_extractor/Model.cpp index 77b1adbeaa0..5fb521d5a36 100644 --- a/src/tools/mesh_extractor/Model.cpp +++ b/src/tools/mesh_extractor/Model.cpp @@ -15,9 +15,9 @@ Model::Model( std::string path ) : IsCollidable(false), IsBad(false) Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f) { IsCollidable = true; - ReadVertices(Stream); - ReadBoundingNormals(Stream); - ReadBoundingTriangles(Stream); + ReadVertices(); + ReadBoundingNormals(); + ReadBoundingTriangles(); } } @@ -27,41 +27,41 @@ Model::~Model() fclose(Stream); } -void Model::ReadVertices( FILE* stream ) +void Model::ReadVertices() { - fseek(stream, Header.OffsetBoundingVertices, SEEK_SET); + fseek(Stream, 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]); } } -void Model::ReadBoundingTriangles( FILE* stream ) +void Model::ReadBoundingTriangles() { - fseek(stream, Header.OffsetBoundingTriangles, SEEK_SET); + fseek(Stream, 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); + 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); Triangles.push_back(tri); } } -void Model::ReadBoundingNormals( FILE* stream ) +void Model::ReadBoundingNormals() { - fseek(stream, Header.OffsetBoundingNormals, SEEK_SET); + fseek(Stream, 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 ea9331e7c30..ed8627dad6f 100644 --- a/src/tools/mesh_extractor/Model.h +++ b/src/tools/mesh_extractor/Model.h @@ -9,9 +9,9 @@ public: Model(std::string path); ~Model(); - void ReadVertices(FILE* stream); - void ReadBoundingTriangles(FILE* stream); - void ReadBoundingNormals(FILE* stream); + void ReadVertices(); + void ReadBoundingTriangles(); + void ReadBoundingNormals(); ModelHeader Header; std::vector<Vector3> Vertices; std::vector<Vector3> Normals; diff --git a/src/tools/mesh_extractor/ObjectDataHandler.cpp b/src/tools/mesh_extractor/ObjectDataHandler.cpp index 789efc6d62c..f98d198eecd 100644 --- a/src/tools/mesh_extractor/ObjectDataHandler.cpp +++ b/src/tools/mesh_extractor/ObjectDataHandler.cpp @@ -5,17 +5,5 @@ void ObjectDataHandler::ProcessMapChunk( MapChunk* chunk ) { - if (!Source->HasObjectData) - return; - // fuck it blizzard, why is this crap necessary? - int32 firstIndex = Source->ObjectData->GetFirstIndex("MCNK"); - if (firstIndex == -1) - return; - if (uint32(firstIndex + chunk->Index) > Source->ObjectData->Chunks.size()) - return; - Chunk* ourChunk = Source->ObjectData->Chunks[firstIndex + chunk->Index]; - if (ourChunk->Length == 0) - return; - ChunkedData* subChunks = new ChunkedData(ourChunk->GetStream(), ourChunk->Length, 2); - ProcessInternal(subChunks); + ProcessInternal(chunk); } diff --git a/src/tools/mesh_extractor/ObjectDataHandler.h b/src/tools/mesh_extractor/ObjectDataHandler.h index 75b4e45700c..9b347ef418a 100644 --- a/src/tools/mesh_extractor/ObjectDataHandler.h +++ b/src/tools/mesh_extractor/ObjectDataHandler.h @@ -9,7 +9,7 @@ public: ObjectDataHandler(ADT* _adt) : Source(_adt) {} void ProcessMapChunk(MapChunk* chunk); - virtual void ProcessInternal(ChunkedData* data) = 0; + virtual void ProcessInternal(MapChunk* data) = 0; ADT* Source; }; #endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp index 9bb9b11619f..51df91d2652 100644 --- a/src/tools/mesh_extractor/TileBuilder.cpp +++ b/src/tools/mesh_extractor/TileBuilder.cpp @@ -1,6 +1,7 @@ #include "ContinentBuilder.h" #include "TileBuilder.h" #include "Geometry.h" +#include "WorldModelRoot.h" #include "Constants.h" #include "Utils.h" #include "Cache.h" @@ -15,50 +16,40 @@ TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId) : World(world), X(x), Y(y), MapId(mapId), _Geometry(NULL), DataSize(0), cBuilder(_cBuilder) { - /* - Test, non-working values - // Cell Size = TileSize / TileVoxelSize - // 1800 = TileVoxelSize - Config.cs = Constants::TileSize / 1800; - // Cell Height - Config.ch = 0.4f; - // Min Region Area = 20^2 - Config.minRegionArea = 20*20; - // Merge Region Area = 40^2 - Config.mergeRegionArea = 40*40; - Config.tileSize = Constants::TileSize / 4; + // Config for normal maps + memset(&Config, 0, sizeof(rcConfig)); + Config.cs = Constants::TileSize / 1800.0f; // TileSize / voxelSize + Config.ch = 0.3f; + Config.minRegionArea = 36; + Config.mergeRegionArea = 144; Config.walkableSlopeAngle = 50.0f; Config.detailSampleDist = 3.0f; Config.detailSampleMaxError = 1.25f; - Config.walkableClimb = floorf(1.0f / Config.ch); - Config.walkableHeight = ceilf(1.652778f / Config.ch); - Config.walkableRadius = ceilf(0.2951389f / Config.cs); + Config.walkableClimb = 1.0f / Config.ch; + Config.walkableHeight = 2.1 / Config.ch; + Config.walkableRadius = 0.6f / Config.cs; Config.maxEdgeLen = Config.walkableRadius * 8; - Config.borderSize = Config.walkableRadius + 4; - Config.width = 1800 + Config.borderSize * 2; - Config.height = 1800 + Config.borderSize * 2; - Config.maxVertsPerPoly = 6; + Config.borderSize = Config.walkableRadius + 8; + Config.tileSize = 1800; Config.maxSimplificationError = 1.3f; - */ - - // All are in UNIT metrics! - memset(&Config, 0, sizeof(rcConfig)); + Config.maxVertsPerPoly = 6; - Config.maxVertsPerPoly = DT_VERTS_PER_POLYGON; - Config.cs = Constants::BaseUnitDim; - Config.ch = Constants::BaseUnitDim; - Config.walkableSlopeAngle = 60.0f; - Config.tileSize = Constants::VertexPerTile; - Config.walkableRadius = 1; - Config.borderSize = Config.walkableRadius + 3; - Config.maxEdgeLen = Constants::VertexPerTile + 1; //anything bigger than tileSize - Config.walkableHeight = 3; - Config.walkableClimb = 2; // keep less than walkableHeight - Config.minRegionArea = rcSqr(60); - Config.mergeRegionArea = rcSqr(50); - Config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons) - Config.detailSampleDist = Config.cs * 64; - Config.detailSampleMaxError = Config.ch * 2; + // Config for instances + memset(&InstanceConfig, 0, sizeof(rcConfig)); + InstanceConfig.cs = 0.2f; + InstanceConfig.ch = 0.3f; + InstanceConfig.minRegionArea = 25; + InstanceConfig.mergeRegionArea = 100; + InstanceConfig.walkableSlopeAngle = 50.0f; + InstanceConfig.detailSampleDist = 3.0f; + InstanceConfig.detailSampleMaxError = 1.5f; + 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.borderSize = 0; Context = new rcContext; } @@ -73,11 +64,148 @@ void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshPara bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1)); } -uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) +void TileBuilder::AddGeometry(WorldModelRoot* root, const WorldModelDefinition& def) +{ + _Geometry = new Geometry(); + _Geometry->Transform = true; + + WorldModelHandler::InsertModelGeometry(_Geometry->Vertices, _Geometry->Triangles, def, root, false); + + OutputDebugVertices(); +} + +uint8* TileBuilder::BuildInstance( dtNavMeshParams& navMeshParams ) +{ + float* bmin = NULL, *bmax = NULL; + + _Geometry->CalculateBoundingBox(bmin, bmax); + + rcVcopy(InstanceConfig.bmax, bmax); + rcVcopy(InstanceConfig.bmin, bmin); + + uint32 numVerts = _Geometry->Vertices.size(); + uint32 numTris = _Geometry->Triangles.size(); + float* vertices; + int* triangles; + uint8* areas; + _Geometry->GetRawData(vertices, triangles, areas); + + // this sets the dimensions of the heightfield + rcCalcGridSize(InstanceConfig.bmin, InstanceConfig.bmax, InstanceConfig.cs, &InstanceConfig.width, &InstanceConfig.height); + + rcHeightfield* hf = rcAllocHeightfield(); + rcCreateHeightfield(Context, *hf, InstanceConfig.width, InstanceConfig.height, InstanceConfig.bmin, InstanceConfig.bmax, InstanceConfig.cs, InstanceConfig.ch); + + rcClearUnwalkableTriangles(Context, InstanceConfig.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas); + rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, InstanceConfig.walkableClimb); + + rcFilterLowHangingWalkableObstacles(Context, InstanceConfig.walkableClimb, *hf); + rcFilterLedgeSpans(Context, InstanceConfig.walkableHeight, InstanceConfig.walkableClimb, *hf); + rcFilterWalkableLowHeightSpans(Context, InstanceConfig.walkableHeight, *hf); + + rcCompactHeightfield* chf = rcAllocCompactHeightfield(); + rcBuildCompactHeightfield(Context, InstanceConfig.walkableHeight, InstanceConfig.walkableClimb, *hf, *chf); + + rcErodeWalkableArea(Context, InstanceConfig.walkableRadius, *chf); + rcBuildDistanceField(Context, *chf); + rcBuildRegions(Context, *chf, InstanceConfig.borderSize, InstanceConfig.minRegionArea, InstanceConfig.minRegionArea); + + rcContourSet* contours = rcAllocContourSet(); + rcBuildContours(Context, *chf, InstanceConfig.maxSimplificationError, InstanceConfig.maxEdgeLen, *contours); + + rcPolyMesh* pmesh = rcAllocPolyMesh(); + rcBuildPolyMesh(Context, *contours, InstanceConfig.maxVertsPerPoly, *pmesh); + + rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); + rcBuildPolyMeshDetail(Context, *pmesh, *chf, InstanceConfig.detailSampleDist, InstanceConfig.detailSampleMaxError, *dmesh); + + // Set flags according to area types (e.g. Swim for Water) + for (int i = 0; i < pmesh->npolys; i++) + { + if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN) + pmesh->flags[i] = Constants::POLY_FLAG_WALK; + else if (pmesh->areas[i] == Constants::POLY_AREA_WATER) + pmesh->flags[i] = Constants::POLY_FLAG_SWIM; + } + + dtNavMeshCreateParams params; + memset(¶ms, 0, sizeof(params)); + // PolyMesh data + params.verts = pmesh->verts; + params.vertCount = pmesh->nverts; + params.polys = pmesh->polys; + params.polyAreas = pmesh->areas; + params.polyFlags = pmesh->flags; + params.polyCount = pmesh->npolys; + params.nvp = pmesh->nvp; + // PolyMeshDetail data + params.detailMeshes = dmesh->meshes; + params.detailVerts = dmesh->verts; + params.detailVertsCount = dmesh->nverts; + params.detailTris = dmesh->tris; + params.detailTriCount = dmesh->ntris; + rcVcopy(params.bmin, pmesh->bmin); + rcVcopy(params.bmax, pmesh->bmax); + // General settings + params.ch = InstanceConfig.ch; + params.cs = InstanceConfig.cs; + params.walkableClimb = InstanceConfig.walkableClimb * InstanceConfig.ch; + params.walkableHeight = InstanceConfig.walkableHeight * InstanceConfig.ch; + params.walkableRadius = InstanceConfig.walkableRadius * InstanceConfig.cs; + params.tileX = X; + params.tileY = Y; + params.tileLayer = 0; + params.buildBvTree = true; + + rcVcopy(params.bmax, bmax); + rcVcopy(params.bmin, bmin); + + // Offmesh-connection settings + params.offMeshConCount = 0; // none for now + + rcFreeHeightField(hf); + rcFreeCompactHeightfield(chf); + rcFreeContourSet(contours); + delete vertices; + delete triangles; + delete areas; + delete bmin; + delete bmax; + + if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount) + { + // we have flat tiles with no actual geometry - don't build those, its useless + // keep in mind that we do output those into debug info + // drop tiles with only exact count - some tiles may have geometry while having less tiles + printf("No polygons to build on tile, skipping.\n"); + rcFreePolyMesh(pmesh); + rcFreePolyMeshDetail(dmesh); + return NULL; + } + + int navDataSize; + uint8* navData; + printf("Creating the navmesh with %i vertices, %i polys, %i triangles!\n", params.vertCount, params.polyCount, params.detailTriCount); + bool result = dtCreateNavMeshData(¶ms, &navData, &navDataSize); + + rcFreePolyMesh(pmesh); + rcFreePolyMeshDetail(dmesh); + + if (result) + { + printf("NavMesh created, size %i!\n", navDataSize); + DataSize = navDataSize; + return navData; + } + + return NULL; +} + +uint8* TileBuilder::BuildTiled(dtNavMeshParams& navMeshParams) { _Geometry = new Geometry(); _Geometry->Transform = true; - ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y)); + ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y), X, Y); adt->Read(); _Geometry->AddAdt(adt); delete adt; @@ -85,17 +213,21 @@ uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty()) return NULL; + float* bmin = NULL, *bmax = NULL; + CalculateTileBounds(bmin, bmax, navMeshParams); + _Geometry->CalculateMinMaxHeight(bmin[1], bmax[1]); + // again, we load everything - wasteful but who cares - for (int ty = Y - 2; ty <= Y + 2; ty++) + for (int ty = Y - 1; ty <= Y + 1; ty++) { - for (int tx = X - 2; tx <= X + 2; tx++) + for (int tx = X - 1; tx <= X + 1; tx++) { // don't load main tile again if (tx == X && ty == Y) continue; - ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty)); - // If this condition is met, it means that this wdt does not contain the ADT + 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) { delete _adt; @@ -107,18 +239,8 @@ uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) } } - if (dbg) - { - char buff[100]; - sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X); - FILE* debug = fopen(buff, "wb"); - for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i) - fprintf(debug, "v %f %f %f\n", _Geometry->Vertices[i].x, _Geometry->Vertices[i].y, _Geometry->Vertices[i].z); - for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i) - fprintf(debug, "f %i %i %i\n", _Geometry->Triangles[i].V0 + 1, _Geometry->Triangles[i].V1 + 1, _Geometry->Triangles[i].V2 + 1); - fclose(debug); - } - + OutputDebugVertices(); + uint32 numVerts = _Geometry->Vertices.size(); uint32 numTris = _Geometry->Triangles.size(); float* vertices; @@ -128,101 +250,38 @@ uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) _Geometry->Vertices.clear(); _Geometry->Triangles.clear(); + // add border + bmin[0] -= Config.borderSize * Config.cs; + bmin[2] -= Config.borderSize * Config.cs; + bmax[0] += Config.borderSize * Config.cs; + bmax[2] += Config.borderSize * Config.cs; - rcVcopy(Config.bmin, cBuilder->bmin); - rcVcopy(Config.bmax, cBuilder->bmax); + rcHeightfield* hf = rcAllocHeightfield(); + int width = Config.tileSize + (Config.borderSize * 2); + rcCreateHeightfield(Context, *hf, width, width, bmin, bmax, Config.cs, Config.ch); - // this sets the dimensions of the heightfield - should maybe happen before border padding - rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height); + rcClearUnwalkableTriangles(Context, Config.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas); + rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb); - // Initialize per tile config. - rcConfig tileCfg = Config; - tileCfg.width = Config.tileSize + Config.borderSize * 2; - tileCfg.height = Config.tileSize + Config.borderSize * 2; + rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf); + rcFilterLedgeSpans(Context, Config.walkableHeight, Config.walkableClimb, *hf); + rcFilterWalkableLowHeightSpans(Context, Config.walkableHeight, *hf); - // merge per tile poly and detail meshes - rcPolyMesh** pmmerge = new rcPolyMesh*[Constants::TilesPerMap * Constants::TilesPerMap]; - rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap]; + rcCompactHeightfield* chf = rcAllocCompactHeightfield(); + rcBuildCompactHeightfield(Context, Config.walkableHeight, Config.walkableClimb, *hf, *chf); - int nmerge = 0; - for (int y = 0; y < Constants::TilesPerMap; ++y) - { - for (int x = 0; x < Constants::TilesPerMap; ++x) - { - // Calculate the per tile bounding box. - tileCfg.bmin[0] = Config.bmin[0] + float(x * Config.tileSize - Config.borderSize) * Config.cs; - tileCfg.bmin[2] = Config.bmin[2] + float(y * Config.tileSize - Config.borderSize) * Config.cs; - tileCfg.bmax[0] = Config.bmin[0] + float((x + 1) * Config.tileSize + Config.borderSize) * Config.cs; - tileCfg.bmax[2] = Config.bmin[2] + float((y + 1) * Config.tileSize + Config.borderSize) * Config.cs; - - - rcHeightfield* hf = rcAllocHeightfield(); - rcCreateHeightfield(Context, *hf, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch); - rcClearUnwalkableTriangles(Context, tileCfg.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas); - rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb); - - // Once all geometry is rasterized, we do initial pass of filtering to - // remove unwanted overhangs caused by the conservative rasterization - // as well as filter spans where the character cannot possibly stand. - rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf); - rcFilterLedgeSpans(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf); - rcFilterWalkableLowHeightSpans(Context, tileCfg.walkableHeight, *hf); - - // Compact the heightfield so that it is faster to handle from now on. - // This will result in more cache coherent data as well as the neighbours - // between walkable cells will be calculated. - rcCompactHeightfield* chf = rcAllocCompactHeightfield(); - rcBuildCompactHeightfield(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf, *chf); - - rcFreeHeightField(hf); - - // Erode the walkable area by agent radius. - rcErodeWalkableArea(Context, Config.walkableRadius, *chf); - // Prepare for region partitioning, by calculating distance field along the walkable surface. - rcBuildDistanceField(Context, *chf); - // Partition the walkable surface into simple regions without holes. - rcBuildRegions(Context, *chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea); - - // Create contours. - rcContourSet* cset = rcAllocContourSet(); - rcBuildContours(Context, *chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset); - - // Build polygon navmesh from the contours. - rcPolyMesh* pmesh = rcAllocPolyMesh(); - rcBuildPolyMesh(Context, *cset, tileCfg.maxVertsPerPoly, *pmesh); - - // Build detail mesh. - rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); - rcBuildPolyMeshDetail(Context, *pmesh, *chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *dmesh); - - // Free memory - rcFreeCompactHeightfield(chf); - rcFreeContourSet(cset); - - pmmerge[nmerge] = pmesh; - dmmerge[nmerge] = dmesh; - ++nmerge; - } - } + rcErodeWalkableArea(Context, Config.walkableRadius, *chf); + rcBuildDistanceField(Context, *chf); + rcBuildRegions(Context, *chf, Config.borderSize, Config.minRegionArea, Config.mergeRegionArea); + + rcContourSet* contours = rcAllocContourSet(); + rcBuildContours(Context, *chf, Config.maxSimplificationError, Config.maxEdgeLen, *contours); rcPolyMesh* pmesh = rcAllocPolyMesh(); - rcMergePolyMeshes(Context, pmmerge, nmerge, *pmesh); + rcBuildPolyMesh(Context, *contours, Config.maxVertsPerPoly, *pmesh); rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); - rcMergePolyMeshDetails(Context, dmmerge, nmerge, *dmesh); - - delete[] pmmerge; - delete[] dmmerge; - - printf("[%02i,%02i] Meshes merged!\n", X, Y); - - // Remove padding from the polymesh data. (Remove this odditity) - for (int i = 0; i < pmesh->nverts; ++i) - { - unsigned short* v = &pmesh->verts[i * 3]; - v[0] -= (unsigned short)Config.borderSize; - v[2] -= (unsigned short)Config.borderSize; - } + rcBuildPolyMeshDetail(Context, *pmesh, *chf, Config.detailSampleDist, Config.detailSampleMaxError, *dmesh); // Set flags according to area types (e.g. Swim for Water) for (int i = 0; i < pmesh->npolys; i++) @@ -249,54 +308,60 @@ uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) params.detailVertsCount = dmesh->nverts; params.detailTris = dmesh->tris; params.detailTriCount = dmesh->ntris; - rcVcopy(params.bmin, pmesh->bmin); - rcVcopy(params.bmax, pmesh->bmax); // General settings params.ch = Config.ch; params.cs = Config.cs; - params.walkableClimb = Constants::BaseUnitDim * Config.walkableClimb; - params.walkableHeight = Constants::BaseUnitDim * Config.walkableHeight; - params.walkableRadius = Constants::BaseUnitDim * Config.walkableRadius; - params.tileX = (((cBuilder->bmin[0] + cBuilder->bmax[0]) / 2) - navMeshParams.orig[0]) / Constants::TileSize; - params.tileY = (((cBuilder->bmin[2] + cBuilder->bmax[2]) / 2) - navMeshParams.orig[2]) / Constants::TileSize; - - rcVcopy(params.bmin, cBuilder->bmin); - rcVcopy(params.bmax, cBuilder->bmax); + params.walkableClimb = Config.walkableClimb * Config.ch; + params.walkableHeight = Config.walkableHeight * Config.ch; + params.walkableRadius = Config.walkableRadius * Config.cs; + params.tileX = X; + params.tileY = Y; + params.tileLayer = 0; + params.buildBvTree = true; + + // Recalculate the bounds with the added geometry + float* bmin2 = NULL, *bmax2 = NULL; + CalculateTileBounds(bmin2, bmax2, navMeshParams); + bmin2[1] = bmin[1]; + bmax2[1] = bmax[1]; + + rcVcopy(params.bmax, bmax2); + rcVcopy(params.bmin, bmin2); // Offmesh-connection settings params.offMeshConCount = 0; // none for now - params.tileSize = Constants::VertexPerMap; + rcFreeHeightField(hf); + rcFreeCompactHeightfield(chf); + rcFreeContourSet(contours); + delete vertices; + delete triangles; + delete areas; + delete bmin; + delete bmax; if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == params.polyCount) { // we have flat tiles with no actual geometry - don't build those, its useless // keep in mind that we do output those into debug info // drop tiles with only exact count - some tiles may have geometry while having less tiles - printf("[%02i,%02i] No polygons to build on tile, skipping.\n", X, Y); + printf("[%02i, %02i] No polygons to build on tile, skipping.\n", X, Y); rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); - delete areas; - delete triangles; - delete vertices; return NULL; } int navDataSize; uint8* navData; - printf("[%02i,%02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, pmesh->nverts, pmesh->npolys, dmesh->ntris); + printf("[%02i, %02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, params.vertCount, params.polyCount, params.detailTriCount); bool result = dtCreateNavMeshData(¶ms, &navData, &navDataSize); - // Free some memory rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); - delete areas; - delete triangles; - delete vertices; if (result) { - printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize); + printf("[%02i, %02i] NavMesh created, size %i!\n", X, Y, navDataSize); DataSize = navDataSize; return navData; } @@ -304,6 +369,27 @@ uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) return NULL; } +void TileBuilder::OutputDebugVertices() +{ + if (Constants::Debug) + { + char buff[100]; + sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X); + FILE* debug = fopen(buff, "wb"); + for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i) + { + const Vector3& vector = _Geometry->Vertices[i]; + fprintf(debug, "v %f %f %f\n", vector.x, vector.y, vector.z); + } + for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i) + { + const Triangle<uint32>& triangle = _Geometry->Triangles[i]; + fprintf(debug, "f %u %u %u\n", triangle.V0 + 1, triangle.V1 + 1, triangle.V2 + 1); + } + fclose(debug); + } +} + TileBuilder::~TileBuilder() { delete Context; diff --git a/src/tools/mesh_extractor/TileBuilder.h b/src/tools/mesh_extractor/TileBuilder.h index 40c96f6ec42..165b3594ed6 100644 --- a/src/tools/mesh_extractor/TileBuilder.h +++ b/src/tools/mesh_extractor/TileBuilder.h @@ -4,6 +4,7 @@ #include "Recast.h" #include "Geometry.h" +#include "WorldModelRoot.h" class ContinentBuilder; class WDT; @@ -15,13 +16,16 @@ public: ~TileBuilder(); void CalculateTileBounds(float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams); - uint8* Build(bool dbg, dtNavMeshParams& navMeshParams); - + uint8* BuildTiled(dtNavMeshParams& navMeshParams); + uint8* BuildInstance(dtNavMeshParams& navMeshParams); + void AddGeometry(WorldModelRoot* root, const WorldModelDefinition& def); + void OutputDebugVertices(); std::string World; int X; int Y; int MapId; rcConfig Config; + rcConfig InstanceConfig; rcContext* Context; Geometry* _Geometry; uint32 DataSize; diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp index 33c30d7522c..24cfb5cd1db 100644 --- a/src/tools/mesh_extractor/Utils.cpp +++ b/src/tools/mesh_extractor/Utils.cpp @@ -21,6 +21,7 @@ const float Constants::PI = 3.1415926f; const float Constants::MaxStandableHeight = 1.5f; const char* Constants::VMAPMagic = "VMAP041"; bool Constants::ToWoWCoords = false; +bool Constants::Debug = false; const float Constants::BaseUnitDim = 0.533333f; const int Constants::VertexPerMap = (Constants::TileSize / Constants::BaseUnitDim) + 0.5f; const int Constants::VertexPerTile = 40; @@ -49,13 +50,12 @@ void Utils::Reverse(char word[]) std::string Utils::ReadString( FILE* file ) { std::string ret; - int i = 0; while (true) { char b; if (fread(&b, sizeof(char), 1, file) != 1 || b == 0) break; - ret[i++] = b; + ret.push_back(b); } return ret; } @@ -72,69 +72,47 @@ uint32 Utils::Size( FILE* file ) return size; } -Vector3 Utils::ToRecast( Vector3 val ) +Vector3 Utils::ToRecast(const Vector3& val ) { return Vector3(-val.y, val.z, -val.x); } -std::string Utils::GetAdtPath( std::string world, int x, int y ) +std::string Utils::GetAdtPath(const std::string& world, int x, int y ) { return "World\\Maps\\" + world + "\\" + world + "_" + Utils::ToString(x) + "_" + Utils::ToString(y) + ".adt"; } -std::string Utils::FixModelPath( std::string path ) +std::string Utils::FixModelPath(const std::string& path ) { return Utils::GetPathBase(path) + ".M2"; } -G3D::Matrix4 Utils::RotationX(float angle) +Vector3 Utils::TransformDoodadVertex(const IDefinition& def, Vector3& vec, bool translate) { - float _cos = cos(angle); - float _sin = sin(angle); - G3D::Matrix4 ret = G3D::Matrix4::identity(); - ret[2][2] = _cos; - ret[2][3] = _sin; - ret[3][2] = -_sin; - ret[3][3] = _cos; - return ret; -} + // Sources of information: + /// http://www.pxr.dk/wowdev/wiki/index.php?title=ADT/v18&oldid=3715 -G3D::Matrix4 Utils::GetTransformation(IDefinition def) -{ - G3D::Matrix4 translation; - if (def.Position.x == 0.0f && def.Position.y == 0.0f && def.Position.z == 0.0f) - translation = G3D::Matrix4::identity(); - else - translation = G3D::Matrix4::translation(-(def.Position.z - Constants::MaxXY), - -(def.Position.x - Constants::MaxXY), def.Position.y); + // This function applies to both external doodads and WMOs - G3D::Matrix4 rotation = RotationX(ToRadians(def.Rotation.z)) * RotationY(ToRadians(def.Rotation.x)) * RotationZ(ToRadians(def.Rotation.y + 180)); - if (def.Scale() < 1.0f || def.Scale() > 1.0f) - return G3D::Matrix4::scale(def.Scale()) * rotation * translation; - return rotation * translation; -} + // Rotate our Doodad vertex + G3D::Matrix4 rot = G3D::Matrix3::fromEulerAnglesXYZ(Utils::ToRadians(def.Rotation.z), Utils::ToRadians(-def.Rotation.x), Utils::ToRadians(def.Rotation.y + 180)); + Vector3 ret = Utils::VectorTransform(vec, rot); -G3D::Matrix4 Utils::RotationY( float angle ) -{ - float _cos = cos(angle); - float _sin = sin(angle); - G3D::Matrix4 ret = G3D::Matrix4::identity(); - ret[1][1] = _cos; - ret[1][3] = -_sin; - ret[3][1] = _sin; - ret[3][3] = _cos; + // And finally scale and translate it to our origin + ret = ret * def.Scale(); + if (translate) + ret = ret + Vector3(Constants::MaxXY - def.Position.z, Constants::MaxXY - def.Position.x, def.Position.y); return ret; } -G3D::Matrix4 Utils::RotationZ( float angle ) +Vector3 Utils::TransformWmoDoodad(const DoodadInstance& inst, const WorldModelDefinition& root, Vector3& vec, bool translate ) { - float _cos = cos(angle); - float _sin = sin(angle); - G3D::Matrix4 ret = G3D::Matrix4::identity(); - ret[1][1] = _cos; - ret[1][2] = _sin; - ret[2][1] = -_sin; - ret[2][2] = _cos; + G3D::Quat quat = G3D::Quat(-inst.QuatY, inst.QuatZ, -inst.QuatX, inst.QuatW); + + Vector3 ret = Utils::VectorTransform(vec, G3D::Matrix4(quat.toRotationMatrix())); + ret = ret * (inst.Scale / 1024.0f); + if (translate) + ret = ret + Vector3(Constants::MaxXY - inst.Position.z, Constants::MaxXY - inst.Position.x, inst.Position.y); return ret; } @@ -143,16 +121,14 @@ float Utils::ToRadians( float degrees ) return Constants::PI * degrees / 180.0f; } -Vector3 Utils::VectorTransform( Vector3 vec, G3D::Matrix4 matrix ) +Vector3 Utils::VectorTransform(const Vector3& vec, const G3D::Matrix4& matrix, bool normal ) { - Vector3 ret; - ret.x = vec.x * matrix[1][1] + vec.y * matrix[2][1] + vec.z * matrix[3][1] + matrix[4][1]; - ret.y = vec.x * matrix[1][2] + vec.y * matrix[2][2] + vec.z * matrix[3][2] + matrix[4][2]; - ret.z = vec.x * matrix[1][3] + vec.y * matrix[2][3] + vec.z * matrix[3][3] + matrix[4][3]; - return ret; + G3D::Vector3 ret(vec.x, vec.y, vec.z); + ret = matrix.homoMul(ret, normal ? 0 : 1); + return Vector3(ret.x, ret.y, ret.z); } -std::string Utils::GetPathBase( std::string path ) +std::string Utils::GetPathBase(const std::string& path ) { size_t lastIndex = path.find_last_of("."); if (lastIndex != std::string::npos) @@ -168,11 +144,11 @@ Vector3 Vector3::Read( FILE* file ) return ret; } -Vector3 Utils::GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int /*x*/, int /*y*/) +Vector3 Utils::GetLiquidVert(const IDefinition& def, Vector3 basePosition, float height, int x, int y, bool translate) { if (Utils::Distance(height, 0.0f) > 0.5f) basePosition.z = 0.0f; - return Utils::VectorTransform(basePosition + Vector3(basePosition.x * Constants::UnitSize, basePosition.y * Constants::UnitSize, height), transformation); + return Utils::TransformDoodadVertex(def, basePosition + Vector3(x * Constants::UnitSize, y * Constants::UnitSize, height), translate); } float Utils::Distance( float x, float y ) @@ -191,47 +167,49 @@ std::string Utils::Replace( std::string str, const std::string& oldStr, const st return str; } -G3D::Matrix4 Utils::GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root ) -{ - G3D::Matrix4 rootTransformation = Utils::GetTransformation(root); - G3D::Matrix4 translation = G3D::Matrix4::translation(inst.Position.x, inst.Position.y, inst.Position.z); - G3D::Matrix4 scale = G3D::Matrix4::scale(inst.Scale); - G3D::Matrix4 rotation = Utils::RotationY(Constants::PI); - G3D::Quat quat(-inst.QuatY, inst.QuatZ, -inst.QuatX, inst.QuatW); - G3D::Matrix4 quatRotation = quat.toRotationMatrix(); - - return scale * rotation * quatRotation ** translation * rootTransformation; -} - -void Utils::SaveToDisk( FILE* stream, std::string path ) +void Utils::SaveToDisk( FILE* 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); return; } uint32 size = Utils::Size(stream); uint8* data = new uint8[size]; // Read the data to an array - if (fread(data, 1, size, stream) != 1) + size_t read = fread(data, size, 1, stream); + if (read != 1) { - printf("SaveToDisk: Error reading from Stream while trying to save file %s to disck.\n", path.c_str()); + printf("SaveToDisk: Error reading from Stream while trying to save file %s to disk.\n", path.c_str()); + fclose(disk); + fclose(stream); return; } + // And write it in the file - fwrite(data, 1, size, disk); + 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); + fclose(disk); + return; + } // Close the filestream fclose(disk); + fclose(stream); + // Free the used memory - delete [] data; + delete[] data; } -Vector3 Utils::ToWoWCoords( Vector3 vec ) +Vector3 Utils::ToWoWCoords(const Vector3& vec ) { - return Vector3(vec.x, -vec.z, vec.y); + return Vector3(-vec.z, -vec.x, vec.y); } std::string Utils::GetExtension( std::string path ) @@ -484,7 +462,8 @@ LiquidData LiquidData::Read(FILE* stream, LiquidHeader& header) H2ORenderMask H2ORenderMask::Read(FILE* stream) { H2ORenderMask ret; - if (int count = fread(&ret.Mask, sizeof(uint8), 8, stream) != 8) + 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); return ret; } diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h index 64fb1bb35ba..d6bb421a633 100644 --- a/src/tools/mesh_extractor/Utils.h +++ b/src/tools/mesh_extractor/Utils.h @@ -13,6 +13,7 @@ #include <ace/Stack_Trace.h> struct WorldModelDefinition; +class DoodadDefinition; class DoodadInstance; #define ASSERT(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } } @@ -25,11 +26,22 @@ struct Vector3 float y; float z; - Vector3 operator +(Vector3 const& other) + Vector3 operator +(Vector3 const& other) const { return Vector3(x + other.x, y + other.y, z + other.z); } + Vector3 operator -(Vector3 const& other) const + { + return Vector3(x - other.x, y - other.y, z - other.z); + } + + template<typename T> + Vector3 operator *(T s) const + { + return Vector3(x * s, y * s, z * s); + } + static Vector3 Read(FILE* file); }; @@ -342,10 +354,9 @@ public: static void Reverse(char word[]); static std::string ReadString(FILE* file); static uint32 Size(FILE* file); - static Vector3 ToRecast( Vector3 val ); - static std::string GetAdtPath(std::string world, int x, int y); - static std::string FixModelPath(std::string path); - static G3D::Matrix4 GetTransformation(IDefinition def); + 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); /// They say its better to declare template functions in the header files. template <typename T> static std::string ToString(T val) @@ -354,13 +365,9 @@ public: ss << val; return ss.str(); } - static G3D::Matrix4 RotationX(float angle); - static G3D::Matrix4 RotationY(float angle); - static G3D::Matrix4 RotationZ(float angle); static float ToRadians(float degrees); - static Vector3 VectorTransform(Vector3 vec, G3D::Matrix4 matrix); - static std::string GetPathBase(std::string path); - static Vector3 GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y); + static std::string GetPathBase(const std::string& path); + static Vector3 GetLiquidVert(const IDefinition& def, Vector3 basePosition, float height, int /*x*/, int /*y*/, bool translate = true); static float Distance(float x, float y); template<typename T> static bool IsAllZero(T* arr, uint32 size) @@ -371,11 +378,13 @@ public: return true; } static std::string Replace( std::string str, const std::string& oldStr, const std::string& newStr ); - static G3D::Matrix4 GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root ); static void CreateDir( const std::string& Path ); - static void SaveToDisk(FILE* stream, std::string path); - static Vector3 ToWoWCoords( Vector3 vec ); + static void SaveToDisk(FILE* 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 ); }; #endif diff --git a/src/tools/mesh_extractor/WDT.cpp b/src/tools/mesh_extractor/WDT.cpp index 70d140e79ed..2a5a18c9848 100644 --- a/src/tools/mesh_extractor/WDT.cpp +++ b/src/tools/mesh_extractor/WDT.cpp @@ -4,7 +4,7 @@ #include "Utils.h" #include "WorldModelHandler.h" -WDT::WDT(std::string file) : IsGlobalModel(false), IsValid(false) +WDT::WDT(std::string file) : IsGlobalModel(false), IsValid(false), Model(NULL) { Data = new ChunkedData(file, 2); ReadTileTable(); @@ -21,6 +21,7 @@ void WDT::ReadGlobalModel() IsGlobalModel = true; ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream()); ModelFile = Utils::ReadString(fileChunk->GetStream()); + Model = new WorldModelRoot(ModelFile); } void WDT::ReadTileTable() diff --git a/src/tools/mesh_extractor/WDT.h b/src/tools/mesh_extractor/WDT.h index a12aa65218b..ce8a97fff00 100644 --- a/src/tools/mesh_extractor/WDT.h +++ b/src/tools/mesh_extractor/WDT.h @@ -5,6 +5,7 @@ #include "ChunkedData.h" #include "WorldModelHandler.h" +#include "WorldModelRoot.h" #include "Utils.h" class WDT @@ -18,6 +19,7 @@ public: bool IsValid; std::string ModelFile; WorldModelDefinition ModelDefinition; + WorldModelRoot* Model; bool HasTile(int x, int y); private: void ReadGlobalModel(); diff --git a/src/tools/mesh_extractor/WorldModelGroup.cpp b/src/tools/mesh_extractor/WorldModelGroup.cpp index 21e1c1e63e1..f76df73aaa6 100644 --- a/src/tools/mesh_extractor/WorldModelGroup.cpp +++ b/src/tools/mesh_extractor/WorldModelGroup.cpp @@ -3,7 +3,7 @@ #include "Chunk.h" #include "Utils.h" -WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false) +WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false), HasLiquidData(false) { Data = new ChunkedData(path); if (!Data->Stream) diff --git a/src/tools/mesh_extractor/WorldModelHandler.cpp b/src/tools/mesh_extractor/WorldModelHandler.cpp index ecfff4e97d4..3565a1d6af0 100644 --- a/src/tools/mesh_extractor/WorldModelHandler.cpp +++ b/src/tools/mesh_extractor/WorldModelHandler.cpp @@ -5,6 +5,7 @@ #include "Model.h" #include "Define.h" #include "G3D/Matrix4.h" +#include "G3D/Quat.h" #include <cstdio> WorldModelDefinition WorldModelDefinition::Read( FILE* file ) @@ -30,22 +31,20 @@ WorldModelDefinition WorldModelDefinition::Read( FILE* file ) WorldModelHandler::WorldModelHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL) { - if (!adt->HasObjectData) - return; ReadModelPaths(); ReadDefinitions(); } -void WorldModelHandler::ProcessInternal( ChunkedData* subChunks ) +void WorldModelHandler::ProcessInternal( MapChunk* mcnk ) { if (!IsSane()) return; - Chunk* wmoReferencesChunk = subChunks->GetChunkByName("MCRW"); - if (!wmoReferencesChunk) - return; - FILE* stream = wmoReferencesChunk->GetStream(); - uint32 refCount = wmoReferencesChunk->Length / 4; - for (uint32 i = 0; i < refCount; i++) + + uint32 refCount = mcnk->Header.MapObjectRefs; + FILE* stream = mcnk->Source->GetStream(); + fseek(stream, 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) @@ -76,20 +75,25 @@ void WorldModelHandler::ProcessInternal( ChunkedData* subChunks ) InsertModelGeometry(Vertices, Triangles, wmo, model); } + // Restore the stream position + fseek(stream, mcnk->Source->Offset, SEEK_SET); } -void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root ) +void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, const WorldModelDefinition& def, WorldModelRoot* root, bool translate ) { - G3D::Matrix4 transformation = Utils::GetTransformation(def); for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group) { uint32 vertOffset = verts.size(); for (std::vector<Vector3>::iterator itr2 = group->Vertices.begin(); itr2 != group->Vertices.end(); ++itr2) - verts.push_back(Utils::VectorTransform(*itr2, transformation)); + { + Vector3 v = Utils::TransformDoodadVertex(def, *itr2, translate); + // If translate is false, then we were called directly from the TileBuilder to add data to it's _Geometry member, hence, we have to manually convert the vertices to Recast format. + verts.push_back(translate ? v : Utils::ToRecast(v)); // Transform the vertex to world space + } for (uint32 i = 0; i < group->Triangles.size(); ++i) { - // only include collidable tris + // only include colliding tris if ((group->TriangleFlags[i] & 0x04) != 0 && group->TriangleMaterials[i] != 0xFF) continue; Triangle<uint16> tri = group->Triangles[i]; @@ -120,10 +124,12 @@ void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::v if (!model->IsCollidable) continue; - G3D::Matrix4 doodadTransformation = Utils::GetWmoDoodadTransformation(*instance, def); int vertOffset = verts.size(); for (std::vector<Vector3>::iterator itr2 = model->Vertices.begin(); itr2 != model->Vertices.end(); ++itr2) - verts.push_back(Utils::VectorTransform(*itr2, doodadTransformation)); + { + Vector3 v = Utils::TransformDoodadVertex(def, Utils::TransformWmoDoodad(*instance, def, *itr2, false), translate); + verts.push_back(translate ? v : Utils::ToRecast(v)); + } for (std::vector<Triangle<uint16> >::iterator itr2 = model->Triangles.begin(); itr2 != model->Triangles.end(); ++itr2) tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, itr2->V0 + vertOffset, itr2->V1 + vertOffset, itr2->V2 + vertOffset)); } @@ -133,22 +139,32 @@ void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::v if (!group->HasLiquidData) continue; - for (uint32 y = 0; y < group->LiquidDataHeader.Height; y++) + const LiquidHeader& liquidHeader = group->LiquidDataHeader; + LiquidData& liquidDataGeometry = group->LiquidDataGeometry; + + for (uint32 y = 0; y < liquidHeader.Height; y++) { - for (uint32 x = 0; x < group->LiquidDataHeader.Width; x++) + for (uint32 x = 0; x < liquidHeader.Width; x++) { - if (!group->LiquidDataGeometry.ShouldRender(x, y)) + + if (!liquidDataGeometry.ShouldRender(x, y)) continue; uint32 vertOffset = verts.size(); - verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, - group->LiquidDataGeometry.HeightMap[x][y], x, y)); - verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, - group->LiquidDataGeometry.HeightMap[x + 1][y], x + 1, y)); - verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, - group->LiquidDataGeometry.HeightMap[x][y + 1], x, y + 1)); - verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, - group->LiquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1)); + + Vector3 v1 = Utils::GetLiquidVert(def, liquidHeader.BaseLocation, + liquidDataGeometry.HeightMap[x][y], x, y, translate); + Vector3 v2 = Utils::GetLiquidVert(def, liquidHeader.BaseLocation, + liquidDataGeometry.HeightMap[x + 1][y], x + 1, y, translate); + Vector3 v3 = Utils::GetLiquidVert(def, liquidHeader.BaseLocation, + liquidDataGeometry.HeightMap[x][y + 1], x, y + 1, translate); + Vector3 v4 = Utils::GetLiquidVert(def, liquidHeader.BaseLocation, + liquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1, translate); + + verts.push_back(translate ? v1 : Utils::ToRecast(v1)); + verts.push_back(translate ? v2 : Utils::ToRecast(v2)); + verts.push_back(translate ? v3 : Utils::ToRecast(v3)); + verts.push_back(translate ? v4 : Utils::ToRecast(v4)); tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset + 2, vertOffset + 1)); tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1)); diff --git a/src/tools/mesh_extractor/WorldModelHandler.h b/src/tools/mesh_extractor/WorldModelHandler.h index 29715ded696..87a5ad62938 100644 --- a/src/tools/mesh_extractor/WorldModelHandler.h +++ b/src/tools/mesh_extractor/WorldModelHandler.h @@ -34,9 +34,9 @@ public: std::vector<Vector3> Vertices; std::vector<Triangle<uint32> > Triangles; bool IsSane() { return _definitions && _paths; } - void InsertModelGeometry(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root); + static void InsertModelGeometry(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, const WorldModelDefinition& def, WorldModelRoot* root, bool translate = true); protected: - void ProcessInternal(ChunkedData* data); + void ProcessInternal(MapChunk* data); private: void ReadDefinitions(); void ReadModelPaths(); diff --git a/src/tools/mesh_extractor/WorldModelRoot.cpp b/src/tools/mesh_extractor/WorldModelRoot.cpp index c34a77e4531..a00506f3b16 100644 --- a/src/tools/mesh_extractor/WorldModelRoot.cpp +++ b/src/tools/mesh_extractor/WorldModelRoot.cpp @@ -12,6 +12,11 @@ WorldModelRoot::WorldModelRoot( std::string path ) ReadDoodadSets(); } +WorldModelRoot::~WorldModelRoot() +{ + delete Data; +} + void WorldModelRoot::ReadGroups() { std::string pathBase = Utils::GetPathBase(Path); diff --git a/src/tools/mesh_extractor/WorldModelRoot.h b/src/tools/mesh_extractor/WorldModelRoot.h index c06ff3d5d2b..ad2e15b36d5 100644 --- a/src/tools/mesh_extractor/WorldModelRoot.h +++ b/src/tools/mesh_extractor/WorldModelRoot.h @@ -11,6 +11,7 @@ class WorldModelRoot { public: WorldModelRoot(std::string path); + ~WorldModelRoot(); std::string Path; ChunkedData* Data; WorldModelHeader Header; diff --git a/src/tools/vmap4_extractor/wmo.cpp b/src/tools/vmap4_extractor/wmo.cpp index 983d0316bc1..d7fbd04679e 100644 --- a/src/tools/vmap4_extractor/wmo.cpp +++ b/src/tools/vmap4_extractor/wmo.cpp @@ -223,7 +223,7 @@ bool WMOGroup::open() else if (!strcmp(fourcc,"MLIQ")) { liquflags |= 1; - hlq = new WMOLiquidHeader; + hlq = new WMOLiquidHeader(); f.read(hlq, 0x1E); LiquEx_size = sizeof(WMOLiquidVert) * hlq->xverts * hlq->yverts; LiquEx = new WMOLiquidVert[hlq->xverts * hlq->yverts]; |
