Torque3D/Engine/source/navigation/guiNavEditorCtrl.cpp
marauder2k7 80473e10b5 added first tool
Added the tileTool with the ability to select tiles
Abstraction layer for navmesh tools created.
2025-07-23 15:08:29 +01:00

706 lines
18 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2014 Daniel Buckmaster
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "torqueConfig.h"
#ifdef TORQUE_TOOLS
#include "platform/platform.h"
#include "guiNavEditorCtrl.h"
#include "duDebugDrawTorque.h"
#include "console/engineAPI.h"
#include "console/consoleTypes.h"
#include "math/util/frustum.h"
#include "math/mathUtils.h"
#include "gfx/primBuilder.h"
#include "gfx/gfxDrawUtil.h"
#include "scene/sceneRenderState.h"
#include "scene/sceneManager.h"
#include "gui/core/guiCanvas.h"
#include "gui/buttons/guiButtonCtrl.h"
#include "gui/worldEditor/undoActions.h"
#include "T3D/gameBase/gameConnection.h"
#include "T3D/AI/AIController.h"
IMPLEMENT_CONOBJECT(GuiNavEditorCtrl);
ConsoleDocClass(GuiNavEditorCtrl,
"@brief GUI tool that makes up the Navigation Editor\n\n"
"Editor use only.\n\n"
"@internal"
);
// Each of the mode names directly correlates with the Nav Editor's tool palette.
const String GuiNavEditorCtrl::mSelectMode = "SelectMode";
const String GuiNavEditorCtrl::mLinkMode = "LinkMode";
const String GuiNavEditorCtrl::mCoverMode = "CoverMode";
const String GuiNavEditorCtrl::mTestMode = "TestMode";
GuiNavEditorCtrl::GuiNavEditorCtrl()
{
mMode = mSelectMode;
mIsDirty = false;
mStartDragMousePoint = InvalidMousePoint;
mMesh = NULL;
mPlayer = mCurPlayer = NULL;
mSpawnClass = mSpawnDatablock = "";
mLinkStart = Point3F::Max;
mLink = mCurLink = -1;
}
GuiNavEditorCtrl::~GuiNavEditorCtrl()
{
}
void GuiNavEditorUndoAction::undo()
{
}
bool GuiNavEditorCtrl::onAdd()
{
if(!Parent::onAdd())
return false;
GFXStateBlockDesc desc;
desc.fillMode = GFXFillSolid;
desc.setBlend(false);
desc.setZReadWrite(false, false);
desc.setCullMode(GFXCullNone);
mZDisableSB = GFX->createStateBlock(desc);
desc.setZReadWrite(true, true);
mZEnableSB = GFX->createStateBlock(desc);
SceneManager::getPreRenderSignal().notify(this, &GuiNavEditorCtrl::_prepRenderImage);
return true;
}
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();
}
void GuiNavEditorCtrl::onSleep()
{
Parent::onSleep();
//mMode = mSelectMode;
}
void GuiNavEditorCtrl::selectMesh(NavMesh *mesh)
{
mesh->setSelected(true);
mMesh = mesh;
}
DefineEngineMethod(GuiNavEditorCtrl, selectMesh, void, (S32 id),,
"@brief Select a NavMesh object.")
{
NavMesh *obj;
if(Sim::findObject(id, obj))
object->selectMesh(obj);
}
S32 GuiNavEditorCtrl::getMeshId()
{
return mMesh.isNull() ? 0 : mMesh->getId();
}
DefineEngineMethod(GuiNavEditorCtrl, getMesh, S32, (),,
"@brief Select a NavMesh object.")
{
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;
mLinkStart = Point3F::Max;
mLink = mCurLink = -1;
}
DefineEngineMethod(GuiNavEditorCtrl, deselect, void, (),,
"@brief Deselect whatever is currently selected in the editor.")
{
object->deselect();
}
void GuiNavEditorCtrl::deleteLink()
{
if(!mMesh.isNull() && mLink != -1)
{
mMesh->selectLink(mLink, false);
mMesh->deleteLink(mLink);
mLink = -1;
Con::executef(this, "onLinkDeselected");
}
}
DefineEngineMethod(GuiNavEditorCtrl, deleteLink, void, (),,
"@brief Delete the currently selected link.")
{
object->deleteLink();
}
void GuiNavEditorCtrl::setLinkFlags(const LinkData &d)
{
if(mMode == mLinkMode && !mMesh.isNull() && mLink != -1)
{
mMesh->setLinkFlags(mLink, d);
}
}
DefineEngineMethod(GuiNavEditorCtrl, setLinkFlags, void, (U32 flags),,
"@Brief Set jump and drop properties of the selected link.")
{
object->setLinkFlags(LinkData(flags));
}
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_)
{
//cursor = mAddNodeCursor;
//visible = false;
cursor = NULL;
visible = false;
GuiCanvas *root = getRoot();
if(!root)
return;
S32 currCursor = PlatformCursorController::curArrow;
if(root->mCursorChanged == currCursor)
return;
PlatformWindow *window = root->getPlatformWindow();
PlatformCursorController *controller = window->getCursorController();
// We've already changed the cursor,
// so set it back before we change it again.
if(root->mCursorChanged != -1)
controller->popCursor();
// Now change the cursor shape
controller->pushCursor(currCursor);
root->mCursorChanged = currCursor;
}
bool GuiNavEditorCtrl::get3DCentre(Point3F &pos)
{
Point3F screen, start, end;
screen.set(getExtent().x / 2, getExtent().y / 2, 0);
unproject(screen, &start);
screen.z = 1.0f;
unproject(screen, &end);
RayInfo ri;
if(gServerContainer.castRay(start, end, StaticObjectType, &ri))
{
pos = ri.point;
return true;
}
return false;
}
void GuiNavEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
{
if (!mMesh)
return;
mGizmo->on3DMouseDown(event);
if (mTool)
mTool->on3DMouseDown(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 == 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
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)
{
if (!mMesh)
return;
// Keep the Gizmo up to date.
mGizmo->on3DMouseUp(event);
if (mTool)
mTool->on3DMouseUp(event);
mouseUnlock();
}
void GuiNavEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
{
if (!mMesh)
return;
if (mTool)
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 == 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))
mCurPlayer = ri.object;
else
mCurPlayer = NULL;
}
}
void GuiNavEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
{
mGizmo->on3DMouseDragged(event);
if(mGizmo->isDirty())
{
Point3F scale = mGizmo->getScale();
const MatrixF &mat = mGizmo->getTransform();
VectorF normal;
mat.getColumn(2, &normal);
//mSelRiver->setNode(pos, scale.x, scale.z, normal, mSelNode);
mIsDirty = true;
}
}
void GuiNavEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
{
}
void GuiNavEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
{
}
void GuiNavEditorCtrl::updateGuiInfo()
{
if (mTool)
{
if (mTool->updateGuiInfo())
return;
}
}
void GuiNavEditorCtrl::onRender(Point2I offset, const RectI &updateRect)
{
PROFILE_SCOPE(GuiNavEditorCtrl_OnRender);
Parent::onRender(offset, 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);
// get the projected size...
GameConnection* connection = GameConnection::getConnectionToServer();
if(!connection)
return;
// Grab the camera's transform
MatrixF mat;
connection->getControlCameraTransform(0, &mat);
// Get the camera position
Point3F camPos;
mat.getColumn(3,&camPos);
if (mTool)
mTool->onRender3D();
if(mMode == mLinkMode)
{
if(mLinkStart != Point3F::Max)
{
GFXStateBlockDesc desc;
desc.setBlend(false);
desc.setZReadWrite(true ,true);
MatrixF linkMat(true);
linkMat.setPosition(mLinkStart);
Point3F scale(0.8f, 0.8f, 0.8f);
GFX->getDrawUtil()->drawTransform(desc, linkMat, &scale);
}
}
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);
// Now draw all the 2d stuff!
GFX->setClipRect(updateRect);
}
bool GuiNavEditorCtrl::getStaticPos(const Gui3DMouseEvent & event, Point3F &tpos)
{
// Find clicked point on the terrain
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
bool hit;
hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri);
tpos = ri.point;
return hit;
}
void GuiNavEditorCtrl::setMode(String mode, bool sourceShortcut = false)
{
mMode = mode;
Con::executef(this, "onModeSet", mode);
if(sourceShortcut)
Con::executef(this, "paletteSync", mode);
}
void GuiNavEditorCtrl::submitUndo(const UTF8 *name)
{
// Grab the mission editor undo manager.
UndoManager *undoMan = NULL;
if(!Sim::findObject("EUndoManager", undoMan))
{
Con::errorf("GuiNavEditorCtrl::submitUndo() - EUndoManager not found!");
return;
}
// Setup the action.
GuiNavEditorUndoAction *action = new GuiNavEditorUndoAction(name);
action->mNavEditor = this;
undoMan->addAction(action);
}
void GuiNavEditorCtrl::_prepRenderImage(SceneManager* sceneGraph, const SceneRenderState* state)
{
/*if(isAwake() && River::smEditorOpen && mSelRiver)
{
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->type = RenderPassManager::RIT_Editor;
ri->renderDelegate.bind(this, &GuiNavEditorCtrl::_renderSelectedRiver);
ri->defaultKey = 100;
state->getRenderPass()->addInst(ri);
}*/
}
void GuiNavEditorCtrl::setActiveTool(NavMeshTool* tool)
{
if (mTool)
{
mTool->onDeactivated();
}
mTool = tool;
if (mTool)
{
mTool->setActiveNavMesh(mMesh);
mTool->onActivated(mLastEvent);
}
}
DefineEngineMethod(GuiNavEditorCtrl, setActiveTool, void, (const char* toolName), , "( NavMeshTool tool )")
{
NavMeshTool* tool = dynamic_cast<NavMeshTool*>(Sim::findObject(toolName));
object->setActiveTool(tool);
}
DefineEngineMethod(GuiNavEditorCtrl, getMode, const char*, (), , "")
{
return object->getMode();
}
DefineEngineMethod(GuiNavEditorCtrl, setMode, void, (String mode),, "setMode(String mode)")
{
object->setMode(mode);
}
#endif