diff --git a/src/Common/EKeyInputs.h b/src/Common/EKeyInputs.h index 787df2c7..1aa11231 100644 --- a/src/Common/EKeyInputs.h +++ b/src/Common/EKeyInputs.h @@ -5,14 +5,15 @@ enum EKeyInput { - eCtrlKey = 0x1, - eAltKey = 0x2, - eQKey = 0x4, - eWKey = 0x8, - eEKey = 0x10, - eAKey = 0x20, - eSKey = 0x40, - eDKey = 0x80 + eCtrlKey = 0x1, + eShiftKey = 0x2, + eAltKey = 0x2, + eQKey = 0x4, + eWKey = 0x8, + eEKey = 0x10, + eAKey = 0x20, + eSKey = 0x40, + eDKey = 0x80 }; DECLARE_FLAGS(EKeyInput, FKeyInputs) diff --git a/src/Core/Resource/CPoiToWorld.cpp b/src/Core/Resource/CPoiToWorld.cpp index 5ec63606..6d26ea5b 100644 --- a/src/Core/Resource/CPoiToWorld.cpp +++ b/src/Core/Resource/CPoiToWorld.cpp @@ -68,10 +68,6 @@ void CPoiToWorld::RemovePoiMeshMap(u32 PoiID, u32 ModelID) if (*ListIt == ModelID) { pMap->ModelIDs.erase(ListIt); - - if (pMap->ModelIDs.empty()) - RemovePoi(PoiID); - break; } } diff --git a/src/Core/SRayIntersection.h b/src/Core/SRayIntersection.h index d62b6472..f0f586ca 100644 --- a/src/Core/SRayIntersection.h +++ b/src/Core/SRayIntersection.h @@ -2,6 +2,7 @@ #define SRAYINTERSECTION #include +#include class CSceneNode; @@ -9,12 +10,15 @@ struct SRayIntersection { bool Hit; float Distance; + CVector3f HitPoint; CSceneNode *pNode; u32 ComponentIndex; - SRayIntersection() {} - SRayIntersection(bool _Hit, float _Distance, CSceneNode *_pNode, u32 _ComponentIndex) - : Hit(_Hit), Distance(_Distance), pNode(_pNode), ComponentIndex(_ComponentIndex) {} + SRayIntersection() + : Hit(false), Distance(0.f), HitPoint(CVector3f::skZero), pNode(nullptr), ComponentIndex(-1) {} + + SRayIntersection(bool _Hit, float _Distance, CVector3f _HitPoint, CSceneNode *_pNode, u32 _ComponentIndex) + : Hit(_Hit), Distance(_Distance), HitPoint(_HitPoint), pNode(_pNode), ComponentIndex(_ComponentIndex) {} }; #endif // SRAYINTERSECTION diff --git a/src/Core/Scene/CRootNode.h b/src/Core/Scene/CRootNode.h index 289b83fb..f552b7f1 100644 --- a/src/Core/Scene/CRootNode.h +++ b/src/Core/Scene/CRootNode.h @@ -18,7 +18,7 @@ public: inline void RayAABoxIntersectTest(CRayCollisionTester&, const SViewInfo&) {} inline SRayIntersection RayNodeIntersectTest(const CRay &, u32, const SViewInfo&) { - return SRayIntersection(false, 0.f, nullptr, 0); + return SRayIntersection(); } inline void DrawSelection() {} diff --git a/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp b/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp index b9c131f2..1d1c334a 100644 --- a/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp +++ b/src/Core/ScriptExtra/CDamageableTriggerExtra.cpp @@ -250,13 +250,15 @@ void CDamageableTriggerExtra::RayAABoxIntersectTest(CRayCollisionTester& Tester, std::pair Result = AABox().IntersectsRay(Ray); if (Result.first) + { Tester.AddNode(this, -1, Result.second); + mCachedRayDistance = Result.second; + } } SRayIntersection CDamageableTriggerExtra::RayNodeIntersectTest(const CRay& Ray, u32 /*ComponentIndex*/, const SViewInfo& /*ViewInfo*/) { // The bounding box and all other tests already passed in RayAABoxIntersectTest, so we - // already know that we have a positive. We just need the distance again. - std::pair Result = AABox().IntersectsRay(Ray); - return SRayIntersection(true, Result.second, mpParent, -1); + // already know that we have a positive. + return SRayIntersection(true, mCachedRayDistance, Ray.PointOnRay(mCachedRayDistance), mpParent, -1); } diff --git a/src/Core/ScriptExtra/CDamageableTriggerExtra.h b/src/Core/ScriptExtra/CDamageableTriggerExtra.h index 9df89d8d..7f33f4fb 100644 --- a/src/Core/ScriptExtra/CDamageableTriggerExtra.h +++ b/src/Core/ScriptExtra/CDamageableTriggerExtra.h @@ -28,6 +28,8 @@ class CDamageableTriggerExtra : public CScriptExtra CMaterial *mpMat; CVector2f mCoordScale; + float mCachedRayDistance; + public: explicit CDamageableTriggerExtra(CScriptObject *pInstance, CScene *pScene, CSceneNode *pParent = 0); ~CDamageableTriggerExtra(); diff --git a/src/Editor/CBasicViewport.cpp b/src/Editor/CBasicViewport.cpp index eeb5695b..aafb4305 100644 --- a/src/Editor/CBasicViewport.cpp +++ b/src/Editor/CBasicViewport.cpp @@ -144,6 +144,8 @@ void CBasicViewport::keyPressEvent(QKeyEvent *pEvent) case Qt::Key_S: mKeysPressed |= eSKey; break; case Qt::Key_D: mKeysPressed |= eDKey; break; case Qt::Key_Control: mKeysPressed |= eCtrlKey; break; + case Qt::Key_Shift: mKeysPressed |= eShiftKey; break; + case Qt::Key_Alt: mKeysPressed |= eAltKey; break; } } @@ -158,6 +160,8 @@ void CBasicViewport::keyReleaseEvent(QKeyEvent *pEvent) case Qt::Key_S: mKeysPressed &= ~eSKey; break; case Qt::Key_D: mKeysPressed &= ~eDKey; break; case Qt::Key_Control: mKeysPressed &= ~eCtrlKey; break; + case Qt::Key_Shift: mKeysPressed &= ~eShiftKey; break; + case Qt::Key_Alt: mKeysPressed &= ~eAltKey; break; } } @@ -204,13 +208,13 @@ bool CBasicViewport::IsCursorVisible() bool CBasicViewport::IsMouseInputActive() { - static const int skMoveButtons = eMiddleButton | eRightButton; + static const FMouseInputs skMoveButtons = eMiddleButton | eRightButton; return ((mButtonsPressed & skMoveButtons) != 0); } bool CBasicViewport::IsKeyboardInputActive() { - static const int skMoveKeys = eQKey | eWKey | eEKey | eAKey | eSKey | eDKey; + static const FKeyInputs skMoveKeys = eQKey | eWKey | eEKey | eAKey | eSKey | eDKey; return ((mKeysPressed & skMoveKeys) != 0); } diff --git a/src/Editor/CBasicViewport.h b/src/Editor/CBasicViewport.h index ab2e72c4..58c04e49 100644 --- a/src/Editor/CBasicViewport.h +++ b/src/Editor/CBasicViewport.h @@ -34,8 +34,8 @@ protected: QPoint mLastMousePos; bool mMouseMoved; CTimer mMoveTimer; - int mButtonsPressed; // int container for EMouseInputs flags - int mKeysPressed; // int container for EKeyInputs flags + FMouseInputs mButtonsPressed; + FKeyInputs mKeysPressed; public: explicit CBasicViewport(QWidget *pParent = 0); diff --git a/src/Editor/CSceneViewport.cpp b/src/Editor/CSceneViewport.cpp index 0d6a5196..236e1021 100644 --- a/src/Editor/CSceneViewport.cpp +++ b/src/Editor/CSceneViewport.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include CSceneViewport::CSceneViewport(QWidget *pParent) @@ -109,7 +110,7 @@ void CSceneViewport::CheckGizmoInput(const CRay& ray) else mGizmoHovering = false; } -void CSceneViewport::SceneRayCast(const CRay& ray) +void CSceneViewport::SceneRayCast(const CRay& rkRay) { if (mpEditor->Gizmo()->IsTransforming()) { @@ -117,16 +118,16 @@ void CSceneViewport::SceneRayCast(const CRay& ray) return; } - SRayIntersection result = mpScene->SceneRayCast(ray, mViewInfo); + mRayIntersection = mpScene->SceneRayCast(rkRay, mViewInfo); - if (result.Hit) + if (mRayIntersection.Hit) { if (mpHoverNode) mpHoverNode->SetMouseHovering(false); - mpHoverNode = result.pNode; + mpHoverNode = mRayIntersection.pNode; mpHoverNode->SetMouseHovering(true); - mHoverPoint = ray.PointOnRay(result.Distance); + mHoverPoint = rkRay.PointOnRay(mRayIntersection.Distance); } else @@ -210,6 +211,11 @@ void CSceneViewport::CreateContextMenu() mpContextMenu->addActions(Actions); } +QMouseEvent CSceneViewport::CreateMouseEvent() +{ + return QMouseEvent(QEvent::MouseMove, mapFromGlobal(QCursor::pos()), Qt::NoButton, qApp->mouseButtons(), qApp->keyboardModifiers()); +} + // ************ PROTECTED SLOTS ************ void CSceneViewport::CheckUserInput() { @@ -219,18 +225,24 @@ void CSceneViewport::CheckUserInput() { ResetHover(); mGizmoHovering = false; - - if (!MouseActive) - return; } - CRay ray = CastRay(); + if (MouseActive) + { + CRay Ray = CastRay(); - if (!mViewInfo.GameMode) - CheckGizmoInput(ray); + if (!mViewInfo.GameMode) + CheckGizmoInput(Ray); - if (!mpEditor->Gizmo()->IsTransforming()) - SceneRayCast(ray); + if (!mpEditor->Gizmo()->IsTransforming()) + SceneRayCast(Ray); + } + + else + mRayIntersection = SRayIntersection(); + + QMouseEvent Event = CreateMouseEvent(); + emit InputProcessed(mRayIntersection, &Event); } void CSceneViewport::Paint() @@ -346,7 +358,7 @@ void CSceneViewport::OnMouseRelease(QMouseEvent *pEvent) // Object selection/deselection else - emit ViewportClick(mpHoverNode, pEvent); + emit ViewportClick(mRayIntersection, pEvent); } } diff --git a/src/Editor/CSceneViewport.h b/src/Editor/CSceneViewport.h index 17c7aea0..be5ba216 100644 --- a/src/Editor/CSceneViewport.h +++ b/src/Editor/CSceneViewport.h @@ -16,6 +16,7 @@ class CSceneViewport : public CBasicViewport // Scene interaction bool mGizmoHovering; bool mGizmoTransforming; + SRayIntersection mRayIntersection; CSceneNode *mpHoverNode; CVector3f mHoverPoint; @@ -54,9 +55,11 @@ public: protected: void CreateContextMenu(); + QMouseEvent CreateMouseEvent(); signals: - void ViewportClick(CSceneNode *pNode, QMouseEvent *pEvent); + void InputProcessed(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); + void ViewportClick(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); void GizmoMoved(); void CameraOrbit(); diff --git a/src/Editor/INodeEditor.cpp b/src/Editor/INodeEditor.cpp index 8c8f6139..4b68b9d8 100644 --- a/src/Editor/INodeEditor.cpp +++ b/src/Editor/INodeEditor.cpp @@ -209,7 +209,7 @@ const QList& INodeEditor::GetSelection() const return mSelection; } -void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, QCursor Cursor /*= Qt::CrossCursor*/) +void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor /*= Qt::CrossCursor*/) { // If we're already in pick mode, exit first so the previous caller has a chance to disconnect if (mPickMode) @@ -219,6 +219,7 @@ void INodeEditor::EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, mAllowedPickNodes = AllowedNodes; mExitOnInvalidPick = ExitOnInvalidPick; mEmitOnInvalidPick = EmitOnInvalidPick; + mEmitOnButtonPress = EmitHoverOnButtonPress; emit PickModeEntered(Cursor); } @@ -263,12 +264,14 @@ void INodeEditor::OnGizmoMoved() } // ************ PROTECTED SLOTS ************ -void INodeEditor::OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent) +void INodeEditor::OnViewportClick(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent) { + CSceneNode *pNode = rkRayIntersect.pNode; + // Not in pick mode: process node selection/deselection if (!mPickMode) { - bool ValidNode = (pHoverNode && (pHoverNode->NodeType() & mSelectionNodeFlags)); + bool ValidNode = (pNode && (pNode->NodeType() & mSelectionNodeFlags)); bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0); bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0); @@ -278,14 +281,14 @@ void INodeEditor::OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent) if (!ValidNode) return; - DeselectNode(pHoverNode); + DeselectNode(pNode); } // Ctrl: Add to selection else if (CtrlPressed) { if (ValidNode) - SelectNode(pHoverNode); + SelectNode(pNode); } // Neither: clear selection + select @@ -294,7 +297,7 @@ void INodeEditor::OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent) if (!mGizmoHovering) { if (ValidNode) - ClearAndSelectNode(pHoverNode); + ClearAndSelectNode(pNode); else ClearSelection(); } @@ -306,16 +309,43 @@ void INodeEditor::OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent) // In pick mode: process node pick else { - bool ValidNode = (pHoverNode && (pHoverNode->NodeType() & mAllowedPickNodes)); + bool ValidNode = (pNode && (pNode->NodeType() & mAllowedPickNodes)); if (ValidNode || mEmitOnInvalidPick) - emit PickModeClick(pHoverNode, pEvent); + emit PickModeClick(rkRayIntersect, pEvent); if (!ValidNode && mExitOnInvalidPick) ExitPickMode(); } } +void INodeEditor::OnViewportInputProcessed(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent) +{ + // In pick mode: process node hover + if (mPickMode) + { + CSceneNode *pNode = rkRayIntersect.pNode; + bool NewNode = pNode != mpPickHoverNode; + + bool ButtonsChanged = mPickButtons != pEvent->buttons(); + bool ModifiersChanged = mPickModifiers != pEvent->modifiers(); + + if (NewNode || ((ModifiersChanged || ButtonsChanged) && mEmitOnButtonPress)) + { + bool ValidNode = (pNode && (pNode->NodeType() & mAllowedPickNodes)); + + if (ValidNode || mEmitOnInvalidPick) + emit PickModeHoverChanged(rkRayIntersect, pEvent); + else + emit PickModeHoverChanged(SRayIntersection(), pEvent); + } + + mpPickHoverNode = rkRayIntersect.pNode; + mPickButtons = pEvent->buttons(); + mPickModifiers = pEvent->modifiers(); + } +} + // ************ PRIVATE ************ void INodeEditor::UpdateTransformActionsEnabled() { diff --git a/src/Editor/INodeEditor.h b/src/Editor/INodeEditor.h index e6093b4a..ec646b2f 100644 --- a/src/Editor/INodeEditor.h +++ b/src/Editor/INodeEditor.h @@ -45,7 +45,11 @@ protected: bool mPickMode; bool mExitOnInvalidPick; bool mEmitOnInvalidPick; + bool mEmitOnButtonPress; FNodeFlags mAllowedPickNodes; + CSceneNode *mpPickHoverNode; + Qt::MouseButtons mPickButtons; + Qt::KeyboardModifiers mPickModifiers; public: explicit INodeEditor(QWidget *pParent = 0); @@ -70,7 +74,7 @@ public: bool HasSelection() const; const QList& GetSelection() const; - void EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, QCursor Cursor = Qt::CrossCursor); + void EnterPickMode(FNodeFlags AllowedNodes, bool ExitOnInvalidPick, bool EmitOnInvalidPick, bool EmitHoverOnButtonPress, QCursor Cursor = Qt::CrossCursor); void ExitPickMode(); signals: @@ -79,7 +83,8 @@ signals: void PickModeEntered(QCursor Cursor); void PickModeExited(); - void PickModeClick(CSceneNode *pNode, QMouseEvent *pEvent); + void PickModeClick(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent); + void PickModeHoverChanged(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent); public slots: void OnGizmoMoved(); @@ -90,7 +95,8 @@ protected: virtual void GizmoModeChanged(CGizmo::EGizmoMode /*mode*/) {} protected slots: - void OnViewportClick(CSceneNode *pHoverNode, QMouseEvent *pEvent); + void OnViewportClick(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent); + void OnViewportInputProcessed(const SRayIntersection& rkRayIntersect, QMouseEvent *pEvent); private: void UpdateTransformActionsEnabled(); diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.cpp b/src/Editor/WorldEditor/CPoiMapEditDialog.cpp index 52b65676..a814a363 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.cpp +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.cpp @@ -12,6 +12,7 @@ const CColor CPoiMapEditDialog::skNormalColor(0.137255f, 0.184314f, 0.776471f, 0.5f); const CColor CPoiMapEditDialog::skImportantColor(0.721569f, 0.066667f, 0.066667f, 0.5f); +const CColor CPoiMapEditDialog::skHoverColor(0.047059f, 0.2f, 0.003922f, 0.5f); CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent) : QMainWindow(parent) @@ -20,12 +21,16 @@ CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent) , mSourceModel(pEditor, this) , mHighlightMode(eHighlightSelected) , mPickType(eNotPicking) + , mPickTool(eNormalTool) + , mHoverModelIsMapped(false) + , mpHoverModel(nullptr) { mModel.setSourceModel(&mSourceModel); mModel.sort(0); ui->setupUi(this); ui->ListView->setModel(&mModel); + ui->ListView->selectionModel()->select(mModel.index(0,0), QItemSelectionModel::Select | QItemSelectionModel::Current); QActionGroup *pGroup = new QActionGroup(this); pGroup->addAction(ui->ActionHighlightSelected); @@ -41,8 +46,8 @@ CPoiMapEditDialog::CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent) connect(ui->ListView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(OnItemDoubleClick(QModelIndex))); connect(ui->AddMeshButton, SIGNAL(clicked()), this, SLOT(PickButtonClicked())); connect(ui->RemoveMeshButton, SIGNAL(clicked()), this, SLOT(PickButtonClicked())); - connect(ui->ButtonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(close())); - connect(ui->ButtonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), this, SLOT(close())); + connect(ui->ToolComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnToolComboBoxChanged(int))); + connect(ui->ButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close())); connect(ui->ButtonBox->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(Save())); } @@ -103,6 +108,54 @@ void CPoiMapEditDialog::UnhighlightModel(CModelNode *pNode) pNode->SetScanOverlayEnabled(false); } +void CPoiMapEditDialog::RevertHoverModelOverlay() +{ + if (mpHoverModel) + { + if (mHighlightMode == eHighlightAll) + { + for (int iRow = 0; iRow < mSourceModel.rowCount(QModelIndex()); iRow++) + { + QModelIndex Index = mSourceModel.index(iRow, 0); + + if (mSourceModel.IsModelMapped(Index, mpHoverModel)) + { + HighlightModel(Index, mpHoverModel); + return; + } + } + + UnhighlightModel(mpHoverModel); + } + + else if (mHighlightMode == eHighlightSelected) + { + QModelIndex Index = GetSelectedRow(); + + if (mSourceModel.IsModelMapped(Index, mpHoverModel)) + { + HighlightModel(Index, mpHoverModel); + mHoverModelIsMapped = true; + } + else + { + UnhighlightModel(mpHoverModel); + mHoverModelIsMapped = false; + } + } + + else + UnhighlightModel(mpHoverModel); + } +} + +CPoiMapEditDialog::EPickType CPoiMapEditDialog::GetRealPickType(bool AltPressed) const +{ + if (!AltPressed) return mPickType; + if (mPickType == eAddMeshes) return eRemoveMeshes; + return eAddMeshes; +} + bool CPoiMapEditDialog::IsImportant(const QModelIndex& rkIndex) { CScriptNode *pPOI = mSourceModel.PoiNodePointer(rkIndex); @@ -116,6 +169,12 @@ bool CPoiMapEditDialog::IsImportant(const QModelIndex& rkIndex) return Important; } +QModelIndex CPoiMapEditDialog::GetSelectedRow() const +{ + QModelIndexList Indices = ui->ListView->selectionModel()->selectedRows(); + return ( Indices.isEmpty() ? QModelIndex() : mModel.mapToSource(Indices.front()) ); +} + void CPoiMapEditDialog::Save() { CPoiToWorld *pPoiToWorld = mpEditor->ActiveArea()->GetPoiToWorldMap(); @@ -135,17 +194,27 @@ void CPoiMapEditDialog::Save() void CPoiMapEditDialog::SetHighlightSelected() { const QItemSelection kSelection = ui->ListView->selectionModel()->selection(); + QList SelectedIndices; + QList UnselectedIndices; for (int iRow = 0; iRow < mModel.rowCount(QModelIndex()); iRow++) { QModelIndex Index = mModel.index(iRow, 0); if (kSelection.contains(Index)) - HighlightPoiModels(Index); + SelectedIndices << Index; else - UnhighlightPoiModels(Index); + UnselectedIndices << Index; } + for (int iIdx = 0; iIdx < UnselectedIndices.size(); iIdx++) + UnhighlightPoiModels(UnselectedIndices[iIdx]); + for (int iIdx = 0; iIdx < SelectedIndices.size(); iIdx++) + HighlightPoiModels(SelectedIndices[iIdx]); + + if (mpHoverModel && !mHoverModelIsMapped) + HighlightModel(GetSelectedRow(), mpHoverModel); + mHighlightMode = eHighlightSelected; } @@ -154,6 +223,13 @@ void CPoiMapEditDialog::SetHighlightAll() for (int iRow = 0; iRow < mModel.rowCount(QModelIndex()); iRow++) HighlightPoiModels(mModel.index(iRow, 0)); + // Call HighlightPoiModels again on the selected index to prioritize it over the non-selected POIs. + if (ui->ListView->selectionModel()->hasSelection()) + HighlightPoiModels(ui->ListView->selectionModel()->selectedRows().front()); + + if (mpHoverModel) + HighlightModel(GetSelectedRow(), mpHoverModel); + mHighlightMode = eHighlightAll; } @@ -162,6 +238,9 @@ void CPoiMapEditDialog::SetHighlightNone() for (int iRow = 0; iRow < mModel.rowCount(QModelIndex()); iRow++) UnhighlightPoiModels(mModel.index(iRow, 0)); + if (mpHoverModel) + UnhighlightModel(mpHoverModel); + mHighlightMode = eHighlightNone; } @@ -190,6 +269,14 @@ void CPoiMapEditDialog::OnItemDoubleClick(QModelIndex Index) mpEditor->ClearAndSelectNode(pPOI); } +void CPoiMapEditDialog::OnToolComboBoxChanged(int NewIndex) +{ + if (NewIndex == 0) + mPickTool = eNormalTool; + else + mPickTool = eSprayCanTool; +} + void CPoiMapEditDialog::PickButtonClicked() { QPushButton *pButton = qobject_cast(sender()); @@ -199,9 +286,10 @@ void CPoiMapEditDialog::PickButtonClicked() else { - mpEditor->EnterPickMode(eModelNode, false, false); + mpEditor->EnterPickMode(eModelNode, false, false, true); connect(mpEditor, SIGNAL(PickModeExited()), this, SLOT(StopPicking())); - connect(mpEditor, SIGNAL(PickModeClick(CSceneNode*,QMouseEvent*)), this, SLOT(OnNodePicked(CSceneNode*,QMouseEvent*))); + connect(mpEditor, SIGNAL(PickModeClick(SRayIntersection,QMouseEvent*)), this, SLOT(OnNodePicked(SRayIntersection,QMouseEvent*))); + connect(mpEditor, SIGNAL(PickModeHoverChanged(SRayIntersection,QMouseEvent*)), this, SLOT(OnNodeHover(SRayIntersection,QMouseEvent*))); pButton->setChecked(true); if (pButton == ui->AddMeshButton) @@ -224,11 +312,16 @@ void CPoiMapEditDialog::StopPicking() ui->RemoveMeshButton->setChecked(false); mPickType = eNotPicking; + RevertHoverModelOverlay(); + mpHoverModel = nullptr; + disconnect(mpEditor, 0, this, 0); } -void CPoiMapEditDialog::OnNodePicked(CSceneNode *pNode, QMouseEvent* pEvent) +void CPoiMapEditDialog::OnNodePicked(const SRayIntersection& rkRayIntersect, QMouseEvent* pEvent) { + if (!rkRayIntersect.pNode) return; + // Check for valid selection QModelIndexList Indices = ui->ListView->selectionModel()->selectedRows(); if (Indices.isEmpty()) return; @@ -239,13 +332,10 @@ void CPoiMapEditDialog::OnNodePicked(CSceneNode *pNode, QMouseEvent* pEvent) SourceIndices << mModel.mapToSource(*it); // If alt is pressed, invert the pick mode - CModelNode *pModel = static_cast(pNode); - bool AltPressed = (pEvent->modifiers() & Qt::AltModifier) != 0; + CModelNode *pModel = static_cast(rkRayIntersect.pNode); - EPickType PickType; - if (!AltPressed) PickType = mPickType; - else if (mPickType == eAddMeshes) PickType = eRemoveMeshes; - else PickType = eAddMeshes; + bool AltPressed = (pEvent->modifiers() & Qt::AltModifier) != 0; + EPickType PickType = GetRealPickType(AltPressed); // Add meshes if (PickType == eAddMeshes) @@ -255,6 +345,8 @@ void CPoiMapEditDialog::OnNodePicked(CSceneNode *pNode, QMouseEvent* pEvent) if (mHighlightMode != eHighlightNone) HighlightModel(SourceIndices.front(), pModel); + + mHoverModelIsMapped = true; } // Remove meshes @@ -264,6 +356,41 @@ void CPoiMapEditDialog::OnNodePicked(CSceneNode *pNode, QMouseEvent* pEvent) mSourceModel.RemoveMapping(*it, pModel); if (mHighlightMode != eHighlightNone) + RevertHoverModelOverlay(); + else UnhighlightModel(pModel); } } + +void CPoiMapEditDialog::OnNodeHover(const SRayIntersection& rkIntersect, QMouseEvent *pEvent) +{ + // Restore old hover model to correct overlay color, and set new hover model + if (mpHoverModel) + RevertHoverModelOverlay(); + + mpHoverModel = static_cast(rkIntersect.pNode); + + // If we're using the spray can and the mouse is pressed, treat this as a click. + if (mPickTool == eSprayCanTool && (pEvent->buttons() & Qt::LeftButton)) + OnNodePicked(rkIntersect, pEvent); + + // Otherwise, process as a mouseover + else + { + QModelIndex Index = GetSelectedRow(); + + // Process new hover model + if (mpHoverModel) + { + bool AltPressed = (pEvent->modifiers() & Qt::AltModifier) != 0; + EPickType PickType = GetRealPickType(AltPressed); + + if ( ((PickType == eAddMeshes) && !mSourceModel.IsModelMapped(Index, mpHoverModel)) || + ((PickType == eRemoveMeshes) && mSourceModel.IsModelMapped(Index, mpHoverModel)) ) + { + mpHoverModel->SetScanOverlayEnabled(true); + mpHoverModel->SetScanOverlayColor(skHoverColor); + } + } + } +} diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.h b/src/Editor/WorldEditor/CPoiMapEditDialog.h index d804f61e..31dbe145 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.h +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.h @@ -35,8 +35,18 @@ class CPoiMapEditDialog : public QMainWindow eAddPOIs } mPickType; + enum EPickTool + { + eNormalTool, + eSprayCanTool + } mPickTool; + + bool mHoverModelIsMapped; + CModelNode *mpHoverModel; + static const CColor skNormalColor; static const CColor skImportantColor; + static const CColor skHoverColor; public: explicit CPoiMapEditDialog(CWorldEditor *pEditor, QWidget *parent = 0); @@ -46,8 +56,10 @@ public: void UnhighlightPoiModels(const QModelIndex& rkIndex); void HighlightModel(const QModelIndex& rkIndex, CModelNode *pNode); void UnhighlightModel(CModelNode *pNode); - void RefreshHighlights(); bool IsImportant(const QModelIndex& rkIndex); + void RevertHoverModelOverlay(); + EPickType GetRealPickType(bool AltPressed) const; + QModelIndex GetSelectedRow() const; public slots: void Save(); @@ -56,10 +68,12 @@ public slots: void SetHighlightNone(); void OnSelectionChanged(const QItemSelection& rkSelected, const QItemSelection& rkDeselected); void OnItemDoubleClick(QModelIndex Index); + void OnToolComboBoxChanged(int NewIndex); void PickButtonClicked(); void StopPicking(); - void OnNodePicked(CSceneNode *pNode, QMouseEvent *pEvent); + void OnNodePicked(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); + void OnNodeHover(const SRayIntersection& rkIntersect, QMouseEvent *pEvent); signals: void Closed(); diff --git a/src/Editor/WorldEditor/CPoiMapEditDialog.ui b/src/Editor/WorldEditor/CPoiMapEditDialog.ui index 303b0ca9..29bddb77 100644 --- a/src/Editor/WorldEditor/CPoiMapEditDialog.ui +++ b/src/Editor/WorldEditor/CPoiMapEditDialog.ui @@ -15,19 +15,6 @@ - - - - QAbstractItemView::NoEditTriggers - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - @@ -58,6 +45,26 @@ + + + + + 80 + 0 + + + + + Normal Tool + + + + + Spray Can + + + + @@ -73,10 +80,23 @@ + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + - QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Save + QDialogButtonBox::Close|QDialogButtonBox::Save diff --git a/src/Editor/WorldEditor/CPoiMapModel.cpp b/src/Editor/WorldEditor/CPoiMapModel.cpp index 3d3098b3..1d9d8123 100644 --- a/src/Editor/WorldEditor/CPoiMapModel.cpp +++ b/src/Editor/WorldEditor/CPoiMapModel.cpp @@ -146,16 +146,26 @@ void CPoiMapModel::RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode) { QList *pList = mModelMap[pPOI]; pList->removeOne(pNode); - - if (pList->isEmpty()) - RemovePOI(rkIndex); - else - mpPoiToWorld->RemovePoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID()); + mpPoiToWorld->RemovePoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID()); } else mpPoiToWorld->RemovePoiMeshMap(pPOI->Object()->InstanceID(), pNode->FindMeshID()); } +bool CPoiMapModel::IsModelMapped(const QModelIndex& rkIndex, CModelNode *pNode) const +{ + if (!pNode) return false; + + CScriptNode *pPOI = PoiNodePointer(rkIndex); + + if (mModelMap.contains(pPOI)) + { + QList *pList = mModelMap[pPOI]; + return (pList->contains(pNode)); + } + else return false; +} + CScriptNode* CPoiMapModel::PoiNodePointer(const QModelIndex& rkIndex) const { if ((u32) rkIndex.row() < mpPoiToWorld->NumMappedPOIs()) diff --git a/src/Editor/WorldEditor/CPoiMapModel.h b/src/Editor/WorldEditor/CPoiMapModel.h index 445f9dbd..2fe3427a 100644 --- a/src/Editor/WorldEditor/CPoiMapModel.h +++ b/src/Editor/WorldEditor/CPoiMapModel.h @@ -31,6 +31,7 @@ public: void AddMapping(const QModelIndex& rkIndex, CModelNode *pNode); void RemovePOI(const QModelIndex& rkIndex); void RemoveMapping(const QModelIndex& rkIndex, CModelNode *pNode); + bool IsModelMapped(const QModelIndex& rkIndex, CModelNode *pNode) const; CScriptNode* PoiNodePointer(const QModelIndex& rkIndex) const; const QList& GetPoiMeshList(const QModelIndex& rkIndex) const; diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index 34cc090a..471cd76e 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -68,7 +68,11 @@ CWorldEditor::CWorldEditor(QWidget *parent) : addAction(ui->ActionDecrementGizmo); // Connect signals and slots - connect(ui->MainViewport, SIGNAL(ViewportClick(CSceneNode*,QMouseEvent*)), this, SLOT(OnViewportClick(CSceneNode*,QMouseEvent*))); + connect(ui->MainViewport, SIGNAL(ViewportClick(SRayIntersection,QMouseEvent*)), this, SLOT(OnViewportClick(SRayIntersection,QMouseEvent*))); + connect(ui->MainViewport, SIGNAL(InputProcessed(SRayIntersection,QMouseEvent*)), this, SLOT(OnViewportInputProcessed(SRayIntersection,QMouseEvent*))); + connect(ui->MainViewport, SIGNAL(InputProcessed(SRayIntersection,QMouseEvent*)), this, SLOT(UpdateGizmoUI()) ); + connect(ui->MainViewport, SIGNAL(InputProcessed(SRayIntersection,QMouseEvent*)), this, SLOT(UpdateStatusBar()) ); + connect(ui->MainViewport, SIGNAL(InputProcessed(SRayIntersection,QMouseEvent*)), this, SLOT(UpdateCursor()) ); connect(ui->MainViewport, SIGNAL(GizmoMoved()), this, SLOT(OnGizmoMoved())); connect(ui->MainViewport, SIGNAL(CameraOrbit()), this, SLOT(UpdateCameraOrbit())); connect(this, SIGNAL(SelectionModified()), this, SLOT(UpdateCameraOrbit())); @@ -166,7 +170,7 @@ CGameArea* CWorldEditor::ActiveArea() return mpArea; } -// ************ UPDATE UI ************ +// ************ PROTECTED SLOTS ************ void CWorldEditor::UpdateStatusBar() { // Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag. @@ -262,13 +266,6 @@ void CWorldEditor::UpdateSelectionUI() UpdateGizmoUI(); } -// ************ PROTECTED ************ -void CWorldEditor::GizmoModeChanged(CGizmo::EGizmoMode mode) -{ - ui->TransformSpinBox->SetSingleStep( (mode == CGizmo::eRotate ? 1.0 : 0.1) ); - ui->TransformSpinBox->SetDefaultValue( (mode == CGizmo::eScale ? 1.0 : 0.0) ); -} - void CWorldEditor::UpdateCursor() { if (ui->MainViewport->IsCursorVisible() && !mPickMode) @@ -284,6 +281,13 @@ void CWorldEditor::UpdateCursor() } } +// ************ PROTECTED ************ +void CWorldEditor::GizmoModeChanged(CGizmo::EGizmoMode mode) +{ + ui->TransformSpinBox->SetSingleStep( (mode == CGizmo::eRotate ? 1.0 : 0.1) ); + ui->TransformSpinBox->SetDefaultValue( (mode == CGizmo::eScale ? 1.0 : 0.0) ); +} + // ************ PRIVATE SLOTS ************ void CWorldEditor::OnPickModeEnter(QCursor Cursor) { @@ -300,11 +304,8 @@ void CWorldEditor::RefreshViewport() if (!mGizmo.IsTransforming()) mGizmo.ResetSelectedAxes(); - // Process input + update UI + // Process input ui->MainViewport->ProcessInput(); - UpdateCursor(); - UpdateStatusBar(); - UpdateGizmoUI(); // Render ui->MainViewport->Render(); diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index 4304d619..ca33eb11 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -45,16 +45,14 @@ public: void SetArea(CWorld *pWorld, CGameArea *pArea); CGameArea* ActiveArea(); - // Update UI - void UpdateStatusBar(); - public slots: + void UpdateStatusBar(); void UpdateGizmoUI(); void UpdateSelectionUI(); + void UpdateCursor(); protected: void GizmoModeChanged(CGizmo::EGizmoMode mode); - void UpdateCursor(); private slots: void OnPickModeEnter(QCursor Cursor);