mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Recast: implement static pathing logic in editor
The editor now takes the static pathing data into account when creating paths/testing the navmesh using the NavMeshTesterTool or CrowdTool. An option is made allowing you to select which traverse anim type you want to use for pathing (each of them uses a different traversal table, thus giving them different options as to which links and jumps they can take). This allows us to test AI withing the editor itself, thus saving a lot of time shuffling navmesh files around and reloading them in-game.
This commit is contained in:
parent
86bdbce7b1
commit
3fbe657577
@ -101,6 +101,7 @@ CrowdToolState::CrowdToolState() :
|
||||
m_toolParams.m_showPerfGraph = false;
|
||||
m_toolParams.m_showDetailAll = false;
|
||||
m_toolParams.m_expandOptions = true;
|
||||
m_toolParams.m_expandTraversalOptions = false;
|
||||
m_toolParams.m_anticipateTurns = true;
|
||||
m_toolParams.m_optimizeVis = true;
|
||||
m_toolParams.m_optimizeTopo = true;
|
||||
@ -110,6 +111,7 @@ CrowdToolState::CrowdToolState() :
|
||||
m_toolParams.m_separationWeight = 20.0f;
|
||||
m_toolParams.m_maxAcceleration = 800.f;
|
||||
m_toolParams.m_maxSpeed = 200.f;
|
||||
m_toolParams.m_traverseAnimType = ANIMTYPE_NONE;
|
||||
|
||||
memset(m_trails, 0, sizeof(m_trails));
|
||||
|
||||
@ -135,6 +137,8 @@ void CrowdToolState::init(class Editor* editor)
|
||||
|
||||
dtNavMesh* nav = m_editor->getNavMesh();
|
||||
dtCrowd* crowd = m_editor->getCrowd();
|
||||
|
||||
m_toolParams.m_traverseAnimType = NavMesh_GetFirstTraverseAnimTypeForType(m_editor->getLoadedNavMeshType());
|
||||
|
||||
if (nav && crowd && (m_nav != nav || m_crowd != crowd))
|
||||
{
|
||||
@ -562,7 +566,12 @@ void CrowdToolState::handleRenderOverlay(double* proj, double* model, int* view)
|
||||
if (gluProject((GLdouble)pos[0], (GLdouble)pos[1], (GLdouble)pos[2]+h,
|
||||
model, proj, view, &x, &y, &z))
|
||||
{
|
||||
snprintf(label, 32, "%d", i);
|
||||
const TraverseAnimType_e animType = ag->params.traverseAnimType;
|
||||
const char* animTypeName = animType == ANIMTYPE_NONE
|
||||
? "none"
|
||||
: g_traverseAnimTypeNames[animType];
|
||||
|
||||
snprintf(label, 32, "%s (%d)", animTypeName, i);
|
||||
imguiDrawText((int)x, (int)y+15, IMGUI_ALIGN_CENTER, label, imguiRGBA(0,0,0,220));
|
||||
}
|
||||
}
|
||||
@ -649,6 +658,7 @@ void CrowdToolState::addAgent(const float* p)
|
||||
ap.updateFlags |= DT_CROWD_SEPARATION;
|
||||
ap.obstacleAvoidanceType = (unsigned char)m_toolParams.m_obstacleAvoidanceType;
|
||||
ap.separationWeight = m_toolParams.m_separationWeight;
|
||||
ap.traverseAnimType = m_toolParams.m_traverseAnimType;
|
||||
|
||||
int idx = crowd->addAgent(p, &ap);
|
||||
if (idx != -1)
|
||||
@ -944,6 +954,37 @@ void CrowdTool::handleMenu()
|
||||
imguiUnindent();
|
||||
}
|
||||
|
||||
if (imguiCollapse("Traverse Animation Type", 0, params->m_expandTraversalOptions))
|
||||
params->m_expandTraversalOptions = !params->m_expandTraversalOptions;
|
||||
|
||||
const NavMeshType_e loadedNavMeshType = m_editor->getLoadedNavMeshType();
|
||||
|
||||
// TODO: perhaps clamp with m_nav->m_params.traversalTableCount? Technically a navmesh should
|
||||
// contain all the traversal tables it supports, so if we crash the navmesh is technically corrupt.
|
||||
const int traverseTableCount = NavMesh_GetTraversalTableCountForNavMeshType(loadedNavMeshType);
|
||||
const TraverseAnimType_e baseType = NavMesh_GetFirstTraverseAnimTypeForType(loadedNavMeshType);
|
||||
|
||||
if (params->m_expandTraversalOptions)
|
||||
{
|
||||
imguiIndent();
|
||||
|
||||
for (int i = ANIMTYPE_NONE; i < traverseTableCount; i++)
|
||||
{
|
||||
const bool noAnimtype = i == ANIMTYPE_NONE;
|
||||
|
||||
const TraverseAnimType_e animTypeIndex = noAnimtype ? ANIMTYPE_NONE : TraverseAnimType_e((int)baseType + i);
|
||||
const char* animtypeName = noAnimtype ? "none" : g_traverseAnimTypeNames[animTypeIndex];
|
||||
|
||||
if (imguiCheck(animtypeName, params->m_traverseAnimType == animTypeIndex))
|
||||
{
|
||||
params->m_traverseAnimType = animTypeIndex;
|
||||
m_state->updateAgentParams();
|
||||
}
|
||||
}
|
||||
|
||||
imguiUnindent();
|
||||
}
|
||||
|
||||
if (imguiCollapse("Selected Debug Draw", 0, params->m_expandSelectedDebugDraw))
|
||||
params->m_expandSelectedDebugDraw = !params->m_expandSelectedDebugDraw;
|
||||
|
||||
|
@ -62,7 +62,7 @@ Editor::Editor() :
|
||||
m_filterLowHangingObstacles(true),
|
||||
m_filterLedgeSpans(true),
|
||||
m_filterWalkableLowHeightSpans(true),
|
||||
m_navMeshType(NAVMESH_SMALL),
|
||||
m_selectedNavMeshType(NAVMESH_SMALL),
|
||||
m_navmeshName(NavMesh_GetNameForType(NAVMESH_SMALL)),
|
||||
m_tool(0),
|
||||
m_ctx(0)
|
||||
|
@ -232,7 +232,7 @@ void Editor_TileMesh::selectNavMeshType(const NavMeshType_e navMeshType)
|
||||
m_navmeshName = h.name;
|
||||
m_tileSize = h.tileSize;
|
||||
|
||||
m_navMeshType = navMeshType;
|
||||
m_selectedNavMeshType = navMeshType;
|
||||
}
|
||||
|
||||
void Editor_TileMesh::handleSettings()
|
||||
@ -299,6 +299,9 @@ void Editor_TileMesh::handleSettings()
|
||||
rdFreeNavMesh(m_navMesh);
|
||||
m_navMesh = Editor::loadAll(m_modelName.c_str());
|
||||
m_navQuery->init(m_navMesh, 2048);
|
||||
|
||||
m_loadedNavMeshType = m_selectedNavMeshType;
|
||||
initToolStates(this);
|
||||
}
|
||||
|
||||
if (imguiButton("Save"))
|
||||
@ -679,6 +682,8 @@ bool Editor_TileMesh::handleBuild()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_loadedNavMeshType = m_selectedNavMeshType;
|
||||
|
||||
dtNavMeshParams params;
|
||||
rcVcopy(params.orig, m_geom->getNavMeshBoundsMin());
|
||||
|
||||
@ -690,7 +695,7 @@ bool Editor_TileMesh::handleBuild()
|
||||
params.maxPolys = m_maxPolysPerTile;
|
||||
params.polyGroupCount = 0;
|
||||
params.traversalTableSize = 0;
|
||||
params.traversalTableCount = NavMesh_GetTraversalTableCountForNavMeshType(m_navMeshType);
|
||||
params.traversalTableCount = NavMesh_GetTraversalTableCountForNavMeshType(m_selectedNavMeshType);
|
||||
params.magicDataCount = 0;
|
||||
|
||||
dtStatus status;
|
||||
@ -842,7 +847,7 @@ void Editor_TileMesh::buildAllTiles()
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build disjoint poly groups.");
|
||||
}
|
||||
|
||||
if (!dtCreateTraversalTableData(m_navMesh, data, NavMesh_GetTraversalTableCountForNavMeshType(m_navMeshType)))
|
||||
if (!dtCreateTraversalTableData(m_navMesh, data, NavMesh_GetTraversalTableCountForNavMeshType(m_selectedNavMeshType)))
|
||||
{
|
||||
m_ctx->log(RC_LOG_ERROR, "buildNavigation: Failed to build traversal table data.");
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ NavMeshTesterTool::NavMeshTesterTool() :
|
||||
m_navQuery(0),
|
||||
m_pathFindStatus(DT_FAILURE),
|
||||
m_toolMode(TOOLMODE_PATHFIND_FOLLOW),
|
||||
m_traverseAnimType(ANIMTYPE_NONE),
|
||||
m_straightPathOptions(0),
|
||||
m_startRef(0),
|
||||
m_endRef(0),
|
||||
@ -401,7 +402,33 @@ void NavMeshTesterTool::handleMenu()
|
||||
}
|
||||
imguiUnindent();
|
||||
|
||||
imguiSeparator();
|
||||
imguiSeparator();
|
||||
imguiLabel("Traverse Anim Type");
|
||||
|
||||
imguiIndent();
|
||||
|
||||
const NavMeshType_e loadedNavMeshType = m_editor->getLoadedNavMeshType();
|
||||
|
||||
// TODO: perhaps clamp with m_nav->m_params.traversalTableCount? Technically a navmesh should
|
||||
// contain all the traversal tables it supports, so if we crash the navmesh is technically corrupt.
|
||||
const int traverseTableCount = NavMesh_GetTraversalTableCountForNavMeshType(loadedNavMeshType);
|
||||
const TraverseAnimType_e baseType = NavMesh_GetFirstTraverseAnimTypeForType(loadedNavMeshType);
|
||||
|
||||
for (int i = ANIMTYPE_NONE; i < traverseTableCount; i++)
|
||||
{
|
||||
const bool noAnimtype = i == ANIMTYPE_NONE;
|
||||
|
||||
const TraverseAnimType_e animTypeIndex = noAnimtype ? ANIMTYPE_NONE : TraverseAnimType_e((int)baseType + i);
|
||||
const char* animtypeName = noAnimtype ? "none" : g_traverseAnimTypeNames[animTypeIndex];
|
||||
|
||||
if (imguiCheck(animtypeName, m_traverseAnimType == animTypeIndex))
|
||||
{
|
||||
m_traverseAnimType = animTypeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
imguiUnindent();
|
||||
imguiSeparator();
|
||||
}
|
||||
|
||||
void NavMeshTesterTool::handleClick(const float* /*s*/, const float* p, bool shift)
|
||||
@ -431,7 +458,19 @@ void NavMeshTesterTool::handleToggle()
|
||||
|
||||
if (!m_sposSet || !m_eposSet || !m_startRef || !m_endRef)
|
||||
return;
|
||||
|
||||
|
||||
const bool hasAnimType = m_traverseAnimType != ANIMTYPE_NONE;
|
||||
const int traversalTableIndex = hasAnimType
|
||||
? NavMesh_GetTraversalTableIndexForAnimType(m_traverseAnimType)
|
||||
: NULL;
|
||||
|
||||
if (!m_navMesh->isGoalPolyReachable(m_startRef, m_endRef, !hasAnimType, traversalTableIndex))
|
||||
{
|
||||
printf("%s: end poly '%d' is unreachable from start poly '%d'\n", "m_navMesh->isGoalPolyReachable", m_startRef, m_endRef);
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
static const float STEP_SIZE = 10.0f;
|
||||
static const float SLOP = 2.0f;
|
||||
|
||||
@ -632,6 +671,18 @@ void NavMeshTesterTool::recalc()
|
||||
m_endRef = 0;
|
||||
|
||||
m_pathFindStatus = DT_FAILURE;
|
||||
|
||||
const bool hasAnimType = m_traverseAnimType != ANIMTYPE_NONE;
|
||||
const int traversalTableIndex = hasAnimType
|
||||
? NavMesh_GetTraversalTableIndexForAnimType(m_traverseAnimType)
|
||||
: NULL;
|
||||
|
||||
if (!m_navMesh->isGoalPolyReachable(m_startRef, m_endRef, !hasAnimType, traversalTableIndex))
|
||||
{
|
||||
printf("%s: end poly '%d' is unreachable from start poly '%d'\n", "m_navMesh->isGoalPolyReachable", m_startRef, m_endRef);
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_toolMode == TOOLMODE_PATHFIND_FOLLOW)
|
||||
{
|
||||
@ -1317,6 +1368,31 @@ void NavMeshTesterTool::handleRenderOverlay(double* proj, double* model, int* vi
|
||||
{
|
||||
imguiDrawText((int)x, (int)(y-25), IMGUI_ALIGN_CENTER, "End", imguiRGBA(0,0,0,220));
|
||||
}
|
||||
|
||||
// Useful utility to draw all polygroup id's at the center of the polygons.
|
||||
// The code has been commented as this is very expensive, and we need to add
|
||||
// an option to only render a range of group id's.
|
||||
//if (m_navMesh)
|
||||
//{
|
||||
// for (int i = 0; i < m_navMesh->getMaxTiles(); i++)
|
||||
// {
|
||||
// const dtMeshTile* tile = m_navMesh->getTile(i);
|
||||
// if (!tile->header) continue;
|
||||
|
||||
// for (int j = 0; j < tile->header->polyCount; j++)
|
||||
// {
|
||||
// const dtPoly* poly = &tile->polys[j];
|
||||
|
||||
// if (gluProject((GLdouble)poly->center[0], (GLdouble)poly->center[1], (GLdouble)poly->center[2] + 30,
|
||||
// model, proj, view, &x, &y, &z))
|
||||
// {
|
||||
// char label[6];
|
||||
// snprintf(label, sizeof(label), "%hu", poly->groupId);
|
||||
// imguiDrawText((int)x, (int)y, IMGUI_ALIGN_CENTER, label, imguiRGBA(0, 0, 0, 220));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// Tool help
|
||||
const int h = view[3];
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "NavEditor/Include/ValueHistory.h"
|
||||
#include "NavEditor/Include/Editor.h"
|
||||
|
||||
#include "game/server/ai_navmesh.h"
|
||||
|
||||
// Tool to create crowds.
|
||||
|
||||
struct CrowdToolParams
|
||||
@ -45,6 +47,7 @@ struct CrowdToolParams
|
||||
bool m_showDetailAll;
|
||||
|
||||
bool m_expandOptions;
|
||||
bool m_expandTraversalOptions;
|
||||
bool m_anticipateTurns;
|
||||
bool m_optimizeVis;
|
||||
bool m_optimizeTopo;
|
||||
@ -55,6 +58,8 @@ struct CrowdToolParams
|
||||
|
||||
float m_maxAcceleration;
|
||||
float m_maxSpeed;
|
||||
|
||||
TraverseAnimType_e m_traverseAnimType;
|
||||
};
|
||||
|
||||
class CrowdToolState : public EditorToolState
|
||||
|
@ -135,7 +135,8 @@ protected:
|
||||
float m_detailSampleMaxError;
|
||||
int m_partitionType;
|
||||
|
||||
NavMeshType_e m_navMeshType;
|
||||
NavMeshType_e m_selectedNavMeshType;
|
||||
NavMeshType_e m_loadedNavMeshType;
|
||||
const char* m_navmeshName;
|
||||
|
||||
EditorTool* m_tool;
|
||||
@ -188,6 +189,9 @@ public:
|
||||
|
||||
inline void toggleNavMeshDrawFlag(unsigned int flag) { m_navMeshDrawFlags ^= flag; }
|
||||
|
||||
inline NavMeshType_e getSelectedNavMeshType() const { return m_selectedNavMeshType; }
|
||||
inline NavMeshType_e getLoadedNavMeshType() const { return m_loadedNavMeshType; }
|
||||
|
||||
void updateToolStates(const float dt);
|
||||
void initToolStates(Editor* editor);
|
||||
void resetToolStates();
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include "Detour/Include/DetourNavMeshQuery.h"
|
||||
#include "NavEditor/Include/Editor.h"
|
||||
|
||||
#include "game/server/ai_navmesh.h"
|
||||
|
||||
class NavMeshTesterTool : public EditorTool
|
||||
{
|
||||
Editor* m_editor;
|
||||
@ -47,6 +49,7 @@ class NavMeshTesterTool : public EditorTool
|
||||
};
|
||||
|
||||
ToolMode m_toolMode;
|
||||
TraverseAnimType_e m_traverseAnimType;
|
||||
|
||||
int m_straightPathOptions;
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "DetourProximityGrid.h"
|
||||
#include "DetourPathQueue.h"
|
||||
|
||||
#include "game/server/ai_navmesh.h"
|
||||
|
||||
/// The maximum number of neighbors that a crowd agent can take into account
|
||||
/// for steering decisions.
|
||||
/// @ingroup crowd
|
||||
@ -83,9 +85,13 @@ struct dtCrowdAgentParams
|
||||
|
||||
float pathOptimizationRange; ///< The path visibility optimization range. [Limit: > 0]
|
||||
|
||||
/// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
|
||||
/// How aggressive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
|
||||
float separationWeight;
|
||||
|
||||
/// The traversal animation type, which is used to determine which traversal table this agent will use.
|
||||
/// [Limit: ANIMTYPE_NONE >= value <= #dtNavMeshParams::traversalTableCount]
|
||||
TraverseAnimType_e traverseAnimType;
|
||||
|
||||
/// Flags that impact steering behavior. (See: #UpdateFlags)
|
||||
unsigned char updateFlags;
|
||||
|
||||
|
@ -459,9 +459,20 @@ void dtCrowd::updateMoveRequest(const float /*dt*/)
|
||||
if (ag->targetState == DT_CROWDAGENT_TARGET_NONE || ag->targetState == DT_CROWDAGENT_TARGET_VELOCITY)
|
||||
continue;
|
||||
|
||||
const dtPolyRef* path = ag->corridor.getPath();
|
||||
const TraverseAnimType_e animType = ag->params.traverseAnimType;
|
||||
|
||||
const bool hasAnimType = animType != ANIMTYPE_NONE;
|
||||
const int traversalTableIndex = hasAnimType
|
||||
? NavMesh_GetTraversalTableIndexForAnimType(animType)
|
||||
: NULL;
|
||||
|
||||
// Don't fire off the request if the goal is unreachable.
|
||||
if (!m_navquery->isGoalPolyReachable(path[0], ag->targetRef, !hasAnimType, traversalTableIndex))
|
||||
continue;
|
||||
|
||||
if (ag->targetState == DT_CROWDAGENT_TARGET_REQUESTING)
|
||||
{
|
||||
const dtPolyRef* path = ag->corridor.getPath();
|
||||
const int npath = ag->corridor.getPathCount();
|
||||
rdAssert(npath);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user