Tools/mmaps_generator: Replace do while (false) loops with unique_ptr using custom deleter

This commit is contained in:
Shauren
2025-10-25 11:30:07 +02:00
parent 82e8c26831
commit f02112d52f
4 changed files with 194 additions and 193 deletions

View File

@@ -188,7 +188,7 @@ namespace MMAP
fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
}
void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData const& meshData)
{
std::string objFileName;
objFileName = Trinity::StringFormat("meshes/map{:04}{:02}{:02}.obj", mapID, tileY, tileX);

View File

@@ -44,7 +44,7 @@ namespace MMAP
void debugWrite(FILE* file, rcPolyMesh const* mesh);
void debugWrite(FILE* file, rcPolyMeshDetail const* mesh);
void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData const& meshData);
};
}
#endif

View File

@@ -571,136 +571,135 @@ namespace MMAP
bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData& meshData, VMAP::VMapManager* vmapManager)
{
VMAP::LoadResult result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
if (result != VMAP::LoadResult::Success)
return false;
auto vmapTile = Trinity::make_unique_ptr_with_deleter(vmapManager, [=](VMAP::VMapManager* mgr)
{
mgr->unloadMap(mapID, tileX, tileY);
});
std::span<VMAP::ModelInstance const> models = vmapManager->getModelsOnMap(mapID);
if (models.empty())
return false;
bool retval = false;
do
for (VMAP::ModelInstance const& instance : models)
{
if (result != VMAP::LoadResult::Success)
break;
// model instances exist in tree even though there are instances of that model in this tile
VMAP::WorldModel const* worldModel = instance.getWorldModel();
if (!worldModel)
continue;
std::span<VMAP::ModelInstance const> models = vmapManager->getModelsOnMap(mapID);
if (models.empty())
break;
// now we have a model to add to the meshdata
retval = true;
for (VMAP::ModelInstance const& instance : models)
std::vector<VMAP::GroupModel> const& groupModels = worldModel->getGroupModels();
// all M2s need to have triangle indices reversed
bool isM2 = worldModel->IsM2();
// transform data
float scale = instance.iScale;
G3D::Matrix3 rotation = instance.GetInvRot();
G3D::Vector3 position = instance.iPos;
position.x -= 32 * GRID_SIZE;
position.y -= 32 * GRID_SIZE;
for (std::vector<VMAP::GroupModel>::const_iterator it = groupModels.begin(); it != groupModels.end(); ++it)
{
// model instances exist in tree even though there are instances of that model in this tile
VMAP::WorldModel const* worldModel = instance.getWorldModel();
if (!worldModel)
continue;
std::vector<G3D::Vector3> const& tempVertices = it->GetVertices();
std::vector<G3D::Vector3> transformedVertices;
std::vector<VMAP::MeshTriangle> const& tempTriangles = it->GetTriangles();
VMAP::WmoLiquid const* liquid = it->GetLiquid();
// now we have a model to add to the meshdata
retval = true;
// first handle collision mesh
transform(tempVertices, transformedVertices, scale, rotation, position);
std::vector<VMAP::GroupModel> const& groupModels = worldModel->getGroupModels();
int offset = meshData.solidVerts.size() / 3;
// all M2s need to have triangle indices reversed
bool isM2 = worldModel->IsM2();
copyVertices(transformedVertices, meshData.solidVerts);
copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
// transform data
float scale = instance.iScale;
G3D::Matrix3 rotation = instance.GetInvRot();
G3D::Vector3 position = instance.iPos;
position.x -= 32 * GRID_SIZE;
position.y -= 32 * GRID_SIZE;
for (std::vector<VMAP::GroupModel>::const_iterator it = groupModels.begin(); it != groupModels.end(); ++it)
// now handle liquid data
if (liquid && liquid->GetFlagsStorage())
{
std::vector<G3D::Vector3> const& tempVertices = it->GetVertices();
std::vector<G3D::Vector3> transformedVertices;
std::vector<VMAP::MeshTriangle> const& tempTriangles = it->GetTriangles();
VMAP::WmoLiquid const* liquid = it->GetLiquid();
std::vector<G3D::Vector3> liqVerts;
std::vector<int> liqTris;
uint32 tilesX, tilesY, vertsX, vertsY;
G3D::Vector3 corner;
liquid->getPosInfo(tilesX, tilesY, corner);
vertsX = tilesX + 1;
vertsY = tilesY + 1;
uint8 const* flags = liquid->GetFlagsStorage();
float const* data = liquid->GetHeightStorage();
uint8 type = NAV_AREA_EMPTY;
// first handle collision mesh
transform(tempVertices, transformedVertices, scale, rotation, position);
// convert liquid type to NavTerrain
EnumFlag<map_liquidHeaderTypeFlags> liquidFlags = map_liquidHeaderTypeFlags(vmapManager->GetLiquidFlagsPtr(liquid->GetType()));
if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean))
type = NAV_AREA_WATER;
else if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Magma | map_liquidHeaderTypeFlags::Slime))
type = NAV_AREA_MAGMA_SLIME;
int offset = meshData.solidVerts.size() / 3;
// indexing is weird...
// after a lot of trial and error, this is what works:
// vertex = y*vertsX+x
// tile = x*tilesY+y
// flag = y*tilesY+x
copyVertices(transformedVertices, meshData.solidVerts);
copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
// now handle liquid data
if (liquid && liquid->GetFlagsStorage())
G3D::Vector3 vert;
for (uint32 x = 0; x < vertsX; ++x)
{
std::vector<G3D::Vector3> liqVerts;
std::vector<int> liqTris;
uint32 tilesX, tilesY, vertsX, vertsY;
G3D::Vector3 corner;
liquid->getPosInfo(tilesX, tilesY, corner);
vertsX = tilesX + 1;
vertsY = tilesY + 1;
uint8 const* flags = liquid->GetFlagsStorage();
float const* data = liquid->GetHeightStorage();
uint8 type = NAV_AREA_EMPTY;
// convert liquid type to NavTerrain
EnumFlag<map_liquidHeaderTypeFlags> liquidFlags = map_liquidHeaderTypeFlags(vmapManager->GetLiquidFlagsPtr(liquid->GetType()));
if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean))
type = NAV_AREA_WATER;
else if (liquidFlags.HasFlag(map_liquidHeaderTypeFlags::Magma | map_liquidHeaderTypeFlags::Slime))
type = NAV_AREA_MAGMA_SLIME;
// indexing is weird...
// after a lot of trial and error, this is what works:
// vertex = y*vertsX+x
// tile = x*tilesY+y
// flag = y*tilesY+x
G3D::Vector3 vert;
for (uint32 x = 0; x < vertsX; ++x)
for (uint32 y = 0; y < vertsY; ++y)
{
for (uint32 y = 0; y < vertsY; ++y)
vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]);
vert = vert * rotation * scale + position;
vert.x *= -1.f;
vert.y *= -1.f;
liqVerts.push_back(vert);
}
}
int idx1, idx2, idx3, idx4;
uint32 square;
for (uint32 x = 0; x < tilesX; ++x)
{
for (uint32 y = 0; y < tilesY; ++y)
{
if ((flags[x + y * tilesX] & 0x0f) != 0x0f)
{
vert = G3D::Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y * vertsX + x]);
vert = vert * rotation * scale + position;
vert.x *= -1.f;
vert.y *= -1.f;
liqVerts.push_back(vert);
square = x * tilesY + y;
idx1 = square + x;
idx2 = square + 1 + x;
idx3 = square + tilesY + 1 + 1 + x;
idx4 = square + tilesY + 1 + x;
// top triangle
liqTris.push_back(idx3);
liqTris.push_back(idx2);
liqTris.push_back(idx1);
// bottom triangle
liqTris.push_back(idx4);
liqTris.push_back(idx3);
liqTris.push_back(idx1);
}
}
}
int idx1, idx2, idx3, idx4;
uint32 square;
for (uint32 x = 0; x < tilesX; ++x)
{
for (uint32 y = 0; y < tilesY; ++y)
{
if ((flags[x + y * tilesX] & 0x0f) != 0x0f)
{
square = x * tilesY + y;
idx1 = square + x;
idx2 = square + 1 + x;
idx3 = square + tilesY + 1 + 1 + x;
idx4 = square + tilesY + 1 + x;
uint32 liqOffset = meshData.liquidVerts.size() / 3;
for (uint32 i = 0; i < liqVerts.size(); ++i)
meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
// top triangle
liqTris.push_back(idx3);
liqTris.push_back(idx2);
liqTris.push_back(idx1);
// bottom triangle
liqTris.push_back(idx4);
liqTris.push_back(idx3);
liqTris.push_back(idx1);
}
}
}
uint32 liqOffset = meshData.liquidVerts.size() / 3;
for (uint32 i = 0; i < liqVerts.size(); ++i)
meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
for (uint32 i = 0; i < liqTris.size() / 3; ++i)
{
meshData.liquidTris.append(liqTris[i * 3 + 1] + liqOffset, liqTris[i * 3 + 2] + liqOffset, liqTris[i * 3] + liqOffset);
meshData.liquidType.append(type);
}
for (uint32 i = 0; i < liqTris.size() / 3; ++i)
{
meshData.liquidTris.append(liqTris[i * 3 + 1] + liqOffset, liqTris[i * 3 + 2] + liqOffset, liqTris[i * 3] + liqOffset);
meshData.liquidType.append(type);
}
}
}
}
while (false);
vmapManager->unloadMap(mapID, tileX, tileY);
return retval;
}

View File

@@ -371,98 +371,100 @@ namespace MMAP
unsigned char* navData = nullptr;
int navDataSize = 0;
do
{
// these values are checked within dtCreateNavMeshData - handle them here
// so we have a clear error message
if (params.nvp > DT_VERTS_PER_POLYGON)
{
TC_LOG_ERROR("maps.mmapgen", "{} Invalid verts-per-polygon value!", tileString);
break;
}
if (params.vertCount >= 0xffff)
{
TC_LOG_ERROR("maps.mmapgen", "{} Too many vertices!", tileString);
break;
}
if (!params.vertCount || !params.verts)
{
// occurs mostly when adjacent tiles have models
// loaded but those models don't span into this tile
// message is an annoyance
//TC_LOG_ERROR("maps.mmapgen", "{} No vertices to build tile!", tileString);
break;
}
if (!params.polyCount || !params.polys)
{
// 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
TC_LOG_ERROR("maps.mmapgen", "{} No polygons to build on tile!", tileString);
break;
}
if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
{
TC_LOG_ERROR("maps.mmapgen", "{} No detail mesh to build tile!", tileString);
break;
}
TC_LOG_DEBUG("maps.mmapgen", "{} Building navmesh tile...", tileString);
if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
{
TC_LOG_ERROR("maps.mmapgen", "{} Failed building navmesh tile!", tileString);
break;
}
dtTileRef tileRef = 0;
TC_LOG_DEBUG("maps.mmapgen", "{} Adding tile to navmesh...", tileString);
// DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
// is removed via removeTile()
dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
if (!tileRef || !dtStatusSucceed(dtResult))
{
TC_LOG_ERROR("maps.mmapgen", "{} Failed adding tile to navmesh!", tileString);
break;
}
// file output
std::string fileName = Trinity::StringFormat("mmaps/{:04}{:02}{:02}.mmtile", mapID, tileY, tileX);
auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "wb"));
if (!file)
{
TC_LOG_ERROR("maps.mmapgen", "{}: [Map {:04}] Failed to open {} for writing!", strerror(errno), mapID, fileName);
navMesh->removeTile(tileRef, nullptr, nullptr);
break;
}
TC_LOG_DEBUG("maps.mmapgen", "{} Writing to file...", tileString);
// write header
MmapTileHeader header;
header.usesLiquids = m_terrainBuilder.usesLiquids();
header.size = uint32(navDataSize);
fwrite(&header, sizeof(MmapTileHeader), 1, file.get());
// write data
fwrite(navData, sizeof(unsigned char), navDataSize, file.get());
// now that tile is written to disk, we can unload it
navMesh->removeTile(tileRef, nullptr, nullptr);
} while (false);
if (m_debugOutput)
auto debugOutputWriter = Trinity::make_unique_ptr_with_deleter(m_debugOutput ? &iv : nullptr, [=, borderSize = static_cast<unsigned short>(config.borderSize), &meshData](IntermediateValues* intermediate)
{
// restore padding so that the debug visualization is correct
for (int i = 0; i < iv.polyMesh->nverts; ++i)
for (std::ptrdiff_t i = 0; i < intermediate->polyMesh->nverts; ++i)
{
unsigned short* v = &iv.polyMesh->verts[i * 3];
v[0] += (unsigned short)config.borderSize;
v[2] += (unsigned short)config.borderSize;
unsigned short* v = &intermediate->polyMesh->verts[i * 3];
v[0] += borderSize;
v[2] += borderSize;
}
iv.generateObjFile(mapID, tileX, tileY, meshData);
iv.writeIV(mapID, tileX, tileY);
intermediate->generateObjFile(mapID, tileX, tileY, meshData);
intermediate->writeIV(mapID, tileX, tileY);
});
// these values are checked within dtCreateNavMeshData - handle them here
// so we have a clear error message
if (params.nvp > DT_VERTS_PER_POLYGON)
{
TC_LOG_ERROR("maps.mmapgen", "{} Invalid verts-per-polygon value!", tileString);
return;
}
if (params.vertCount >= 0xffff)
{
TC_LOG_ERROR("maps.mmapgen", "{} Too many vertices!", tileString);
return;
}
if (!params.vertCount || !params.verts)
{
// occurs mostly when adjacent tiles have models
// loaded but those models don't span into this tile
// message is an annoyance
//TC_LOG_ERROR("maps.mmapgen", "{} No vertices to build tile!", tileString);
return;
}
if (!params.polyCount || !params.polys)
{
// 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
TC_LOG_ERROR("maps.mmapgen", "{} No polygons to build on tile!", tileString);
return;
}
if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
{
TC_LOG_ERROR("maps.mmapgen", "{} No detail mesh to build tile!", tileString);
return;
}
TC_LOG_DEBUG("maps.mmapgen", "{} Building navmesh tile...", tileString);
if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
{
TC_LOG_ERROR("maps.mmapgen", "{} Failed building navmesh tile!", tileString);
return;
}
dtTileRef tileRef = 0;
TC_LOG_DEBUG("maps.mmapgen", "{} Adding tile to navmesh...", tileString);
// DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
// is removed via removeTile()
dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
if (!tileRef || !dtStatusSucceed(dtResult))
{
TC_LOG_ERROR("maps.mmapgen", "{} Failed adding tile to navmesh!", tileString);
return;
}
auto navMeshTile = Trinity::make_unique_ptr_with_deleter(&tileRef, [navMesh](dtTileRef const* ref)
{
navMesh->removeTile(*ref, nullptr, nullptr);
});
// file output
std::string fileName = Trinity::StringFormat("mmaps/{:04}{:02}{:02}.mmtile", mapID, tileY, tileX);
auto file = Trinity::make_unique_ptr_with_deleter<&::fclose>(fopen(fileName.c_str(), "wb"));
if (!file)
{
TC_LOG_ERROR("maps.mmapgen", "{}: [Map {:04}] Failed to open {} for writing!", strerror(errno), mapID, fileName);
return;
}
TC_LOG_DEBUG("maps.mmapgen", "{} Writing to file...", tileString);
// write header
MmapTileHeader header;
header.usesLiquids = m_terrainBuilder.usesLiquids();
header.size = uint32(navDataSize);
fwrite(&header, sizeof(MmapTileHeader), 1, file.get());
// write data
fwrite(navData, sizeof(unsigned char), navDataSize, file.get());
}
/**************************************************************************/