Implemented long-range off-mesh connections and dynamic off-mesh connections

This commit is contained in:
RGreenlees 2023-10-23 19:57:45 +01:00 committed by pierow
parent 53b51d68a7
commit 46efcdaeda
9 changed files with 696 additions and 132 deletions

View file

@ -284,7 +284,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);
return addTile(data, dataSize, flags, 0, 0, false);
}
/// @par
@ -979,7 +979,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)
dtTileRef lastRef, dtTileRef* result, bool bMarkOffMeshDirty)
{
// Make sure the data is in right format.
dtMeshHeader* header = (dtMeshHeader*)data;
@ -1057,7 +1057,8 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
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);
unsigned char* d = data + headerSize;
tile->verts = dtGetThenAdvanceBufferPointer<float>(d, vertsSize);
tile->polys = dtGetThenAdvanceBufferPointer<dtPoly>(d, polysSize);
@ -1067,6 +1068,8 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
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);
// If there are no items in the bvtree, reset the tree pointer.
if (!bvtreeSize)
@ -1088,7 +1091,6 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
// Base off-mesh connections to their starting polygons and connect connections inside the tile.
baseOffMeshLinks(tile);
connectExtOffMeshLinks(tile, tile, -1);
// Create connections with neighbour tiles.
static const int MAX_NEIS = 32;
@ -1104,8 +1106,6 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
connectExtLinks(tile, neis[j], -1);
connectExtLinks(neis[j], tile, -1);
connectExtOffMeshLinks(tile, neis[j], -1);
connectExtOffMeshLinks(neis[j], tile, -1);
}
// Connect with neighbour tiles.
@ -1116,10 +1116,19 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
{
connectExtLinks(tile, neis[j], i);
connectExtLinks(neis[j], tile, dtOppositeTile(i));
connectExtOffMeshLinks(tile, neis[j], i);
connectExtOffMeshLinks(neis[j], tile, dtOppositeTile(i));
}
}
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)
@ -1128,6 +1137,96 @@ dtStatus dtNavMesh::addTile(unsigned char* data, int dataSize, int flags,
return DT_SUCCESS;
}
void dtNavMesh::GlobalOffMeshLinks(dtMeshTile* target)
{
if (!target)
return;
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 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)
{
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 dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer) const
{
// Find tile based on hash.

View file

@ -289,40 +289,41 @@ 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)
{
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->detailVertsCount; ++i)
{
const float h = params->detailVerts[i*3+1];
hmin = dtMin(hmin,h);
hmax = dtMax(hmax,h);
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 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 = dtMin(hmin, h);
hmax = dtMax(hmax, h);
}
}
hmin -= params->walkableClimb;
@ -335,55 +336,62 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
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);
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);
// Zero out off-mesh start positions which are not even potentially touching the mesh.
if (offMeshConClass[i*2+0] == 0xff)
if (offMeshConClass[i * 2 + 0] == 0xff)
{
if (p0[1] < bmin[1] || p0[1] > bmax[1])
offMeshConClass[i*2+0] = 0;
offMeshConClass[i * 2 + 0] = 0;
}
// Cound how many links should be allocated for off-mesh connections.
if (offMeshConClass[i*2+0] == 0xff)
if (offMeshConClass[i * 2 + 0] == 0xff)
offMeshConLinkCount++;
if (offMeshConClass[i*2+1] == 0xff)
if (offMeshConClass[i * 2 + 1] == 0xff)
offMeshConLinkCount++;
if (offMeshConClass[i*2+0] == 0xff)
if (offMeshConClass[i * 2 + 0] == 0xff)
{
storedOffMeshConCount++;
}
else if (offMeshConClass[i * 2 + 1] == 0xff)
{
receiveOffMeshConCount++;
}
}
}
// 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;
@ -393,8 +401,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)
{
@ -412,40 +420,41 @@ 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 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 dataSize = headerSize + vertsSize + polysSize + linksSize +
detailMeshesSize + detailVertsSize + detailTrisSize +
bvTreeSize + offMeshConsSize;
unsigned char* data = (unsigned char*)dtAlloc(sizeof(unsigned char)*dataSize, DT_ALLOC_PERM);
detailMeshesSize + detailVertsSize + detailTrisSize +
bvTreeSize + offMeshConsSize + receiveOffMeshConsSize;
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);
@ -457,8 +466,9 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
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);
// Store header
header->magic = DT_NAVMESH_MAGIC;
header->version = DT_NAVMESH_VERSION;
@ -480,17 +490,18 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
header->walkableRadius = params->walkableRadius;
header->walkableClimb = params->walkableClimb;
header->offMeshConCount = storedOffMeshConCount;
header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0;
header->receivingOffMeshConCount = receiveOffMeshConCount;
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;
@ -500,16 +511,16 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
for (int i = 0; i < params->offMeshConCount; ++i)
{
// Only store connections which start from this tile.
if (offMeshConClass[i*2+0] == 0xff)
if (offMeshConClass[i * 2 + 0] == 0xff)
{
const float* linkv = &params->offMeshConVerts[i*2*3];
float* v = &navVerts[(offMeshVertsBase + n*2)*3];
const float* linkv = &params->offMeshConVerts[i * 2 * 3];
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;
@ -524,10 +535,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-
@ -542,24 +553,24 @@ 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)
{
// Only store connections which start from this tile.
if (offMeshConClass[i*2+0] == 0xff)
if (offMeshConClass[i * 2 + 0] == 0xff)
{
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->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->setType(DT_POLYTYPE_OFFMESH_CONNECTION);
@ -576,22 +587,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
{
@ -604,18 +615,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++;
}
}
@ -624,36 +635,63 @@ 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)
{
// Only store connections which start from this tile.
if (offMeshConClass[i*2+0] == 0xff)
if (offMeshConClass[i * 2 + 0] == 0xff)
{
dtOffMeshConnection* con = &offMeshCons[n];
con->poly = (unsigned short)(offMeshPolyBase + n);
// Copy connection end-points.
const float* endPts = &params->offMeshConVerts[i*2*3];
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];
con->side = offMeshConClass[i * 2 + 1];
if (params->offMeshConUserID)
con->userId = params->offMeshConUserID[i];
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);
}
dtFree(offMeshConClass);
*outData = data;
*outDataSize = dataSize;
return true;
}
@ -686,6 +724,7 @@ 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);
@ -728,6 +767,7 @@ 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);
@ -740,6 +780,7 @@ 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)
@ -797,6 +838,16 @@ 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

@ -584,7 +584,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
{
// Build mesh
const dtCompressedTileRef ref = m_update[0];
status = buildNavMeshTile(ref, navmesh);
status = buildNavMeshTile(ref, navmesh, true);
m_nupdate--;
if (m_nupdate > 0)
memmove(m_update, m_update+1, m_nupdate*sizeof(dtCompressedTileRef));
@ -628,6 +628,56 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
}
}
}
if (navmesh->GetNumPendingOffMeshConnections() > 0)
{
for (int i = 0; i < navmesh->GetNumPendingOffMeshConnections(); i++)
{
int NumTiles = 0;
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++)
{
const dtCompressedTile* Tile = getTileByRef(AffectedTiles[i]);
buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, navmesh, false);
}
queryTiles(searchsposMin, searchsposMax, AffectedTiles, &NumTiles, DT_MAX_TOUCHED_TILES);
for (int i = 0; i < NumTiles; i++)
{
const dtCompressedTile* Tile = getTileByRef(AffectedTiles[i]);
buildNavMeshTilesAt(Tile->header->tx, Tile->header->ty, navmesh, false);
}
}
navmesh->ClearPendingOffMeshConnections();
}
if (upToDate)
*upToDate = m_nupdate == 0 && m_nreqs == 0;
@ -636,7 +686,7 @@ dtStatus dtTileCache::update(const float /*dt*/, dtNavMesh* navmesh,
}
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh)
dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh* navmesh, bool bMarkOffMeshDirty)
{
const int MAX_TILES = 32;
dtCompressedTileRef tiles[MAX_TILES];
@ -644,7 +694,7 @@ dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh*
for (int i = 0; i < ntiles; ++i)
{
dtStatus status = buildNavMeshTile(tiles[i], navmesh);
dtStatus status = buildNavMeshTile(tiles[i], navmesh, bMarkOffMeshDirty);
if (dtStatusFailed(status))
return status;
}
@ -652,7 +702,7 @@ dtStatus dtTileCache::buildNavMeshTilesAt(const int tx, const int ty, dtNavMesh*
return DT_SUCCESS;
}
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh)
dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh* navmesh, bool bMarkOffMeshDirty)
{
dtAssert(m_talloc);
dtAssert(m_tcomp);
@ -768,7 +818,7 @@ dtStatus dtTileCache::buildNavMeshTile(const dtCompressedTileRef ref, dtNavMesh*
if (navData)
{
// Let the navmesh own the data.
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0);
status = navmesh->addTile(navData,navDataSize,DT_TILE_FREE_DATA,0,0, bMarkOffMeshDirty);
if (dtStatusFailed(status))
{
dtFree(navData);

View file

@ -265,6 +265,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,6 +302,7 @@ struct dtMeshTile
dtBVNode* bvTree;
dtOffMeshConnection* offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
dtOffMeshConnection* receivingOffMeshCons; ///< 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.
@ -367,7 +369,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);
dtStatus addTile(unsigned char* data, int dataSize, int flags, dtTileRef lastRef, dtTileRef* result, bool bMarkOffMeshDirty);
/// Removes the specified tile from the navigation mesh.
/// @param[in] ref The reference of the tile to remove.
@ -465,6 +467,10 @@ 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]; }
/// @}
@ -514,6 +520,8 @@ 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);
/// @}
@ -663,6 +671,9 @@ private:
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

@ -166,9 +166,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);
dtStatus buildNavMeshTilesAt(const int tx, const int ty, class dtNavMesh* navmesh, bool bMarkOffMeshDirty);
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh);
dtStatus buildNavMeshTile(const dtCompressedTileRef ref, class dtNavMesh* navmesh, bool bMarkOffMeshDirty);
void calcTightTileBounds(const struct dtTileCacheLayerHeader* header, float* bmin, float* bmax) const;

View file

@ -27,12 +27,13 @@ static const int DT_TILECACHE_VERSION = 1;
static const unsigned char DT_TILECACHE_NULL_AREA = 0;
static const unsigned char DT_TILECACHE_CROUCH_AREA = 1;
static const unsigned char DT_TILECACHE_BLOCKED_AREA = 3;
static const unsigned char DT_TILECACHE_CLIMBABLE_AREA = 4;
static const unsigned char DT_TILECACHE_LADDER_AREA = 5;
static const unsigned char DT_TILECACHE_MSTRUCTURE_AREA = 12;
static const unsigned char DT_TILECACHE_ASTRUCTURE_AREA = 13;
static const unsigned char DT_TILECACHE_WELD_AREA = 15;
static const unsigned char DT_TILECACHE_BLOCKED_AREA = 2;
static const unsigned char DT_TILECACHE_WALLCLIMB_AREA = 3;
static const unsigned char DT_TILECACHE_LADDER_AREA = 4;
static const unsigned char DT_TILECACHE_TEAM1STRUCTURE_AREA = 5;
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;

View file

@ -272,6 +272,9 @@ void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot)
case SAMPLE_POLYFLAGS_BLOCKED:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 128, 128, 128);
break;
case SAMPLE_POLYFLAGS_PHASEGATE:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 255, 128, 128);
break;
default:
UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc);
break;

View file

@ -236,6 +236,62 @@ struct MeshProcess : public dtTileCacheMeshProcess
}
}
void GetOffMeshConnectionPoints(int Index, Vector& OutStartLoc, Vector& OutEndLoc)
{
OutStartLoc = ZERO_VECTOR;
OutEndLoc = ZERO_VECTOR;
if (Index > -1 && Index < MAX_OFFMESH_CONNS)
{
float* src = &OffMeshVerts[Index * 3 * 2];
OutStartLoc.x = src[0];
OutStartLoc.y = -src[2];
OutStartLoc.z = src[1];
OutEndLoc.x = src[3];
OutEndLoc.y = -src[5];
OutEndLoc.z = src[4];
}
}
void DrawAllConnections(float DrawTime)
{
Vector StartLine = ZERO_VECTOR;
Vector EndLine = ZERO_VECTOR;
for (int i = 0; i < NumOffMeshConns; i++)
{
Vector StartLine = Vector(OffMeshVerts[i * 6], -OffMeshVerts[(i * 6) + 2], OffMeshVerts[(i * 6) + 1]);
Vector EndLine = Vector(OffMeshVerts[(i * 6) + 3], -OffMeshVerts[(i * 6) + 5], OffMeshVerts[(i * 6) + 4]);
switch (OffMeshFlags[i])
{
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_PHASEGATE:
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)
{
@ -300,6 +356,19 @@ struct MeshProcess : public dtTileCacheMeshProcess
}
};
void AIDEBUG_DrawOffMeshConnections(float DrawTime)
{
if (NavMeshes[0].tileCache)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[0].tileCache->getMeshProcess();
if (m_tmproc)
{
m_tmproc->DrawAllConnections(DrawTime);
}
}
}
void UTIL_UpdateTileCache()
{
for (int i = 0; i < MAX_NAV_MESHES; i++)
@ -799,7 +868,7 @@ bool LoadNavMesh(const char* mapname)
}
if (tile)
NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[REGULAR_NAV_MESH].navMesh);
NavMeshes[REGULAR_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[REGULAR_NAV_MESH].navMesh, false);
}
for (int i = 0; i < header.numOnosTiles; ++i)
@ -838,7 +907,7 @@ bool LoadNavMesh(const char* mapname)
}
if (tile)
NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[ONOS_NAV_MESH].navMesh);
NavMeshes[ONOS_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[ONOS_NAV_MESH].navMesh, false);
}
for (int i = 0; i < header.numBuildingTiles; ++i)
@ -877,7 +946,7 @@ bool LoadNavMesh(const char* mapname)
}
if (tile)
NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[BUILDING_NAV_MESH].navMesh);
NavMeshes[BUILDING_NAV_MESH].tileCache->buildNavMeshTile(tile, NavMeshes[BUILDING_NAV_MESH].navMesh, false);
}
fclose(savedFile);
@ -949,7 +1018,6 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_PHASEGATE);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DUCKJUMP);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.addExcludeFlags(SAMPLE_POLYFLAGS_LADDER);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH;
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].bFlyingProfile = false;
@ -2882,8 +2950,17 @@ void NewMove(AvHAIPlayer* pBot)
}
break;
case SAMPLE_POLYFLAGS_LADDER:
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, NextArea);
break;
{
if (IsPlayerSkulk(pBot->Edict))
{
SkulkLadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, NextArea);
}
else
{
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, NextArea);
}
}
break;
case SAMPLE_POLYFLAGS_PHASEGATE:
PhaseGateMove(pBot, MoveFrom, MoveTo);
break;
@ -3081,8 +3158,6 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
edict_t* pEdict = pBot->Edict;
AvHPlayer* AIPlayer = pBot->Player;
const Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
bool bIsGoingUpLadder = (EndPoint.z > StartPoint.z);
@ -3346,6 +3421,131 @@ void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
}
}
void SkulkLadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea)
{
edict_t* pEdict = pBot->Edict;
AvHPlayer* AIPlayer = pBot->Player;
const Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
bool bIsGoingUpLadder = (EndPoint.z > StartPoint.z);
Vector LadderNormal = UTIL_GetNearestLadderNormal(pBot->Edict);
const Vector LadderRightNormal = UTIL_GetVectorNormal(UTIL_GetCrossProduct(LadderNormal, UP_VECTOR));
Vector ClimbRightNormal = LadderRightNormal;
if (bIsGoingUpLadder)
{
pBot->Button &= ~IN_DUCK;
ClimbRightNormal = -LadderRightNormal;
Vector HullTraceTo = EndPoint;
HullTraceTo.z = pBot->CollisionHullBottomLocation.z;
// We have reached our desired climb height and want to get off the ladder
if ((pBot->Edict->v.origin.z >= RequiredClimbHeight) && UTIL_QuickHullTrace(pEdict, pEdict->v.origin, Vector(EndPoint.x, EndPoint.y, pEdict->v.origin.z), head_hull))
{
// Move directly towards the desired get-off point, looking slightly up still
pBot->desiredMovementDir = vForward;
Vector LookLocation = EndPoint;
LookLocation.z = pBot->CurrentEyePosition.z + 64.0f;
BotMoveLookAt(pBot, LookLocation);
// If the get-off point is opposite the ladder, then jump to get to it
if (UTIL_GetDotProduct(pBot->CurrentLadderNormal, vForward) > 0.75f)
{
BotJump(pBot);
}
return;
}
else
{
// This is for cases where the ladder physically doesn't reach the desired get-off point and the bot kind of has to "jump" up off the ladder.
if (pBot->CollisionHullTopLocation.z >= UTIL_GetNearestLadderTopPoint(pEdict).z)
{
pBot->desiredMovementDir = vForward;
// We look up really far to get maximum launch
BotMoveLookAt(pBot, EndPoint + Vector(0.0f, 0.0f, 100.0f));
return;
}
// Still climbing the ladder. Look up, and move left/right on the ladder to avoid any blockages
Vector StartLeftTrace = pBot->CollisionHullTopLocation - (ClimbRightNormal * GetPlayerRadius(pBot->Player));
Vector StartRightTrace = pBot->CollisionHullTopLocation + (ClimbRightNormal * GetPlayerRadius(pBot->Player));
bool bBlockedLeft = !UTIL_QuickTrace(pEdict, StartLeftTrace, StartLeftTrace + Vector(0.0f, 0.0f, 32.0f));
bool bBlockedRight = !UTIL_QuickTrace(pEdict, StartRightTrace, StartRightTrace + Vector(0.0f, 0.0f, 32.0f));
// Look up at the top of the ladder
// If we are blocked going up the ladder, face the ladder and slide left/right to avoid blockage
if (bBlockedLeft && !bBlockedRight)
{
Vector LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f);
LookLocation.z = RequiredClimbHeight + 100.0f;
BotMoveLookAt(pBot, LookLocation);
pBot->desiredMovementDir = ClimbRightNormal;
return;
}
if (bBlockedRight && !bBlockedLeft)
{
Vector LookLocation = pBot->Edict->v.origin - (pBot->CurrentLadderNormal * 50.0f);
LookLocation.z = RequiredClimbHeight + 100.0f;
BotMoveLookAt(pBot, LookLocation);
pBot->desiredMovementDir = -ClimbRightNormal;
return;
}
Vector LookLocation = UTIL_GetNearestLadderTopPoint(pBot->Edict);
LookLocation.z = RequiredClimbHeight + 100.0f;
BotMoveLookAt(pBot, LookLocation);
pBot->desiredMovementDir = -UTIL_GetNearestLadderNormal(pBot->Edict);
}
}
else
{
// We're going down the ladder
Vector StartLeftTrace = pBot->CollisionHullBottomLocation - (LadderRightNormal * (GetPlayerRadius(pBot->Player) + 2.0f));
Vector StartRightTrace = pBot->CollisionHullBottomLocation + (LadderRightNormal * (GetPlayerRadius(pBot->Player) + 2.0f));
bool bBlockedLeft = !UTIL_QuickTrace(pEdict, StartLeftTrace, StartLeftTrace - Vector(0.0f, 0.0f, 32.0f));
bool bBlockedRight = !UTIL_QuickTrace(pEdict, StartRightTrace, StartRightTrace - Vector(0.0f, 0.0f, 32.0f));
if (bBlockedLeft)
{
pBot->desiredMovementDir = LadderRightNormal;
return;
}
if (bBlockedRight)
{
pBot->desiredMovementDir = -LadderRightNormal;
return;
}
pBot->desiredMovementDir = pBot->CurrentLadderNormal;
BotMoveLookAt(pBot, EndPoint);
}
}
void PhaseGateMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
{
DeployableSearchFilter PGFilter;
@ -4465,27 +4665,41 @@ bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
{
if (bReverseCourse)
{
LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveTo.z > MoveFrom.z)
if (IsPlayerSkulk(pBot->Edict))
{
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
SkulkLadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
}
else
{
LadderMove(pBot, MoveTo, MoveFrom, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveTo.z > MoveFrom.z)
{
BotJump(pBot);
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
{
BotJump(pBot);
}
}
}
}
else
{
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveFrom.z > MoveTo.z)
if (IsPlayerSkulk(pBot->Edict))
{
if (pBot->Edict->v.origin.z - MoveTo.z < 150.0f)
SkulkLadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
}
else
{
LadderMove(pBot, MoveFrom, MoveTo, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].requiredZ, (unsigned char)SAMPLE_POLYAREA_CROUCH);
// We're going DOWN the ladder
if (MoveFrom.z > MoveTo.z)
{
BotJump(pBot);
if (pBot->Edict->v.origin.z - MoveFrom.z < 150.0f)
{
BotJump(pBot);
}
}
}
}
@ -6959,4 +7173,130 @@ unsigned char UTIL_GetNextBotCurrentPathArea(AvHAIPlayer* pBot)
if (pBot->BotNavInfo.PathSize == 0 || pBot->BotNavInfo.CurrentPathPoint >= pBot->BotNavInfo.PathSize - 1) { return SAMPLE_POLYAREA_GROUND; }
return pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1].area;
}
int UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned char flags, bool bBiDirectional)
{
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess();
if (m_tmproc)
{
return m_tmproc->AddOffMeshConnectionDef(StartLoc, EndLoc, area, flags, bBiDirectional);
UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc);
}
}
}
void UTIL_RemoveOffMeshConnection(int ConnectionIndex)
{
Vector StartLoc, EndLoc;
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
{
MeshProcess* m_tmproc = (MeshProcess*)NavMeshes[REGULAR_NAV_MESH].tileCache->getMeshProcess();
if (m_tmproc)
{
m_tmproc->GetOffMeshConnectionPoints(ConnectionIndex, StartLoc, EndLoc);
m_tmproc->RemoveOffMeshConnectionDef(ConnectionIndex);
}
}
UTIL_OnOffMeshConnectionModified(StartLoc, EndLoc);
}
void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc)
{
float ext[3] = { 10.0f, 10.0f, 10.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);
}
}
}

View file

@ -236,6 +236,8 @@ void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoi
void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint);
// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint
void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea);
// Called by NewMove, determines the movement direction and inputs required to climb a ladder to reach endpoint as skulk, which requires different movement
void SkulkLadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea);
// Called by NewMove, determines the movement direction and inputs required to climb a wall to reach endpoint
void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight);
void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight);
@ -269,6 +271,8 @@ bool IsBotStuck(AvHAIPlayer* pBot, const Vector MoveDestination);
// Called every bot frame (default is 60fps). Ensures the tile cache is updated after obstacles are placed
void UTIL_UpdateTileCache();
void AIDEBUG_DrawOffMeshConnections(float DrawTime);
Vector UTIL_GetNearestPointOnNavWall(AvHAIPlayer* pBot, const float MaxRadius);
Vector UTIL_GetNearestPointOnNavWall(const nav_profile& NavProfile, const Vector Location, const float MaxRadius);
@ -289,6 +293,11 @@ void UTIL_RemoveTemporaryObstacle(unsigned int ObstacleRef);
void UTIL_RemoveTemporaryObstacles(unsigned int* ObstacleRefs);
int UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned char flags, bool bBiDirectional);
void UTIL_RemoveOffMeshConnection(int ConnectionIndex);
void UTIL_OnOffMeshConnectionModified(Vector StartLoc, Vector EndLoc);
/*
Safely aborts the current movement the bot is performing. Returns true if the bot has successfully aborted, and is ready to calculate a new path.