hello, add and implement water in this tilecache build. I want to avoid rivers when pathfinding (so pathfind prefer bridge over bottom of river or river (i exclude swim flag in path filter))): int NavMeshBuilder::rasterizeTileLayers(const int tx, const int ty, const rcConfig& cfg, TileCacheData* tiles, const int maxTiles) { //if (!m_World || !m_World->GetStaticWorldMesh().isValid()) //{ // std::cout << "NavMeshBuilder::rasterizeTileLayers: Input mesh is not specified.\n"; // return 0; //} FastLZCompressor comp; RasterizationContext rc; const float* verts; int nverts = 0; const rcChunkyTriMesh* chunkyMesh; const rcChunkyTriMesh* chunkyWaterMesh; int waternverts; const float* waterverts; { verts = m_World->GetStaticWorldMesh().getVerts(); nverts = m_World->GetStaticWorldMesh().getVertCount(); chunkyMesh = m_World->GetChunkyMesh(); chunkyWaterMesh = m_World->GetWaterChunkyMesh(); waternverts = m_World->GetWaterWorldMesh().getVertCount(); waterverts = m_World->GetWaterWorldMesh().getVerts(); } float tcs = cfg.tileSize * cfg.cs; rcConfig tcfg; memcpy(&tcfg, &cfg, sizeof(tcfg)); tcfg.bmin[0] = cfg.bmin[0] + tx * tcs; tcfg.bmin[1] = cfg.bmin[1]; tcfg.bmin[2] = cfg.bmin[2] + ty * tcs; tcfg.bmax[0] = cfg.bmin[0] + (tx + 1) * tcs; tcfg.bmax[1] = cfg.bmax[1]; tcfg.bmax[2] = cfg.bmin[2] + (ty + 1) * tcs; tcfg.bmin[0] -= tcfg.borderSize * tcfg.cs; tcfg.bmin[2] -= tcfg.borderSize * tcfg.cs; tcfg.bmax[0] += tcfg.borderSize * tcfg.cs; tcfg.bmax[2] += tcfg.borderSize * tcfg.cs; // Allocate voxel heightfield where we rasterize our input data to. rc.solid = rcAllocHeightfield(); if (!rc.solid) { printf("buildNavigation: Out of memory 'solid'.\n"); return 0; } std::unique_ptr<rcContext> m_ctx = std::make_unique<rcContext>(); if (!rcCreateHeightfield(m_ctx.get(), *rc.solid, tcfg.width, tcfg.height, tcfg.bmin, tcfg.bmax, tcfg.cs, tcfg.ch)) { printf("buildNavigation: Could not create solid heightfield.\n"); return 0; } rc.triareas = new unsigned char[chunkyMesh->maxTrisPerChunk]; if (!rc.triareas) { printf("buildNavigation: Out of memory 'm_triareas' (%d).\n", chunkyMesh->maxTrisPerChunk); return 0; } float tbmin[2], tbmax[2]; tbmin[0] = tcfg.bmin[0]; tbmin[1] = tcfg.bmin[2]; tbmax[0] = tcfg.bmax[0]; tbmax[1] = tcfg.bmax[2]; int cid[512]; const int ncid = rcChunkyTriMesh::rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512); if (!ncid) { return 0; } for (int i = 0; i < ncid; ++i) { const rcChunkyTriMeshNode& node = chunkyMesh->nodes[cid[i]]; const int* tris = &chunkyMesh->tris[node.i * 3]; const int ntris = node.n; memset(rc.triareas, 0, ntris * sizeof(unsigned char)); rcMarkWalkableTriangles(m_ctx.get(), tcfg.walkableSlopeAngle, verts, nverts, tris, ntris, rc.triareas); if (!rcRasterizeTriangles(m_ctx.get(), verts, nverts, tris, rc.triareas, ntris, *rc.solid, tcfg.walkableClimb)) return 0; } if (chunkyWaterMesh) { // } rcFilterLowHangingWalkableObstacles(m_ctx.get(), tcfg.walkableClimb, *rc.solid); rcFilterLedgeSpans(m_ctx.get(), tcfg.walkableHeight, tcfg.walkableClimb, *rc.solid); rcFilterWalkableLowHeightSpans(m_ctx.get(), tcfg.walkableHeight, *rc.solid); rc.chf = rcAllocCompactHeightfield(); if (!rc.chf) { printf("buildNavigation: Out of memory 'chf'.\n"); return 0; } if (!rcBuildCompactHeightfield(m_ctx.get(), tcfg.walkableHeight, tcfg.walkableClimb, *rc.solid, *rc.chf)) { printf("buildNavigation: Could not build compact data.\n"); return 0; } for (int i = 0; i < rc.chf->spanCount; ++i) { if (rc.chf->areas[i] == SAMPLE_POLYAREA_WATER) { rc.chf->areas[i] = SAMPLE_POLYAREA_WATER; } } if (!rcErodeWalkableArea(m_ctx.get(), tcfg.walkableRadius, *rc.chf)) { printf("buildNavigation: Could not erode.\n"); return 0; } for (int i = 0; i < rc.chf->spanCount; ++i) { if (rc.chf->areas[i] == SAMPLE_POLYAREA_WATER) { rc.chf->areas[i] = SAMPLE_POLYAREA_WATER; } } rc.lset = rcAllocHeightfieldLayerSet(); if (!rc.lset) { printf("buildNavigation: Out of memory 'lset'.\n"); return 0; } if (!rcBuildHeightfieldLayers(m_ctx.get(), *rc.chf, tcfg.borderSize, tcfg.walkableHeight, *rc.lset)) { //printf("buildNavigation: Could not build heighfield layers.\n"); return 0; } for (int i = 0; i < rc.lset->nlayers; ++i) { rcHeightfieldLayer* layer = &rc.lset->layers[i]; for (int j = 0; j < layer->width * layer->height; ++j) { if (layer->areas[j] == SAMPLE_POLYAREA_WATER) { layer->areas[j] = SAMPLE_POLYAREA_WATER; } } } rc.ntiles = 0; for (int i = 0; i < rcMin(rc.lset->nlayers, MAX_LAYERS); ++i) { TileCacheData* tile = &rc.tiles[rc.ntiles++]; const rcHeightfieldLayer* layer = &rc.lset->layers[i]; dtTileCacheLayerHeader header; header.magic = DT_TILECACHE_MAGIC; header.version = DT_TILECACHE_VERSION; header.tx = tx; header.ty = ty; header.tlayer = i; dtVcopy(header.bmin, layer->bmin); dtVcopy(header.bmax, layer->bmax); header.width = (unsigned char)layer->width; header.height = (unsigned char)layer->height; header.minx = (unsigned char)layer->minx; header.maxx = (unsigned char)layer->maxx; header.miny = (unsigned char)layer->miny; header.maxy = (unsigned char)layer->maxy; header.hmin = (unsigned short)layer->hmin; header.hmax = (unsigned short)layer->hmax; dtStatus status = dtBuildTileCacheLayer(&comp, &header, layer->heights, layer->areas, layer->cons, &tile->data, &tile->dataSize); if (dtStatusFailed(status)) { return 0; } } // Transfer ownership of tile data from build context to the caller. int n = 0; for (int i = 0; i < rcMin(rc.ntiles, maxTiles); ++i) { tiles[n++] = rc.tiles[i]; rc.tiles[i].data = 0; rc.tiles[i].dataSize = 0; } return n; }