Implement new off-mesh connection system

This commit is contained in:
RGreenlees 2023-11-15 21:39:02 +00:00 committed by pierow
parent 97b87089f6
commit 12fb10f998
13 changed files with 912 additions and 925 deletions

View file

@ -134,7 +134,6 @@ inline void freeLink(dtMeshTile* tile, unsigned int link)
tile->linksFreeList = link;
}
dtNavMesh* dtAllocNavMesh()
{
void* mem = dtAlloc(sizeof(dtNavMesh), DT_ALLOC_PERM);
@ -284,7 +283,7 @@ dtStatus dtNavMesh::init(unsigned char* data, const int dataSize, const int flag
if (dtStatusFailed(status))
return status;
return addTile(data, dataSize, flags, 0, 0, false);
return addTile(data, dataSize, flags, 0, 0);
}
/// @par
@ -350,6 +349,169 @@ int dtNavMesh::findConnectingPolys(const float* va, const float* vb,
return n;
}
void dtNavMesh::LinkOffMeshConnectionToTiles(dtOffMeshConnection* con)
{
con->FromTileX = -1;
con->FromTileY = -1;
con->FromTileLayer = -1;
con->ToTileX = -1;
con->ToTileY = -1;
con->ToTileLayer = -1;
const float ext[3] = { con->rad, 18.0f, con->rad };
float bmin[3], bmax[3];
dtVsub(bmin, &con->pos[0], ext);
dtVadd(bmax, &con->pos[0], ext);
// Find tiles the query touches.
int minx, miny, maxx, maxy;
this->calcTileLoc(bmin, &minx, &miny);
this->calcTileLoc(bmax, &maxx, &maxy);
static const int MAX_NEIS = 32;
dtMeshTile* neis[MAX_NEIS];
float NearestDist = FLT_MAX;
float nearestPt[3];
for (int y = miny; y <= maxy; ++y)
{
for (int x = minx; x <= maxx; ++x)
{
const int nneis = this->getTilesAt(x, y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j)
{
dtMeshTile* tile = neis[j];
dtPolyRef NearestPoly = findNearestPolyInTile(tile, &con->pos[0], ext, nearestPt);
if (NearestPoly)
{
float thisDist = dtVdistSqr(nearestPt, &con->pos[0]);
if (thisDist < NearestDist)
{
thisDist = NearestDist;
con->FromTileX = tile->header->x;
con->FromTileY = tile->header->y;
con->FromTileLayer = tile->header->layer;
}
}
}
}
}
NearestDist = FLT_MAX;
dtVsub(bmin, &con->pos[3], ext);
dtVadd(bmax, &con->pos[3], ext);
this->calcTileLoc(bmin, &minx, &miny);
this->calcTileLoc(bmax, &maxx, &maxy);
for (int y = miny; y <= maxy; ++y)
{
for (int x = minx; x <= maxx; ++x)
{
const int nneis = this->getTilesAt(x, y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j)
{
dtMeshTile* tile = neis[j];
dtPolyRef NearestPoly = findNearestPolyInTile(tile, &con->pos[3], ext, nearestPt);
if (NearestPoly)
{
float thisDist = dtVdistSqr(nearestPt, &con->pos[3]);
if (thisDist < NearestDist)
{
thisDist = NearestDist;
con->ToTileX = tile->header->x;
con->ToTileY = tile->header->y;
con->ToTileLayer = tile->header->layer;
}
}
}
}
}
}
void dtNavMesh::unconnectOffMeshLink(dtOffMeshConnection* con)
{
if (!con || con->FromTileX < 0 || con->ToTileX < 0)
return;
dtMeshTile* tile = getTileAt(con->FromTileX, con->FromTileY, con->FromTileLayer);
dtMeshTile* target = this->getTileAt(con->ToTileX, con->ToTileY, con->ToTileLayer);
if (!tile || !target) return;
const unsigned int targetNum = decodePolyIdTile(getTileRef(target));
for (int i = 0; i < tile->header->polyCount; ++i)
{
dtPoly* poly = &tile->polys[i];
unsigned int j = poly->firstLink;
unsigned int pj = DT_NULL_LINK;
while (j != DT_NULL_LINK)
{
if (decodePolyIdTile(tile->links[j].ref) == targetNum && tile->links[j].OffMeshID == con->userId)
{
// Remove link.
unsigned int nj = tile->links[j].next;
if (pj == DT_NULL_LINK)
poly->firstLink = nj;
else
tile->links[pj].next = nj;
freeLink(tile, j);
j = nj;
}
else
{
// Advance
pj = j;
j = tile->links[j].next;
}
}
}
const unsigned int sourceNum = decodePolyIdTile(getTileRef(tile));
for (int i = 0; i < target->header->polyCount; ++i)
{
dtPoly* poly = &target->polys[i];
unsigned int j = poly->firstLink;
unsigned int pj = DT_NULL_LINK;
while (j != DT_NULL_LINK)
{
if (decodePolyIdTile(target->links[j].ref) == sourceNum && target->links[j].OffMeshID == con->userId)
{
// Remove link.
unsigned int nj = target->links[j].next;
if (pj == DT_NULL_LINK)
poly->firstLink = nj;
else
target->links[pj].next = nj;
freeLink(target, j);
j = nj;
}
else
{
// Advance
pj = j;
j = target->links[j].next;
}
}
}
}
void dtNavMesh::unconnectLinks(dtMeshTile* tile, dtMeshTile* target)
{
if (!tile || !target) return;
@ -451,76 +613,6 @@ void dtNavMesh::connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side)
}
}
void dtNavMesh::connectDistantExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side)
{
if (!tile) return;
// Connect off-mesh links.
// We are interested on links which land from target tile to this tile.
const unsigned char oppositeSide = (side == -1) ? 0xff : (unsigned char)dtOppositeTile(side);
for (int i = 0; i < target->header->offMeshConCount; ++i)
{
dtOffMeshConnection* targetCon = &target->offMeshCons[i];
dtPoly* targetPoly = &target->polys[targetCon->poly];
// Skip off-mesh connections which start location could not be connected at all.
if (targetPoly->firstLink == DT_NULL_LINK)
{
continue;
}
const float halfExtents[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
// Find polygon to connect to.
const float* p = &targetCon->pos[3];
float nearestPt[3];
dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt);
if (!ref)
continue;
// findNearestPoly may return too optimistic results, further check to make sure.
if (dtSqr(nearestPt[0] - p[0]) + dtSqr(nearestPt[2] - p[2]) > dtSqr(targetCon->rad))
continue;
// Make sure the location is on current mesh.
float* v = &target->verts[targetPoly->verts[1] * 3];
dtVcopy(v, nearestPt);
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(target);
if (idx != DT_NULL_LINK)
{
dtLink* link = &target->links[idx];
link->ref = ref;
link->edge = (unsigned char)1;
link->side = oppositeSide;
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = targetPoly->firstLink;
targetPoly->firstLink = idx;
}
// Link target poly to off-mesh connection.
if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
{
unsigned int tidx = allocLink(tile);
if (tidx != DT_NULL_LINK)
{
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
dtPoly* landPoly = &tile->polys[landPolyIdx];
dtLink* link = &tile->links[tidx];
link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
link->edge = 0xff;
link->side = (unsigned char)(side == -1 ? 0xff : side);
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = landPoly->firstLink;
landPoly->firstLink = tidx;
}
}
}
}
void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side)
{
if (!tile) return;
@ -531,7 +623,7 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
for (int i = 0; i < target->header->offMeshConCount; ++i)
{
dtOffMeshConnection* targetCon = &target->offMeshCons[i];
dtOffMeshConnection* targetCon = target->offMeshCons[i];
if (targetCon->side != oppositeSide)
continue;
@ -541,7 +633,6 @@ void dtNavMesh::connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int
{
continue;
}
const float halfExtents[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
@ -631,6 +722,67 @@ void dtNavMesh::connectIntLinks(dtMeshTile* tile)
}
}
void dtNavMesh::baseOffMeshLinks(dtOffMeshConnection* con)
{
if (!con) return;
dtMeshTile* tile = getTileAt(con->FromTileX, con->FromTileY, con->FromTileLayer);
if (!tile) { return; }
dtPolyRef base = getPolyRefBase(tile);
dtPoly* poly = &tile->polys[con->poly];
const float halfExtents[3] = { con->rad, tile->header->walkableClimb, con->rad };
// Find polygon to connect to.
const float* p = &con->pos[0]; // First vertex
float nearestPt[3];
dtPolyRef ref = findNearestPolyInTile(tile, p, halfExtents, nearestPt);
if (!ref) return;
// findNearestPoly may return too optimistic results, further check to make sure.
if (dtSqr(nearestPt[0] - p[0]) + dtSqr(nearestPt[2] - p[2]) > dtSqr(con->rad))
return;
// Make sure the location is on current mesh.
float* v = &tile->verts[poly->verts[0] * 3];
dtVcopy(v, nearestPt);
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(tile);
if (idx != DT_NULL_LINK)
{
dtLink* link = &tile->links[idx];
link->OffMeshID = -1;
link->ref = ref;
link->edge = (unsigned char)0;
link->side = 0xff;
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = poly->firstLink;
poly->firstLink = idx;
}
// Start end-point is always connect back to off-mesh connection.
unsigned int tidx = allocLink(tile);
if (tidx != DT_NULL_LINK)
{
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
dtPoly* landPoly = &tile->polys[landPolyIdx];
dtLink* link = &tile->links[tidx];
link->OffMeshID = -1;
link->ref = base | (dtPolyRef)(con->poly);
link->edge = 0xff;
link->side = 0xff;
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = landPoly->firstLink;
landPoly->firstLink = tidx;
}
con->bBased = true;
}
void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
{
if (!tile) return;
@ -640,7 +792,7 @@ void dtNavMesh::baseOffMeshLinks(dtMeshTile* tile)
// Base off-mesh connection start points.
for (int i = 0; i < tile->header->offMeshConCount; ++i)
{
dtOffMeshConnection* con = &tile->offMeshCons[i];
dtOffMeshConnection* con = tile->offMeshCons[i];
dtPoly* poly = &tile->polys[con->poly];
const float halfExtents[3] = { con->rad, tile->header->walkableClimb, con->rad };
@ -943,7 +1095,7 @@ int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, co
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION)
continue;
// Calc polygon bounds.
const float* v = &tile->verts[p->verts[0]*3];
const float* v = &tile->verts[p->verts[0] * 3];
dtVcopy(bmin, v);
dtVcopy(bmax, v);
for (int j = 1; j < p->vertCount; ++j)
@ -979,7 +1131,7 @@ int dtNavMesh::queryPolygonsInTile(const dtMeshTile* tile, const float* qmin, co
///
/// @see dtCreateNavMeshData, #removeTile
dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
dtTileRef lastRef, dtTileRef* result, bool bMarkOffMeshDirty)
dtTileRef lastRef, dtTileRef* result)
{
// Make sure the data is in right format.
dtMeshHeader* header = (dtMeshHeader*)data;
@ -994,11 +1146,11 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
if (m_polyBits < dtIlog2(dtNextPow2((unsigned int)header->polyCount)))
return DT_FAILURE | DT_INVALID_PARAM;
#endif
// Make sure the location is free.
if (getTileAt(header->x, header->y, header->layer))
return DT_FAILURE | DT_ALREADY_OCCUPIED;
// Allocate a tile.
dtMeshTile* tile = 0;
if (!lastRef)
@ -1041,23 +1193,22 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
// Make sure we could allocate a tile.
if (!tile)
return DT_FAILURE | DT_OUT_OF_MEMORY;
// Insert tile into the position lut.
int h = computeTileHash(header->x, header->y, m_tileLutMask);
tile->next = m_posLookup[h];
m_posLookup[h] = tile;
// Patch header pointers.
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
const int vertsSize = dtAlign4(sizeof(float)*3*header->vertCount);
const int polysSize = dtAlign4(sizeof(dtPoly)*header->polyCount);
const int linksSize = dtAlign4(sizeof(dtLink)*(header->maxLinkCount));
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*header->detailMeshCount);
const int detailVertsSize = dtAlign4(sizeof(float)*3*header->detailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
const int receivingOffMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->receivingOffMeshConCount);
const int vertsSize = dtAlign4(sizeof(float) * 3 * header->vertCount);
const int polysSize = dtAlign4(sizeof(dtPoly) * header->polyCount);
const int linksSize = dtAlign4(sizeof(dtLink) * (header->maxLinkCount));
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail) * header->detailMeshCount);
const int detailVertsSize = dtAlign4(sizeof(float) * 3 * header->detailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char) * 4 * header->detailTriCount);
const int bvtreeSize = dtAlign4(sizeof(dtBVNode) * header->bvNodeCount);
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->offMeshConCount);
unsigned char* d = data + headerSize;
tile->verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
@ -1067,9 +1218,7 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
tile->detailVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
tile->detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
tile->bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
tile->offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
tile->receivingOffMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, receivingOffMeshLinksSize);
tile->offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection*>(d, offMeshLinksSize);
// If there are no items in the bvtree, reset the tree pointer.
if (!bvtreeSize)
@ -1077,9 +1226,9 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
// Build links freelist
tile->linksFreeList = 0;
tile->links[header->maxLinkCount-1].next = DT_NULL_LINK;
for (int i = 0; i < header->maxLinkCount-1; ++i)
tile->links[i].next = i+1;
tile->links[header->maxLinkCount - 1].next = DT_NULL_LINK;
for (int i = 0; i < header->maxLinkCount - 1; ++i)
tile->links[i].next = i + 1;
// Init tile.
tile->header = header;
@ -1089,25 +1238,28 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
connectIntLinks(tile);
// Base off-mesh connections to their starting polygons and connect connections inside the tile.
baseOffMeshLinks(tile);
for (int i = 0; i < tile->header->offMeshConCount; i++)
{
baseOffMeshLinks(tile->offMeshCons[i]);
}
// Create connections with neighbour tiles.
static const int MAX_NEIS = 32;
dtMeshTile* neis[MAX_NEIS];
int nneis;
// Connect with layers in current tile.
nneis = getTilesAt(header->x, header->y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j)
{
if (neis[j] == tile)
continue;
connectExtLinks(tile, neis[j], -1);
connectExtLinks(neis[j], tile, -1);
}
// Connect with neighbour tiles.
for (int i = 0; i < 8; ++i)
{
@ -1119,115 +1271,101 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
}
}
GlobalOffMeshLinks(tile);
if (bMarkOffMeshDirty)
{
for (int i = 0; i < tile->header->receivingOffMeshConCount; i++)
{
dtOffMeshConnection* targetCon = &tile->receivingOffMeshCons[i];
m_PendingOffMeshs[m_NumPendingConnections++] = targetCon;
}
}
if (result)
*result = getTileRef(tile);
return DT_SUCCESS;
}
void dtNavMesh::GlobalOffMeshLinks(dtMeshTile* target)
void dtNavMesh::GlobalOffMeshLinks(dtOffMeshConnection* con)
{
if (!target)
if (!con)
return;
for (int i = 0; i < target->header->offMeshConCount; ++i)
dtMeshTile* tile = getTileAt(con->FromTileX, con->FromTileY, con->FromTileLayer);
if (!tile) { return; }
dtPoly* targetPoly = &tile->polys[con->poly];
// Skip off-mesh connections which start location could not be connected at all.
if (targetPoly->firstLink == DT_NULL_LINK)
return;
dtMeshTile* TargetTile = this->getTileAt(con->ToTileX, con->ToTileY, con->ToTileLayer);
if (!TargetTile) { return; }
const float ext[3] = { con->rad, tile->header->walkableClimb, con->rad };
// Find polygon to connect to.
const float* p = &con->pos[3];
float nearestPt[3];
dtPolyRef ref = findNearestPolyInTile(TargetTile, p, ext, nearestPt);
if (!ref)
return;
// findNearestPoly may return too optimistic results, further check to make sure.
if (dtSqr(nearestPt[0] - p[0]) + dtSqr(nearestPt[2] - p[2]) > dtSqr(con->rad))
return;
// Make sure the location is on current mesh.
float* v = &tile->verts[targetPoly->verts[1] * 3];
dtVcopy(v, nearestPt);
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(tile);
if (idx != DT_NULL_LINK)
{
dtOffMeshConnection* targetCon = &target->offMeshCons[i];
dtLink* link = &tile->links[idx];
link->ref = ref;
link->edge = (unsigned char)1;
link->side = (unsigned char)0xff;
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = targetPoly->firstLink;
link->OffMeshID = con->userId;
targetPoly->firstLink = idx;
}
dtPoly* targetPoly = &target->polys[targetCon->poly];
// Skip off-mesh connections which start location could not be connected at all.
if (targetPoly->firstLink == DT_NULL_LINK)
continue;
const float ext[3] = { targetCon->rad, target->header->walkableClimb, targetCon->rad };
float bmin[3], bmax[3];
dtVsub(bmin, &targetCon->pos[3], ext);
dtVadd(bmax, &targetCon->pos[3], ext);
// Find tiles the query touches.
int minx, miny, maxx, maxy;
this->calcTileLoc(bmin, &minx, &miny);
this->calcTileLoc(bmax, &maxx, &maxy);
static const int MAX_NEIS = 32;
dtMeshTile* neis[MAX_NEIS];
for (int y = miny; y <= maxy; ++y)
// Link target poly to off-mesh connection.
if (con->bBiDir)
{
unsigned int tidx = allocLink(TargetTile);
if (tidx != DT_NULL_LINK)
{
for (int x = minx; x <= maxx; ++x)
{
const int nneis = this->getTilesAt(x, y, neis, MAX_NEIS);
for (int j = 0; j < nneis; ++j)
{
auto tile = neis[j];
// Find polygon to connect to.
const float* p = &targetCon->pos[3];
float nearestPt[3];
dtPolyRef ref = findNearestPolyInTile(tile, p, ext, nearestPt);
if (!ref)
continue;
// findNearestPoly may return too optimistic results, further check to make sure.
if (dtSqr(nearestPt[0] - p[0]) + dtSqr(nearestPt[2] - p[2]) > dtSqr(targetCon->rad))
continue;
// Make sure the location is on current mesh.
float* v = &target->verts[targetPoly->verts[1] * 3];
dtVcopy(v, nearestPt);
// Link off-mesh connection to target poly.
unsigned int idx = allocLink(target);
if (idx != DT_NULL_LINK)
{
dtLink* link = &target->links[idx];
link->ref = ref;
link->edge = (unsigned char)1;
link->side = (unsigned char)0xff;
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = targetPoly->firstLink;
targetPoly->firstLink = idx;
}
// Link target poly to off-mesh connection.
if (targetCon->flags & DT_OFFMESH_CON_BIDIR)
{
unsigned int tidx = allocLink(tile);
if (tidx != DT_NULL_LINK)
{
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
dtPoly* landPoly = &tile->polys[landPolyIdx];
dtLink* link = &tile->links[tidx];
link->ref = getPolyRefBase(target) | (dtPolyRef)(targetCon->poly);
link->edge = (unsigned char)0xff;
link->side = (unsigned char)(0xff);
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = landPoly->firstLink;
landPoly->firstLink = tidx;
}
}
}
}
const unsigned short landPolyIdx = (unsigned short)decodePolyIdPoly(ref);
dtPoly* landPoly = &TargetTile->polys[landPolyIdx];
dtLink* link = &TargetTile->links[tidx];
link->ref = getPolyRefBase(tile) | (dtPolyRef)(con->poly);
link->edge = (unsigned char)0xff;
link->side = (unsigned char)(0xff);
link->bmin = link->bmax = 0;
// Add to linked list.
link->next = landPoly->firstLink;
link->OffMeshID = con->userId;
landPoly->firstLink = tidx;
}
}
}
const dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer) const
const dtMeshTile* dtNavMesh::getTileAtConst(const int x, const int y, const int layer) const
{
// Find tile based on hash.
int h = computeTileHash(x, y, m_tileLutMask);
dtMeshTile* tile = m_posLookup[h];
while (tile)
{
if (tile->header &&
tile->header->x == x &&
tile->header->y == y &&
tile->header->layer == layer)
{
return tile;
}
tile = tile->next;
}
return 0;
}
dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer)
{
// Find tile based on hash.
int h = computeTileHash(x,y,m_tileLutMask);
@ -1693,7 +1831,7 @@ const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) c
const unsigned int idx = ip - tile->header->offMeshBase;
dtAssert(idx < (unsigned int)tile->header->offMeshConCount);
return &tile->offMeshCons[idx];
return tile->offMeshCons[idx];
}

View file

@ -289,109 +289,69 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
return false;
const int nvp = params->nvp;
// Classify off-mesh connection points. We store only the connections
// whose start point is inside the tile.
unsigned char* offMeshConClass = 0;
int storedOffMeshConCount = 0;
int receiveOffMeshConCount = 0;
int offMeshConLinkCount = 0;
if (params->offMeshConCount > 0)
if (params->NumOffMeshConnections > 0)
{
offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char) * params->offMeshConCount * 2, DT_ALLOC_TEMP);
offMeshConClass = (unsigned char*)dtAlloc(sizeof(unsigned char)*params->offMeshConCount*2, DT_ALLOC_TEMP);
if (!offMeshConClass)
return false;
// Find tight heigh bounds, used for culling out off-mesh start locations.
float hmin = FLT_MAX;
float hmax = -FLT_MAX;
if (params->detailVerts && params->detailVertsCount)
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
for (int i = 0; i < params->detailVertsCount; ++i)
{
const float h = params->detailVerts[i * 3 + 1];
hmin = dtMin(hmin, h);
hmax = dtMax(hmax, h);
}
}
else
{
for (int i = 0; i < params->vertCount; ++i)
{
const unsigned short* iv = &params->verts[i * 3];
const float h = params->bmin[1] + iv[1] * params->ch;
hmin = dtMin(hmin, h);
hmax = dtMax(hmax, h);
}
}
hmin -= params->walkableClimb;
hmax += params->walkableClimb;
float bmin[3], bmax[3];
dtVcopy(bmin, params->bmin);
dtVcopy(bmax, params->bmax);
bmin[1] = hmin;
bmax[1] = hmax;
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
for (int i = 0; i < params->offMeshConCount; ++i)
{
const float* p0 = &params->offMeshConVerts[(i * 2 + 0) * 3];
const float* p1 = &params->offMeshConVerts[(i * 2 + 1) * 3];
offMeshConClass[i * 2 + 0] = classifyOffMeshPoint(p0, bmin, bmax);
offMeshConClass[i * 2 + 1] = classifyOffMeshPoint(p1, bmin, bmax);
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Zero out off-mesh start positions which are not even potentially touching the mesh.
if (offMeshConClass[i * 2 + 0] == 0xff)
bool bOriginates = (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer);
bool bTargets = (con->ToTileX == params->tileX && con->ToTileY == params->tileY && con->ToTileLayer == params->tileLayer);
if (bOriginates)
{
if (p0[1] < bmin[1] || p0[1] > bmax[1])
offMeshConClass[i * 2 + 0] = 0;
}
// Cound how many links should be allocated for off-mesh connections.
if (offMeshConClass[i * 2 + 0] == 0xff)
con->state = DT_OFFMESH_DIRTY;
offMeshConLinkCount++;
if (offMeshConClass[i * 2 + 1] == 0xff)
offMeshConLinkCount++;
if (offMeshConClass[i * 2 + 0] == 0xff)
{
storedOffMeshConCount++;
}
else if (offMeshConClass[i * 2 + 1] == 0xff)
{
receiveOffMeshConCount++;
}
if (bTargets)
{
con->state = DT_OFFMESH_DIRTY;
offMeshConLinkCount++;
}
}
}
// Off-mesh connectionss are stored as polygons, adjust values.
const int totPolyCount = params->polyCount + storedOffMeshConCount;
const int totVertCount = params->vertCount + storedOffMeshConCount * 2;
const int totVertCount = params->vertCount + storedOffMeshConCount*2;
// Find portal edges which are at tile borders.
int edgeCount = 0;
int portalCount = 0;
for (int i = 0; i < params->polyCount; ++i)
{
const unsigned short* p = &params->polys[i * 2 * nvp];
const unsigned short* p = &params->polys[i*2*nvp];
for (int j = 0; j < nvp; ++j)
{
if (p[j] == MESH_NULL_IDX) break;
edgeCount++;
if (p[nvp + j] & 0x8000)
if (p[nvp+j] & 0x8000)
{
unsigned short dir = p[nvp + j] & 0xf;
unsigned short dir = p[nvp+j] & 0xf;
if (dir != 0xf)
portalCount++;
}
}
}
const int maxLinkCount = edgeCount + portalCount * 2 + offMeshConLinkCount * 2;
const int maxLinkCount = edgeCount + portalCount*2 + offMeshConLinkCount*2;
// Find unique detail vertices.
int uniqueDetailVertCount = 0;
int detailTriCount = 0;
@ -401,8 +361,8 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
detailTriCount = params->detailTriCount;
for (int i = 0; i < params->polyCount; ++i)
{
const unsigned short* p = &params->polys[i * nvp * 2];
int ndv = params->detailMeshes[i * 4 + 1];
const unsigned short* p = &params->polys[i*nvp*2];
int ndv = params->detailMeshes[i*4+1];
int nv = 0;
for (int j = 0; j < nvp; ++j)
{
@ -420,41 +380,40 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
detailTriCount = 0;
for (int i = 0; i < params->polyCount; ++i)
{
const unsigned short* p = &params->polys[i * nvp * 2];
const unsigned short* p = &params->polys[i*nvp*2];
int nv = 0;
for (int j = 0; j < nvp; ++j)
{
if (p[j] == MESH_NULL_IDX) break;
nv++;
}
detailTriCount += nv - 2;
detailTriCount += nv-2;
}
}
// Calculate data size
const int headerSize = dtAlign4(sizeof(dtMeshHeader));
const int vertsSize = dtAlign4(sizeof(float) * 3 * totVertCount);
const int polysSize = dtAlign4(sizeof(dtPoly) * totPolyCount);
const int linksSize = dtAlign4(sizeof(dtLink) * maxLinkCount);
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail) * params->polyCount);
const int detailVertsSize = dtAlign4(sizeof(float) * 3 * uniqueDetailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char) * 4 * detailTriCount);
const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode) * params->polyCount * 2) : 0;
const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection) * storedOffMeshConCount);
const int receiveOffMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection) * receiveOffMeshConCount);
const int vertsSize = dtAlign4(sizeof(float)*3*totVertCount);
const int polysSize = dtAlign4(sizeof(dtPoly)*totPolyCount);
const int linksSize = dtAlign4(sizeof(dtLink)*maxLinkCount);
const int detailMeshesSize = dtAlign4(sizeof(dtPolyDetail)*params->polyCount);
const int detailVertsSize = dtAlign4(sizeof(float)*3*uniqueDetailVertCount);
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*detailTriCount);
const int bvTreeSize = params->buildBvTree ? dtAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0;
const int offMeshConsSize = dtAlign4(sizeof(dtOffMeshConnection*)*storedOffMeshConCount);
const int dataSize = headerSize + vertsSize + polysSize + linksSize +
detailMeshesSize + detailVertsSize + detailTrisSize +
bvTreeSize + offMeshConsSize + receiveOffMeshConsSize;
unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char) * dataSize, DT_ALLOC_PERM);
detailMeshesSize + detailVertsSize + detailTrisSize +
bvTreeSize + offMeshConsSize;
unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
if (!data)
{
dtFree(offMeshConClass);
return false;
}
memset(data, 0, dataSize);
unsigned char* d = data;
dtMeshHeader* header = dtGetThenAdvanceBufferPointer<dtMeshHeader>(d, headerSize);
@ -465,10 +424,9 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
float* navDVerts = dtGetThenAdvanceBufferPointer<float>(d, detailVertsSize);
unsigned char* navDTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
dtBVNode* navBvtree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
dtOffMeshConnection* receivingOffMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, receiveOffMeshConsSize);
dtOffMeshConnection** offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection*>(d, offMeshConsSize);
// Store header
header->magic = DT_NAVMESH_MAGIC;
header->version = DT_NAVMESH_VERSION;
@ -489,38 +447,41 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
header->walkableHeight = params->walkableHeight;
header->walkableRadius = params->walkableRadius;
header->walkableClimb = params->walkableClimb;
header->offMeshConCount = storedOffMeshConCount;
header->receivingOffMeshConCount = receiveOffMeshConCount;
header->bvNodeCount = params->buildBvTree ? params->polyCount * 2 : 0;
header->offMeshConCount = offMeshConLinkCount;
header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0;
const int offMeshVertsBase = params->vertCount;
const int offMeshPolyBase = params->polyCount;
// Store vertices
// Mesh vertices
for (int i = 0; i < params->vertCount; ++i)
{
const unsigned short* iv = &params->verts[i * 3];
float* v = &navVerts[i * 3];
const unsigned short* iv = &params->verts[i*3];
float* v = &navVerts[i*3];
v[0] = params->bmin[0] + iv[0] * params->cs;
v[1] = params->bmin[1] + iv[1] * params->ch;
v[2] = params->bmin[2] + iv[2] * params->cs;
}
// Off-mesh link vertices.
int n = 0;
for (int i = 0; i < params->offMeshConCount; ++i)
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Only store connections which start from this tile.
if (offMeshConClass[i * 2 + 0] == 0xff)
if (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer)
{
const float* linkv = &params->offMeshConVerts[i * 2 * 3];
float* v = &navVerts[(offMeshVertsBase + n * 2) * 3];
const float* linkv = &con->pos[0];
float* v = &navVerts[(offMeshVertsBase + n*2)*3];
dtVcopy(&v[0], &linkv[0]);
dtVcopy(&v[3], &linkv[3]);
n++;
}
}
// Store polygons
// Mesh polys
const unsigned short* src = params->polys;
@ -535,10 +496,10 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
{
if (src[j] == MESH_NULL_IDX) break;
p->verts[j] = src[j];
if (src[nvp + j] & 0x8000)
if (src[nvp+j] & 0x8000)
{
// Border or portal edge.
unsigned short dir = src[nvp + j] & 0xf;
unsigned short dir = src[nvp+j] & 0xf;
if (dir == 0xf) // Border
p->neis[j] = 0;
else if (dir == 0) // Portal x-
@ -553,26 +514,30 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
else
{
// Normal connection
p->neis[j] = src[nvp + j] + 1;
p->neis[j] = src[nvp+j]+1;
}
p->vertCount++;
}
src += nvp * 2;
src += nvp*2;
}
// Off-mesh connection vertices.
n = 0;
for (int i = 0; i < params->offMeshConCount; ++i)
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Only store connections which start from this tile.
if (offMeshConClass[i * 2 + 0] == 0xff)
if (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer)
{
dtPoly* p = &navPolys[offMeshPolyBase + n];
dtPoly* p = &navPolys[offMeshPolyBase+n];
p->vertCount = 2;
p->verts[0] = (unsigned short)(offMeshVertsBase + n * 2 + 0);
p->verts[1] = (unsigned short)(offMeshVertsBase + n * 2 + 1);
p->flags = params->offMeshConFlags[i];
p->setArea(params->offMeshConAreas[i]);
p->verts[0] = (unsigned short)(offMeshVertsBase + n*2+0);
p->verts[1] = (unsigned short)(offMeshVertsBase + n*2+1);
p->flags = con->flags;
p->setArea(con->area);
p->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
n++;
}
@ -587,22 +552,22 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
for (int i = 0; i < params->polyCount; ++i)
{
dtPolyDetail& dtl = navDMeshes[i];
const int vb = (int)params->detailMeshes[i * 4 + 0];
const int ndv = (int)params->detailMeshes[i * 4 + 1];
const int vb = (int)params->detailMeshes[i*4+0];
const int ndv = (int)params->detailMeshes[i*4+1];
const int nv = navPolys[i].vertCount;
dtl.vertBase = (unsigned int)vbase;
dtl.vertCount = (unsigned char)(ndv - nv);
dtl.triBase = (unsigned int)params->detailMeshes[i * 4 + 2];
dtl.triCount = (unsigned char)params->detailMeshes[i * 4 + 3];
dtl.vertCount = (unsigned char)(ndv-nv);
dtl.triBase = (unsigned int)params->detailMeshes[i*4+2];
dtl.triCount = (unsigned char)params->detailMeshes[i*4+3];
// Copy vertices except the first 'nv' verts which are equal to nav poly verts.
if (ndv - nv)
if (ndv-nv)
{
memcpy(&navDVerts[vbase * 3], &params->detailVerts[(vb + nv) * 3], sizeof(float) * 3 * (ndv - nv));
vbase += (unsigned short)(ndv - nv);
memcpy(&navDVerts[vbase*3], &params->detailVerts[(vb+nv)*3], sizeof(float)*3*(ndv-nv));
vbase += (unsigned short)(ndv-nv);
}
}
// Store triangles.
memcpy(navDTris, params->detailTris, sizeof(unsigned char) * 4 * params->detailTriCount);
memcpy(navDTris, params->detailTris, sizeof(unsigned char)*4*params->detailTriCount);
}
else
{
@ -615,18 +580,18 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
dtl.vertBase = 0;
dtl.vertCount = 0;
dtl.triBase = (unsigned int)tbase;
dtl.triCount = (unsigned char)(nv - 2);
dtl.triCount = (unsigned char)(nv-2);
// Triangulate polygon (local indices).
for (int j = 2; j < nv; ++j)
{
unsigned char* t = &navDTris[tbase * 4];
unsigned char* t = &navDTris[tbase*4];
t[0] = 0;
t[1] = (unsigned char)(j - 1);
t[1] = (unsigned char)(j-1);
t[2] = (unsigned char)j;
// Bit for each edge that belongs to poly boundary.
t[3] = (1 << 2);
if (j == 2) t[3] |= (1 << 0);
if (j == nv - 1) t[3] |= (1 << 4);
t[3] = (1<<2);
if (j == 2) t[3] |= (1<<0);
if (j == nv-1) t[3] |= (1<<4);
tbase++;
}
}
@ -635,63 +600,34 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
// Store and create BVtree.
if (params->buildBvTree)
{
createBVTree(params, navBvtree, 2 * params->polyCount);
createBVTree(params, navBvtree, 2*params->polyCount);
}
// Store Off-Mesh connections.
n = 0;
for (int i = 0; i < params->offMeshConCount; ++i)
for (int i = 0; i < params->NumOffMeshConnections; i++)
{
dtOffMeshConnection* con = &params->GlobalOffMeshConnections[i];
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
// Only store connections which start from this tile.
if (offMeshConClass[i * 2 + 0] == 0xff)
if (con->FromTileX == params->tileX && con->FromTileY == params->tileY && con->FromTileLayer == params->tileLayer)
{
dtOffMeshConnection* con = &offMeshCons[n];
con->poly = (unsigned short)(offMeshPolyBase + n);
// Copy connection end-points.
const float* endPts = &params->offMeshConVerts[i * 2 * 3];
dtVcopy(&con->pos[0], &endPts[0]);
dtVcopy(&con->pos[3], &endPts[3]);
con->rad = params->offMeshConRad[i];
con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
con->side = offMeshConClass[i * 2 + 1];
if (params->offMeshConUserID)
con->userId = params->offMeshConUserID[i];
offMeshCons[n] = con;
n++;
}
}
n = 0;
for (int i = 0; i < params->offMeshConCount; ++i)
{
// Only store connections which start from this tile.
if (offMeshConClass[i * 2 + 0] != 0xff && offMeshConClass[i * 2 + 1] == 0xff)
{
dtOffMeshConnection* con = &receivingOffMeshCons[n];
con->poly = (unsigned short)(offMeshPolyBase + n);
// Copy connection end-points.
const float* endPts = &params->offMeshConVerts[i * 2 * 3];
dtVcopy(&con->pos[0], &endPts[0]);
dtVcopy(&con->pos[3], &endPts[3]);
con->rad = params->offMeshConRad[i];
con->flags = params->offMeshConDir[i] ? DT_OFFMESH_CON_BIDIR : 0;
con->side = offMeshConClass[i * 2 + 0];
if (params->offMeshConUserID)
con->userId = params->offMeshConUserID[i];
n++;
}
}
if (header->offMeshConCount > 0)
{
int thing = 0;
dtIgnoreUnused(thing);
}
header->offMeshConCount = n;
dtFree(offMeshConClass);
*outData = data;
*outDataSize = dataSize;
return true;
}
@ -724,7 +660,6 @@ bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
dtSwapEndian(&header->detailTriCount);
dtSwapEndian(&header->bvNodeCount);
dtSwapEndian(&header->offMeshConCount);
dtSwapEndian(&header->receivingOffMeshConCount);
dtSwapEndian(&header->offMeshBase);
dtSwapEndian(&header->walkableHeight);
dtSwapEndian(&header->walkableRadius);
@ -767,7 +702,6 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
const int detailTrisSize = dtAlign4(sizeof(unsigned char)*4*header->detailTriCount);
const int bvtreeSize = dtAlign4(sizeof(dtBVNode)*header->bvNodeCount);
const int offMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection)*header->offMeshConCount);
const int receiveOffMeshLinksSize = dtAlign4(sizeof(dtOffMeshConnection) * header->receivingOffMeshConCount);
unsigned char* d = data + headerSize;
float* verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
@ -780,7 +714,6 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
//unsigned char* detailTris = dtGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
dtBVNode* bvTree = dtGetThenAdvanceBufferPointer<dtBVNode>(d, bvtreeSize);
dtOffMeshConnection* offMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshLinksSize);
dtOffMeshConnection* receivingOffMeshCons = dtGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, receiveOffMeshLinksSize);
// Vertices
for (int i = 0; i < header->vertCount*3; ++i)
@ -838,16 +771,6 @@ bool dtNavMeshDataSwapEndian(unsigned char* data, const int /*dataSize*/)
dtSwapEndian(&con->rad);
dtSwapEndian(&con->poly);
}
// Receiving Off-mesh Connections.
for (int i = 0; i < header->receivingOffMeshConCount; ++i)
{
dtOffMeshConnection* con = &receivingOffMeshCons[i];
for (int j = 0; j < 6; ++j)
dtSwapEndian(&con->pos[j]);
dtSwapEndian(&con->rad);
dtSwapEndian(&con->poly);
}
return true;
}

View file

@ -347,7 +347,7 @@ dtStatus dtNavMeshQuery::findRandomPointAroundCircleIgnoreReachability(dtPolyRef
int thisTileXIndex = StartTileX + tileX;
int thisTileYIndex = StartTileY + tileY;
const dtMeshTile* t = m_nav->getTileAt(thisTileXIndex, thisTileYIndex, layer);
const dtMeshTile* t = m_nav->getTileAtConst(thisTileXIndex, thisTileYIndex, layer);
if (!t || !t->header) continue;

View file

@ -74,10 +74,12 @@ dtTileCache::dtTileCache() :
m_obstacles(0),
m_nextFreeObstacle(0),
m_nreqs(0),
m_nOffMeshReqs(0),
m_nupdate(0)
{
memset(&m_params, 0, sizeof(m_params));
memset(m_reqs, 0, sizeof(ObstacleRequest) * MAX_REQUESTS);
memset(m_OffMeshReqs, 0, sizeof(OffMeshRequest) * MAX_REQUESTS);
}
dtTileCache::~dtTileCache()
@ -92,11 +94,14 @@ dtTileCache::~dtTileCache()
}
dtFree(m_obstacles);
m_obstacles = 0;
dtFree(m_offMeshConnections);
m_offMeshConnections = 0;
dtFree(m_posLookup);
m_posLookup = 0;
dtFree(m_tiles);
m_tiles = 0;
m_nreqs = 0;
m_nOffMeshReqs = 0;
m_nupdate = 0;
}
@ -138,6 +143,19 @@ dtStatus dtTileCache::init(const dtTileCacheParams* params,
m_obstacles[i].next = m_nextFreeObstacle;
m_nextFreeObstacle = &m_obstacles[i];
}
// Alloc space for off-mesh connections.
m_offMeshConnections = (dtOffMeshConnection*)dtAlloc(sizeof(dtOffMeshConnection) * m_params.maxOffMeshConnections, DT_ALLOC_PERM);
if (!m_offMeshConnections)
return DT_FAILURE | DT_OUT_OF_MEMORY;
memset(m_offMeshConnections, 0, sizeof(dtOffMeshConnection) * m_params.maxOffMeshConnections);
m_nextFreeOffMeshConnection = 0;
for (int i = m_params.maxOffMeshConnections - 1; i >= 0; --i)
{
m_offMeshConnections[i].salt = 1;
m_offMeshConnections[i].next = m_nextFreeOffMeshConnection;
m_nextFreeOffMeshConnection = &m_offMeshConnections[i];
}
// Init tiles
m_tileLutSize = dtNextPow2(m_params.maxTiles/4);
@ -225,6 +243,13 @@ dtObstacleRef dtTileCache::getObstacleRef(const dtTileCacheObstacle* ob) const
return encodeObstacleId(ob->salt, idx);
}
dtOffMeshConnectionRef dtTileCache::getOffMeshRef(const dtOffMeshConnection* con) const
{
if (!con) return 0;
const unsigned int idx = (unsigned int)(con - m_offMeshConnections);
return encodeObstacleId(con->salt, idx);
}
const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
{
if (!ref)
@ -239,6 +264,20 @@ const dtTileCacheObstacle* dtTileCache::getObstacleByRef(dtObstacleRef ref)
return ob;
}
dtOffMeshConnection* dtTileCache::getOffMeshConnectionByRef(dtOffMeshConnectionRef ref)
{
if (!ref)
return 0;
unsigned int idx = decodeOffMeshIdCon(ref);
if ((int)idx >= m_params.maxOffMeshConnections)
return 0;
dtOffMeshConnection* con = &m_offMeshConnections[idx];
unsigned int salt = decodeOffMeshIdSalt(ref);
if (con->salt != salt)
return 0;
return con;
}
dtStatus dtTileCache::addTile(unsigned char* data, const int dataSize, unsigned char flags, dtCompressedTileRef* result)
{
// Make sure the data is in right format.
@ -350,6 +389,42 @@ dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data,
return DT_SUCCESS;
}
dtStatus dtTileCache::addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned short flags, const bool bBiDirectional, dtOffMeshConnectionRef* result)
{
if (m_nOffMeshReqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
dtOffMeshConnection* con = 0;
if (m_nextFreeOffMeshConnection)
{
con = m_nextFreeOffMeshConnection;
m_nextFreeOffMeshConnection = con->next;
con->next = 0;
}
if (!con)
return DT_FAILURE | DT_OUT_OF_MEMORY;
unsigned short salt = con->salt;
memset(con, 0, sizeof(dtOffMeshConnection));
con->salt = salt;
con->state = DT_OFFMESH_NEW;
dtVcopy(&con->pos[0], spos);
dtVcopy(&con->pos[3], epos);
con->rad = radius;
con->area = area;
con->flags = flags;
con->bBiDir = bBiDirectional;
OffMeshRequest* req = &m_OffMeshReqs[m_nOffMeshReqs++];
memset(req, 0, sizeof(OffMeshRequest));
req->action = REQUEST_OFFMESH_ADD;
req->ref = getOffMeshRef(con);
if (result)
*result = req->ref;
return DT_SUCCESS;
}
dtStatus dtTileCache::addObstacle(const float* pos, const float radius, const float height, const int area, dtObstacleRef* result)
{
@ -387,7 +462,7 @@ dtStatus dtTileCache::addObstacle(const float* pos, const float radius, const fl
return DT_SUCCESS;
}
dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, const int area, dtObstacleRef* result)
dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result)
{
if (m_nreqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
@ -409,7 +484,6 @@ dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, const
ob->type = DT_OBSTACLE_BOX;
dtVcopy(ob->box.bmin, bmin);
dtVcopy(ob->box.bmax, bmax);
ob->box.area = area;
ObstacleRequest* req = &m_reqs[m_nreqs++];
memset(req, 0, sizeof(ObstacleRequest));
@ -422,7 +496,7 @@ dtStatus dtTileCache::addBoxObstacle(const float* bmin, const float* bmax, const
return DT_SUCCESS;
}
dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, const int area, dtObstacleRef* result)
dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result)
{
if (m_nreqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
@ -449,7 +523,6 @@ dtStatus dtTileCache::addBoxObstacle(const float* center, const float* halfExten
float sinhalf = sinf(-0.5f*yRadians);
ob->orientedBox.rotAux[0] = coshalf*sinhalf;
ob->orientedBox.rotAux[1] = coshalf*coshalf - 0.5f;
ob->orientedBox.area = area;
ObstacleRequest* req = &m_reqs[m_nreqs++];
memset(req, 0, sizeof(ObstacleRequest));
@ -477,6 +550,22 @@ dtStatus dtTileCache::removeObstacle(const dtObstacleRef ref)
return DT_SUCCESS;
}
dtStatus dtTileCache::removeOffMeshConnection(const dtOffMeshConnectionRef ref)
{
if (!ref)
return DT_SUCCESS;
if (m_nOffMeshReqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
OffMeshRequest* req = &m_OffMeshReqs[m_nOffMeshReqs++];
memset(req, 0, sizeof(OffMeshRequest));
req->action = REQUEST_OFFMESH_REMOVE;
req->ref = ref;
return DT_SUCCESS;
}
dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
dtCompressedTileRef* results, int* resultCount, const int maxResults) const
{
@ -519,7 +608,7 @@ dtStatus dtTileCache::queryTiles(const float* bmin, const float* bmax,
}
dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
bool* upToDate)
bool* upToDate)
{
if (m_nupdate == 0)
{
@ -527,7 +616,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
for (int i = 0; i < m_nreqs; ++i)
{
ObstacleRequest* req = &m_reqs[i];
unsigned int idx = decodeObstacleIdObstacle(req->ref);
if ((int)idx >= m_params.maxObstacles)
continue;
@ -535,7 +624,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
unsigned int salt = decodeObstacleIdSalt(req->ref);
if (ob->salt != salt)
continue;
if (req->action == REQUEST_ADD)
{
// Find touched tiles.
@ -574,20 +663,104 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
}
}
}
m_nreqs = 0;
for (int i = 0; i < m_nOffMeshReqs; ++i)
{
OffMeshRequest* req = &m_OffMeshReqs[i];
unsigned int idx = decodeOffMeshIdCon(req->ref);
if ((int)idx >= m_params.maxOffMeshConnections)
continue;
dtOffMeshConnection* con = &m_offMeshConnections[idx];
unsigned int salt = decodeOffMeshIdSalt(req->ref);
if (con->salt != salt)
continue;
if (req->action == REQUEST_OFFMESH_ADD)
{
con->state = DT_OFFMESH_DIRTY;
// Find touched tiles.
float bmin[3], bmax[3];
float ext[3] = { con->rad, 18.0f, con->rad };
dtVsub(bmin, &con->pos[0], ext);
dtVadd(bmax, &con->pos[0], ext);
int ntouched = 0;
dtCompressedTileRef touched[DT_MAX_TOUCHED_TILES];
dtCompressedTileRef StartTileRef = 0;
dtCompressedTileRef EndTileRef = 0;
queryTiles(bmin, bmax, touched, &ntouched, DT_MAX_TOUCHED_TILES);
if (ntouched > 0)
{
StartTileRef = touched[0];
}
dtVsub(bmin, &con->pos[3], ext);
dtVadd(bmax, &con->pos[3], ext);
queryTiles(bmin, bmax, touched, &ntouched, DT_MAX_TOUCHED_TILES);
if (ntouched > 0)
{
EndTileRef = touched[0];
}
if (!StartTileRef || !EndTileRef) { continue; }
const dtCompressedTile* StartTile = getTileByRef(StartTileRef);
const dtCompressedTile* EndTile = getTileByRef(EndTileRef);
con->FromTileX = StartTile->header->tx;
con->FromTileY = StartTile->header->ty;
con->FromTileLayer = StartTile->header->tlayer;
con->ToTileX = EndTile->header->tx;
con->ToTileY = EndTile->header->ty;
con->ToTileLayer = EndTile->header->tlayer;
if (m_nupdate < MAX_UPDATE)
{
if (!contains(m_update, m_nupdate, StartTileRef))
m_update[m_nupdate++] = StartTileRef;
}
}
else if (req->action == REQUEST_OFFMESH_REMOVE)
{
// Prepare to remove obstacle.
con->state = DT_OFFMESH_REMOVING;
// Add tiles to update list.
navmesh->unconnectOffMeshLink(con);
if (m_nupdate < MAX_UPDATE)
{
dtCompressedTile* Tile = getTileAt(con->FromTileX, con->FromTileY, con->FromTileLayer);
dtCompressedTileRef TileRef = getTileRef(Tile);
if (!contains(m_update, m_nupdate, TileRef))
m_update[m_nupdate++] = TileRef;
}
}
}
m_nOffMeshReqs = 0;
}
dtStatus status = DT_SUCCESS;
// Process updates
if (m_nupdate)
{
// Build mesh
const dtCompressedTileRef ref = m_update[0];
status = buildNavMeshTile(ref, navmesh, true);
status = buildNavMeshTile(ref, navmesh);
m_nupdate--;
if (m_nupdate > 0)
memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
memmove(m_update, m_update + 1, m_nupdate * sizeof(dtCompressedTileRef));
// Update obstacle states.
for (int i = 0; i < m_params.maxObstacles; ++i)
@ -600,12 +773,12 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
{
if (ob->pending[j] == ref)
{
ob->pending[j] = ob->pending[(int)ob->npending-1];
ob->pending[j] = ob->pending[(int)ob->npending - 1];
ob->npending--;
break;
}
}
// If all pending tiles processed, change state.
if (ob->npending == 0)
{
@ -617,7 +790,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
{
ob->state = DT_OBSTACLE_EMPTY;
// Update salt, salt should never be zero.
ob->salt = (ob->salt+1) & ((1<<16)-1);
ob->salt = (ob->salt + 1) & ((1 << 16) - 1);
if (ob->salt == 0)
ob->salt++;
// Return obstacle to free list.
@ -627,74 +800,49 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
}
}
}
}
if (navmesh->GetNumPendingOffMeshConnections() > 0)
if (m_nupdate == 0)
{
for (int i = 0; i < navmesh->GetNumPendingOffMeshConnections(); i++)
for (int i = 0; i < m_params.maxOffMeshConnections; ++i)
{
int NumTiles = 0;
dtOffMeshConnection* con = &m_offMeshConnections[i];
float* connspos = &navmesh->GetPendingConnection(i)->pos[0];
float* connepos = &navmesh->GetPendingConnection(i)->pos[3];
float ext[3] = { 10.0f, 10.0f, 10.0f };
float searchsposMin[3];
float searchsposMax[3];
float searcheposMin[3];
float searcheposMax[3];
dtVsub(searchsposMin, connspos, ext);
dtVadd(searchsposMax, connspos, ext);
dtVsub(searcheposMin, connepos, ext);
dtVadd(searcheposMax, connepos, ext);
dtCompressedTileRef AffectedTiles[DT_MAX_TOUCHED_TILES];
queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
if (con->state == DT_OFFMESH_DIRTY)
{
const dtCompressedTile* Tile = getTileByRef(AffectedTiles[i]);
buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, navmesh, false);
navmesh->unconnectOffMeshLink(con);
navmesh->GlobalOffMeshLinks(con);
con->state = DT_OFFMESH_CLEAN;
}
queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
if (con->state == DT_OFFMESH_REMOVING)
{
const dtCompressedTile* Tile = getTileByRef(AffectedTiles[i]);
buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, navmesh, false);
con->state = DT_OFFMESH_EMPTY;
con->salt = (con->salt + 1) & ((1 << 16) - 1);
if (con->salt == 0)
con->salt++;
con->next = m_nextFreeOffMeshConnection;
m_nextFreeOffMeshConnection = con;
}
}
navmesh->ClearPendingOffMeshConnections();
}
if (upToDate)
*upToDate = m_nupdate == 0 && m_nreqs == 0;
*upToDate = m_nupdate == 0 && m_nreqs == 0 && m_nOffMeshReqs == 0;
return status;
}
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh, bool bMarkOffMeshDirty)
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh)
{
const int MAX_TILES = 32;
dtCompressedTileRef tiles[MAX_TILES];
const int ntiles = getTilesAt(tx,ty,tiles,MAX_TILES);
for (int i = 0; i < ntiles; ++i)
{
dtStatus status = buildNavMeshTile(tiles[i], navmesh, bMarkOffMeshDirty);
dtStatus status = buildNavMeshTile(tiles[i], navmesh);
if (dtStatusFailed(status))
return status;
}
@ -702,7 +850,7 @@ dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh*
return DT_SUCCESS;
}
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh, bool bMarkOffMeshDirty)
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)
{
dtAssert(m_talloc);
dtAssert(m_tcomp);
@ -720,7 +868,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
NavMeshTileBuildContext bc(m_talloc);
const int walkableClimbVx = (int)(m_params.walkableClimb / m_params.ch);
dtStatus status;
// Decompress tile layer data.
status = dtDecompressTileCacheLayer(m_talloc, m_tcomp, tile->data, tile->dataSize, &bc.layer);
if (dtStatusFailed(status))
@ -805,6 +953,9 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
{
m_tmproc->process(&params, bc.lmesh->areas, bc.lmesh->flags);
}
params.GlobalOffMeshConnections = m_offMeshConnections;
params.NumOffMeshConnections = getOffMeshCount();
unsigned char* navData = 0;
int navDataSize = 0;
@ -817,8 +968,9 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
// Add new tile, or leave the location empty.
if (navData)
{
// Let the navmesh own the data.
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0, bMarkOffMeshDirty);
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
if (dtStatusFailed(status))
{
dtFree(navData);

View file

@ -149,6 +149,14 @@ enum dtPolyTypes
DT_POLYTYPE_OFFMESH_CONNECTION = 1,
};
enum OffMeshState
{
DT_OFFMESH_EMPTY,
DT_OFFMESH_NEW,
DT_OFFMESH_DIRTY,
DT_OFFMESH_CLEAN,
DT_OFFMESH_REMOVING,
};
/// Defines a polygon within a dtMeshTile object.
/// @ingroup detour
@ -183,6 +191,8 @@ struct dtPoly
/// Gets the user defined area id.
inline unsigned char getArea() const { return areaAndtype & 0x3f; }
inline unsigned short getFlags() const { return flags; }
/// Gets the polygon type. (See: #dtPolyTypes)
inline unsigned char getType() const { return areaAndtype >> 6; }
};
@ -207,6 +217,7 @@ struct dtLink
unsigned char side; ///< If a boundary link, defines on which side the link is.
unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area.
unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area.
int OffMeshID = -1; ///< If an off-mesh connection, this will be the UserID of the connection that made this link
};
/// Bounding volume node.
@ -224,24 +235,45 @@ struct dtBVNode
struct dtOffMeshConnection
{
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
float pos[6];
float pos[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
/// The radius of the endpoints. [Limit: >= 0]
float rad;
float rad = 0.0f;
/// The polygon reference of the connection within the tile.
unsigned short poly;
unsigned short poly = 0;
/// Link flags.
/// @note These are not the connection's user defined flags. Those are assigned via the
/// connection's dtPoly definition. These are link flags used for internal purposes.
unsigned char flags;
unsigned short flags = 0;
unsigned char area = 0;
/// End point side.
unsigned char side;
unsigned char side = 0;
bool bBiDir = false;
/// The id of the offmesh connection. (User assigned when the navigation mesh is built.)
unsigned int userId;
unsigned int userId = 0;
int FromTileX = -1;
int FromTileY = -1;
int FromTileLayer = -1;
int ToTileX = -1;
int ToTileY = -1;
int ToTileLayer = -1;
bool bPendingDelete = false; // This off-mesh needs to be removed completely
bool bDirty = false; // This off-mesh connection has had its from or to tile rebuilt recently so the links need to be re-established
bool bBased = false; // This off-mesh connection has had its links based in the source tile
dtOffMeshConnection* next = nullptr;
OffMeshState state = DT_OFFMESH_EMPTY;
unsigned short salt;
};
/// Provides high level information related to a dtMeshTile object.
@ -265,7 +297,7 @@ struct dtMeshHeader
int detailTriCount; ///< The number of triangles in the detail mesh.
int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
int offMeshConCount; ///< The number of off-mesh connections.
int receivingOffMeshConCount;
int offMeshBase; ///< The index of the first polygon which is an off-mesh connection.
float walkableHeight; ///< The height of the agents using the tile.
float walkableRadius; ///< The radius of the agents using the tile.
@ -301,9 +333,8 @@ struct dtMeshTile
/// (Will be null if bounding volumes are disabled.)
dtBVNode* bvTree;
dtOffMeshConnection* offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
dtOffMeshConnection* receivingOffMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
dtOffMeshConnection** offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.)
int dataSize; ///< Size of the tile data.
int flags; ///< Tile flags. (See: #dtTileFlags)
@ -369,7 +400,7 @@ public:
/// @param[in] lastRef The desired reference for the tile. (When reloading a tile.) [opt] [Default: 0]
/// @param[out] result The tile reference. (If the tile was succesfully added.) [opt]
/// @return The status flags for the operation.
dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result, bool bMarkOffMeshDirty);
dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result);
/// Removes the specified tile from the navigation mesh.
/// @param[in] ref The reference of the tile to remove.
@ -378,6 +409,13 @@ public:
/// @return The status flags for the operation.
dtStatus removeTile(dtTileRef ref, unsigned char** data, int* dataSize);
void GlobalOffMeshLinks(dtOffMeshConnection* con);
void baseOffMeshLinks(dtOffMeshConnection* Connection);
void LinkOffMeshConnectionToTiles(dtOffMeshConnection* con);
void unconnectOffMeshLink(dtOffMeshConnection* con);
/// @}
/// @{
@ -394,7 +432,9 @@ public:
/// @param[in] y The tile's y-location. (x, y, layer)
/// @param[in] layer The tile's layer. (x, y, layer)
/// @return The tile, or null if the tile does not exist.
const dtMeshTile* getTileAt(const int x, const int y, const int layer) const;
dtMeshTile* getTileAt(const int x, const int y, const int layer);
const dtMeshTile* getTileAtConst(const int x, const int y, const int layer) const;
/// Gets all tiles at the specified grid location. (All layers.)
/// @param[in] x The tile's x-location. (x, y)
@ -467,10 +507,6 @@ public:
/// @param[in] ref The polygon reference of the off-mesh connection.
/// @return The specified off-mesh connection, or null if the polygon reference is not valid.
const dtOffMeshConnection* getOffMeshConnectionByRef(dtPolyRef ref) const;
int GetNumPendingOffMeshConnections() { return m_NumPendingConnections; }
void ClearPendingOffMeshConnections() { m_NumPendingConnections = 0; }
dtOffMeshConnection* GetPendingConnection(int index) { return m_PendingOffMeshs[index]; }
/// @}
@ -520,8 +556,6 @@ public:
/// @param[in] maxDataSize The size of the state within the data buffer.
/// @return The status flags for the operation.
dtStatus restoreTileState(dtMeshTile* tile, const unsigned char* data, const int maxDataSize);
void GlobalOffMeshLinks(dtMeshTile* target);
/// @}
@ -642,11 +676,12 @@ private:
/// Builds internal polygons links for a tile.
void baseOffMeshLinks(dtMeshTile* tile);
/// Builds external polygon links for a tile.
void connectExtLinks(dtMeshTile* tile, dtMeshTile* target, int side);
/// Builds external polygon links for a tile.
void connectExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
void connectDistantExtOffMeshLinks(dtMeshTile* tile, dtMeshTile* target, int side);
/// Removes external links at specified side.
void unconnectLinks(dtMeshTile* tile, dtMeshTile* target);
@ -670,10 +705,7 @@ private:
int m_maxTiles; ///< Max number of tiles.
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
int m_tileLutMask; ///< Tile hash lookup mask.
dtOffMeshConnection* m_PendingOffMeshs[1024];
int m_NumPendingConnections = 0;
dtMeshTile** m_posLookup; ///< Tile hash lookup.
dtMeshTile* m_nextFree; ///< Freelist of tiles.
dtMeshTile* m_tiles; ///< List of tiles.

View file

@ -20,6 +20,9 @@
#define DETOURNAVMESHBUILDER_H
#include "DetourAlloc.h"
#include "DetourNavMesh.h"
#include <vector>
/// Represents the source data used to build an navigation mesh tile.
/// @ingroup detour
@ -102,6 +105,9 @@ struct dtNavMeshCreateParams
/// @note The BVTree is not normally needed for layered navigation meshes.
bool buildBvTree;
dtOffMeshConnection* GlobalOffMeshConnections;
int NumOffMeshConnections;
};
/// Builds navigation mesh tile data from the provided tile creation data.

View file

@ -2,10 +2,12 @@
#define DETOURTILECACHE_H
#include "DetourStatus.h"
#include "DetourNavMesh.h"
#include <vector>
typedef unsigned int dtObstacleRef;
typedef unsigned int dtOffMeshConnectionRef;
typedef unsigned int dtCompressedTileRef;
@ -96,6 +98,7 @@ struct dtTileCacheParams
float maxSimplificationError;
int maxTiles;
int maxObstacles;
int maxOffMeshConnections;
};
struct dtTileCacheMeshProcess
@ -104,6 +107,8 @@ struct dtTileCacheMeshProcess
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned short* polyFlags) = 0;
};
@ -122,12 +127,16 @@ public:
inline int getTileCount() const { return m_params.maxTiles; }
inline const dtCompressedTile* getTile(const int i) const { return &m_tiles[i]; }
inline int getOffMeshCount() const { return m_params.maxOffMeshConnections; }
inline int getObstacleCount() const { return m_params.maxObstacles; }
inline const dtTileCacheObstacle* getObstacle(const int i) const { return &m_obstacles[i]; }
inline const dtOffMeshConnection* getOffMeshConnection(const int i) const { return &m_offMeshConnections[i]; }
const dtTileCacheObstacle* getObstacleByRef(dtObstacleRef ref);
dtOffMeshConnection* getOffMeshConnectionByRef(dtOffMeshConnectionRef ref);
dtObstacleRef getObstacleRef(const dtTileCacheObstacle* obmin) const;
dtOffMeshConnectionRef getOffMeshRef(const dtOffMeshConnection* con) const;
dtStatus init(const dtTileCacheParams* params,
struct dtTileCacheAlloc* talloc,
@ -147,13 +156,16 @@ public:
// Cylinder obstacle.
dtStatus addObstacle(const float* pos, const float radius, const float height, const int area, dtObstacleRef* result);
dtStatus addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned short flags, const bool bBiDirectional, dtOffMeshConnectionRef* result);
// Aabb obstacle.
dtStatus addBoxObstacle(const float* bmin, const float* bmax, const int area, dtObstacleRef* result);
dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);
// Box obstacle: can be rotated in Y.
dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, const int area, dtObstacleRef* result);
dtStatus addBoxObstacle(const float* center, const float* halfExtents, const float yRadians, dtObstacleRef* result);
dtStatus removeObstacle(const dtObstacleRef ref);
dtStatus removeOffMeshConnection(const dtOffMeshConnectionRef ref);
dtStatus queryTiles(const float* bmin, const float* bmax,
dtCompressedTileRef* results, int* resultCount, const int maxResults) const;
@ -166,9 +178,9 @@ public:
/// otherwise another call will continue processing obstacle requests and tile rebuilds.
dtStatus update(const float dt, class dtNavMesh* navmesh, bool* upToDate = 0);
dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh, bool bMarkOffMeshDirty);
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh, bool bMarkOffMeshDirty);
dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh);
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh);
void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;
@ -200,6 +212,12 @@ public:
{
return ((dtObstacleRef)salt << 16) | (dtObstacleRef)it;
}
/// Encodes an obstacle id.
inline dtOffMeshConnectionRef encodeOffMeshId(unsigned int salt, unsigned int it) const
{
return ((dtOffMeshConnectionRef)salt << 16) | (dtOffMeshConnectionRef)it;
}
/// Decodes an obstacle salt.
inline unsigned int decodeObstacleIdSalt(dtObstacleRef ref) const
@ -207,6 +225,12 @@ public:
const dtObstacleRef saltMask = ((dtObstacleRef)1<<16)-1;
return (unsigned int)((ref >> 16) & saltMask);
}
inline unsigned int decodeOffMeshIdSalt(dtOffMeshConnectionRef ref) const
{
const dtOffMeshConnectionRef saltMask = ((dtOffMeshConnectionRef)1 << 16) - 1;
return (unsigned int)((ref >> 16) & saltMask);
}
/// Decodes an obstacle id.
inline unsigned int decodeObstacleIdObstacle(dtObstacleRef ref) const
@ -214,6 +238,12 @@ public:
const dtObstacleRef tileMask = ((dtObstacleRef)1<<16)-1;
return (unsigned int)(ref & tileMask);
}
inline unsigned int decodeOffMeshIdCon(dtOffMeshConnectionRef ref) const
{
const dtOffMeshConnectionRef tileMask = ((dtOffMeshConnectionRef)1 << 16) - 1;
return (unsigned int)(ref & tileMask);
}
private:
@ -226,12 +256,25 @@ private:
REQUEST_ADD,
REQUEST_REMOVE,
};
enum OffMeshRequestAction
{
REQUEST_OFFMESH_ADD,
REQUEST_OFFMESH_REFRESH,
REQUEST_OFFMESH_REMOVE
};
struct ObstacleRequest
{
int action;
dtObstacleRef ref;
};
struct OffMeshRequest
{
int action;
dtOffMeshConnectionRef ref;
};
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
int m_tileLutMask; ///< Tile hash lookup mask.
@ -251,10 +294,16 @@ private:
dtTileCacheObstacle* m_obstacles;
dtTileCacheObstacle* m_nextFreeObstacle;
dtOffMeshConnection* m_offMeshConnections;
dtOffMeshConnection* m_nextFreeOffMeshConnection;
static const int MAX_REQUESTS = 64;
static const int MAX_REQUESTS = 256;
ObstacleRequest m_reqs[MAX_REQUESTS];
int m_nreqs;
OffMeshRequest m_OffMeshReqs[MAX_REQUESTS];
int m_nOffMeshReqs;
static const int MAX_UPDATE = 64;
dtCompressedTileRef m_update[MAX_UPDATE];

View file

@ -35,6 +35,7 @@ static const unsigned char DT_TILECACHE_TEAM2STRUCTURE_AREA = 6;
static const unsigned char DT_TILECACHE_WELD_AREA = 7;
static const unsigned char DT_TILECACHE_DOOR_AREA = 8;
static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63;
static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff;
struct dtTileCacheLayerHeader

View file

@ -171,7 +171,7 @@ typedef enum _STRUCTUREPURPOSE
typedef struct _OFF_MESH_CONN
{
int MeshConnectionIndex = -1;
unsigned int ConnectionRefs[2];
unsigned short ConnectionFlags = 0;
Vector FromLocation = g_vecZero;
Vector ToLocation = g_vecZero;

View file

@ -72,23 +72,7 @@ struct TileCacheSetExportHeader
int NumOffMeshCons;
int OffMeshConVertsOffset;
int OffMeshConVertsLength;
int OffMeshConRadsOffset;
int OffMeshConRadsLength;
int OffMeshConDirsOffset;
int OffMeshConDirsLength;
int OffMeshConAreasOffset;
int OffMeshConAreasLength;
int OffMeshConFlagsOffset;
int OffMeshConFlagsLength;
int OffMeshConUserIDsOffset;
int OffMeshConUserIDsLength;
int OffMeshConsOffset;
};
struct TileCacheTileHeader
@ -187,21 +171,6 @@ struct LinearAllocator : public dtTileCacheAlloc
struct MeshProcess : public dtTileCacheMeshProcess
{
int NumOffMeshConns = 0;
float OffMeshVerts[MAX_OFFMESH_CONNS * 6];
float OffMeshRads[MAX_OFFMESH_CONNS];
unsigned char OffMeshDirs[MAX_OFFMESH_CONNS];
unsigned char OffMeshAreas[MAX_OFFMESH_CONNS];
unsigned short OffMeshFlags[MAX_OFFMESH_CONNS];
unsigned int OffMeshIDs[MAX_OFFMESH_CONNS];
bool bNavDataDirty = false;
OffMeshConnectionDef ConnectionDefinitions[MAX_OFFMESH_CONNS];
vector<OffMeshConnectionDef> OffMeshConnections;
unsigned int NextUserID = 0;
inline MeshProcess()
{}
@ -211,197 +180,6 @@ struct MeshProcess : public dtTileCacheMeshProcess
}
void AddOffMeshConnectionDef(Vector Start, Vector End, unsigned char area, unsigned short flag, bool bBiDirectional, AvHAIOffMeshConnection* ConnectionRef)
{
OffMeshConnectionDef NewDefinition;
NewDefinition.Area = area;
NewDefinition.bBiDir = bBiDirectional;
NewDefinition.spos[0] = Start.x;
NewDefinition.spos[1] = Start.z;
NewDefinition.spos[2] = -Start.y;
NewDefinition.epos[0] = End.x;
NewDefinition.epos[1] = End.z;
NewDefinition.epos[2] = -End.y;
NewDefinition.Flag = flag;
NewDefinition.Rad = 18.0f;
NewDefinition.UserID = NextUserID;
NewDefinition.bDirty = true;
if (ConnectionRef)
{
ConnectionRef->MeshConnectionIndex = NextUserID;
}
NextUserID++;
OffMeshConnections.push_back(NewDefinition);
bNavDataDirty = true;
};
void RemoveOffMeshConnectionDef(int UserID)
{
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
{
if (it->UserID == UserID)
{
it->bDirty = true;
it->bPendingDelete = true;
}
}
bNavDataDirty = true;
}
void PurgeDeletedConnections()
{
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end();)
{
if (it->bPendingDelete)
{
it = OffMeshConnections.erase(it);
}
else
{
it++;
}
}
}
void UpdateOffMeshData()
{
int CurrIndex = 0;
int VertIndex = 0;
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
{
if (!it->bPendingDelete)
{
OffMeshVerts[VertIndex++] = it->spos[0];
OffMeshVerts[VertIndex++] = it->spos[1];
OffMeshVerts[VertIndex++] = it->spos[2];
OffMeshVerts[VertIndex++] = it->epos[0];
OffMeshVerts[VertIndex++] = it->epos[1];
OffMeshVerts[VertIndex++] = it->epos[2];
OffMeshRads[CurrIndex] = it->Rad;
OffMeshDirs[CurrIndex] = it->bBiDir;
OffMeshAreas[CurrIndex] = it->Area;
OffMeshFlags[CurrIndex] = it->Flag;
OffMeshIDs[CurrIndex] = it->UserID;
CurrIndex++;
}
}
NumOffMeshConns = CurrIndex;
bNavDataDirty = false;
}
void PopulateOffMeshConnectionVector()
{
OffMeshConnections.clear();
for (int i = 0; i < NumOffMeshConns; i++)
{
float* v = &OffMeshVerts[i*3*2];
Vector StartPos = Vector(v[0], -v[2], v[1]);
Vector EndPos = Vector(v[3], -v[5], v[4]);
AddOffMeshConnectionDef(StartPos, EndPos, OffMeshAreas[i], OffMeshFlags[i], OffMeshDirs[i], nullptr);
}
bNavDataDirty = false;
}
void GetOffMeshConnectionPoints(int UserID, Vector& OutStartLoc, Vector& OutEndLoc)
{
OutStartLoc = ZERO_VECTOR;
OutEndLoc = ZERO_VECTOR;
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
{
if (it->UserID == UserID)
{
OutStartLoc.x = it->spos[0];
OutStartLoc.y = -it->spos[2];
OutStartLoc.z = it->spos[1];
OutEndLoc.x = it->epos[0];
OutEndLoc.y = -it->epos[2];
OutEndLoc.z = it->epos[1];
return;
}
}
}
vector<OffMeshConnectionDef> GetOffMeshConnections()
{
return OffMeshConnections;
}
void MarkOffMeshConnectionsClean()
{
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
{
it->bDirty = false;
}
}
vector<OffMeshConnectionDef*> GetDirtyOffMeshConnections()
{
vector<OffMeshConnectionDef*> Result;
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
{
if (it->bDirty || it->bPendingDelete)
{
Result.push_back(&(*it));
}
}
return Result;
}
void DrawAllConnections(float DrawTime)
{
Vector StartLine = ZERO_VECTOR;
Vector EndLine = ZERO_VECTOR;
for (auto it = OffMeshConnections.begin(); it != OffMeshConnections.end(); it++)
{
Vector StartLine = Vector(it->spos[0], -it->spos[2], it->spos[1]);
Vector EndLine = Vector(it->epos[0], -it->epos[2], it->epos[1]);
switch (it->Flag)
{
case SAMPLE_POLYFLAGS_WALK:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 255);
break;
case SAMPLE_POLYFLAGS_JUMP:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 0);
break;
case SAMPLE_POLYFLAGS_WALLCLIMB:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 0);
break;
case SAMPLE_POLYFLAGS_FALL:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 0, 0);
break;
case SAMPLE_POLYFLAGS_LADDER:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 0, 255);
break;
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 128, 128);
break;
default:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 255);
break;
}
}
}
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned short* polyFlags)
{
@ -455,32 +233,48 @@ struct MeshProcess : public dtTileCacheMeshProcess
}
}
if (bNavDataDirty)
{
UpdateOffMeshData();
}
params->offMeshConAreas = OffMeshAreas;
params->offMeshConCount = NumOffMeshConns;
params->offMeshConDir = OffMeshDirs;
params->offMeshConFlags = OffMeshFlags;
params->offMeshConRad = OffMeshRads;
params->offMeshConUserID = OffMeshIDs;
params->offMeshConVerts = OffMeshVerts;
}
};
void AIDEBUG_DrawOffMeshConnections(float DrawTime)
{
if (NavMeshes[0].tileCache)
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[0].tileCache->getMeshProcess();
if (m_tmproc)
for (int i = 0; i < NavMeshes[REGULAR_NAV_MESH].tileCache->getOffMeshCount(); i++)
{
m_tmproc->DrawAllConnections(DrawTime);
const dtOffMeshConnection* con = NavMeshes[REGULAR_NAV_MESH].tileCache->getOffMeshConnection(i);
if (con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING) { continue; }
Vector StartLine = Vector(con->pos[0], -con->pos[2], con->pos[1]);
Vector EndLine = Vector(con->pos[3], -con->pos[5], con->pos[4]);
switch (con->flags)
{
case SAMPLE_POLYFLAGS_WALK:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 255);
break;
case SAMPLE_POLYFLAGS_JUMP:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 255, 0);
break;
case SAMPLE_POLYFLAGS_WALLCLIMB:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 0);
break;
case SAMPLE_POLYFLAGS_FALL:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 0, 0);
break;
case SAMPLE_POLYFLAGS_LADDER:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 0, 255);
break;
case SAMPLE_POLYFLAGS_TEAM1PHASEGATE:
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 128, 128);
break;
default:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 255);
break;
}
}
}
}
@ -494,40 +288,7 @@ void UTIL_UpdateTileCache()
{
NavMeshes[i].tileCache->update(0.0f, NavMeshes[i].navMesh, &bUpToDate);
}
}
if (bUpToDate)
{
if (NavMeshes[0].tileCache)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[0].tileCache->getMeshProcess();
if (m_tmproc)
{
vector<OffMeshConnectionDef*> DirtyOffMeshConnections = m_tmproc->GetDirtyOffMeshConnections();
Vector StartPos, EndPos;
if (DirtyOffMeshConnections.size() > 0)
{
m_tmproc->bNavDataDirty = true;
}
for (auto it = DirtyOffMeshConnections.begin(); it != DirtyOffMeshConnections.end(); it++)
{
m_tmproc->GetOffMeshConnectionPoints((*it)->UserID, StartPos, EndPos);
UTIL_OnOffMeshConnectionModified(StartPos, EndPos);
(*it)->bDirty = false;
}
m_tmproc->PurgeDeletedConnections();
}
}
}
}
}
Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall)
@ -923,35 +684,12 @@ bool LoadNavMesh(const char* mapname)
return false;
}
int CurrFilePos = ftell(savedFile);
LinearAllocator* m_talloc = new LinearAllocator(32000);
FastLZCompressor* m_tcomp = new FastLZCompressor;
MeshProcess* m_tmproc = new MeshProcess;
m_tmproc->NumOffMeshConns = header.NumOffMeshCons;
fseek(savedFile, header.OffMeshConAreasOffset, SEEK_SET);
size_t ReadResult = fread(m_tmproc->OffMeshAreas, header.OffMeshConAreasLength, 1, savedFile);
fseek(savedFile, header.OffMeshConDirsOffset, SEEK_SET);
ReadResult = fread(m_tmproc->OffMeshDirs, header.OffMeshConDirsLength, 1, savedFile);
fseek(savedFile, header.OffMeshConFlagsOffset, SEEK_SET);
ReadResult = fread(m_tmproc->OffMeshFlags, header.OffMeshConFlagsLength, 1, savedFile);
fseek(savedFile, header.OffMeshConRadsOffset, SEEK_SET);
ReadResult = fread(m_tmproc->OffMeshRads, header.OffMeshConRadsLength, 1, savedFile);
fseek(savedFile, header.OffMeshConUserIDsOffset, SEEK_SET);
ReadResult = fread(m_tmproc->OffMeshIDs, header.OffMeshConUserIDsLength, 1, savedFile);
fseek(savedFile, header.OffMeshConVertsOffset, SEEK_SET);
ReadResult = fread(m_tmproc->OffMeshVerts, header.OffMeshConVertsLength, 1, savedFile);
m_tmproc->PopulateOffMeshConnectionVector();
// TODO: Need to pass all off mesh connection verts, areas, flags etc as arrays to m_tmproc. Needs to be exported from recast as such
status = NavMeshes[REGULAR_NAV_MESH].tileCache->init(&header.regularCacheParams, m_talloc, m_tcomp, m_tmproc);
@ -1020,7 +758,7 @@ bool LoadNavMesh(const char* mapname)
}
if (tile)
NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[REGULAR_NAV_MESH].navMesh, false);
NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[REGULAR_NAV_MESH].navMesh);
}
for (int i = 0; i < header.numOnosTiles; ++i)
@ -1059,7 +797,7 @@ bool LoadNavMesh(const char* mapname)
}
if (tile)
NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[ONOS_NAV_MESH].navMesh, false);
NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[ONOS_NAV_MESH].navMesh);
}
for (int i = 0; i < header.numBuildingTiles; ++i)
@ -1098,7 +836,21 @@ bool LoadNavMesh(const char* mapname)
}
if (tile)
NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[BUILDING_NAV_MESH].navMesh, false);
NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[BUILDING_NAV_MESH].navMesh);
}
fseek(savedFile, header.OffMeshConsOffset, SEEK_SET);
for (int i = 0; i < header.NumOffMeshCons; i++)
{
dtOffMeshConnection def;
fread(&def, sizeof(dtOffMeshConnection), 1, savedFile);
for (int ii = 0; ii < BUILDING_NAV_MESH; ii++)
{
NavMeshes[ii].tileCache->addOffMeshConnection(&def.pos[0], &def.pos[3], def.rad, def.area, def.flags, def.bBiDir, 0);
}
}
fclose(savedFile);
@ -1136,31 +888,21 @@ bool LoadNavMesh(const char* mapname)
return false;
}
UTIL_RefreshOffMeshConnections();
return true;
}
void UTIL_RefreshOffMeshConnections()
void OnOffMeshConnectionAdded(dtOffMeshConnection* NewConnection)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[0].tileCache->getMeshProcess();
if (!m_tmproc) { return; }
vector<OffMeshConnectionDef> Connections = m_tmproc->GetOffMeshConnections();
for (auto it = Connections.begin(); it != Connections.end(); it++)
for (int i = 0; i <= BUILDING_NAV_MESH; i++)
{
if (it->bPendingDelete) { continue; }
if (NavMeshes[i].navMesh && NavMeshes[i].tileCache)
{
NavMeshes[i].navMesh->LinkOffMeshConnectionToTiles(NewConnection);
Vector StartLoc, EndLoc;
m_tmproc->GetOffMeshConnectionPoints(it->UserID, StartLoc, EndLoc);
UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc);
dtCompressedTile* ModifiedTile = NavMeshes[i].tileCache->getTileAt(NewConnection->FromTileX, NewConnection->FromTileY, NewConnection->FromTileLayer);
NavMeshes[i].tileCache->buildNavMeshTile(NavMeshes[i].tileCache->getTileRef(ModifiedTile), NavMeshes[i].navMesh);
}
}
m_tmproc->MarkOffMeshConnectionsClean();
}
void UTIL_PopulateBaseNavProfiles()
@ -7347,7 +7089,7 @@ nav_door* UTIL_GetNavDoorByEdict(const edict_t* DoorEdict)
return nullptr;
}
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* NewConnectionDef)
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef)
{
Vector ConnStart, ConnEnd;
@ -7360,132 +7102,29 @@ void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char are
ConnEnd = (hit.flFraction < 1.0f) ? hit.vecEndPos : EndLoc;
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
{
NewConnectionDef->MeshConnectionIndex = -1;
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess();
ConnStart = Vector(ConnStart.x, ConnStart.z, -ConnStart.y);
ConnEnd = Vector(ConnEnd.x, ConnEnd.z, -ConnEnd.y);
if (m_tmproc)
{
m_tmproc->AddOffMeshConnectionDef(ConnStart, ConnEnd, area, flags, bBiDirectional, NewConnectionDef);
}
for (int i = 0; i < BUILDING_NAV_MESH; i++)
{
dtOffMeshConnectionRef ref = 0;
NavMeshes[i].tileCache->addOffMeshConnection(ConnStart, ConnEnd, 18.0f, area, flags, bBiDirectional, &ref);
RemoveConnectionDef->ConnectionRefs[i] = (unsigned int)ref;
}
}
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* NewConnectionDef)
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef)
{
if (NewConnectionDef->MeshConnectionIndex < 0) { return; }
Vector StartLoc, EndLoc;
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
for (int i = 0; i < BUILDING_NAV_MESH; i++)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess();
dtOffMeshConnectionRef ref = 0;
if (m_tmproc)
{
m_tmproc->RemoveOffMeshConnectionDef(NewConnectionDef->MeshConnectionIndex);
}
NavMeshes[i].tileCache->removeOffMeshConnection(RemoveConnectionDef->ConnectionRefs[i]);
RemoveConnectionDef->ConnectionRefs[i] = 0;
}
NewConnectionDef->MeshConnectionIndex = -1;
}
void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc)
{
float ext[3] = { 50.0f, 50.0f, 50.0f };
float spos[3] = { StartLoc.x, StartLoc.z, -StartLoc.y };
float epos[3] = { EndLoc.x, EndLoc.z, -EndLoc.y };
float searchsposMin[3];
float searchsposMax[3];
float searcheposMin[3];
float searcheposMax[3];
dtVsub(searchsposMin, spos, ext);
dtVadd(searchsposMax, spos, ext);
dtVsub(searcheposMin, epos, ext);
dtVadd(searcheposMax, epos, ext);
int NumTiles = 0;
dtCompressedTileRef AffectedTiles[DT_MAX_TOUCHED_TILES];
if (NavMeshes[REGULAR_NAV_MESH].tileCache && NavMeshes[REGULAR_NAV_MESH].navMesh)
{
NumTiles = 0;
NavMeshes[REGULAR_NAV_MESH].tileCache->queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = NavMeshes[REGULAR_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]);
NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[REGULAR_NAV_MESH].navMesh, false);
}
NavMeshes[REGULAR_NAV_MESH].tileCache->queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = NavMeshes[REGULAR_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]);
NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[REGULAR_NAV_MESH].navMesh, false);
}
}
if (NavMeshes[ONOS_NAV_MESH].tileCache && NavMeshes[ONOS_NAV_MESH].navMesh)
{
NumTiles = 0;
NavMeshes[ONOS_NAV_MESH].tileCache->queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = NavMeshes[ONOS_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]);
NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[ONOS_NAV_MESH].navMesh, false);
}
NavMeshes[ONOS_NAV_MESH].tileCache->queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = NavMeshes[ONOS_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]);
NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[ONOS_NAV_MESH].navMesh, false);
}
}
if (NavMeshes[BUILDING_NAV_MESH].tileCache && NavMeshes[BUILDING_NAV_MESH].navMesh)
{
NumTiles = 0;
NavMeshes[BUILDING_NAV_MESH].tileCache->queryTiles(searcheposMin, searcheposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = NavMeshes[BUILDING_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]);
NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[BUILDING_NAV_MESH].navMesh, false);
}
NavMeshes[BUILDING_NAV_MESH].tileCache->queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = NavMeshes[BUILDING_NAV_MESH].tileCache->getTileByRef(AffectedTiles[i]);
NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, NavMeshes[BUILDING_NAV_MESH].navMesh, false);
}
}
bNavMeshModified = true;
}
const nav_profile GetBaseNavProfile(const int index)

View file

@ -292,9 +292,8 @@ void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef);
void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs);
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* NewConnectionDef);
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* NewConnectionDef);
void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc);
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef);
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef);
/*
@ -465,7 +464,7 @@ Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDis
void UTIL_PopulateBaseNavProfiles();
void UTIL_RefreshOffMeshConnections();
void OnOffMeshConnectionAdded(dtOffMeshConnection* NewConnection);
#endif // BOT_NAVIGATION_H

View file

@ -1567,7 +1567,7 @@ void AITAC_OnStructureCompleted(AvHAIBuildableStructure* NewStructure)
NewConnection.ToLocation = OtherPhaseGate->Location;
NewConnection.ConnectionFlags = NewFlag;
NewConnection.TargetObject = OtherPhaseGate->edict;
NewConnection.MeshConnectionIndex = -1;
memset(&NewConnection.ConnectionRefs[0], 0, sizeof(NewConnection.ConnectionRefs));
UTIL_AddOffMeshConnection(NewStructure->Location, OtherPhaseGate->Location, SAMPLE_POLYAREA_GROUND, NewFlag, true, &NewConnection);

View file

@ -1420,6 +1420,36 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
else if (FStrEq(pcmd, "refreshoffmesh"))
{
theSuccess = true;
}
else if (FStrEq(pcmd, "evolvelerk"))
{
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAllAIPlayers();
for (auto it = AIPlayers.begin(); it != AIPlayers.end(); it++)
{
AvHAIPlayer* thisBot = (*it);
AITASK_SetEvolveTask(thisBot, &thisBot->PrimaryBotTask, thisBot->Edict->v.origin, ALIEN_LIFEFORM_THREE, true);
}
theSuccess = true;
}
else if (FStrEq(pcmd, "evolvegorge"))
{
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAllAIPlayers();
for (auto it = AIPlayers.begin(); it != AIPlayers.end(); it++)
{
AvHAIPlayer* thisBot = (*it);
AITASK_SetEvolveTask(thisBot, &thisBot->PrimaryBotTask, thisBot->Edict->v.origin, ALIEN_LIFEFORM_TWO, true);
}
theSuccess = true;
}
else if (FStrEq(pcmd, "tracedoor"))
{
Vector TraceStart = GetPlayerEyePosition(theAvHPlayer->edict()); // origin + pev->view_ofs
@ -1435,6 +1465,14 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
if (Door)
{
for (auto it = Door->TriggerEnts.begin(); it != Door->TriggerEnts.end(); it++)
{
const char* ButtonTarget = STRING(it->Edict->v.target);
const char* DoorTargetName = STRING(Door->DoorEdict->v.targetname);
bool bThing = true;
}
DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(theAvHPlayer->pev->origin, Door, nullptr);
if (Trigger)
@ -1448,11 +1486,11 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
}
else if (FStrEq(pcmd, "cometome"))
{
for (int i = 0; i < AIMGR_GetNumAIPlayers(); i++)
{
AvHAIPlayer* thisBot = AIMGR_GetAIPlayerAtIndex(i);
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAllAIPlayers();
if (thisBot)
for (auto it = AIPlayers.begin(); it != AIPlayers.end(); it++)
{
AvHAIPlayer* thisBot = (*it);
{
AITASK_SetMoveTask(thisBot, &thisBot->PrimaryBotTask, theAvHPlayer->pev->origin, true);
}
@ -1460,15 +1498,25 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
else if (FStrEq(pcmd, "aiteamstarts"))
else if (FStrEq(pcmd, "testpointadjust"))
{
Vector TeamOneLoc = AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber());
Vector NewLoc = AdjustPointForPathfinding(theAvHPlayer->pev->origin);
UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, TeamOneLoc, 10.0f, 0, 0, 255);
if (!vIsZero(NewLoc))
{
UTIL_DrawLine(INDEXENT(1), theAvHPlayer->pev->origin, NewLoc, 10.0f);
}
Vector TeamTwoLoc = AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber());
theSuccess = true;
}
else if (FStrEq(pcmd, "testflightpath"))
{
vector<bot_path_node> path;
path.clear();
UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, TeamTwoLoc, 10.0f, 0, 128, 0);
FindFlightPathToPoint(GetBaseNavProfile(ALL_NAV_PROFILE), AITAC_GetTeamStartingLocation(TEAM_ONE) + Vector(0.0f, 0.0f, 50.0f), theAvHPlayer->pev->origin, path, 100.0f);
AIDEBUG_DrawPath(path, 20.0f);
theSuccess = true;
}