Tools/MeshExtractor: Implemented mmap mesh generation for instances (They do not have ADT files, they're just a big WMO).

Cleaned up some things and fixed some errors.
ToDo:
* Test this a bit more.
* Cleanup the codebase for this, remove duplicated code.
This commit is contained in:
Sebastian Valle
2013-09-30 01:05:07 -05:00
parent 9876108f3e
commit 15e7f7ab21
9 changed files with 287 additions and 47 deletions
+108 -41
View File
@@ -11,46 +11,99 @@ class BuilderThread : public ACE_Task_Base
{
private:
int X, Y, MapId;
bool Instance;
std::string Continent;
dtNavMeshParams Params;
ContinentBuilder* cBuilder;
WorldModelRoot* Model;
const WorldModelDefinition* Definition;
public:
BuilderThread(ContinentBuilder* _cBuilder, dtNavMeshParams& params) : Params(params), cBuilder(_cBuilder), Free(true) {}
void SetData(int x, int y, int map, const std::string& cont) { X = x; Y = y; MapId = map; Continent = cont; }
void SetData(int x, int y, int map, const std::string& cont, bool instance, WorldModelRoot* model, const WorldModelDefinition* def)
{
X = x;
Y = y;
MapId = map;
Continent = cont;
Instance = instance;
if (Instance)
{
Model = model;
Definition = def;
}
else
Model = NULL;
}
int svc()
{
Free = false;
printf("[%02i,%02i] Building tile\n", X, Y);
TileBuilder builder(cBuilder, Continent, X, Y, MapId);
char buff[100];
sprintf(buff, "mmaps/%03u%02i%02i.mmtile", MapId, Y, X);
FILE* f = fopen(buff, "r");
if (f) // Check if file already exists.
if (Instance)
{
printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y);
fclose(f);
Free = true;
return 0;
}
uint8* nav = builder.Build(Params);
if (nav)
{
f = fopen(buff, "wb");
if (!f)
// Build a WMO
printf("Building WMO %s ( %u )", Continent.c_str(), MapId);
TileBuilder builder(cBuilder, Continent, X, Y, MapId);
char buff[100];
sprintf(buff, "mmaps/%03u%02i%02i.mmtile", MapId, Y, X);
FILE* f = fopen(buff, "r");
if (f) // Check if file already exists.
{
printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
printf("Instance %s ( %u ) skipped, file already exists\n", Continent.c_str(), MapId);
fclose(f);
return 0;
}
MmapTileHeader header;
header.size = builder.DataSize;
fwrite(&header, sizeof(MmapTileHeader), 1, f);
fwrite(nav, sizeof(unsigned char), builder.DataSize, f);
fclose(f);
uint8* nav = builder.BuildInstance(Params, Model, *Definition);
if (nav)
{
f = fopen(buff, "wb");
if (!f)
{
printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
return 0;
}
MmapTileHeader header;
header.size = builder.DataSize;
fwrite(&header, sizeof(MmapTileHeader), 1, f);
fwrite(nav, sizeof(unsigned char), builder.DataSize, f);
fclose(f);
}
dtFree(nav);
return 0;
}
else
{
Free = false;
printf("[%02i,%02i] Building tile\n", X, Y);
TileBuilder builder(cBuilder, Continent, X, Y, MapId);
char buff[100];
sprintf(buff, "mmaps/%03u%02i%02i.mmtile", MapId, Y, X);
FILE* f = fopen(buff, "r");
if (f) // Check if file already exists.
{
printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y);
fclose(f);
Free = true;
return 0;
}
uint8* nav = builder.Build(Params);
if (nav)
{
f = fopen(buff, "wb");
if (!f)
{
printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff);
return 0;
}
MmapTileHeader header;
header.size = builder.DataSize;
fwrite(&header, sizeof(MmapTileHeader), 1, f);
fwrite(nav, sizeof(unsigned char), builder.DataSize, f);
fclose(f);
}
dtFree(nav);
printf("[%02i,%02i] Tile Built!\n", X, Y);
Free = true;
}
dtFree(nav);
printf("[%02i,%02i] Tile Built!\n", X, Y);
Free = true;
return 0;
}
@@ -110,28 +163,42 @@ void ContinentBuilder::Build()
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, 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)
if (TileMap->IsGlobalModel)
{
bool next = false;
while (!next)
printf("Map %s ( %u ) is a WMO. Building with 1 thread.\n", Continent.c_str(), MapId);
BuilderThread* thread = new BuilderThread(this, params);
Threads.push_back(thread);
thread->SetData(65, 65, MapId, Continent, true, TileMap->Model, &TileMap->ModelDefinition);
thread->activate();
thread->wait();
}
else
{
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, false, NULL, NULL);
(*_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
+6
View File
@@ -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>::min();
min[i] = std::numeric_limits<float>::max();
}
for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
{
+4 -2
View File
@@ -35,7 +35,7 @@ void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads)
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;
@@ -273,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()));
}
@@ -391,7 +393,7 @@ int main(int argc, char* argv[])
//
dtQueryFilter m_filter;
m_filter.setIncludeFlags(0xffff) ;
m_filter.setIncludeFlags(0xffff);
m_filter.setExcludeFlags(0);
//
+160
View File
@@ -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"
@@ -60,6 +61,22 @@ TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x,
Config.detailSampleDist = Config.cs * 64;
Config.detailSampleMaxError = Config.ch * 2;
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,6 +90,149 @@ void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshPara
bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1));
}
uint8* TileBuilder::BuildInstance( dtNavMeshParams& navMeshParams, WorldModelRoot* root, const WorldModelDefinition& def )
{
_Geometry = new Geometry();
_Geometry->Transform = true;
WorldModelHandler::InsertModelGeometry(_Geometry->Vertices, _Geometry->Triangles, def, root);
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);
}
float* bmin, *bmax;
_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);
_Geometry->Vertices.clear();
_Geometry->Triangles.clear();
// this sets the dimensions of the heightfield - should maybe happen before border padding
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 = 0;
params.tileY = 0;
params.tileLayer = 0;
rcVcopy(params.bmax, bmax);
rcVcopy(params.bmin, bmin);
// Offmesh-connection settings
params.offMeshConCount = 0; // none for now
//params.tileSize = Constants::VertexPerMap;
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");
delete areas;
delete triangles;
delete vertices;
return NULL;
}
int navDataSize;
uint8* navData;
printf("Creating the navmesh with %i vertices, %i polys, %i triangles!\n", pmesh->nverts, pmesh->npolys, dmesh->ntris);
bool result = dtCreateNavMeshData(&params, &navData, &navDataSize);
delete areas;
delete triangles;
delete vertices;
if (result)
{
printf("NavMesh created, size %i!\n", navDataSize);
DataSize = navDataSize;
return navData;
}
return NULL;
}
uint8* TileBuilder::Build(dtNavMeshParams& navMeshParams)
{
_Geometry = new Geometry();
+3 -1
View File
@@ -4,6 +4,7 @@
#include "Recast.h"
#include "Geometry.h"
#include "WorldModelRoot.h"
class ContinentBuilder;
class WDT;
@@ -16,12 +17,13 @@ public:
void CalculateTileBounds(float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams);
uint8* Build(dtNavMeshParams& navMeshParams);
uint8* BuildInstance(dtNavMeshParams& navMeshParams, WorldModelRoot* root, const WorldModelDefinition& def);
std::string World;
int X;
int Y;
int MapId;
rcConfig Config;
rcConfig InstanceConfig;
rcContext* Context;
Geometry* _Geometry;
uint32 DataSize;
+2 -1
View File
@@ -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()
+2
View File
@@ -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();
@@ -79,7 +79,7 @@ void WorldModelHandler::ProcessInternal( MapChunk* mcnk )
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 )
{
for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
{
+1 -1
View File
@@ -34,7 +34,7 @@ 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);
protected:
void ProcessInternal(MapChunk* data);
private: