Compare commits

...

2 Commits

Author SHA1 Message Date
azw
f0b02eafc6 1 2021-09-15 22:03:35 +08:00
azw
f136b4c91e add navmesh* 2021-09-13 22:18:10 +08:00
9 changed files with 1006 additions and 0 deletions

View File

@ -26,6 +26,7 @@ include_directories(
../../third_party/recastnavigation/Recast/Include
../../third_party/recastnavigation/Detour/Include
../../third_party/recastnavigation/DetourTileCache/Include
../../third_party/recastnavigation/RecastDemo/Contrib/fastlz
.
)
@ -46,6 +47,10 @@ list(REMOVE_ITEM SRC_LIST "../../third_party/framework/cpp/im_msgid.pb.cc")
list(REMOVE_ITEM SRC_LIST "../../third_party/framework/cpp/im_proto.pb.cc")
list(REMOVE_ITEM SRC_LIST "../../third_party/framework/cpp/btmgr.cc")
aux_source_directory(../../third_party/recastnavigation/RecastDemo/Contrib/fastlz
SRC_LIST
)
aux_source_directory(../../third_party/recastnavigation/Recast/Source
SRC_LIST
)

View File

@ -80,6 +80,7 @@ class Entity
EntityWeakPtr& GetEntityWeakPtrRef();
bool IsClientCached(Human* hum);
bool CanClientCache(Human* hum);
std::list<ColliderComponent*>* GetColliders() { return &colliders_; };
protected:
void AddClientCache(Human* hum);

View File

@ -1,5 +1,10 @@
#include "precompile.h"
#include "DetourNavMeshBuilder.h"
#include "DetourNavMeshQuery.h"
#include "DetourCommon.h"
#include "DetourNavMesh.h"
#include "mapinstance.h"
#include "mapservice.h"
#include "gridservice.h"
@ -13,7 +18,10 @@
#include "dummyentity.h"
#include "mapblock.h"
#include "roommgr.h"
#include "navmeshbuilder.h"
const int MAX_POLYS = 256;
static const int INVALID_NAVMESH_POLYREF = 0;
const int MAP_GRID_WIDTH = 64;
void MapInstance::Init()
@ -59,6 +67,8 @@ void MapInstance::Init()
if (current_uniid_ >= FIXED_OBJECT_MAXID) {
abort();
}
NavMeshBuilder::Instance()->Build(this);
map_service_->SetNavMesh(navmesh_);
}
void MapInstance::UnInit()
@ -67,6 +77,10 @@ void MapInstance::UnInit()
grid_service_->UnInit();
A8_SAFE_DELETE(map_service_);
A8_SAFE_DELETE(grid_service_);
if (navmesh_) {
dtFreeNavMesh(navmesh_);
navmesh_ = nullptr;
}
}
void MapInstance::AttachRoom(Room* room, RoomInitInfo& init_info)

View File

@ -11,6 +11,8 @@ namespace metatable
class MapBlockJson;
}
class dtNavMesh;
class dtTileCache;
class Entity;
class Obstacle;
class Building;
@ -30,6 +32,7 @@ class MapInstance
void AttachRoom(Room* room, RoomInitInfo& init_info);
MetaData::Map* GetMapMeta() { return map_meta_; }
Entity* GetEntityByUniId(int uniid);
private:
void CreateThings();
void CreateTerrain();
@ -46,6 +49,9 @@ class MapInstance
int AllocUniid();
private:
dtTileCache* tile_cache_ = nullptr;
dtNavMesh* navmesh_ = nullptr;
int current_uniid_ = 0;
int current_map_block_uniid_ = 0;
std::map<int, Entity*> uniid_hash_;
@ -69,4 +75,7 @@ class MapInstance
int obstacle0_num_ = 0;
int obstacle1_num_ = 0;
int obstacle2_num_ = 0;
friend class NavMeshBuilder;
friend class NavMeshHelper;
};

View File

@ -1,8 +1,16 @@
#pragma once
class dtNavMesh;
class Human;
class ColliderComponent;
enum NavMeshSearchState_e
{
kNavMesh_Searching = 0,
kNavMesh_SearchOk = 1,
kNavMesh_SearchFailed = 2,
};
struct CellNode
{
ColliderComponent* collider;
@ -25,6 +33,9 @@ struct FindPathStatus
int curr_step = 0;
int max_step_num = 0;
std::vector<MovePathPoint> out_ponits;
int navmesh_search_state = kNavMesh_Searching;
class dtNavMeshQuery* navmesh_query = nullptr;
};
class Room;
@ -63,11 +74,13 @@ class MapService
const a8::Vec2& pos,
ColliderComponent* collider,
ColliderComponent** pickup_collider);
void SetNavMesh(dtNavMesh* navmesh) { navmesh_ = navmesh; };
private:
int GetGridId(float world_x, float world_y);
private:
dtNavMesh* navmesh_ = nullptr;
list_head* map_cells_ = nullptr;
int map_width_ = 0;
int map_height_ = 0;

View File

@ -0,0 +1,525 @@
#include "precompile.h"
#include <string.h>
#include <a8/udplog.h>
#include "navmeshbuilder.h"
#include "mapinstance.h"
#include "collider.h"
#include "entity.h"
#include "metamgr.h"
#include "navmeshhelper.h"
#include "framework/cpp/inputgeom.h"
void NavMeshBuilder::Init()
{
}
void NavMeshBuilder::UnInit()
{
}
void NavMeshBuilder::Build(MapInstance* map_instance)
{
BuilderParams builder_params;
{
builder_params.map_instance = map_instance;
builder_params.geom = new f8::InputGeom();
builder_params.geom->Init(map_instance->GetMapMeta()->i->map_width(),
map_instance->GetMapMeta()->i->map_height());
builder_params.ctx = new BuildContext();
}
InitBuilderParams(builder_params);
CreateTileCache(builder_params);
CreateNavMesh(builder_params);
BuildTiles(builder_params);
#if 0
BuildMapObstalce(builder_params);
#endif
map_instance->navmesh_ = builder_params.navmesh;
Save(builder_params);
}
void NavMeshBuilder::InitBuilderParams(BuilderParams& builder_params)
{
// Init cache
rcCalcGridSize(builder_params.geom->GetMeshBoundsMin(),
builder_params.geom->GetMeshBoundsMax(),
builder_params.kCellSize,
&builder_params.grid_width,
&builder_params.grid_height);
builder_params.tile_size = (int)builder_params.kTileSize;
builder_params.tile_width = (builder_params.grid_width + builder_params.tile_size-1) /
builder_params.tile_size;
builder_params.tile_height = (builder_params.grid_height + builder_params.tile_size-1) /
builder_params.tile_size;
}
void NavMeshBuilder::InitTileCacheParams(BuilderParams& builder_params, dtTileCacheParams& tcparams)
{
// Tile cache params.
memset(&tcparams, 0, sizeof(tcparams));
rcVcopy(tcparams.orig, builder_params.geom->GetMeshBoundsMin());
tcparams.cs = builder_params.kCellSize;
tcparams.ch = builder_params.kCellHeight;
tcparams.width = (int)builder_params.kTileSize;
tcparams.height = (int)builder_params.kTileSize;
tcparams.walkableHeight = builder_params.kAgentHeight;
tcparams.walkableRadius = builder_params.kAgentRadius;
tcparams.walkableClimb = builder_params.kAgentMaxClimb;
tcparams.maxSimplificationError = builder_params.kEdgeMaxError;
tcparams.maxTiles = builder_params.tile_width * builder_params.tile_height * EXPECTED_LAYERS_PER_TILE;
tcparams.maxObstacles = 128 * 1000;
}
void NavMeshBuilder::InitNavMeshParams(BuilderParams& builder_params, dtNavMeshParams& params)
{
memset(&params, 0, sizeof(params));
rcVcopy(params.orig, builder_params.geom->GetMeshBoundsMin());
params.tileWidth = builder_params.kTileSize * builder_params.kCellSize;
params.tileHeight = builder_params.kTileSize * builder_params.kCellSize;
#if 1
int tile_bits = rcMin((int)dtIlog2(
dtNextPow2(params.tileWidth*params.tileHeight*EXPECTED_LAYERS_PER_TILE)),
14);
if (tile_bits > 14) {
tile_bits = 14;
}
int poly_bits = 22 - tile_bits;
params.maxTiles = 1 << tile_bits;
params.maxPolys = 1 << poly_bits;
#else
params.maxTiles = builder_params.kMaxTiles;
params.maxPolys = builder_params.kMaxPolysPerTile;
#endif
}
void NavMeshBuilder::BuildTiles(BuilderParams& builder_params)
{
rcConfig cfg;
{
memset(&cfg, 0, sizeof(cfg));
cfg.cs = builder_params.kCellSize;
cfg.ch = builder_params.kCellHeight;
cfg.walkableSlopeAngle = builder_params.kAgentMaxSlope;
cfg.walkableHeight = (int)ceilf(builder_params.kAgentHeight / cfg.ch);
cfg.walkableClimb = (int)floorf(builder_params.kAgentMaxClimb / cfg.ch);
cfg.walkableRadius = (int)ceilf(builder_params.kAgentRadius / cfg.cs);
cfg.maxEdgeLen = (int)(builder_params.kEdgeMaxLen / builder_params.kCellSize);
cfg.maxSimplificationError = builder_params.kEdgeMaxError;
cfg.minRegionArea = (int)rcSqr(builder_params.kRegionMinSize); // Note: area = size*size
cfg.mergeRegionArea = (int)rcSqr(builder_params.kRegionMergeSize); // Note: area = size*size
cfg.maxVertsPerPoly = (int)builder_params.kVertsPerPoly;
cfg.tileSize = (int)builder_params.kTileSize;
cfg.borderSize = cfg.walkableRadius + 3; // Reserve enough padding.
cfg.width = cfg.tileSize + cfg.borderSize*2;
cfg.height = cfg.tileSize + cfg.borderSize*2;
cfg.detailSampleDist = builder_params.kDetailSampleDist < 0.9f ? 0 : builder_params.kCellSize * builder_params.kDetailSampleDist;
cfg.detailSampleMaxError = builder_params.kCellHeight * builder_params.kDetailSampleMaxError;
rcVcopy(cfg.bmin, builder_params.geom->GetMeshBoundsMin());
rcVcopy(cfg.bmax, builder_params.geom->GetMeshBoundsMax());
}
for (int y = 0; y < builder_params.tile_height; ++y) {
for (int x = 0; x < builder_params.tile_width; ++x) {
TileCacheData tiles[MAX_LAYERS];
memset(tiles, 0, sizeof(tiles));
int ntiles = RasterizeTileLayers(builder_params, x, y, cfg, tiles, MAX_LAYERS);
for (int i = 0; i < ntiles; ++i) {
TileCacheData* tile = &tiles[i];
dtStatus status = builder_params.tile_cache->addTile(tile->data,
tile->dataSize,
DT_COMPRESSEDTILE_FREE_DATA,
0);
if (dtStatusFailed(status)) {
abort();
dtFree(tile->data);
tile->data = 0;
continue;
}
}
}
}
// Build initial meshes
for (int y = 0; y < builder_params.tile_height; ++y) {
for (int x = 0; x < builder_params.tile_width; ++x) {
builder_params.tile_cache->buildNavMeshTilesAt(x, y, builder_params.navmesh);
}
}
}
int NavMeshBuilder::RasterizeTileLayers(BuilderParams& builder_params,
const int tx,
const int ty,
const rcConfig& cfg,
TileCacheData* tiles,
const int maxTiles)
{
rcAreaModification SAMPLE_AREAMOD_GROUND(SAMPLE_POLYAREA_TYPE_GROUND, SAMPLE_POLYAREA_TYPE_MASK);
FastLZCompressor comp;
RasterizationContext rc;
const float* verts = builder_params.geom->GetVerts();
const int nverts = builder_params.geom->GetVertCount();
const rcChunkyTriMesh* chunkyMesh = builder_params.geom->GetChunkyMesh();
// Tile bounds.
const 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) {
return 0;
}
if (!rcCreateHeightfield(builder_params.ctx,
*rc.solid,
tcfg.width,
tcfg.height,
tcfg.bmin,
tcfg.bmax,
tcfg.cs,
tcfg.ch)) {
return 0;
}
// Allocate array that can hold triangle flags.
// If you have multiple meshes you need to process, allocate
// and array which can hold the max number of triangles you need to process.
rc.triareas = new unsigned char[chunkyMesh->maxTrisPerChunk];
if (!rc.triareas) {
builder_params.ctx->log(RC_LOG_ERROR,
"buildNavigation: Out of memory 'm_triareas' (%d).",
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];// TODO: Make grow when returning too many items.
const int ncid = rcGetChunksOverlappingRect(chunkyMesh, tbmin, tbmax, cid, 512);
if (!ncid) {
return 0; // empty
}
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(builder_params.ctx, tcfg.walkableSlopeAngle,
verts, nverts, tris, ntris, rc.triareas,
SAMPLE_AREAMOD_GROUND);
if (!rcRasterizeTriangles(builder_params.ctx,
verts,
nverts,
tris,
rc.triareas,
ntris,
*rc.solid,
tcfg.walkableClimb)) {
return 0;
}
}
// 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.
if (builder_params.kFilterLowHangingObstacles) {
rcFilterLowHangingWalkableObstacles(builder_params.ctx, tcfg.walkableClimb, *rc.solid);
}
if (builder_params.kFilterLedgeSpans) {
rcFilterLedgeSpans(builder_params.ctx, tcfg.walkableHeight, tcfg.walkableClimb, *rc.solid);
}
if (builder_params.kFilterWalkableLowHeightSpans) {
rcFilterWalkableLowHeightSpans(builder_params.ctx, tcfg.walkableHeight, *rc.solid);
}
rc.chf = rcAllocCompactHeightfield();
if (!rc.chf) {
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'chf'.");
return 0;
}
if (!rcBuildCompactHeightfield(builder_params.ctx,
tcfg.walkableHeight,
tcfg.walkableClimb,
*rc.solid,
*rc.chf)) {
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build compact data.");
return 0;
}
// Erode the walkable area by agent radius.
if (!rcErodeWalkableArea(builder_params.ctx, tcfg.walkableRadius, *rc.chf)) {
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Could not erode.");
return 0;
}
#if 0
// (Optional) Mark areas.
const ConvexVolume* vols = builder_params.geom->GetConvexVolumes();
for (int i = 0; i < builder_params.geom->GetConvexVolumeCount(); ++i) {
rcMarkConvexPolyArea(builder_params.ctx, vols[i].verts, vols[i].nverts,
vols[i].hmin, vols[i].hmax,
vols[i].areaMod, *rc.chf);
}
#endif
rc.lset = rcAllocHeightfieldLayerSet();
if (!rc.lset) {
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Out of memory 'lset'.");
return 0;
}
if (!rcBuildHeightfieldLayers(builder_params.ctx,
*rc.chf,
tcfg.borderSize,
tcfg.walkableHeight,
*rc.lset)) {
builder_params.ctx->log(RC_LOG_ERROR, "buildNavigation: Could not build heighfield layers.");
return 0;
}
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];
// Store header
dtTileCacheLayerHeader header;
header.magic = DT_TILECACHE_MAGIC;
header.version = DT_TILECACHE_VERSION;
// Tile layer location in the navmesh.
header.tx = tx;
header.ty = ty;
header.tlayer = i;
dtVcopy(header.bmin, layer->bmin);
dtVcopy(header.bmax, layer->bmax);
// Tile info.
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 ownsership 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;
}
bool NavMeshBuilder::CreateTileCache(BuilderParams& builder_params)
{
LinearAllocator* talloc = new LinearAllocator(320000);
FastLZCompressor* tcomp = new FastLZCompressor();
MeshProcess* tmproc = new MeshProcess();
dtTileCacheParams tcparams;
InitTileCacheParams(builder_params, tcparams);
builder_params.tile_cache = dtAllocTileCache();
dtStatus status = builder_params.tile_cache->init
(&tcparams,
talloc,
tcomp,
tmproc);
if (dtStatusFailed(status)) {
abort();
}
return true;
}
bool NavMeshBuilder::CreateNavMesh(BuilderParams& builder_params)
{
dtNavMeshParams params;
InitNavMeshParams(builder_params, params);
builder_params.navmesh = dtAllocNavMesh();
dtStatus status = builder_params.navmesh->init(&params);
if (dtStatusFailed(status)) {
abort();
}
return true;
}
void NavMeshBuilder::BuildMapObstalce(BuilderParams& builder_params)
{
dtTileCache* tile_cache = builder_params.tile_cache;
for (auto& pair : builder_params.map_instance->uniid_hash_) {
for (ColliderComponent* collider : *pair.second->GetColliders()) {
switch (collider->type) {
case CT_Aabb:
{
AabbCollider* aabb = (AabbCollider*)collider;
float bmin[3] = {0};
bmin[0] = (aabb->owner->GetPos() + aabb->_min).x;
bmin[1] = 0;
bmin[2] = (aabb->owner->GetPos() + aabb->_min).y;
float bmax[3] = {0};
bmax[0] = (aabb->owner->GetPos() + aabb->_max).x;
bmax[1] = 0;
bmax[2] = (aabb->owner->GetPos() + aabb->_max).y;
dtObstacleRef ref;
dtStatus status = tile_cache->addBoxObstacle(bmin,
bmax,
&ref
);
if (status != DT_SUCCESS) {
abort();
}
{
bool uptodate = false;
int update_count = 0;
while (!uptodate) {
tile_cache->update(0, builder_params.navmesh, &uptodate);
++update_count;
if (update_count > 10000 * 10) {
abort();
}
}
}
}
break;
case CT_Circle:
{
CircleCollider* circle = (CircleCollider*)collider;
float pos[3] = {0};
pos[0] = (circle->owner->GetPos() + circle->pos).x;
pos[1] = 0;
pos[2] = (circle->owner->GetPos() + circle->pos).y;
dtObstacleRef ref;
dtStatus status = tile_cache->addObstacle(pos,
circle->rad,
0,
&ref
);
if (status != DT_SUCCESS) {
abort();
}
{
bool uptodate = false;
int update_count = 0;
while (!uptodate) {
tile_cache->update(0, builder_params.navmesh, &uptodate);
++update_count;
if (update_count > 10000 * 10) {
abort();
}
}
}
}
break;
default:
{
}
break;
}
}
}//end for pair
}
void NavMeshBuilder::Save(BuilderParams& builder_params)
{
FILE* fp = fopen("all_tiles_tilecache.bin", "wb");
if (!fp) {
abort();
}
static const int TILECACHESET_MAGIC = 'T'<<24 | 'S'<<16 | 'E'<<8 | 'T'; //'TSET';
static const int TILECACHESET_VERSION = 1;
struct TileCacheSetHeader
{
int magic;
int version;
int numTiles;
dtNavMeshParams meshParams;
dtTileCacheParams cacheParams;
};
struct TileCacheTileHeader
{
dtCompressedTileRef tileRef;
int dataSize;
};
TileCacheSetHeader header;
header.magic = TILECACHESET_MAGIC;
header.version = TILECACHESET_VERSION;
header.numTiles = 0;
for (int i = 0; i < builder_params.tile_cache->getTileCount(); ++i) {
const dtCompressedTile* tile = builder_params.tile_cache->getTile(i);
if (!tile || !tile->header || !tile->dataSize) continue;
header.numTiles++;
}
memcpy(&header.cacheParams, builder_params.tile_cache->getParams(), sizeof(dtTileCacheParams));
memcpy(&header.meshParams, builder_params.navmesh->getParams(), sizeof(dtNavMeshParams));
fwrite(&header, sizeof(TileCacheSetHeader), 1, fp);
// Store tiles.
for (int i = 0; i < builder_params.tile_cache->getTileCount(); ++i) {
const dtCompressedTile* tile = builder_params.tile_cache->getTile(i);
if (!tile || !tile->header || !tile->dataSize) continue;
TileCacheTileHeader tileHeader;
tileHeader.tileRef = builder_params.tile_cache->getTileRef(tile);
tileHeader.dataSize = tile->dataSize;
fwrite(&tileHeader, sizeof(tileHeader), 1, fp);
fwrite(tile->data, tile->dataSize, 1, fp);
}
fclose(fp);
a8::UdpLog::Instance()->Debug("Save OK", {});
}

View File

@ -0,0 +1,37 @@
#pragma once
struct rcConfig;
struct dtTileCacheParams;
struct dtNavMeshParams;
struct BuilderParams;
struct TileCacheData;
class dtNavMesh;
class MapInstance;
class NavMeshBuilder : public a8::Singleton<NavMeshBuilder>
{
private:
NavMeshBuilder() {};
friend class a8::Singleton<NavMeshBuilder>;
public:
void Init();
void UnInit();
void Build(MapInstance* map_instance);
private:
void InitBuilderParams(BuilderParams& builder_params);
void InitTileCacheParams(BuilderParams& builder_params, dtTileCacheParams& tcparams);
void InitNavMeshParams(BuilderParams& builder_params, dtNavMeshParams& params);
void BuildTiles(BuilderParams& builder_params);
int RasterizeTileLayers(BuilderParams& builder_params,
const int tx,
const int ty,
const rcConfig& cfg,
TileCacheData* tiles,
const int maxTiles);
bool CreateTileCache(BuilderParams& builder_params);
bool CreateNavMesh(BuilderParams& builder_params);
void BuildMapObstalce(BuilderParams& builder_params);
void Save(BuilderParams& builder_params);
};

View File

@ -0,0 +1,145 @@
#include "precompile.h"
#include <a8/udplog.h>
#include "mapinstance.h"
#include "metamgr.h"
#include "navmeshhelper.h"
#include "collider.h"
#include "entity.h"
void BuildContext::doResetLog()
{
a8::UdpLog::Instance()->Debug("doResetLog", {});
}
void BuildContext::doLog(const rcLogCategory category, const char* msg, const int len)
{
a8::UdpLog::Instance()->Debug("doLog category:%d msg:%s",
{
category,
msg
});
}
void BuildContext::doResetTimers()
{
a8::UdpLog::Instance()->Debug("doResetTimers", {});
}
void BuildContext::doStartTimer(const rcTimerLabel label)
{
a8::UdpLog::Instance()->Debug("doStartTimer lable:%d", {label});
}
void BuildContext::doStopTimer(const rcTimerLabel label)
{
a8::UdpLog::Instance()->Debug("doStopTimer lable:%d", {label});
}
int BuildContext::doGetAccumulatedTime(const rcTimerLabel label) const
{
return 0;
}
void NavMeshHelper::OutputObjFile(MapInstance* map_instance)
{
std::vector<a8::Vec2> vertexs;
std::vector<std::tuple<int, int, int>> faces;
vertexs.reserve(10000);
for (auto& pair : map_instance->uniid_hash_) {
for (ColliderComponent* collider : *pair.second->GetColliders()) {
if (collider->type == CT_Aabb) {
AabbCollider* aabb_box = (AabbCollider*)collider;
{
a8::Vec2 vert = collider->owner->GetPos() + aabb_box->_min;
vert.x = vert.x - map_instance->map_meta_->i->map_width() / 2.0f;
vert.y = vert.y - map_instance->map_meta_->i->map_height() / 2.0f;
vertexs.push_back(vert);
vert.y += aabb_box->_max.y - aabb_box->_min.y;
vertexs.push_back(vert);
vert.x += aabb_box->_max.x - aabb_box->_min.x;
vertexs.push_back(vert);
vert.y -= aabb_box->_max.y - aabb_box->_min.y;
vertexs.push_back(vert);
}
//0 1 2
faces.push_back
(std::make_tuple
(
vertexs.size() - 4 + 1,
vertexs.size() - 3 + 1,
vertexs.size() - 2 + 1
));
//0 2 3
faces.push_back
(std::make_tuple
(
vertexs.size() - 4 + 1,
vertexs.size() - 2 + 1,
vertexs.size() - 1 + 1
));
}
}
}
{
std::string filename = a8::Format("%s.obj", {map_instance->map_tpl_name_});
FILE* fp = fopen(filename.c_str(), "wb");
#if 1
{
vertexs.clear();
faces.clear();
{
a8::Vec2 vert;
//vert.x = 0 - map_instance->map_meta_->i->map_width() / 2.0f;
//vert.y = 0 - map_instance->map_meta_->i->map_height() / 2.0f;
vertexs.push_back(vert);
vert.y += map_instance->map_meta_->i->map_height();
vertexs.push_back(vert);
vert.x += map_instance->map_meta_->i->map_width();
vertexs.push_back(vert);
vert.y -= map_instance->map_meta_->i->map_height();
vertexs.push_back(vert);
//0 1 2
faces.push_back
(std::make_tuple
(
vertexs.size() - 4 + 1,
vertexs.size() - 3 + 1,
vertexs.size() - 2 + 1
));
//0 2 3
faces.push_back
(std::make_tuple
(
vertexs.size() - 4 + 1,
vertexs.size() - 2 + 1,
vertexs.size() - 1 + 1
));
}
}
#endif
for (auto& vert : vertexs) {
std::string data = a8::Format("v %f %f %f\r\n",
{
vert.x,
0,
vert.y,
});
fwrite(data.data(), 1, data.size(), fp);
}
for (auto& tuple : faces) {
std::string data = a8::Format("f %d %d %d\r\n",
{
std::get<0>(tuple),
std::get<1>(tuple),
std::get<2>(tuple)
});
fwrite(data.data(), 1, data.size(), fp);
}
fclose(fp);
}
}

View File

@ -0,0 +1,257 @@
#pragma once
#include "Recast.h"
#include "RecastAlloc.h"
#include "DetourTileCache.h"
#include "DetourTileCacheBuilder.h"
#include "DetourNavMeshBuilder.h"
#include "DetourNavMeshQuery.h"
#include "DetourCommon.h"
#include "DetourNavMesh.h"
#include "fastlz.h"
static const int EXPECTED_LAYERS_PER_TILE = 4;
static const int MAX_LAYERS = 32;
/// Mask of the ceil part of the area id (3 lower bits)
/// the 0 value (RC_NULL_AREA) is left unused
static const unsigned char SAMPLE_POLYAREA_TYPE_MASK = 0x07;
/// Value for the kind of ceil "ground"
static const unsigned char SAMPLE_POLYAREA_TYPE_GROUND = 0x1;
/// Value for the kind of ceil "water"
static const unsigned char SAMPLE_POLYAREA_TYPE_WATER = 0x2;
/// Value for the kind of ceil "road"
static const unsigned char SAMPLE_POLYAREA_TYPE_ROAD = 0x3;
/// Value for the kind of ceil "grass"
static const unsigned char SAMPLE_POLYAREA_TYPE_GRASS = 0x4;
/// Flag for door area. Can be combined with area types and jump flag.
static const unsigned char SAMPLE_POLYAREA_FLAG_DOOR = 0x08;
/// Flag for jump area. Can be combined with area types and door flag.
static const unsigned char SAMPLE_POLYAREA_FLAG_JUMP = 0x10;
struct TileCacheData
{
unsigned char* data;
int dataSize;
};
struct RasterizationContext
{
RasterizationContext() :
solid(0),
triareas(0),
lset(0),
chf(0),
ntiles(0)
{
memset(tiles, 0, sizeof(TileCacheData)*MAX_LAYERS);
}
~RasterizationContext()
{
rcFreeHeightField(solid);
delete [] triareas;
rcFreeHeightfieldLayerSet(lset);
rcFreeCompactHeightfield(chf);
for (int i = 0; i < MAX_LAYERS; ++i)
{
dtFree(tiles[i].data);
tiles[i].data = 0;
}
}
rcHeightfield* solid;
unsigned char* triareas;
rcHeightfieldLayerSet* lset;
rcCompactHeightfield* chf;
TileCacheData tiles[MAX_LAYERS];
int ntiles;
};
struct LinearAllocator : public dtTileCacheAlloc
{
unsigned char* buffer;
size_t capacity;
size_t top;
size_t high;
LinearAllocator(const size_t cap) : buffer(0), capacity(0), top(0), high(0)
{
resize(cap);
}
~LinearAllocator()
{
dtFree(buffer);
}
void resize(const size_t cap)
{
if (buffer) dtFree(buffer);
buffer = (unsigned char*)dtAlloc(cap, DT_ALLOC_PERM);
capacity = cap;
}
virtual void reset()
{
high = dtMax(high, top);
top = 0;
}
virtual void* alloc(const size_t size)
{
if (!buffer)
return 0;
if (top+size > capacity)
return 0;
unsigned char* mem = &buffer[top];
top += size;
return mem;
}
virtual void free(void* /*ptr*/)
{
// Empty
}
};
struct FastLZCompressor : public dtTileCacheCompressor
{
virtual int maxCompressedSize(const int bufferSize)
{
return (int)(bufferSize* 1.05f);
}
virtual dtStatus compress(const unsigned char* buffer, const int bufferSize,
unsigned char* compressed, const int /*maxCompressedSize*/, int* compressedSize)
{
*compressedSize = fastlz_compress((const void *const)buffer, bufferSize, compressed);
return DT_SUCCESS;
}
virtual dtStatus decompress(const unsigned char* compressed, const int compressedSize,
unsigned char* buffer, const int maxBufferSize, int* bufferSize)
{
*bufferSize = fastlz_decompress(compressed, compressedSize, buffer, maxBufferSize);
return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
}
};
struct MeshProcess : public dtTileCacheMeshProcess
{
inline MeshProcess()
{
}
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas,
unsigned short* polyFlags)
{
#if 0
// Update poly flags from areas.
for (int i = 0; i < params->polyCount; ++i)
{
polyFlags[i] = sampleAreaToFlags(polyAreas[i]);
}
// Pass in off-mesh connections.
if (m_geom)
{
params->offMeshConVerts = m_geom->getOffMeshConnectionVerts();
params->offMeshConRad = m_geom->getOffMeshConnectionRads();
params->offMeshConDir = m_geom->getOffMeshConnectionDirs();
params->offMeshConAreas = m_geom->getOffMeshConnectionAreas();
params->offMeshConFlags = m_geom->getOffMeshConnectionFlags();
params->offMeshConUserID = m_geom->getOffMeshConnectionId();
params->offMeshConCount = m_geom->getOffMeshConnectionCount();
}
#endif
}
};
class BuildContext : public rcContext
{
protected:
virtual void doResetLog() override;
virtual void doLog(const rcLogCategory category, const char* msg, const int len) override;
virtual void doResetTimers() override;
virtual void doStartTimer(const rcTimerLabel label) override;
virtual void doStopTimer(const rcTimerLabel label) override;
virtual int doGetAccumulatedTime(const rcTimerLabel label) const override;
};
class MapInstance;
class NavMeshHelper
{
public:
static void OutputObjFile(MapInstance* map_instance);
};
namespace f8
{
class InputGeom;
}
struct BuilderParams
{
const float kCellSize = 1; //体素大小
const float kCellHeight = 1; //体素高度
const float kAgentMaxSlope = 90; //角色可走的最大坡度
const float kAgentHeight = 1; //角色高
const float kAgentMaxClimb = 1; //角色能爬的最大高度
const float kAgentRadius = 20; //角色半径
const float kEdgeMaxLen = 6; //简化列表中相邻两点间的距离
const float kEdgeMaxError = 6; //从简化边到实边的最大距离
const float kRegionMinSize = 6; //最小区域的大小, 网格面积小于该值的地方,将不生成导航网格
/*
: >= 0
,,.
,.,.
,,..
[,]
[,,]
*/
#if 1
const float kRegionMergeSize = 400;
#else
const float kRegionMergeSize = 20;
#endif
/*
: >= 3
,.
6,,*/
const int kVertsPerPoly = 6;
const int kTileSize = 64; //单个瓦片大小
const float kDetailSampleDist = 6;
const float kDetailSampleMaxError = 1;
#if 0
const int kMaxTiles = 0;
const int kMaxPolysPerTile = 0;
#endif
const bool kFilterLowHangingObstacles = false;
const bool kFilterLedgeSpans = false;
const bool kFilterWalkableLowHeightSpans = false;
int grid_width = 0;
int grid_height = 0;
int tile_size = 0;
int tile_width = 0;
int tile_height = 0;
MapInstance* map_instance = nullptr;
f8::InputGeom* geom = nullptr;
dtNavMesh* navmesh = nullptr;
rcContext* ctx = nullptr;
dtTileCache* tile_cache = nullptr;
};