mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Recast: implement polygon cell generation algorithm
Fully generates polygon cells for the entire navmesh. Previously we never build this which caused ai to clip into, or walk into each other in-game (this system is an MSET 8 feature).
This commit is contained in:
parent
0269882bea
commit
ebfc4ec091
@ -151,6 +151,7 @@ void Editor::handleMeshChanged(InputGeom* geom)
|
||||
m_edgeMaxLen = buildSettings->edgeMaxLen;
|
||||
m_edgeMaxError = buildSettings->edgeMaxError;
|
||||
m_vertsPerPoly = buildSettings->vertsPerPoly;
|
||||
m_polyCellRes = buildSettings->polyCellRes;
|
||||
m_detailSampleDist = buildSettings->detailSampleDist;
|
||||
m_detailSampleMaxError = buildSettings->detailSampleMaxError;
|
||||
m_partitionType = buildSettings->partitionType;
|
||||
@ -170,6 +171,7 @@ void Editor::collectSettings(BuildSettings& settings)
|
||||
settings.edgeMaxLen = m_edgeMaxLen;
|
||||
settings.edgeMaxError = m_edgeMaxError;
|
||||
settings.vertsPerPoly = m_vertsPerPoly;
|
||||
settings.polyCellRes = m_polyCellRes;
|
||||
settings.detailSampleDist = m_detailSampleDist;
|
||||
settings.detailSampleMaxError = m_detailSampleMaxError;
|
||||
settings.partitionType = m_partitionType;
|
||||
@ -317,6 +319,7 @@ void Editor::handleCommonSettings()
|
||||
ImGui::SliderInt("Max Edge Length", &m_edgeMaxLen, 0, 50); // todo(amos): increase due to larger scale maps?
|
||||
ImGui::SliderFloat("Max Edge Error", &m_edgeMaxError, 0.1f, 3.0f);
|
||||
ImGui::SliderInt("Verts Per Poly", &m_vertsPerPoly, 3, 6);
|
||||
ImGui::SliderInt("Poly Cell Resolution", &m_polyCellRes, 1, 16);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Detail Mesh");
|
||||
@ -524,11 +527,11 @@ void Editor::renderDetourDebugMenu()
|
||||
// NOTE: the climb height should never equal or exceed the agent's height, see https://groups.google.com/g/recastnavigation/c/L5rBamxcOBk/m/5xGLj6YP25kJ
|
||||
// Quote: "you will get into trouble in cases where there is an overhand which is low enough to step over and high enough for the agent to walk under."
|
||||
const hulldef hulls[NAVMESH_COUNT] = {
|
||||
{ g_navMeshNames[NAVMESH_SMALL] , NAI_Hull::Width(HULL_HUMAN) * NAI_Hull::Scale(HULL_HUMAN) , NAI_Hull::Height(HULL_HUMAN) , NAI_Hull::Height(HULL_HUMAN) * NAI_Hull::Scale(HULL_HUMAN) , 32 },
|
||||
{ g_navMeshNames[NAVMESH_MED_SHORT] , NAI_Hull::Width(HULL_PROWLER) * NAI_Hull::Scale(HULL_PROWLER), NAI_Hull::Height(HULL_PROWLER), NAI_Hull::Height(HULL_PROWLER) * NAI_Hull::Scale(HULL_PROWLER), 32 },
|
||||
{ g_navMeshNames[NAVMESH_MEDIUM] , NAI_Hull::Width(HULL_MEDIUM) * NAI_Hull::Scale(HULL_MEDIUM) , NAI_Hull::Height(HULL_MEDIUM) , NAI_Hull::Height(HULL_MEDIUM) * NAI_Hull::Scale(HULL_MEDIUM) , 32 },
|
||||
{ g_navMeshNames[NAVMESH_LARGE] , NAI_Hull::Width(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , 64 },
|
||||
{ g_navMeshNames[NAVMESH_EXTRA_LARGE], NAI_Hull::Width(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), 64 },
|
||||
{ g_navMeshNames[NAVMESH_SMALL] , NAI_Hull::Width(HULL_HUMAN) * NAI_Hull::Scale(HULL_HUMAN) , NAI_Hull::Height(HULL_HUMAN) , NAI_Hull::Height(HULL_HUMAN) * NAI_Hull::Scale(HULL_HUMAN) , 32, 8 },
|
||||
{ g_navMeshNames[NAVMESH_MED_SHORT] , NAI_Hull::Width(HULL_PROWLER) * NAI_Hull::Scale(HULL_PROWLER), NAI_Hull::Height(HULL_PROWLER), NAI_Hull::Height(HULL_PROWLER) * NAI_Hull::Scale(HULL_PROWLER), 32, 4 },
|
||||
{ g_navMeshNames[NAVMESH_MEDIUM] , NAI_Hull::Width(HULL_MEDIUM) * NAI_Hull::Scale(HULL_MEDIUM) , NAI_Hull::Height(HULL_MEDIUM) , NAI_Hull::Height(HULL_MEDIUM) * NAI_Hull::Scale(HULL_MEDIUM) , 32, 4 },
|
||||
{ g_navMeshNames[NAVMESH_LARGE] , NAI_Hull::Width(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) , NAI_Hull::Height(HULL_TITAN) * NAI_Hull::Scale(HULL_TITAN) , 64, 2 },
|
||||
{ g_navMeshNames[NAVMESH_EXTRA_LARGE], NAI_Hull::Width(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH), NAI_Hull::Height(HULL_GOLIATH) * NAI_Hull::Scale(HULL_GOLIATH), 64, 2 },
|
||||
};
|
||||
|
||||
void Editor::selectNavMeshType(const NavMeshType_e navMeshType)
|
||||
@ -540,6 +543,7 @@ void Editor::selectNavMeshType(const NavMeshType_e navMeshType)
|
||||
m_agentHeight = h.height;
|
||||
m_navmeshName = h.name;
|
||||
m_tileSize = h.tileSize;
|
||||
m_polyCellRes = h.cellResolution;
|
||||
|
||||
m_selectedNavMeshType = navMeshType;
|
||||
}
|
||||
|
@ -1011,6 +1011,7 @@ unsigned char* Editor_TileMesh::buildTileMesh(const int tx, const int ty, const
|
||||
params.polyFlags = m_pmesh->flags;
|
||||
params.polyCount = m_pmesh->npolys;
|
||||
params.nvp = m_pmesh->nvp;
|
||||
params.cellResolution = m_polyCellRes;
|
||||
params.detailMeshes = m_dmesh->meshes;
|
||||
params.detailVerts = m_dmesh->verts;
|
||||
params.detailVertsCount = m_dmesh->nverts;
|
||||
|
@ -320,7 +320,7 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath)
|
||||
{
|
||||
// Settings
|
||||
m_hasBuildSettings = true;
|
||||
sscanf(row + 1, "%f %f %f %f %f %f %d %d %d %f %d %f %f %d %f %f %f %f %f %f %d",
|
||||
sscanf(row + 1, "%f %f %f %f %f %f %d %d %d %f %d %d %f %f %d %f %f %f %f %f %f %d",
|
||||
&m_buildSettings.cellSize,
|
||||
&m_buildSettings.cellHeight,
|
||||
&m_buildSettings.agentHeight,
|
||||
@ -332,6 +332,7 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath)
|
||||
&m_buildSettings.edgeMaxLen,
|
||||
&m_buildSettings.edgeMaxError,
|
||||
&m_buildSettings.vertsPerPoly,
|
||||
&m_buildSettings.polyCellRes,
|
||||
&m_buildSettings.detailSampleDist,
|
||||
&m_buildSettings.detailSampleMaxError,
|
||||
&m_buildSettings.partitionType,
|
||||
@ -397,7 +398,7 @@ bool InputGeom::saveGeomSet(const BuildSettings* settings)
|
||||
if (settings)
|
||||
{
|
||||
fprintf(fp,
|
||||
"s %f %f %f %f %f %f %d %d %d %f %d %f %f %d %f %f %f %f %f %f %d\n",
|
||||
"s %f %f %f %f %f %f %d %d %d %f %d %d %f %f %d %f %f %f %f %f %f %d\n",
|
||||
settings->cellSize,
|
||||
settings->cellHeight,
|
||||
settings->agentHeight,
|
||||
@ -409,6 +410,7 @@ bool InputGeom::saveGeomSet(const BuildSettings* settings)
|
||||
settings->edgeMaxLen,
|
||||
settings->edgeMaxError,
|
||||
settings->vertsPerPoly,
|
||||
settings->polyCellRes,
|
||||
settings->detailSampleDist,
|
||||
settings->detailSampleMaxError,
|
||||
settings->partitionType,
|
||||
|
@ -31,6 +31,7 @@ struct hulldef
|
||||
float height;
|
||||
float climbHeight;
|
||||
int tileSize;
|
||||
int cellResolution;
|
||||
};
|
||||
extern const hulldef hulls[5];
|
||||
|
||||
@ -131,6 +132,7 @@ protected:
|
||||
int m_edgeMaxLen;
|
||||
float m_edgeMaxError;
|
||||
int m_vertsPerPoly;
|
||||
int m_polyCellRes;
|
||||
float m_detailSampleDist;
|
||||
float m_detailSampleMaxError;
|
||||
int m_partitionType;
|
||||
|
@ -56,6 +56,8 @@ struct BuildSettings
|
||||
// Edge max error in voxels
|
||||
float edgeMaxError;
|
||||
int vertsPerPoly;
|
||||
// The polygon cell resolution.
|
||||
int polyCellRes;
|
||||
// Detail sample distance in voxels
|
||||
float detailSampleDist;
|
||||
// Detail sample max error in voxel heights.
|
||||
|
@ -38,6 +38,7 @@ struct dtNavMeshCreateParams
|
||||
const unsigned char* polyAreas; ///< The user defined area ids assigned to each polygon. [Size: #polyCount]
|
||||
int polyCount; ///< Number of polygons in the mesh. [Limit: >= 1]
|
||||
int nvp; ///< Maximum number of vertices per polygon. [Limit: >= 3]
|
||||
int cellResolution; ///< The resolution of the diamond cell grid [Limit: >= 1]
|
||||
|
||||
/// @}
|
||||
/// @name Height Detail Attributes (Optional)
|
||||
|
@ -508,6 +508,100 @@ bool dtCreateTraversalTableData(dtNavMesh* nav, const dtDisjointSet& disjoint, c
|
||||
return true;
|
||||
}
|
||||
|
||||
static const unsigned short DT_MESH_NULL_IDX = 0xffff;
|
||||
static int countPolyVerts(const unsigned short* p, const int nvp) // todo(amos): deduplicate
|
||||
{
|
||||
for (int i = 0; i < nvp; ++i)
|
||||
if (p[i] == DT_MESH_NULL_IDX)
|
||||
return i;
|
||||
return nvp;
|
||||
}
|
||||
|
||||
struct CellItem
|
||||
{
|
||||
float pos[3];
|
||||
int polyIndex;
|
||||
};
|
||||
|
||||
bool createPolyMeshCells(const dtNavMeshCreateParams* params, rdTempVector<CellItem>& cellItems)
|
||||
{
|
||||
const int nvp = params->nvp;
|
||||
const int resolution = params->cellResolution;
|
||||
const float stepX = (params->bmax[0]-params->bmin[0]) / resolution;
|
||||
const float stepY = (params->bmax[1]-params->bmin[1]) / resolution;
|
||||
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
const unsigned short* p = ¶ms->polys[i*2*nvp];
|
||||
const int nv = countPolyVerts(p, nvp);
|
||||
|
||||
if (nv < 3) // Don't generate cells for off-mesh connections.
|
||||
continue;
|
||||
|
||||
const unsigned int vb = params->detailMeshes[i*4+0];
|
||||
const unsigned int ndv = params->detailMeshes[i*4+1];
|
||||
const unsigned int tb = params->detailMeshes[i*4+2];
|
||||
|
||||
float polyVerts[DT_VERTS_PER_POLYGON*3];
|
||||
|
||||
for (int j = 0; j < nv; ++j)
|
||||
{
|
||||
const unsigned short* polyVert = ¶ms->verts[p[j]*3];
|
||||
float* flPolyVert = &polyVerts[j*3];
|
||||
|
||||
flPolyVert[0] = params->bmin[0]+polyVert[0]*params->cs;
|
||||
flPolyVert[1] = params->bmin[1]+polyVert[1]*params->cs;
|
||||
flPolyVert[2] = params->bmin[2]+polyVert[2]*params->ch;
|
||||
}
|
||||
|
||||
for (int j = 0; j <= resolution; j++)
|
||||
{
|
||||
for (int k = 0; k <= resolution; k++)
|
||||
{
|
||||
const float offsetX = (k % 2 == 0) ? 0.0f : stepX / 2.0f;
|
||||
|
||||
float targetCellPos[3];
|
||||
targetCellPos[0] = params->bmin[0]+j*stepX+offsetX;
|
||||
targetCellPos[1] = params->bmin[1]+k*stepY;
|
||||
targetCellPos[2] = 0; // todo(amos): might need a proper fallback, but so far this never failed.
|
||||
|
||||
if (!rdPointInPolygon(targetCellPos, polyVerts, nv))
|
||||
continue;
|
||||
|
||||
for (int l = 0; l < params->detailTriCount; ++l)
|
||||
{
|
||||
const unsigned char* t = ¶ms->detailTris[(tb+l)*4];
|
||||
float storage[3][3];
|
||||
const float* v[3];
|
||||
|
||||
for (int m = 0; m < 3; ++m)
|
||||
{
|
||||
if (t[m] < nv)
|
||||
{
|
||||
for (int n = 0; n < 3; ++n)
|
||||
{
|
||||
storage[m][n] = params->bmin[n] + params->verts[p[t[m]]*3+n] * (n == 2 ? params->ch : params->cs);
|
||||
}
|
||||
v[m] = storage[m];
|
||||
}
|
||||
else
|
||||
{
|
||||
v[m] = ¶ms->detailVerts[(vb+t[m])*3];
|
||||
}
|
||||
}
|
||||
|
||||
if (rdClosestHeightPointTriangle(targetCellPos, v[0],v[1],v[2], targetCellPos[2]))
|
||||
break;
|
||||
}
|
||||
|
||||
cellItems.push_back({ targetCellPos[0],targetCellPos[1],targetCellPos[2], i });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Better error handling.
|
||||
|
||||
/// @par
|
||||
@ -663,6 +757,11 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
}
|
||||
}
|
||||
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
rdTempVector<CellItem> cellItems;
|
||||
createPolyMeshCells(params, cellItems);
|
||||
#endif
|
||||
|
||||
// Calculate data size
|
||||
const int headerSize = rdAlign4(sizeof(dtMeshHeader));
|
||||
const int vertsSize = rdAlign4(sizeof(float)*3*totVertCount);
|
||||
@ -673,13 +772,21 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
const int detailTrisSize = rdAlign4(sizeof(unsigned char)*4*detailTriCount);
|
||||
const int bvTreeSize = params->buildBvTree ? rdAlign4(sizeof(dtBVNode)*params->polyCount*2) : 0;
|
||||
const int offMeshConsSize = rdAlign4(sizeof(dtOffMeshConnection)*storedOffMeshConCount);
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
const int cellsSize = rdAlign4(sizeof(dtCell)*(int)cellItems.size());
|
||||
#endif
|
||||
|
||||
int polyMapCount = 0; // TODO: this data has to be reversed still from the NavMesh!
|
||||
const int polyMapSize = polyMapCount * totPolyCount;
|
||||
|
||||
const int dataSize = headerSize + vertsSize + polysSize + linksSize +
|
||||
detailMeshesSize + detailVertsSize + detailTrisSize +
|
||||
bvTreeSize + offMeshConsSize + polyMapSize;
|
||||
const int dataSize = headerSize + vertsSize + polysSize + polyMapSize + linksSize +
|
||||
detailMeshesSize + detailVertsSize + detailTrisSize +
|
||||
bvTreeSize + offMeshConsSize
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
+ cellsSize;
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
//printf("%i %i %i %i(%i links) %i %i %i %i %i\n", headerSize, vertsSize, polysSize, linksSize, maxLinkCount, detailMeshesSize, detailVertsSize, detailTrisSize, bvTreeSize, offMeshConsSize);
|
||||
//printf("%i\n", dataSize);
|
||||
@ -705,6 +812,9 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
unsigned char* navDTris = rdGetThenAdvanceBufferPointer<unsigned char>(d, detailTrisSize);
|
||||
dtBVNode* navBvtree = rdGetThenAdvanceBufferPointer<dtBVNode>(d, bvTreeSize);
|
||||
dtOffMeshConnection* offMeshCons = rdGetThenAdvanceBufferPointer<dtOffMeshConnection>(d, offMeshConsSize);
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
dtCell* navCells = rdGetThenAdvanceBufferPointer<dtCell>(d, cellsSize);
|
||||
#endif
|
||||
|
||||
rdIgnoreUnused(polyMap);
|
||||
//for(int i=0;i<unkPerPoly*totPolyCount;i++)
|
||||
@ -718,6 +828,7 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
header->layer = params->tileLayer;
|
||||
header->userId = params->userId;
|
||||
header->polyCount = totPolyCount;
|
||||
header->polyMapCount = polyMapCount;
|
||||
header->vertCount = totVertCount;
|
||||
header->maxLinkCount = maxLinkCount;
|
||||
rdVcopy(header->bmin, params->bmin);
|
||||
@ -727,13 +838,14 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
header->detailTriCount = detailTriCount;
|
||||
header->bvQuantFactor = 1.0f / params->cs;
|
||||
header->offMeshBase = params->polyCount;
|
||||
header->maxCellCount = -1;
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
header->maxCellCount = (int)cellItems.size();
|
||||
#endif
|
||||
header->walkableHeight = params->walkableHeight;
|
||||
header->walkableRadius = params->walkableRadius;
|
||||
header->walkableClimb = params->walkableClimb;
|
||||
header->offMeshConCount = storedOffMeshConCount;
|
||||
header->bvNodeCount = params->buildBvTree ? params->polyCount*2 : 0;
|
||||
header->polyMapCount = polyMapCount;
|
||||
|
||||
const int offMeshVertsBase = params->vertCount;
|
||||
const int offMeshPolyBase = params->polyCount;
|
||||
@ -762,13 +874,24 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
// Polygon cells.
|
||||
for (int i = 0; i < (int)cellItems.size(); i++)
|
||||
{
|
||||
const CellItem& cellItem = cellItems[i];
|
||||
dtCell& cell = navCells[i];
|
||||
|
||||
rdVcopy(cell.pos, cellItem.pos);
|
||||
cell.polyIndex = cellItem.polyIndex;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Store polygons
|
||||
// Mesh polys
|
||||
const unsigned short* src = params->polys;
|
||||
for (int i = 0; i < params->polyCount; ++i)
|
||||
{
|
||||
|
||||
dtPoly* p = &navPolys[i];
|
||||
p->vertCount = 0;
|
||||
p->flags = params->polyFlags[i];
|
||||
@ -806,6 +929,7 @@ bool dtCreateNavMeshData(dtNavMeshCreateParams* params, unsigned char** outData,
|
||||
|
||||
src += nvp*2;
|
||||
}
|
||||
|
||||
// Off-mesh connection vertices.
|
||||
n = 0;
|
||||
for (int i = 0; i < params->offMeshConCount; ++i)
|
||||
@ -953,6 +1077,9 @@ bool dtNavMeshHeaderSwapEndian(unsigned char* data, const int /*dataSize*/)
|
||||
rdSwapEndian(&header->bvNodeCount);
|
||||
rdSwapEndian(&header->offMeshConCount);
|
||||
rdSwapEndian(&header->offMeshBase);
|
||||
#if DT_NAVMESH_SET_VERSION >= 8
|
||||
rdSwapEndian(&header->maxCellCount);
|
||||
#endif
|
||||
rdSwapEndian(&header->walkableHeight);
|
||||
rdSwapEndian(&header->walkableRadius);
|
||||
rdSwapEndian(&header->walkableClimb);
|
||||
|
Loading…
x
Reference in New Issue
Block a user