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:
@@ -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(¶ms, 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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
//
|
||||
|
||||
@@ -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(¶ms, 0, sizeof(params));
|
||||
// PolyMesh data
|
||||
params.verts = pmesh->verts;
|
||||
params.vertCount = pmesh->nverts;
|
||||
params.polys = pmesh->polys;
|
||||
params.polyAreas = pmesh->areas;
|
||||
params.polyFlags = pmesh->flags;
|
||||
params.polyCount = pmesh->npolys;
|
||||
params.nvp = pmesh->nvp;
|
||||
// PolyMeshDetail data
|
||||
params.detailMeshes = dmesh->meshes;
|
||||
params.detailVerts = dmesh->verts;
|
||||
params.detailVertsCount = dmesh->nverts;
|
||||
params.detailTris = dmesh->tris;
|
||||
params.detailTriCount = dmesh->ntris;
|
||||
rcVcopy(params.bmin, pmesh->bmin);
|
||||
rcVcopy(params.bmax, pmesh->bmax);
|
||||
// General settings
|
||||
params.ch = InstanceConfig.ch;
|
||||
params.cs = InstanceConfig.cs;
|
||||
params.walkableClimb = InstanceConfig.walkableClimb * InstanceConfig.ch;
|
||||
params.walkableHeight = InstanceConfig.walkableHeight * InstanceConfig.ch;
|
||||
params.walkableRadius = InstanceConfig.walkableRadius * InstanceConfig.cs;
|
||||
params.tileX = 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(¶ms, &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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user