added navmesh tester tool

Added ground work for tester tool
tester tool works but needs to fill out list of acceptable datablocks and spawnclasses
navpaths now share 1 navmeshquery
AIControllerData now has a vector of area costs for different polyareas
General cleanup
This commit is contained in:
marauder2k7 2025-07-26 10:34:19 +01:00
parent edf4d47be0
commit 6d36e17d91
17 changed files with 604 additions and 421 deletions

View file

@ -535,6 +535,10 @@ AIControllerData::AIControllerData()
#ifdef TORQUE_NAVIGATION_ENABLED
mLinkTypes = LinkData(AllFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(0);
mAreaCosts.setSize(PolyAreas::NumAreas);
mAreaCosts.fill(1.0f);
mNavSize = AINavigation::Regular;
mFlocking.mChance = 90;
mFlocking.mMin = 1.0f;
@ -560,6 +564,8 @@ AIControllerData::AIControllerData(const AIControllerData& other, bool temp_clon
#ifdef TORQUE_NAVIGATION_ENABLED
mLinkTypes = other.mLinkTypes;
mFilter = other.mFilter;
mAreaCosts = other.mAreaCosts;
mNavSize = other.mNavSize;
mFlocking.mChance = other.mFlocking.mChance;
mFlocking.mMin = other.mFlocking.mMin;
@ -629,6 +635,8 @@ void AIControllerData::initPersistFields()
addFieldV("FlockSideStep", TypeRangedF32, Offset(mFlocking.mSideStep, AIControllerData), &CommonValidators::PositiveFloat,
"@brief Distance from destination before we stop moving out of the way.");
addField("areaCosts", TypeF32Vector, Offset(mAreaCosts, AIControllerData),
"Vector of costs for each PolyArea.");
addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIControllerData),
"Allow the character to walk on dry land.");
addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIControllerData),
@ -662,6 +670,10 @@ void AIControllerData::packData(BitStream* stream)
#ifdef TORQUE_NAVIGATION_ENABLED
//enums
stream->write(mAreaCosts.size());
for (U32 i = 0; i < mAreaCosts.size(); i++) {
stream->write(mAreaCosts[i]);
}
stream->write(mLinkTypes.getFlags());
stream->write((U32)mNavSize);
// end enums
@ -684,10 +696,23 @@ void AIControllerData::unpackData(BitStream* stream)
stream->read(&mFollowTolerance);
#ifdef TORQUE_NAVIGATION_ENABLED
U32 num;
stream->read(&num);
mAreaCosts.setSize(num);
for (U32 i = 0; i < num; i++)
{
stream->read(&mAreaCosts[i]);
}
//enums
U16 linkFlags;
stream->read(&linkFlags);
mLinkTypes = LinkData(linkFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(mLinkTypes.getExcludeFlags());
for (U32 i = 0; i < PolyAreas::NumAreas; i++)
{
mFilter.setAreaCost((PolyAreas)i, mAreaCosts[i]);
}
U32 navSize;
stream->read(&navSize);
mNavSize = (AINavigation::NavSize)(navSize);

View file

@ -168,6 +168,8 @@ public:
/// Types of link we can use.
LinkData mLinkTypes;
dtQueryFilter mFilter;
Vector<F32> mAreaCosts;
AINavigation::NavSize mNavSize;
#endif
Delegate<void(AIController* obj, Point3F location, Move* movePtr)> resolveYawPtr;

View file

@ -77,6 +77,7 @@ bool AINavigation::setPathDestination(const Point3F& pos, bool replace)
path->mAlwaysRender = true;
path->mLinkTypes = getCtrl()->mControllerData->mLinkTypes;
path->mXray = true;
path->mFilter = getCtrl()->mControllerData->mFilter;
// Paths plan automatically upon being registered.
if (!path->registerObject())
{

View file

@ -114,6 +114,10 @@ AIPlayer::AIPlayer()
mJump = None;
mNavSize = Regular;
mLinkTypes = LinkData(AllFlags);
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(0);
mAreaCosts.setSize(PolyAreas::NumAreas);
mAreaCosts.fill(1.0f);
#endif
mIsAiControlled = true;
@ -163,7 +167,8 @@ void AIPlayer::initPersistFields()
#ifdef TORQUE_NAVIGATION_ENABLED
addGroup("Pathfinding");
addField("areaCosts", TypeF32Vector, Offset(mAreaCosts, AIPlayer),
"Vector of costs for each PolyArea.");
addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, AIPlayer),
"Allow the character to walk on dry land.");
addField("allowJump", TypeBool, Offset(mLinkTypes.jump, AIPlayer),
@ -785,6 +790,7 @@ void AIPlayer::moveToNode(S32 node)
bool AIPlayer::setPathDestination(const Point3F &pos)
{
#ifdef TORQUE_NAVIGATION_ENABLED
// Pathfinding only happens on the server.
if(!isServerObject())
return false;
@ -799,6 +805,13 @@ bool AIPlayer::setPathDestination(const Point3F &pos)
return false;
}
mFilter.setIncludeFlags(mLinkTypes.getFlags());
mFilter.setExcludeFlags(mLinkTypes.getExcludeFlags());
for (U32 i = 0; i < PolyAreas::NumAreas; i++)
{
mFilter.setAreaCost((PolyAreas)i, mAreaCosts[i]);
}
// Create a new path.
NavPath *path = new NavPath();
@ -808,6 +821,7 @@ bool AIPlayer::setPathDestination(const Point3F &pos)
path->mFromSet = path->mToSet = true;
path->mAlwaysRender = true;
path->mLinkTypes = mLinkTypes;
path->mFilter = mFilter;
path->mXray = true;
// Paths plan automatically upon being registered.
if(!path->registerObject())
@ -839,6 +853,9 @@ bool AIPlayer::setPathDestination(const Point3F &pos)
path->deleteObject();
return false;
}
#else
setMoveDestination(pos, false);
#endif
}
DefineEngineMethod(AIPlayer, setPathDestination, bool, (Point3F goal),,

View file

@ -59,8 +59,6 @@ GuiNavEditorCtrl::GuiNavEditorCtrl()
mIsDirty = false;
mStartDragMousePoint = InvalidMousePoint;
mMesh = NULL;
mPlayer = mCurPlayer = NULL;
mSpawnClass = mSpawnDatablock = "";
}
GuiNavEditorCtrl::~GuiNavEditorCtrl()
@ -97,11 +95,6 @@ void GuiNavEditorCtrl::initPersistFields()
docsURL;
addField("isDirty", TypeBool, Offset(mIsDirty, GuiNavEditorCtrl));
addField("spawnClass", TypeRealString, Offset(mSpawnClass, GuiNavEditorCtrl),
"Class of object to spawn in test mode.");
addField("spawnDatablock", TypeRealString, Offset(mSpawnDatablock, GuiNavEditorCtrl),
"Datablock to give new objects in test mode.");
Parent::initPersistFields();
}
@ -140,79 +133,6 @@ DefineEngineMethod(GuiNavEditorCtrl, getMesh, S32, (),,
return object->getMeshId();
}
S32 GuiNavEditorCtrl::getPlayerId()
{
return mPlayer.isNull() ? 0 : mPlayer->getId();
}
DefineEngineMethod(GuiNavEditorCtrl, getPlayer, S32, (),,
"@brief Select a NavMesh object.")
{
return object->getPlayerId();
}
void GuiNavEditorCtrl::deselect()
{
if(!mMesh.isNull())
mMesh->setSelected(false);
mMesh = NULL;
mPlayer = mCurPlayer = NULL;
}
DefineEngineMethod(GuiNavEditorCtrl, deselect, void, (),,
"@brief Deselect whatever is currently selected in the editor.")
{
object->deselect();
}
void GuiNavEditorCtrl::spawnPlayer(const Point3F &pos)
{
SceneObject *obj = (SceneObject*)Sim::spawnObject(mSpawnClass, mSpawnDatablock);
if(obj)
{
MatrixF mat(true);
mat.setPosition(pos);
obj->setTransform(mat);
SimObject* cleanup = Sim::findObject("MissionCleanup");
if(cleanup)
{
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
missionCleanup->addObject(obj);
}
mPlayer = obj;
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(obj);
if (asAIPlayer) //try direct
{
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(obj);
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
}
else
{
#endif
Con::executef(this, "onPlayerSelected");
#ifdef TORQUE_NAVIGATION_ENABLED
}
}
#endif
}
}
DefineEngineMethod(GuiNavEditorCtrl, spawnPlayer, void, (),,
"@brief Spawn an AIPlayer at the centre of the screen.")
{
Point3F c;
if(object->get3DCentre(c))
object->spawnPlayer(c);
}
void GuiNavEditorCtrl::get3DCursor(GuiCursor *&cursor,
bool &visible,
const Gui3DMouseEvent &event_)
@ -277,89 +197,6 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
mouseLock();
return;
// Construct a LineSegment from the camera position to 1000 meters away in
// the direction clicked.
// If that segment hits the terrain, truncate the ray to only be that length.
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
U8 keys = Input::getModifierKeys();
bool shift = keys & SI_LSHIFT;
bool ctrl = keys & SI_LCTRL;
if(mMode == mTestMode)
{
// Spawn new character
if(ctrl)
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
spawnPlayer(ri.point);
}
// Deselect character
else if(shift)
{
mPlayer = NULL;
Con::executef(this, "onPlayerDeselected");
}
// Select/move character
else
{
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
{
if(ri.object)
{
mPlayer = ri.object;
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer) //try direct
{
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
}
else
{
#endif
Con::executef(this, "onPlayerSelected");
}
#ifdef TORQUE_NAVIGATION_ENABLED
}
}
#endif
}
else if (!mPlayer.isNull() && gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer) //try direct
{
#ifdef TORQUE_NAVIGATION_ENABLED
asAIPlayer->setPathDestination(ri.point);
#else
asAIPlayer->setMoveDestination(ri.point,false);
#endif
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
sbo->getAIController()->getNav()->setPathDestination(ri.point, true);
}
}
}
}
}
}
void GuiNavEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
@ -385,22 +222,6 @@ void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
mTool->on3DMouseMove(event);
return;
//if(mSelRiver != NULL && mSelNode != -1)
//mGizmo->on3DMouseMove(event);
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
if(mMode == mTestMode)
{
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
mCurPlayer = ri.object;
else
mCurPlayer = NULL;
}
}
void GuiNavEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
@ -443,22 +264,6 @@ void GuiNavEditorCtrl::onRender(Point2I offset, const RectI &updateRect)
return;
}
static void renderBoxOutline(const Box3F &box, const ColorI &col)
{
if(box != Box3F::Invalid)
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setFillModeSolid();
desc.setZReadWrite(true, false);
desc.setBlend(true);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 20));
desc.setFillModeWireframe();
desc.setBlend(false);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 255));
}
}
void GuiNavEditorCtrl::renderScene(const RectI & updateRect)
{
GFX->setStateBlock(mZDisableSB);
@ -479,14 +284,6 @@ void GuiNavEditorCtrl::renderScene(const RectI & updateRect)
if (mTool)
mTool->onRender3D();
if(mMode == mTestMode)
{
if(!mCurPlayer.isNull())
renderBoxOutline(mCurPlayer->getWorldBox(), ColorI::BLUE);
if(!mPlayer.isNull())
renderBoxOutline(mPlayer->getWorldBox(), ColorI::GREEN);
}
duDebugDrawTorque d;
if(!mMesh.isNull())
mMesh->renderLinks(d);

View file

@ -103,17 +103,8 @@ public:
String getMode() { return mMode; }
void selectMesh(NavMesh *mesh);
void deselect();
S32 getMeshId();
S32 getPlayerId();
String mSpawnClass;
String mSpawnDatablock;
void deleteLink();
void setLinkFlags(const LinkData &d);
void spawnPlayer(const Point3F &pos);
/// @}
void setActiveTool(NavMeshTool* tool);
@ -148,14 +139,6 @@ protected:
/// @}
/// @name Test mode
/// @{
SimObjectPtr<SceneObject> mPlayer;
SimObjectPtr<SceneObject> mCurPlayer;
/// @}
Gui3DMouseEvent mLastMouseEvent;
#define InvalidMousePoint Point2I(-100,-100)

View file

@ -225,10 +225,13 @@ NavMesh::NavMesh()
mWaterVertStart = 0;
mWaterTriStart = 0;
mQuery = NULL;
}
NavMesh::~NavMesh()
{
dtFreeNavMeshQuery(mQuery);
dtFreeNavMesh(nm);
nm = NULL;
delete ctx;
@ -677,32 +680,6 @@ bool NavMesh::build(bool background, bool saveIntermediates)
return false;
}
Box3F worldBox = getWorldBox();
SceneContainer::CallbackInfo info;
info.context = PLC_Navigation;
info.boundingBox = worldBox;
m_geo = new RecastPolyList;
info.polyList = m_geo;
info.key = this;
getContainer()->findObjects(worldBox, StaticObjectType | DynamicShapeObjectType, buildCallback, &info);
// Parse water objects into the same list, but remember how much geometry was /not/ water.
mWaterVertStart = m_geo->getVertCount();
mWaterTriStart = m_geo->getTriCount();
if (mWaterMethod != Ignore)
{
getContainer()->findObjects(worldBox, WaterObjectType, buildCallback, &info);
}
// Check for no geometry.
if (!m_geo->getVertCount())
{
m_geo->clear();
return false;
}
m_geo->getChunkyMesh();
// Needed for the recast config and generation params.
Box3F rc_box = DTStoRC(getWorldBox());
S32 gw = 0, gh = 0;
@ -803,33 +780,6 @@ void NavMesh::createNewFile()
mFileName = StringTable->insert(fileName.c_str());
}
void NavMesh::updateConfig()
{
//// Build rcConfig object from our console members.
//dMemset(&cfg, 0, sizeof(cfg));
//cfg.cs = mCellSize;
//cfg.ch = mCellHeight;
//Box3F box = DTStoRC(getWorldBox());
//rcVcopy(cfg.bmin, box.minExtents);
//rcVcopy(cfg.bmax, box.maxExtents);
//rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
//cfg.walkableHeight = mCeil(mWalkableHeight / mCellHeight);
//cfg.walkableClimb = mCeil(mWalkableClimb / mCellHeight);
//cfg.walkableRadius = mCeil(mWalkableRadius / mCellSize);
//cfg.walkableSlopeAngle = mWalkableSlope;
//cfg.borderSize = cfg.walkableRadius + 3;
//cfg.detailSampleDist = mDetailSampleDist;
//cfg.detailSampleMaxError = mDetailSampleMaxError;
//cfg.maxEdgeLen = mMaxEdgeLen;
//cfg.maxSimplificationError = mMaxSimplificationError;
//cfg.maxVertsPerPoly = mMaxVertsPerPoly;
//cfg.minRegionArea = mMinRegionArea;
//cfg.mergeRegionArea = mMergeRegionArea;
//cfg.tileSize = mTileSize / cfg.cs;
}
S32 NavMesh::getTile(const Point3F& pos)
{
if(mBuilding)
@ -938,6 +888,8 @@ void NavMesh::buildNextTile()
if(!mDirtyTiles.empty())
{
dtFreeNavMeshQuery(mQuery);
mQuery = NULL;
// Pop a single dirty tile and process it.
U32 i = mDirtyTiles.front();
mDirtyTiles.pop_front();
@ -1002,6 +954,12 @@ void NavMesh::buildNextTile()
// Did we just build the last tile?
if(mDirtyTiles.empty())
{
mQuery = dtAllocNavMeshQuery();
if (dtStatusFailed(mQuery->init(nm, 2048)))
{
Con::errorf("NavMesh - Failed to create navmesh query");
}
ctx->stopTimer(RC_TIMER_TOTAL);
if(getEventManager())
{
@ -1012,6 +970,15 @@ void NavMesh::buildNextTile()
mBuilding = false;
}
}
if (mQuery == NULL)
{
mQuery = dtAllocNavMeshQuery();
if (dtStatusFailed(mQuery->init(nm, 2048)))
{
Con::errorf("NavMesh - Failed to create navmesh query");
}
}
}
unsigned char *NavMesh::buildTileData(const Tile &tile, U32 &dataSize)
@ -1614,11 +1581,13 @@ void NavMesh::renderToDrawer()
m_drawMode == DRAWMODE_NAVMESH_INVIS))
{
if (m_drawMode != DRAWMODE_NAVMESH_INVIS)
duDebugDrawNavMesh(&mDbgDraw, *n->nm, 0);
duDebugDrawNavMeshWithClosedList(&mDbgDraw, *n->nm, *n->mQuery, 0);
if(m_drawMode == DRAWMODE_NAVMESH_BVTREE)
duDebugDrawNavMeshBVTree(&mDbgDraw, *n->nm);
if(m_drawMode == DRAWMODE_NAVMESH_PORTALS)
duDebugDrawNavMeshPortals(&mDbgDraw, *n->nm);
if (m_drawMode == DRAWMODE_NAVMESH_NODES)
duDebugDrawNavMeshNodes(&mDbgDraw, *n->mQuery);
}
mDbgDraw.depthMask(true, false);

View file

@ -187,6 +187,8 @@ public:
/// Delete the selected link.
void deleteLink(U32 idx);
dtNavMeshQuery* getNavMeshQuery() { return mQuery; }
/// @}
/// Should small characters use this mesh?
@ -381,16 +383,9 @@ private:
/// @}
/// @name Intermediate data
/// @{
/// Updates our config from console members.
void updateConfig();
dtNavMesh *nm;
rcContext *ctx;
/// @}
dtNavMeshQuery* mQuery;
/// @name Cover
/// @{

View file

@ -0,0 +1,302 @@
#include "navMeshTestTool.h"
#include "navigation/guiNavEditorCtrl.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
#include "scene/sceneManager.h"
#include "math/mathUtils.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/AI/AIController.h"
IMPLEMENT_CONOBJECT(NavMeshTestTool);
//// take control
// GameConnection* conn = GameConnection::getConnectionToServer();
// if (conn)
// conn->setControlObject(static_cast<GameBase*>(obj));
//}
static void renderBoxOutline(const Box3F& box, const ColorI& col)
{
if (box != Box3F::Invalid)
{
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setFillModeSolid();
desc.setZReadWrite(true, false);
desc.setBlend(true);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 20));
desc.setFillModeWireframe();
desc.setBlend(false);
GFX->getDrawUtil()->drawCube(desc, box, ColorI(col, 255));
}
}
void NavMeshTestTool::spawnPlayer(const Point3F& position)
{
if (mSpawnClass.isEmpty() || mSpawnDatablock.isEmpty())
{
Con::warnf("NavMeshTestTool::spawnPlayer - spawn class/datablock not set!");
return;
}
SimObject* spawned = Sim::spawnObject(mSpawnClass, mSpawnDatablock);
SceneObject* obj = dynamic_cast<SceneObject*>(spawned);
if (!obj)
{
Con::warnf("NavMeshTestTool::spawnPlayer - spawn failed or not a SceneObject");
if (spawned)
spawned->deleteObject();
return;
}
obj->setPosition(position);
obj->registerObject();
SimObject* cleanup = Sim::findObject("MissionCleanup");
if (cleanup)
{
SimGroup* missionCleanup = dynamic_cast<SimGroup*>(cleanup);
missionCleanup->addObject(obj);
}
mPlayer = obj;
Con::executef(this, "onPlayerSpawned", obj->getIdString());
}
void NavMeshTestTool::drawAgent(duDebugDrawTorque& dd, const F32* pos, F32 r, F32 h, F32 c, const U32 col)
{
dd.depthMask(false);
// Agent dimensions.
duDebugDrawCylinderWire(&dd, pos[0] - r, pos[1] + 0.02f, pos[2] - r, pos[0] + r, pos[1] + h, pos[2] + r, col, 2.0f);
duDebugDrawCircle(&dd, pos[0], pos[1] + c, pos[2], r, duRGBA(0, 0, 0, 64), 1.0f);
U32 colb = duRGBA(0, 0, 0, 196);
dd.begin(DU_DRAW_LINES);
dd.vertex(pos[0], pos[1] - c, pos[2], colb);
dd.vertex(pos[0], pos[1] + c, pos[2], colb);
dd.vertex(pos[0] - r / 2, pos[1] + 0.02f, pos[2], colb);
dd.vertex(pos[0] + r / 2, pos[1] + 0.02f, pos[2], colb);
dd.vertex(pos[0], pos[1] + 0.02f, pos[2] - r / 2, colb);
dd.vertex(pos[0], pos[1] + 0.02f, pos[2] + r / 2, colb);
dd.end();
dd.depthMask(true);
}
NavMeshTestTool::NavMeshTestTool()
{
mSpawnClass = String::EmptyString;
mSpawnDatablock = String::EmptyString;
mPlayer = NULL;
mCurPlayer = NULL;
mPathStart = Point3F::Max;
mPathEnd = Point3F::Max;
}
void NavMeshTestTool::onActivated(const Gui3DMouseEvent& evt)
{
Con::executef(this, "onActivated");
}
void NavMeshTestTool::onDeactivated()
{
Con::executef(this, "onDeactivated");
}
void NavMeshTestTool::on3DMouseDown(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
bool shift = evt.modifier & SI_LSHIFT;
bool ctrl = evt.modifier & SI_LCTRL;
if (ctrl)
{
if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
spawnPlayer(ri.point);
}
return;
}
if (shift)
{
mPlayer = NULL;
Con::executef(this, "onPlayerDeselected");
return;
}
if (gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
{
if (!ri.object)
return;
mPlayer = ri.object;
#ifdef TORQUE_NAVIGATION_ENABLED
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer)
{
Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags()));
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo && sbo->getAIController() && sbo->getAIController()->mControllerData)
{
Con::executef(this, "onPlayerSelected", Con::getIntArg(sbo->getAIController()->mControllerData->mLinkTypes.getFlags()));
}
else
{
Con::executef(this, "onPlayerSelected");
}
}
#else
Con::executef(this, "onPlayerSelected");
#endif
}
else if (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
if (mPlayer.isNull())
{
if (mPathStart != Point3F::Max && mPathEnd != Point3F::Max) // start a new path.
{
mPathStart = Point3F::Max;
mPathEnd = Point3F::Max;
}
if (mPathStart != Point3F::Max)
{
mPathEnd = ri.point;
}
else
{
mPathStart = ri.point;
}
}
else
{
AIPlayer* asAIPlayer = dynamic_cast<AIPlayer*>(mPlayer.getPointer());
if (asAIPlayer) //try direct
{
asAIPlayer->setPathDestination(ri.point);
mPathStart = mPlayer->getPosition();
mPathEnd = ri.point;
}
else
{
ShapeBase* sbo = dynamic_cast<ShapeBase*>(mPlayer.getPointer());
if (sbo->getAIController())
{
if (sbo->getAIController()->mControllerData)
{
sbo->getAIController()->getNav()->setPathDestination(ri.point, true);
mPathStart = mPlayer->getPosition();
mPathEnd = ri.point;
}
}
}
}
}
}
void NavMeshTestTool::on3DMouseMove(const Gui3DMouseEvent& evt)
{
if (mNavMesh.isNull())
return;
Point3F startPnt = evt.pos;
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
RayInfo ri;
if (gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))
mCurPlayer = ri.object;
else
mCurPlayer = NULL;
}
void NavMeshTestTool::onRender3D()
{
if (mNavMesh.isNull())
return;
static const U32 startCol = duRGBA(128, 25, 0, 192);
static const U32 endCol = duRGBA(51, 102, 0, 129);
const F32 agentRadius = mNavMesh->mWalkableRadius;
const F32 agentHeight = mNavMesh->mWalkableHeight;
const F32 agentClimb = mNavMesh->mWalkableClimb;
duDebugDrawTorque dd;
dd.depthMask(false);
if (mPathStart != Point3F::Max)
{
drawAgent(dd, DTStoRC(mPathStart), agentRadius, agentHeight, agentClimb, startCol);
}
if (mPathEnd != Point3F::Max)
{
drawAgent(dd, DTStoRC(mPathEnd), agentRadius, agentHeight, agentClimb, endCol);
}
dd.depthMask(true);
dd.immediateRender();
if (!mCurPlayer.isNull())
renderBoxOutline(mCurPlayer->getWorldBox(), ColorI::BLUE);
if (!mPlayer.isNull())
renderBoxOutline(mPlayer->getWorldBox(), ColorI::GREEN);
}
bool NavMeshTestTool::updateGuiInfo()
{
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
String text;
if (statusbar)
Con::executef(statusbar, "setInfo", text.c_str());
text = "";
if (selectionBar)
selectionBar->setText(text);
return true;
}
S32 NavMeshTestTool::getPlayerId()
{
return mPlayer.isNull() ? 0 : mPlayer->getId();
}
DefineEngineMethod(NavMeshTestTool, getPlayer, S32, (), ,
"@brief Return the current player id.")
{
return object->getPlayerId();
}
DefineEngineMethod(NavMeshTestTool, setSpawnClass, void, (String className), , "")
{
object->setSpawnClass(className);
}
DefineEngineMethod(NavMeshTestTool, setSpawnDatablock, void, (String dbName), , "")
{
object->setSpawnDatablock(dbName);
}

View file

@ -0,0 +1,47 @@
#ifndef _NAVMESHTESTTOOL_H_
#define _NAVMESHTESTTOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
class NavMeshTestTool : public NavMeshTool
{
typedef NavMeshTool Parent;
protected:
String mSpawnClass;
String mSpawnDatablock;
SimObjectPtr<SceneObject> mPlayer;
SimObjectPtr<SceneObject> mCurPlayer;
Point3F mPathStart;
Point3F mPathEnd;
public:
DECLARE_CONOBJECT(NavMeshTestTool);
void spawnPlayer(const Point3F& position);
void drawAgent(duDebugDrawTorque& dd, const F32* pos, F32 r, F32 h, F32 c, const U32 col);
NavMeshTestTool();
virtual ~NavMeshTestTool() {}
void onActivated(const Gui3DMouseEvent& evt) override;
void onDeactivated() override;
void on3DMouseDown(const Gui3DMouseEvent& evt) override;
void on3DMouseMove(const Gui3DMouseEvent& evt) override;
void onRender3D() override;
bool updateGuiInfo() override;
S32 getPlayerId();
void setSpawnClass(String className) { mSpawnClass = className; }
void setSpawnDatablock(String dbName) { mSpawnDatablock = dbName; }
};
#endif // !_NAVMESHTESTTOOL_H_

View file

@ -69,14 +69,11 @@ NavPath::NavPath() :
mXray = false;
mRenderSearch = false;
mQuery = NULL;
mStatus = DT_FAILURE;
}
NavPath::~NavPath()
{
dtFreeNavMeshQuery(mQuery);
mQuery = NULL;
}
void NavPath::checkAutoUpdate()
@ -264,9 +261,6 @@ bool NavPath::onAdd()
if(isServerObject())
{
mQuery = dtAllocNavMeshQuery();
if(!mQuery)
return false;
checkAutoUpdate();
if(!plan())
setProcessTick(true);
@ -293,7 +287,8 @@ bool NavPath::init()
return false;
// Initialise our query.
if(dtStatusFailed(mQuery->init(mMesh->getNavMesh(), MaxPathLen)))
mQuery = mMesh->getNavMeshQuery();
if(!mQuery)
return false;
mPoints.clear();
@ -372,9 +367,6 @@ void NavPath::resize()
bool NavPath::plan()
{
PROFILE_SCOPE(NavPath_plan);
// Initialise filter.
mFilter.setIncludeFlags(mLinkTypes.getFlags());
// Initialise query and visit locations.
if(!init())
return false;
@ -641,6 +633,7 @@ void NavPath::renderSimple(ObjectRenderInst *ri, SceneRenderState *state, BaseMa
{
duDebugDrawTorque dd;
duDebugDrawNavMeshNodes(&dd, *np->mQuery);
dd.immediateRender();
}
}
}

View file

@ -57,7 +57,7 @@ public:
/// What sort of link types are we allowed to move on?
LinkData mLinkTypes;
dtQueryFilter mFilter;
/// Plan the path.
bool plan();
@ -152,7 +152,6 @@ private:
dtNavMeshQuery *mQuery;
dtStatus mStatus;
dtQueryFilter mFilter;
S32 mCurIndex;
Vector<Point3F> mPoints;
Vector<U16> mFlags;

View file

@ -52,7 +52,8 @@ inline void rcCol(unsigned int col, U8 &r, U8 &g, U8 &b, U8 &a)
}
enum PolyAreas {
GroundArea = 1,
NullArea = 0,
GroundArea,
WaterArea,
OffMeshArea,
NumAreas
@ -99,6 +100,10 @@ struct LinkData {
(climb ? ClimbFlag : 0) |
(teleport ? TeleportFlag : 0);
}
U16 getExcludeFlags() const
{
return AllFlags & ~getFlags();
}
};
#endif

View file

@ -857,7 +857,27 @@ $guiContent = new GuiNavEditorCtrl(NavEditorGui, EditorGuiGroup) {
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character walk on ground?";
toolTip = "Can this character walk on ground?";
hovertime = "1000";
isContainer = "0";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
internalName = "LinkSwimFlag";
class = "NavMeshTestFlagButton";
text = " Swim";
buttonType = "ToggleButton";
useMouseEvents = "0";
extent = "159 15";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "0";
tooltipProfile = "GuiToolTipProfile";
toolTip = "Can this character swim?";
hovertime = "1000";
isContainer = "0";
canSave = "1";

View file

@ -77,68 +77,5 @@ $guiContent = new GuiControl(NavEditorToolbar,EditorGuiGroup) {
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
text = "Mesh";
groupNum = "-1";
buttonType = "ToggleButton";
useMouseEvents = "0";
position = "167 1";
extent = "50 30";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "1";
variable = "$Nav::Editor::renderMesh";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
internalName = "MeshButton";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
text = "Portals";
groupNum = "-1";
buttonType = "ToggleButton";
useMouseEvents = "0";
position = "224 1";
extent = "54 30";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "1";
variable = "$Nav::Editor::renderPortals";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
internalName = "PortalButton";
canSave = "1";
canSaveDynamicFields = "0";
};
new GuiCheckBoxCtrl() {
text = "BV tree";
groupNum = "-1";
buttonType = "ToggleButton";
useMouseEvents = "0";
position = "286 1";
extent = "140 30";
minExtent = "8 2";
horizSizing = "right";
vertSizing = "bottom";
profile = "ToolsGuiCheckBoxProfile";
visible = "1";
active = "1";
variable = "$Nav::Editor::renderBVTree";
tooltipProfile = "GuiToolTipProfile";
hovertime = "1000";
isContainer = "0";
internalName = "BVTreeButton";
canSave = "1";
canSaveDynamicFields = "0";
};
};
//--- OBJECT WRITE END ---

View file

@ -66,6 +66,13 @@ function initializeNavEditor()
buttonImage = "ToolsModule:nav_link_n_image";
};
new NavMeshTestTool()
{
internalName = "TestTool";
toolTip = "PathFinding Test tool";
buttonImage = "ToolsModule:3rd_person_camera_n_image";
};
new TileTool()
{
internalName = "TileTool";
@ -140,7 +147,7 @@ function EditorGui::SetNavPalletBar()
EWToolsPaletteWindow.addButton("LinkMode", "ToolsModule:nav_link_n_image", "NavEditorGui.setActiveTool(NavMeshTools->LinkTool);", "", "Create off-mesh links", "2");
// EWToolsPaletteWindow.addButton("CoverMode", "ToolsModule:nav_cover_n_image", "NavEditorGui.setMode(\"CoverMode\");", "","Edit cover", "3");
// EWToolsPaletteWindow.addButton("TileMode", "ToolsModule:select_bounds_n_image", "NavEditorGui.setMode(\"TileMode\");", "", "View tiles", "4");
// EWToolsPaletteWindow.addButton("TestMode", "ToolsModule:3rd_person_camera_n_image", "NavEditorGui.setMode(\"TestMode\");", "", "Test pathfinding", "5");
EWToolsPaletteWindow.addButton("TestMode", "ToolsModule:3rd_person_camera_n_image", "NavEditorGui.setActiveTool(NavMeshTools->TestTool);", "", "Test pathfinding", "5");
EWToolsPaletteWindow.addButton("TileMode", "ToolsModule:select_bounds_n_image", "NavEditorGui.setActiveTool(NavMeshTools->TileTool);" , "", "View and Edit Tiles", "4");
EWToolsPaletteWindow.refresh();
}
@ -263,7 +270,7 @@ function NavEditorPlugin::initSettings(%this)
EditorSettings.beginGroup("NavEditor", true);
EditorSettings.setDefaultValue("SpawnClass", "AIPlayer");
EditorSettings.setDefaultValue("SpawnDatablock", "DefaultPlayerData");
EditorSettings.setDefaultValue("SpawnDatablock", "ProtoPlayer");
EditorSettings.endGroup();
}
@ -273,9 +280,6 @@ function NavEditorPlugin::readSettings(%this)
EditorSettings.beginGroup("NavEditor", true);
// Currently these are globals because of the way they are accessed in navMesh.cpp.
$Nav::Editor::renderMesh = EditorSettings.value("RenderMesh");
$Nav::Editor::renderPortals = EditorSettings.value("RenderPortals");
$Nav::Editor::renderBVTree = EditorSettings.value("RenderBVTree");
NavEditorGui.spawnClass = EditorSettings.value("SpawnClass");
NavEditorGui.spawnDatablock = EditorSettings.value("SpawnDatablock");
NavEditorGui.backgroundBuild = EditorSettings.value("BackgroundBuild");
@ -295,9 +299,6 @@ function NavEditorPlugin::writeSettings(%this)
{
EditorSettings.beginGroup("NavEditor", true);
EditorSettings.setValue("RenderMesh", $Nav::Editor::renderMesh);
EditorSettings.setValue("RenderPortals", $Nav::Editor::renderPortals);
EditorSettings.setValue("RenderBVTree", $Nav::Editor::renderBVTree);
EditorSettings.setValue("SpawnClass", NavEditorGui.spawnClass);
EditorSettings.setValue("SpawnDatablock", NavEditorGui.spawnDatablock);
EditorSettings.setValue("BackgroundBuild", NavEditorGui.backgroundBuild);

View file

@ -411,6 +411,142 @@ function NavMeshLinkBiDirection::onClick(%this)
//------------------------------------------------------
function NavMeshTestTool::onActivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->SelectActions.setVisible(false);
%actions->LinkActions.setVisible(false);
%actions->CoverActions.setVisible(false);
%actions->TileActions.setVisible(false);
%actions->TestActions.setVisible(false);
%properties = NavEditorOptionsWindow->PropertiesBox;
%properties->LinkProperties.setVisible(false);
%properties->TileProperties.setVisible(false);
%properties->TestProperties.setVisible(false);
%actions->TestActions.setVisible(true);
%properties->TestProperties.setVisible(true);
}
function NavMeshTestTool::onDeactivated(%this)
{
NavInspector.setVisible(false);
%actions = NavEditorOptionsWindow->ActionsBox;
%actions->SelectActions.setVisible(false);
%actions->LinkActions.setVisible(false);
%actions->CoverActions.setVisible(false);
%actions->TileActions.setVisible(false);
%actions->TestActions.setVisible(false);
%properties = NavEditorOptionsWindow->PropertiesBox;
%properties->LinkProperties.setVisible(false);
%properties->TileProperties.setVisible(false);
%properties->TestProperties.setVisible(false);
}
function updateTestData(%control, %flags)
{
%control->LinkWalkFlag.setActive(true);
%control->LinkSwimFlag.setActive(true);
%control->LinkJumpFlag.setActive(true);
%control->LinkDropFlag.setActive(true);
%control->LinkLedgeFlag.setActive(true);
%control->LinkClimbFlag.setActive(true);
%control->LinkTeleportFlag.setActive(true);
%control->LinkWalkFlag.setStateOn(%flags & $Nav::WalkFlag);
%control->LinkSwimFlag.setStateOn(%flags & $Nav::SwimFlag);
%control->LinkJumpFlag.setStateOn(%flags & $Nav::JumpFlag);
%control->LinkDropFlag.setStateOn(%flags & $Nav::DropFlag);
%control->LinkLedgeFlag.setStateOn(%flags & $Nav::LedgeFlag);
%control->LinkClimbFlag.setStateOn(%flags & $Nav::ClimbFlag);
%control->LinkTeleportFlag.setStateOn(%flags & $Nav::TeleportFlag);
}
function getTestFlags(%control)
{
return (%control->LinkWalkFlag.isStateOn() ? $Nav::WalkFlag : 0) |
(%control->LinkSwimFlag.isStateOn() ? $Nav::SwimFlag : 0) |
(%control->LinkJumpFlag.isStateOn() ? $Nav::JumpFlag : 0) |
(%control->LinkDropFlag.isStateOn() ? $Nav::DropFlag : 0) |
(%control->LinkLedgeFlag.isStateOn() ? $Nav::LedgeFlag : 0) |
(%control->LinkClimbFlag.isStateOn() ? $Nav::ClimbFlag : 0) |
(%control->LinkTeleportFlag.isStateOn() ? $Nav::TeleportFlag : 0);
}
function disableTestData(%control)
{
%control->LinkWalkFlag.setActive(false);
%control->LinkSwimFlag.setActive(false);
%control->LinkJumpFlag.setActive(false);
%control->LinkDropFlag.setActive(false);
%control->LinkLedgeFlag.setActive(false);
%control->LinkClimbFlag.setActive(false);
%control->LinkTeleportFlag.setActive(false);
}
function getControllerDataFlags(%controllerData)
{
return (%controllerData.allowWalk ? $Nav::WalkFlag : 0 ) |
(%controllerData.allowSwim ? $Nav::SwimFlag : 0 ) |
(%controllerData.allowJump ? $Nav::JumpFlag : 0 ) |
(%controllerData.allowDrop ? $Nav::DropFlag : 0 ) |
(%controllerData.allowLedge ? $Nav::LedgeFlag : 0) |
(%controllerData.allowClimb ? $Nav::ClimbFlag : 0) |
(%controllerData.allowTeleport ? $Nav::TeleportFlag : 0 );
}
function NavMeshTestTool::updateTestFlags(%this)
{
if(isObject(%this.getPlayer()))
{
%properties = NavEditorOptionsWindow-->TestProperties;
%player = %this.getPlayer();
%controllerData = %player.getDatablock().aiControllerData;
%controllerData.allowWalk = %properties->LinkWalkFlag.isStateOn();
%controllerData.allowSwim = %properties->LinkSwimFlag.isStateOn();
%controllerData.allowJump = %properties->LinkJumpFlag.isStateOn();
%controllerData.allowDrop = %properties->LinkDropFlag.isStateOn();
%controllerData.allowLedge = %properties->LinkLedgeFlag.isStateOn();
%controllerData.allowClimb = %properties->LinkClimbFlag.isStateOn();
%controllerData.allowTeleport = %properties->LinkTeleportFlag.isStateOn();
%player.aiController = new AIController(){ ControllerData = %controllerData; };
%player.setAIController(%player.aiController);
}
}
function NavMeshTestTool::onPlayerSelected(%this, %flags)
{
if (!isObject(%this.getPlayer().aiController) || (isObject(%this.getPlayer().aiController) && !(%this.getPlayer().isMemberOfClass("AIPlayer"))))
{
%this.getPlayer().aiController = new AIController(){ ControllerData = %this.getPlayer().getDatablock().aiControllerData; };
%this.getPlayer().setAIController(%this.getPlayer().aiController);
%flags = getControllerDataFlags(%this.getPlayer().getDatablock().aiControllerData);
}
NavMeshIgnore(%this.getPlayer(), true);
%this.getPlayer().setDamageState("Enabled");
updateTestData(NavEditorOptionsWindow-->TestProperties, %flags);
}
function NavMeshTestTool::onPlayerDeselected(%this)
{
disableTestData(NavEditorOptionsWindow-->TestProperties);
}
function NavMeshTestFlagButton::onClick(%this)
{
NavMeshTools->TestTool.updateTestFlags();
}
//------------------------------------------------------
function TileTool::onActivated(%this)
{
NavInspector.setVisible(false);
@ -540,13 +676,13 @@ function NavEditorGui::deleteSelected(%this)
toolsMessageBoxYesNo("Warning",
"Are you sure you want to delete" SPC NavEditorGui.selectedObject.getName(),
"NavEditorGui.deleteMesh();");
case "TestMode":
%this.getPlayer().delete();
%this.onPlayerDeselected();
case "LinkMode":
%this.deleteLink();
%this.isDirty = true;
}
// case "TestMode":
// %this.getPlayer().delete();
// %this.onPlayerDeselected();
// case "LinkMode":
// %this.deleteLink();
// %this.isDirty = true;
// }
}
function NavEditorGui::buildSelectedMeshes(%this)
@ -567,47 +703,6 @@ function NavEditorGui::buildLinks(%this)
}
}
function NavEditorGui::onLinkSelected(%this, %flags)
{
updateLinkData(NavEditorOptionsWindow-->LinkProperties, %flags);
}
function NavEditorGui::onPlayerSelected(%this, %flags)
{
if (!isObject(%this.getPlayer().aiController) || (isObject(%this.getPlayer().aiController) && !(%this.getPlayer().isMemberOfClass("AIPlayer"))))
{
%this.getPlayer().aiController = new AIController(){ ControllerData = %this.getPlayer().getDatablock().aiControllerData; };
%this.getPlayer().setAIController(%this.getPlayer().aiController);
}
NavMeshIgnore(%this.getPlayer(), true);
%this.getPlayer().setDamageState("Enabled");
updateLinkData(NavEditorOptionsWindow-->TestProperties, %flags);
}
function NavEditorGui::updateTestFlags(%this)
{
if(isObject(%this.getPlayer()))
{
%properties = NavEditorOptionsWindow-->TestProperties;
%player = %this.getPlayer();
%player.allowWwalk = %properties->LinkWalkFlag.isStateOn();
%player.allowJump = %properties->LinkJumpFlag.isStateOn();
%player.allowDrop = %properties->LinkDropFlag.isStateOn();
%player.allowLedge = %properties->LinkLedgeFlag.isStateOn();
%player.allowClimb = %properties->LinkClimbFlag.isStateOn();
%player.allowTeleport = %properties->LinkTeleportFlag.isStateOn();
%this.isDirty = true;
}
}
function NavEditorGui::onPlayerDeselected(%this)
{
disableLinkData(NavEditorOptionsWindow-->TestProperties);
}
function NavEditorGui::createCoverPoints(%this)
{
if(isObject(%this.getMesh()))
@ -718,11 +813,6 @@ function ENavEditorPaletteButton::onClick(%this)
//-----------------------------------------------------------------------------
function NavMeshTestFlagButton::onClick(%this)
{
NavEditorGui.updateTestFlags();
}
singleton GuiControlProfile(NavEditorProfile)
{
canKeyFocus = true;