You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/apps/opencs/view/render/pathgridmode.cpp

294 lines
9.9 KiB
C++

#include "pathgridmode.hpp"
#include <QIcon>
#include <components/sceneutil/pathgridutil.hpp>
#include "../../model/prefs/state.hpp"
#include "../../model/world/commandmacro.hpp"
#include "../widget/scenetoolbar.hpp"
#include "cell.hpp"
#include "mask.hpp"
#include "pathgrid.hpp"
#include "pathgridselectionmode.hpp"
#include "worldspacewidget.hpp"
#include <apps/opencs/model/doc/document.hpp>
#include <apps/opencs/model/prefs/category.hpp>
#include <apps/opencs/model/prefs/setting.hpp>
#include <apps/opencs/view/render/editmode.hpp>
#include <apps/opencs/view/render/tagbase.hpp>
#include <osg/Camera>
#include <osg/Matrixd>
#include <osg/Vec3d>
#include <osg/ref_ptr>
#include <vector>
class QPoint;
class QUndoStack;
class QWidget;
namespace CSVRender
{
PathgridMode::PathgridMode(WorldspaceWidget* worldspaceWidget, QWidget* parent)
: EditMode(worldspaceWidget, QIcon(":scenetoolbar/editing-pathgrid"),
Mask_Pathgrid | Mask_Terrain | Mask_Reference, getTooltip(), parent)
, mDragMode(DragMode_None)
, mFromNode(0)
, mSelectionMode(nullptr)
{
}
QString PathgridMode::getTooltip()
{
return QString(
"Pathgrid editing"
"<ul><li>Press {scene-edit-primary} to add a node to the cursor location</li>"
"<li>Press {scene-edit-secondary} to connect the selected nodes to the node beneath the cursor</li>"
"<li>Press {scene-edit-primary} and drag to move selected nodes</li>"
"<li>Press {scene-edit-secondary} and drag to connect one node to another</li>"
"</ul><p>Note: Only a single cell's pathgrid may be edited at a time");
}
void PathgridMode::activate(CSVWidget::SceneToolbar* toolbar)
{
if (!mSelectionMode)
{
mSelectionMode = new PathgridSelectionMode(toolbar, getWorldspaceWidget());
}
EditMode::activate(toolbar);
toolbar->addTool(mSelectionMode);
}
void PathgridMode::deactivate(CSVWidget::SceneToolbar* toolbar)
{
if (mSelectionMode)
{
toolbar->removeTool(mSelectionMode);
delete mSelectionMode;
mSelectionMode = nullptr;
}
}
void PathgridMode::primaryOpenPressed(const WorldspaceHitResult& hitResult) {}
void PathgridMode::primaryEditPressed(const WorldspaceHitResult& hitResult)
{
if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue()
&& dynamic_cast<PathgridTag*>(hitResult.tag.get()))
{
primarySelectPressed(hitResult);
}
else if (Cell* cell = getWorldspaceWidget().getCell(hitResult.worldPos))
{
if (cell->getPathgrid())
{
// Add node
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Add node";
CSMWorld::CommandMacro macro(undoStack, description);
cell->getPathgrid()->applyPoint(macro, hitResult.worldPos);
}
}
}
void PathgridMode::secondaryEditPressed(const WorldspaceHitResult& hit)
{
if (hit.tag)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))
{
if (tag->getPathgrid()->isSelected())
{
unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Connect node to selected nodes";
CSMWorld::CommandMacro macro(undoStack, description);
tag->getPathgrid()->applyEdges(macro, node);
}
}
}
}
void PathgridMode::primarySelectPressed(const WorldspaceHitResult& hit)
{
getWorldspaceWidget().clearSelection(Mask_Pathgrid);
if (hit.tag)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))
{
mLastId = tag->getPathgrid()->getId();
unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));
tag->getPathgrid()->toggleSelected(node);
}
}
}
void PathgridMode::secondarySelectPressed(const WorldspaceHitResult& hit)
{
if (hit.tag)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))
{
if (tag->getPathgrid()->getId() != mLastId)
{
getWorldspaceWidget().clearSelection(Mask_Pathgrid);
mLastId = tag->getPathgrid()->getId();
}
unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));
tag->getPathgrid()->toggleSelected(node);
return;
}
}
getWorldspaceWidget().clearSelection(Mask_Pathgrid);
}
bool PathgridMode::primaryEditStartDrag(const QPoint& pos)
{
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Pathgrid);
if (CSMPrefs::get()["3D Scene Input"]["context-select"].isTrue())
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
if (dynamic_cast<PathgridTag*>(hit.tag.get()))
{
primarySelectPressed(hit);
selection = getWorldspaceWidget().getSelection(Mask_Pathgrid);
}
}
if (!selection.empty())
{
mDragMode = DragMode_Move;
return true;
}
return false;
}
bool PathgridMode::secondaryEditStartDrag(const QPoint& pos)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
if (hit.tag)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))
{
mDragMode = DragMode_Edge;
mEdgeId = tag->getPathgrid()->getId();
mFromNode = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));
tag->getPathgrid()->setDragOrigin(mFromNode);
return true;
}
}
return false;
}
void PathgridMode::drag(const QPoint& pos, int diffX, int diffY, double speedFactor)
{
if (mDragMode == DragMode_Move)
{
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Pathgrid);
for (std::vector<osg::ref_ptr<TagBase>>::iterator it = selection.begin(); it != selection.end(); ++it)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))
{
osg::Vec3d eye, center, up, offset;
getWorldspaceWidget().getCamera()->getViewMatrix().getLookAt(eye, center, up);
offset = (up * diffY * speedFactor) + (((center - eye) ^ up) * diffX * speedFactor);
tag->getPathgrid()->moveSelected(offset);
}
}
}
else if (mDragMode == DragMode_Edge)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
Cell* cell = getWorldspaceWidget().getCell(hit.worldPos);
if (cell && cell->getPathgrid())
{
PathgridTag* tag = nullptr;
if (hit.tag && (tag = dynamic_cast<PathgridTag*>(hit.tag.get()))
&& tag->getPathgrid()->getId() == mEdgeId)
{
unsigned short node = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));
cell->getPathgrid()->setDragEndpoint(node);
}
else
{
cell->getPathgrid()->setDragEndpoint(hit.worldPos);
}
}
}
}
void PathgridMode::dragCompleted(const QPoint& pos)
{
if (mDragMode == DragMode_Move)
{
std::vector<osg::ref_ptr<TagBase>> selection = getWorldspaceWidget().getSelection(Mask_Pathgrid);
for (std::vector<osg::ref_ptr<TagBase>>::iterator it = selection.begin(); it != selection.end(); ++it)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(it->get()))
{
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Move pathgrid node(s)";
CSMWorld::CommandMacro macro(undoStack, description);
tag->getPathgrid()->applyPosition(macro);
}
}
}
else if (mDragMode == DragMode_Edge)
{
WorldspaceHitResult hit = getWorldspaceWidget().mousePick(pos, getWorldspaceWidget().getInteractionMask());
if (hit.tag)
{
if (PathgridTag* tag = dynamic_cast<PathgridTag*>(hit.tag.get()))
{
if (tag->getPathgrid()->getId() == mEdgeId)
{
unsigned short toNode = SceneUtil::getPathgridNode(static_cast<unsigned short>(hit.index0));
QUndoStack& undoStack = getWorldspaceWidget().getDocument().getUndoStack();
QString description = "Add edge between nodes";
CSMWorld::CommandMacro macro(undoStack, description);
tag->getPathgrid()->applyEdge(macro, mFromNode, toNode);
}
}
}
mEdgeId.clear();
mFromNode = 0;
}
mDragMode = DragMode_None;
getWorldspaceWidget().reset(Mask_Pathgrid);
}
void PathgridMode::dragAborted()
{
getWorldspaceWidget().reset(Mask_Pathgrid);
}
}