#include "ContinentBuilder.h" #include "TileBuilder.h" #include "Geometry.h" #include "Constants.h" #include "Utils.h" #include "Cache.h" #include "ADT.h" #include "WDT.h" #include "Recast.h" #include "RecastAlloc.h" #include "DetourNavMeshBuilder.h" #include 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.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.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.maxSimplificationError = 1.3f; */ // All are in UNIT metrics! memset(&Config, 0, sizeof(rcConfig)); 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; Context = new rcContext; } void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshParams& /*navMeshParams*/ ) { bmin = new float[3]; bmax = new float[3]; bmin[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * X); bmin[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * Y); bmax[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * (X + 1)); bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1)); } uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) { _Geometry = new Geometry(); _Geometry->Transform = true; ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y)); adt->Read(); _Geometry->AddAdt(adt); delete adt; if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty()) return NULL; // again, we load everything - wasteful but who cares for (int ty = Y - 2; ty <= Y + 2; ty++) { for (int tx = X - 2; tx <= X + 2; 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 if (!_adt->Data->Stream) { delete _adt; continue; } _adt->Read(); _Geometry->AddAdt(_adt); delete _adt; } } 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); } 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(); rcVcopy(Config.bmin, cBuilder->bmin); rcVcopy(Config.bmax, cBuilder->bmax); // this sets the dimensions of the heightfield - should maybe happen before border padding rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height); // Initialize per tile config. rcConfig tileCfg = Config; tileCfg.width = Config.tileSize + Config.borderSize * 2; tileCfg.height = Config.tileSize + Config.borderSize * 2; // merge per tile poly and detail meshes rcPolyMesh** pmmerge = new rcPolyMesh*[Constants::TilesPerMap * Constants::TilesPerMap]; rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap]; 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; } } rcPolyMesh* pmesh = rcAllocPolyMesh(); rcMergePolyMeshes(Context, pmmerge, nmerge, *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; } // 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 = 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); // 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("[%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); bool result = dtCreateNavMeshData(¶ms, &navData, &navDataSize); // Free some memory rcFreePolyMesh(pmesh); rcFreePolyMeshDetail(dmesh); delete areas; delete triangles; delete vertices; if (result) { printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize); DataSize = navDataSize; return navData; } return NULL; } TileBuilder::~TileBuilder() { delete Context; delete _Geometry; }