off mesh connection tool

Adds off mesh connection tool
upgrade functionality to allow setting the direction to be bi-directional
added immediate draw to duDebugDrawtorque so we can draw offmesh connections
This commit is contained in:
marauder2k7 2025-07-24 23:43:35 +01:00
parent de1642c33e
commit 2df2cb5c15
8 changed files with 287 additions and 87 deletions

View file

@ -421,3 +421,25 @@ void duDebugDrawTorque::render(SceneRenderState* state)
}
}
void duDebugDrawTorque::immediateRender()
{
for (U32 i = 0; i < mDrawCache.size(); ++i)
{
const CachedDraw& draw = mDrawCache[i];
GFX->setPrimitiveBuffer(draw.primitiveBuffer);
GFX->setStateBlockByDesc(draw.state);
GFX->setupGenericShaders(GFXDevice::GSColor);
GFX->setVertexBuffer(draw.buffer);
GFX->drawIndexedPrimitive(
draw.primType,
0, // start vertex
0, // min vertex index
draw.vertexCount, // vertex count
0, // start index
draw.primitiveCount // primitive count
);
}
}

View file

@ -105,6 +105,7 @@ public:
void clearCache();
void render(SceneRenderState* state);
void immediateRender();
private:

View file

@ -323,51 +323,6 @@ void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
bool shift = keys & SI_LSHIFT;
bool ctrl = keys & SI_LCTRL;
if(mMode == mLinkMode && !mMesh.isNull())
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mMesh->getLink(ri.point);
if(link != -1)
{
if(mLink != -1)
mMesh->selectLink(mLink, false);
mMesh->selectLink(link, true, false);
mLink = link;
LinkData d = mMesh->getLinkFlags(mLink);
Con::executef(this, "onLinkSelected", Con::getIntArg(d.getFlags()));
}
else
{
if(mLink != -1)
{
mMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
else
{
if(mLinkStart != Point3F::Max)
{
mMesh->addLink(mLinkStart, ri.point);
if(!shift)
mLinkStart = Point3F::Max;
}
else
{
mLinkStart = ri.point;
}
}
}
}
else
{
mMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
}
if(mMode == mTestMode)
{
// Spawn new character
@ -471,35 +426,6 @@ void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
RayInfo ri;
if(mMode == mLinkMode && !mMesh.isNull())
{
if(gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mMesh->getLink(ri.point);
if(link != -1)
{
if(link != mLink)
{
if(mCurLink != -1)
mMesh->selectLink(mCurLink, false);
mMesh->selectLink(link, true, true);
}
mCurLink = link;
}
else
{
if(mCurLink != mLink)
mMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
else
{
mMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
if(mMode == mTestMode)
{
if(gServerContainer.castRay(startPnt, endPnt, PlayerObjectType | VehicleObjectType, &ri))

View file

@ -405,7 +405,7 @@ void NavMesh::setScale(const VectorF &scale)
Parent::setScale(scale);
}
S32 NavMesh::addLink(const Point3F &from, const Point3F &to, U32 flags)
S32 NavMesh::addLink(const Point3F &from, const Point3F &to, bool biDir, U32 flags)
{
Point3F rcFrom = DTStoRC(from), rcTo = DTStoRC(to);
mLinkVerts.push_back(rcFrom.x);
@ -416,7 +416,7 @@ S32 NavMesh::addLink(const Point3F &from, const Point3F &to, U32 flags)
mLinkVerts.push_back(rcTo.z);
mLinksUnsynced.push_back(true);
mLinkRads.push_back(mWalkableRadius);
mLinkDirs.push_back(0);
mLinkDirs.push_back(biDir ? 1 : 0);
mLinkAreas.push_back(OffMeshArea);
if (flags == 0) {
Point3F dir = to - from;
@ -435,11 +435,11 @@ S32 NavMesh::addLink(const Point3F &from, const Point3F &to, U32 flags)
return mLinkIDs.size() - 1;
}
DefineEngineMethod(NavMesh, addLink, S32, (Point3F from, Point3F to, U32 flags), (0),
DefineEngineMethod(NavMesh, addLink, S32, (Point3F from, Point3F to, bool biDir, U32 flags), (0),
"Add a link to this NavMesh between two points.\n\n"
"")
{
return object->addLink(from, to, flags);
return object->addLink(from, to, biDir, flags);
}
S32 NavMesh::getLink(const Point3F &pos)
@ -482,6 +482,23 @@ LinkData NavMesh::getLinkFlags(U32 idx)
return LinkData();
}
bool NavMesh::getLinkDir(U32 idx)
{
if (idx < mLinkIDs.size())
{
return mLinkDirs[idx];
}
}
void NavMesh::setLinkDir(U32 idx, bool biDir)
{
if (idx < mLinkIDs.size())
{
mLinkDirs[idx] = biDir ? 1 : 0;
mLinksUnsynced[idx] = true;
}
}
DefineEngineMethod(NavMesh, getLinkFlags, S32, (U32 id),,
"Get the flags set for a particular off-mesh link.")
{
@ -1667,8 +1684,8 @@ void NavMesh::renderLinks(duDebugDraw &dd)
{
if(mBuilding)
return;
dd.depthMask(true);
dd.begin(DU_DRAW_LINES);
dd.depthMask(false);
for(U32 i = 0; i < mLinkIDs.size(); i++)
{
U32 col = 0;
@ -1686,7 +1703,7 @@ void NavMesh::renderLinks(duDebugDraw &dd)
s[0], s[1], s[2],
e[0], e[1], e[2],
0.3f,
0.0f, mLinkFlags[i] == DropFlag ? 0.0f : 0.4f,
(mLinkDirs[i]&1) ? 0.6f : 0.0f, mLinkFlags[i] == DropFlag ? 0.0f : 0.6f,
col);
if(!mDeleteLinks[i])
duAppendCircle(&dd, e[0], e[1], e[2], mLinkRads[i], col);

View file

@ -151,7 +151,7 @@ public:
/// @{
/// Add an off-mesh link.
S32 addLink(const Point3F &from, const Point3F &to, U32 flags = 0);
S32 addLink(const Point3F &from, const Point3F &to, bool biDir, U32 flags = 0);
/// Get the ID of the off-mesh link near the point.
S32 getLink(const Point3F &pos);
@ -168,6 +168,10 @@ public:
/// Get the flags used by a link.
LinkData getLinkFlags(U32 idx);
bool getLinkDir(U32 idx);
void setLinkDir(U32 idx, bool biDir);
/// Set flags used by a link.
void setLinkFlags(U32 idx, const LinkData &d);

View file

@ -0,0 +1,189 @@
#include "offMeshConnTool.h"
#include "navigation/guiNavEditorCtrl.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
#include "scene/sceneManager.h"
#include "math/mathUtils.h"
IMPLEMENT_CONOBJECT(OffMeshConnectionTool);
void OffMeshConnectionTool::onActivated(const Gui3DMouseEvent& evt)
{
Con::executef(this, "onActivated");
}
void OffMeshConnectionTool::onDeactivated()
{
Con::executef(this, "onDeactivated");
}
void OffMeshConnectionTool::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 (gServerContainer.castRay(startPnt, endPnt, StaticObjectType, &ri))
{
U32 link = mNavMesh->getLink(ri.point);
if (link != -1)
{
if (mLink != -1)
mNavMesh->selectLink(mLink, false);
mNavMesh->selectLink(link, true, false);
mLink = link;
if (ctrl)
{
mNavMesh->selectLink(mLink, false);
mNavMesh->deleteLink(mLink);
mLink = -1;
Con::executef(this, "onLinkDeselected");
return;
}
else
{
LinkData d = mNavMesh->getLinkFlags(mLink);
bool biDir = mNavMesh->getLinkDir(mLink);
Con::executef(this, "onLinkSelected", Con::getIntArg(d.getFlags()), Con::getBoolArg(biDir));
}
}
else
{
if (mLink != -1)
{
mNavMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
if (mLinkStart != Point3F::Max)
{
mLink = mNavMesh->addLink(mLinkStart, ri.point, mBiDir);
mNavMesh->selectLink(mLink, true, false);
mLinkStart = Point3F::Max;
Con::executef(this, "onLinkSelected", Con::getIntArg(mLinkCache.getFlags()), Con::getBoolArg(mBiDir));
}
else
{
mLinkStart = ri.point;
}
}
}
else
{
if (mLink != -1)
{
mNavMesh->selectLink(mLink, false);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
}
}
void OffMeshConnectionTool::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, StaticObjectType, &ri))
{
U32 link = mNavMesh->getLink(ri.point);
if (link != -1)
{
if (link != mLink)
{
if (mCurLink != -1)
mNavMesh->selectLink(mCurLink, false);
mNavMesh->selectLink(link, true, true);
}
mCurLink = link;
}
else
{
if (mCurLink != mLink)
mNavMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
else
{
mNavMesh->selectLink(mCurLink, false);
mCurLink = -1;
}
}
void OffMeshConnectionTool::onRender3D()
{
if (mNavMesh.isNull())
return;
duDebugDrawTorque dd;
if (mLinkStart != Point3F::Max)
{
Point3F rcFrom = DTStoRC(mLinkStart);
dd.begin(DU_DRAW_LINES);
dd.depthMask(false);
duAppendCircle(&dd, rcFrom.x, rcFrom.y, rcFrom.z, mNavMesh->mWalkableRadius, duRGBA(0, 255, 0, 255));
dd.end();
}
mNavMesh->renderLinks(dd);
dd.immediateRender();
}
bool OffMeshConnectionTool::updateGuiInfo()
{
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
String text;
text = "LMB To Select Link. CTRL+LMB To Delete Link";
if (statusbar)
Con::executef(statusbar, "setInfo", text.c_str());
if (mLink != -1)
text = String::ToString("Selected Link: %d", mLink);
else
text = "";
if (selectionBar)
selectionBar->setText(text);
return true;
}
void OffMeshConnectionTool::setLinkProperties(const LinkData& d, bool biDir)
{
if (!mNavMesh.isNull() && mLink != -1)
{
mNavMesh->setLinkFlags(mLink, d);
mNavMesh->setLinkDir(mLink, biDir);
}
mLinkCache = d;
mBiDir = biDir;
}
DefineEngineMethod(OffMeshConnectionTool, setLinkProperties, void, (U32 flags, bool biDir), ,
"@Brief Set properties of the selected link.")
{
object->setLinkProperties(LinkData(flags), biDir);
}

View file

@ -0,0 +1,44 @@
#ifndef _OFFMESHCONNTOOL_H_
#define _OFFMESHCONNTOOL_H_
#ifndef _NAVMESH_TOOL_H_
#include "navigation/navMeshTool.h"
#endif
class OffMeshConnectionTool : public NavMeshTool
{
typedef NavMeshTool Parent;
bool mStartPosSet;
bool mBiDir;
S32 mLink;
S32 mCurLink;
Point3F mLinkStart;
LinkData mLinkCache;
public:
DECLARE_CONOBJECT(OffMeshConnectionTool);
OffMeshConnectionTool() {
mStartPosSet = false;
mBiDir = false;
mLink = -1;
mCurLink = -1;
mLinkStart = Point3F::Max;
mLinkCache = LinkData(0);
}
virtual ~OffMeshConnectionTool() {}
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;
void setLinkProperties(const LinkData& d, bool biDir);
};
#endif

View file

@ -69,9 +69,6 @@ void TileTool::onRender3D()
if (mNavMesh.isNull())
return;
// Optional: Draw all tile bounds as overlays
//mNavMesh->renderTilesOverlay(DebugDraw::get()->getDD());
if(mCurTile != -1)
renderBoxOutline(mNavMesh->getTileBox(mCurTile), ColorI::BLUE);
@ -87,8 +84,8 @@ void TileTool::buildTile()
bool TileTool::updateGuiInfo()
{
GuiTextCtrl* statusbar;
Sim::findObject("EWorldEditorStatusBarInfo", statusbar);
SimObject* statusbar;
Sim::findObject("EditorGuiStatusBar", statusbar);
GuiTextCtrl* selectionBar;
Sim::findObject("EWorldEditorStatusBarSelection", selectionBar);
@ -98,7 +95,7 @@ bool TileTool::updateGuiInfo()
text = "LMB To select NavMesh Tile";
if (statusbar)
statusbar->setText(text);
Con::executef(statusbar, "setInfo", text.c_str());
if (mSelTile != -1)
text = String::ToString("Selected Tile: %d", mSelTile);