aboutsummaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2013-10-07 14:30:37 +0200
committerShauren <shauren.trinity@gmail.com>2013-10-07 14:30:37 +0200
commitfca4a5448ac476b942c330108a76032ae242711e (patch)
tree81fe500a02353fa5079532fe53c21a4f3a269b82 /src/tools
parent1b56bd1b6a860c325f3e4e76d94dc367429fbb16 (diff)
parentc8f525c76e162b5b546c91628a1457f9aef43699 (diff)
Merge branch 'master' of github.com:TrinityCore/TrinityCore into 4.3.4
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/mesh_extractor/ADT.cpp6
-rw-r--r--src/tools/mesh_extractor/ADT.h5
-rw-r--r--src/tools/mesh_extractor/Cache.h10
-rw-r--r--src/tools/mesh_extractor/ChunkedData.cpp6
-rw-r--r--src/tools/mesh_extractor/ChunkedData.h6
-rw-r--r--src/tools/mesh_extractor/Constants.h1
-rw-r--r--src/tools/mesh_extractor/ContinentBuilder.cpp108
-rw-r--r--src/tools/mesh_extractor/ContinentBuilder.h2
-rw-r--r--src/tools/mesh_extractor/DoodadHandler.cpp26
-rw-r--r--src/tools/mesh_extractor/DoodadHandler.h9
-rw-r--r--src/tools/mesh_extractor/Geometry.cpp10
-rw-r--r--src/tools/mesh_extractor/LiquidHandler.cpp2
-rw-r--r--src/tools/mesh_extractor/MPQ.cpp8
-rw-r--r--src/tools/mesh_extractor/MPQ.h3
-rw-r--r--src/tools/mesh_extractor/MPQManager.cpp17
-rw-r--r--src/tools/mesh_extractor/MPQManager.h6
-rw-r--r--src/tools/mesh_extractor/MapChunk.cpp2
-rw-r--r--src/tools/mesh_extractor/MeshExtractor.cpp127
-rw-r--r--src/tools/mesh_extractor/Model.cpp28
-rw-r--r--src/tools/mesh_extractor/Model.h6
-rw-r--r--src/tools/mesh_extractor/ObjectDataHandler.cpp14
-rw-r--r--src/tools/mesh_extractor/ObjectDataHandler.h2
-rw-r--r--src/tools/mesh_extractor/TileBuilder.cpp412
-rw-r--r--src/tools/mesh_extractor/TileBuilder.h8
-rw-r--r--src/tools/mesh_extractor/Utils.cpp127
-rw-r--r--src/tools/mesh_extractor/Utils.h37
-rw-r--r--src/tools/mesh_extractor/WDT.cpp3
-rw-r--r--src/tools/mesh_extractor/WDT.h2
-rw-r--r--src/tools/mesh_extractor/WorldModelGroup.cpp2
-rw-r--r--src/tools/mesh_extractor/WorldModelHandler.cpp68
-rw-r--r--src/tools/mesh_extractor/WorldModelHandler.h4
-rw-r--r--src/tools/mesh_extractor/WorldModelRoot.cpp5
-rw-r--r--src/tools/mesh_extractor/WorldModelRoot.h1
-rw-r--r--src/tools/vmap4_extractor/wmo.cpp2
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(&params, 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(&params, 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(&params, 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(&params, sizeof(dtNavMeshParams), 1, mmap);
fclose(mmap);
@@ -401,13 +422,16 @@ int main(int argc, char* argv[])
dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery();
navMesh->init(&params);
- 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(&params, 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(&params, &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(&params, &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];