From 6d36e17d915fb29b95b3b0dbc2d44471dbee3e81 Mon Sep 17 00:00:00 2001 From: marauder2k7 Date: Sat, 26 Jul 2025 10:34:19 +0100 Subject: [PATCH] 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 --- Engine/source/T3D/AI/AIController.cpp | 25 ++ Engine/source/T3D/AI/AIController.h | 2 + Engine/source/T3D/AI/AINavigation.cpp | 1 + Engine/source/T3D/aiPlayer.cpp | 19 +- Engine/source/navigation/guiNavEditorCtrl.cpp | 203 ------------ Engine/source/navigation/guiNavEditorCtrl.h | 17 - Engine/source/navigation/navMesh.cpp | 77 ++--- Engine/source/navigation/navMesh.h | 11 +- .../navMeshTools/navMeshTestTool.cpp | 302 ++++++++++++++++++ .../navigation/navMeshTools/navMeshTestTool.h | 47 +++ Engine/source/navigation/navPath.cpp | 13 +- Engine/source/navigation/navPath.h | 3 +- Engine/source/navigation/torqueRecast.h | 7 +- .../game/tools/navEditor/NavEditorGui.gui | 22 +- .../game/tools/navEditor/NavEditorToolbar.gui | 63 ---- .../game/tools/navEditor/main.tscript | 17 +- .../game/tools/navEditor/navEditor.tscript | 196 +++++++++--- 17 files changed, 604 insertions(+), 421 deletions(-) create mode 100644 Engine/source/navigation/navMeshTools/navMeshTestTool.cpp create mode 100644 Engine/source/navigation/navMeshTools/navMeshTestTool.h diff --git a/Engine/source/T3D/AI/AIController.cpp b/Engine/source/T3D/AI/AIController.cpp index 8546826c7..73f179d7d 100644 --- a/Engine/source/T3D/AI/AIController.cpp +++ b/Engine/source/T3D/AI/AIController.cpp @@ -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); diff --git a/Engine/source/T3D/AI/AIController.h b/Engine/source/T3D/AI/AIController.h index 18d95e210..340ee5d04 100644 --- a/Engine/source/T3D/AI/AIController.h +++ b/Engine/source/T3D/AI/AIController.h @@ -168,6 +168,8 @@ public: /// Types of link we can use. LinkData mLinkTypes; + dtQueryFilter mFilter; + Vector mAreaCosts; AINavigation::NavSize mNavSize; #endif Delegate resolveYawPtr; diff --git a/Engine/source/T3D/AI/AINavigation.cpp b/Engine/source/T3D/AI/AINavigation.cpp index 258939f06..ffb29fdd9 100644 --- a/Engine/source/T3D/AI/AINavigation.cpp +++ b/Engine/source/T3D/AI/AINavigation.cpp @@ -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()) { diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 68032893a..a3e54d9ab 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -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),, diff --git a/Engine/source/navigation/guiNavEditorCtrl.cpp b/Engine/source/navigation/guiNavEditorCtrl.cpp index 3e71ba240..72cd5ff71 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.cpp +++ b/Engine/source/navigation/guiNavEditorCtrl.cpp @@ -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(cleanup); - missionCleanup->addObject(obj); - } - mPlayer = obj; -#ifdef TORQUE_NAVIGATION_ENABLED - AIPlayer* asAIPlayer = dynamic_cast(obj); - if (asAIPlayer) //try direct - { - Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); - } - else - { - ShapeBase* sbo = dynamic_cast(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(mPlayer.getPointer()); - if (asAIPlayer) //try direct - { - Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); - } - else - { - ShapeBase* sbo = dynamic_cast(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(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(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); diff --git a/Engine/source/navigation/guiNavEditorCtrl.h b/Engine/source/navigation/guiNavEditorCtrl.h index db60c4c85..5d1642143 100644 --- a/Engine/source/navigation/guiNavEditorCtrl.h +++ b/Engine/source/navigation/guiNavEditorCtrl.h @@ -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 mPlayer; - SimObjectPtr mCurPlayer; - - /// @} - Gui3DMouseEvent mLastMouseEvent; #define InvalidMousePoint Point2I(-100,-100) diff --git a/Engine/source/navigation/navMesh.cpp b/Engine/source/navigation/navMesh.cpp index 4cc863361..cea2922a0 100644 --- a/Engine/source/navigation/navMesh.cpp +++ b/Engine/source/navigation/navMesh.cpp @@ -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); diff --git a/Engine/source/navigation/navMesh.h b/Engine/source/navigation/navMesh.h index 947a64774..61db2a32f 100644 --- a/Engine/source/navigation/navMesh.h +++ b/Engine/source/navigation/navMesh.h @@ -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 /// @{ diff --git a/Engine/source/navigation/navMeshTools/navMeshTestTool.cpp b/Engine/source/navigation/navMeshTools/navMeshTestTool.cpp new file mode 100644 index 000000000..751cb537b --- /dev/null +++ b/Engine/source/navigation/navMeshTools/navMeshTestTool.cpp @@ -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(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(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(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(mPlayer.getPointer()); + if (asAIPlayer) + { + Con::executef(this, "onPlayerSelected", Con::getIntArg(asAIPlayer->mLinkTypes.getFlags())); + } + else + { + ShapeBase* sbo = dynamic_cast(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(mPlayer.getPointer()); + if (asAIPlayer) //try direct + { + asAIPlayer->setPathDestination(ri.point); + mPathStart = mPlayer->getPosition(); + mPathEnd = ri.point; + } + else + { + ShapeBase* sbo = dynamic_cast(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); +} diff --git a/Engine/source/navigation/navMeshTools/navMeshTestTool.h b/Engine/source/navigation/navMeshTools/navMeshTestTool.h new file mode 100644 index 000000000..a229c2255 --- /dev/null +++ b/Engine/source/navigation/navMeshTools/navMeshTestTool.h @@ -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 mPlayer; + SimObjectPtr 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_ diff --git a/Engine/source/navigation/navPath.cpp b/Engine/source/navigation/navPath.cpp index 857af9730..db9ba20ee 100644 --- a/Engine/source/navigation/navPath.cpp +++ b/Engine/source/navigation/navPath.cpp @@ -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(); } } } diff --git a/Engine/source/navigation/navPath.h b/Engine/source/navigation/navPath.h index fe6246c40..194058b8a 100644 --- a/Engine/source/navigation/navPath.h +++ b/Engine/source/navigation/navPath.h @@ -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 mPoints; Vector mFlags; diff --git a/Engine/source/navigation/torqueRecast.h b/Engine/source/navigation/torqueRecast.h index 36187f255..914de002c 100644 --- a/Engine/source/navigation/torqueRecast.h +++ b/Engine/source/navigation/torqueRecast.h @@ -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 diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui index 3445d3752..ac7b55db0 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorGui.gui @@ -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"; diff --git a/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui b/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui index 38c86f678..8068d3526 100644 --- a/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui +++ b/Templates/BaseGame/game/tools/navEditor/NavEditorToolbar.gui @@ -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 --- diff --git a/Templates/BaseGame/game/tools/navEditor/main.tscript b/Templates/BaseGame/game/tools/navEditor/main.tscript index a40d2f772..d4c47a1ec 100644 --- a/Templates/BaseGame/game/tools/navEditor/main.tscript +++ b/Templates/BaseGame/game/tools/navEditor/main.tscript @@ -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); diff --git a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript index 2cfd23104..31d5e5eec 100644 --- a/Templates/BaseGame/game/tools/navEditor/navEditor.tscript +++ b/Templates/BaseGame/game/tools/navEditor/navEditor.tscript @@ -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;