mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-29 10:41:22 +00:00
511 lines
15 KiB
C++
511 lines
15 KiB
C++
#include "CSceneViewport.h"
|
|
#include "CSelectionIterator.h"
|
|
#include "UICommon.h"
|
|
#include "Editor/Undo/UndoCommands.h"
|
|
#include <Core/Render/CDrawUtil.h>
|
|
#include <Core/Render/SViewInfo.h>
|
|
#include <Core/Resource/Script/CScriptLayer.h>
|
|
#include <Core/Scene/CSceneIterator.h>
|
|
#include <QApplication>
|
|
#include <QMenu>
|
|
|
|
CSceneViewport::CSceneViewport(QWidget *pParent)
|
|
: CBasicViewport(pParent)
|
|
, mpEditor(nullptr)
|
|
, mpScene(nullptr)
|
|
, mRenderingMergedWorld(true)
|
|
, mGizmoTransforming(false)
|
|
, mpHoverNode(nullptr)
|
|
, mHoverPoint(CVector3f::skZero)
|
|
, mpContextMenu(nullptr)
|
|
, mpMenuNode(nullptr)
|
|
{
|
|
mpRenderer = new CRenderer();
|
|
mpRenderer->SetClearColor(CColor::skBlack);
|
|
mpRenderer->SetViewportSize(width(), height());
|
|
|
|
mViewInfo.pScene = mpScene;
|
|
mViewInfo.pRenderer = mpRenderer;
|
|
mViewInfo.ShowFlags = eShowMergedWorld | eShowObjectGeometry | eShowLights | eShowSky;
|
|
|
|
CreateContextMenu();
|
|
}
|
|
|
|
CSceneViewport::~CSceneViewport()
|
|
{
|
|
delete mpRenderer;
|
|
}
|
|
|
|
void CSceneViewport::SetScene(INodeEditor *pEditor, CScene *pScene)
|
|
{
|
|
mpEditor = pEditor;
|
|
mpScene = pScene;
|
|
}
|
|
|
|
void CSceneViewport::SetShowWorld(bool Visible)
|
|
{
|
|
if (mRenderingMergedWorld)
|
|
SetShowFlag(eShowMergedWorld, Visible);
|
|
else
|
|
SetShowFlag(eShowSplitWorld, Visible);
|
|
}
|
|
|
|
void CSceneViewport::SetRenderMergedWorld(bool RenderMerged)
|
|
{
|
|
mRenderingMergedWorld = RenderMerged;
|
|
|
|
if (mViewInfo.ShowFlags & (eShowSplitWorld | eShowMergedWorld))
|
|
{
|
|
SetShowFlag(eShowSplitWorld, !RenderMerged);
|
|
SetShowFlag(eShowMergedWorld, RenderMerged);
|
|
}
|
|
}
|
|
|
|
FShowFlags CSceneViewport::ShowFlags() const
|
|
{
|
|
return mViewInfo.ShowFlags;
|
|
}
|
|
|
|
CRenderer* CSceneViewport::Renderer()
|
|
{
|
|
return mpRenderer;
|
|
}
|
|
|
|
CSceneNode* CSceneViewport::HoverNode()
|
|
{
|
|
return mpHoverNode;
|
|
}
|
|
|
|
CVector3f CSceneViewport::HoverPoint()
|
|
{
|
|
return mHoverPoint;
|
|
}
|
|
|
|
void CSceneViewport::CheckGizmoInput(const CRay& rkRay)
|
|
{
|
|
CGizmo *pGizmo = mpEditor->Gizmo();
|
|
|
|
// Gizmo not transforming: Check for gizmo hover
|
|
if (!pGizmo->IsTransforming())
|
|
{
|
|
if (mpEditor->IsGizmoVisible())
|
|
mGizmoHovering = pGizmo->CheckSelectedAxes(rkRay);
|
|
else
|
|
mGizmoHovering = false;
|
|
}
|
|
|
|
// Gizmo transforming: Run gizmo input with ray/mouse coords
|
|
else if (mGizmoTransforming)
|
|
{
|
|
bool transformed = pGizmo->TransformFromInput(rkRay, mCamera);
|
|
if (transformed) emit GizmoMoved();
|
|
}
|
|
|
|
else mGizmoHovering = false;
|
|
}
|
|
|
|
SRayIntersection CSceneViewport::SceneRayCast(const CRay& rkRay)
|
|
{
|
|
if (mpEditor->Gizmo()->IsTransforming())
|
|
{
|
|
ResetHover();
|
|
return SRayIntersection();
|
|
}
|
|
|
|
SRayIntersection Intersect = mpScene->SceneRayCast(rkRay, mViewInfo);
|
|
|
|
if (Intersect.Hit)
|
|
{
|
|
if (mpHoverNode)
|
|
mpHoverNode->SetMouseHovering(false);
|
|
|
|
mpHoverNode = Intersect.pNode;
|
|
mpHoverNode->SetMouseHovering(true);
|
|
mHoverPoint = rkRay.PointOnRay(Intersect.Distance);
|
|
}
|
|
|
|
else
|
|
{
|
|
mHoverPoint = rkRay.PointOnRay(10.f);
|
|
ResetHover();
|
|
}
|
|
|
|
return Intersect;
|
|
}
|
|
|
|
void CSceneViewport::ResetHover()
|
|
{
|
|
if (mpHoverNode) mpHoverNode->SetMouseHovering(false);
|
|
mpHoverNode = nullptr;
|
|
}
|
|
|
|
bool CSceneViewport::IsHoveringGizmo()
|
|
{
|
|
return mGizmoHovering;
|
|
}
|
|
|
|
void CSceneViewport::keyPressEvent(QKeyEvent *pEvent)
|
|
{
|
|
CBasicViewport::keyPressEvent(pEvent);
|
|
|
|
if (!pEvent->modifiers() && pEvent->key() == Qt::Key_Z && !pEvent->isAutoRepeat())
|
|
{
|
|
mCamera.SetMoveMode(eOrbitCamera);
|
|
emit CameraOrbit();
|
|
}
|
|
}
|
|
|
|
void CSceneViewport::keyReleaseEvent(QKeyEvent* pEvent)
|
|
{
|
|
CBasicViewport::keyReleaseEvent(pEvent);
|
|
|
|
if (pEvent->key() == Qt::Key_Z && !pEvent->isAutoRepeat())
|
|
{
|
|
mCamera.SetMoveMode(eFreeCamera);
|
|
}
|
|
}
|
|
|
|
// ************ PROTECTED ************
|
|
void CSceneViewport::CreateContextMenu()
|
|
{
|
|
mpContextMenu = new QMenu(this);
|
|
|
|
// Main context menu
|
|
mpToggleSelectAction = new QAction("ToggleSelect", this);
|
|
connect(mpToggleSelectAction, SIGNAL(triggered()), this, SLOT(OnToggleSelect()));
|
|
|
|
mpHideSelectionSeparator = new QAction(this);
|
|
mpHideSelectionSeparator->setSeparator(true);
|
|
|
|
mpHideSelectionAction = new QAction("Hide selection", this);
|
|
connect(mpHideSelectionAction, SIGNAL(triggered()), this, SLOT(OnHideSelection()));
|
|
|
|
mpHideUnselectedAction = new QAction("Hide unselected", this);
|
|
connect(mpHideUnselectedAction, SIGNAL(triggered()), this, SLOT(OnHideUnselected()));
|
|
|
|
mpHideHoverSeparator = new QAction(this);
|
|
mpHideHoverSeparator->setSeparator(this);
|
|
|
|
mpHideHoverNodeAction = new QAction("HideNode", this);
|
|
connect(mpHideHoverNodeAction, SIGNAL(triggered()), this, SLOT(OnHideNode()));
|
|
|
|
mpHideHoverTypeAction = new QAction("HideType", this);
|
|
connect(mpHideHoverTypeAction, SIGNAL(triggered()), this, SLOT(OnHideType()));
|
|
|
|
mpHideHoverLayerAction = new QAction("HideLayer", this);
|
|
connect(mpHideHoverLayerAction, SIGNAL(triggered()), this, SLOT(OnHideLayer()));
|
|
|
|
mpUnhideSeparator = new QAction(this);
|
|
mpUnhideSeparator->setSeparator(true);
|
|
|
|
mpUnhideAllAction = new QAction("Unhide all", this);
|
|
connect(mpUnhideAllAction, SIGNAL(triggered()), this, SLOT(OnUnhideAll()));
|
|
|
|
QList<QAction*> Actions;
|
|
Actions << mpToggleSelectAction
|
|
<< mpHideSelectionSeparator << mpHideSelectionAction << mpHideUnselectedAction
|
|
<< mpHideHoverSeparator << mpHideHoverNodeAction << mpHideHoverTypeAction << mpHideHoverLayerAction
|
|
<< mpUnhideSeparator << mpUnhideAllAction;
|
|
|
|
mpContextMenu->addActions(Actions);
|
|
|
|
// Select Connected menu
|
|
mpSelectConnectedMenu = new QMenu("Select connected...", this);
|
|
|
|
mpSelectConnectedOutgoingAction = new QAction("...via outgoing links", this);
|
|
connect(mpSelectConnectedOutgoingAction, SIGNAL(triggered()), this, SLOT(OnSelectConnected()));
|
|
|
|
mpSelectConnectedIncomingAction = new QAction("...via incoming links", this);
|
|
connect(mpSelectConnectedIncomingAction, SIGNAL(triggered()), this, SLOT(OnSelectConnected()));
|
|
|
|
mpSelectConnectedAllAction = new QAction("...via all links", this);
|
|
connect(mpSelectConnectedAllAction, SIGNAL(triggered()), this, SLOT(OnSelectConnected()));
|
|
|
|
QList<QAction*> SelectConnectedActions;
|
|
SelectConnectedActions << mpSelectConnectedOutgoingAction << mpSelectConnectedIncomingAction << mpSelectConnectedAllAction;
|
|
mpSelectConnectedMenu->addActions(SelectConnectedActions);
|
|
|
|
mpContextMenu->insertMenu(mpHideSelectionSeparator, mpSelectConnectedMenu);
|
|
}
|
|
|
|
QMouseEvent CSceneViewport::CreateMouseEvent()
|
|
{
|
|
return QMouseEvent(QEvent::MouseMove, mapFromGlobal(QCursor::pos()), Qt::NoButton, qApp->mouseButtons(), qApp->keyboardModifiers());
|
|
}
|
|
|
|
void CSceneViewport::FindConnectedObjects(u32 InstanceID, bool SearchOutgoing, bool SearchIncoming, QList<u32>& rIDList)
|
|
{
|
|
CScriptNode *pScript = mpScene->NodeForInstanceID(InstanceID);
|
|
if (!pScript) return;
|
|
|
|
CScriptObject *pInst = pScript->Instance();
|
|
rIDList << InstanceID;
|
|
|
|
if (SearchOutgoing)
|
|
{
|
|
for (u32 iLink = 0; iLink < pInst->NumLinks(eOutgoing); iLink++)
|
|
{
|
|
CLink *pLink = pInst->Link(eOutgoing, iLink);
|
|
|
|
if (!rIDList.contains(pLink->ReceiverID()))
|
|
FindConnectedObjects(pLink->ReceiverID(), SearchOutgoing, SearchIncoming, rIDList);
|
|
}
|
|
}
|
|
|
|
if (SearchIncoming)
|
|
{
|
|
for (u32 iLink = 0; iLink < pInst->NumLinks(eIncoming); iLink++)
|
|
{
|
|
CLink *pLink = pInst->Link(eIncoming, iLink);
|
|
|
|
if (!rIDList.contains(pLink->SenderID()))
|
|
FindConnectedObjects(pLink->SenderID(), SearchOutgoing, SearchIncoming, rIDList);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ************ PROTECTED SLOTS ************
|
|
void CSceneViewport::CheckUserInput()
|
|
{
|
|
bool MouseActive = (underMouse() && !IsMouseInputActive());
|
|
|
|
if (!MouseActive || mViewInfo.GameMode)
|
|
{
|
|
ResetHover();
|
|
mGizmoHovering = false;
|
|
}
|
|
|
|
if (MouseActive)
|
|
{
|
|
CRay Ray = CastRay();
|
|
|
|
if (!mViewInfo.GameMode)
|
|
CheckGizmoInput(Ray);
|
|
|
|
if (!mpEditor->Gizmo()->IsTransforming())
|
|
mRayIntersection = SceneRayCast(Ray);
|
|
}
|
|
|
|
else
|
|
mRayIntersection = SRayIntersection();
|
|
|
|
QMouseEvent Event = CreateMouseEvent();
|
|
emit InputProcessed(mRayIntersection, &Event);
|
|
}
|
|
|
|
void CSceneViewport::Paint()
|
|
{
|
|
if (!mpScene) return;
|
|
|
|
mpRenderer->BeginFrame();
|
|
|
|
if ((mViewInfo.ShowFlags & eShowSky) || mViewInfo.GameMode)
|
|
{
|
|
CModel *pSky = mpScene->ActiveSkybox();
|
|
if (pSky) mpRenderer->RenderSky(pSky, mViewInfo);
|
|
}
|
|
|
|
mCamera.LoadMatrices();
|
|
mpScene->AddSceneToRenderer(mpRenderer, mViewInfo);
|
|
|
|
// Draw the line for the link the user is editing. This is a little hacky but I don't really have a better way to do this atm.
|
|
if (mLinkLineEnabled)
|
|
{
|
|
CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity;
|
|
CGraphics::UpdateMVPBlock();
|
|
glDepthRange(0.f, 1.f);
|
|
CDrawUtil::DrawLine(mLinkLinePoints[0], mLinkLinePoints[1], CColor::skYellow);
|
|
}
|
|
|
|
mpRenderer->RenderBuckets(mViewInfo);
|
|
mpRenderer->RenderBloom();
|
|
|
|
if (mpEditor->IsGizmoVisible() && !mViewInfo.GameMode)
|
|
{
|
|
CGizmo *pGizmo = mpEditor->Gizmo();
|
|
mCamera.LoadMatrices();
|
|
|
|
mpRenderer->ClearDepthBuffer();
|
|
pGizmo->UpdateForCamera(mCamera);
|
|
pGizmo->AddToRenderer(mpRenderer, mViewInfo);
|
|
mpRenderer->RenderBuckets(mViewInfo);
|
|
}
|
|
|
|
mpRenderer->EndFrame();
|
|
}
|
|
|
|
void CSceneViewport::ContextMenu(QContextMenuEvent *pEvent)
|
|
{
|
|
// mpHoverNode is cleared during mouse input, so this call is necessary. todo: better way?
|
|
mRayIntersection = SceneRayCast(CastRay());
|
|
|
|
// Set up actions
|
|
TString NodeName;
|
|
bool HasHoverNode = (mpHoverNode && (mpHoverNode->NodeType() != eStaticNode) && (mpHoverNode->NodeType() != eModelNode));
|
|
bool HasSelection = mpEditor->HasSelection();
|
|
bool IsScriptNode = (mpHoverNode && mpHoverNode->NodeType() == eScriptNode);
|
|
|
|
mpToggleSelectAction->setVisible(HasHoverNode);
|
|
mpSelectConnectedMenu->menuAction()->setVisible(IsScriptNode);
|
|
mpHideSelectionSeparator->setVisible(HasHoverNode);
|
|
mpHideSelectionAction->setVisible(HasSelection);
|
|
mpHideUnselectedAction->setVisible(HasSelection);
|
|
mpHideHoverSeparator->setVisible(HasSelection);
|
|
mpHideHoverNodeAction->setVisible(HasHoverNode);
|
|
mpHideHoverTypeAction->setVisible(IsScriptNode);
|
|
mpHideHoverLayerAction->setVisible(IsScriptNode);
|
|
mpUnhideSeparator->setVisible(HasHoverNode);
|
|
|
|
if (HasHoverNode)
|
|
{
|
|
TString Name = IsScriptNode ? static_cast<CScriptNode*>(mpHoverNode)->Instance()->InstanceName() : mpHoverNode->Name();
|
|
|
|
if (mpHoverNode->IsSelected())
|
|
mpToggleSelectAction->setText(QString("Deselect %1").arg(TO_QSTRING(Name)));
|
|
else
|
|
mpToggleSelectAction->setText(QString("Select %1").arg(TO_QSTRING(Name)));
|
|
}
|
|
|
|
if (IsScriptNode)
|
|
{
|
|
CScriptNode *pScript = static_cast<CScriptNode*>(mpHoverNode);
|
|
NodeName = pScript->Instance()->InstanceName();
|
|
mpHideHoverTypeAction->setText( QString("Hide all %1 objects").arg(TO_QSTRING(pScript->Template()->Name())) );
|
|
mpHideHoverLayerAction->setText( QString("Hide layer %1").arg(TO_QSTRING(pScript->Instance()->Layer()->Name())) );
|
|
}
|
|
|
|
else if (HasHoverNode)
|
|
NodeName = mpHoverNode->Name();
|
|
|
|
mpHideHoverNodeAction->setText(QString("Hide %1").arg(TO_QSTRING(NodeName)));
|
|
|
|
// Show menu
|
|
mpMenuNode = mpHoverNode;
|
|
mpContextMenu->exec(pEvent->pos());
|
|
}
|
|
|
|
void CSceneViewport::OnResize()
|
|
{
|
|
mpRenderer->SetViewportSize(width(), height());
|
|
}
|
|
|
|
void CSceneViewport::OnMouseClick(QMouseEvent *pEvent)
|
|
{
|
|
bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0);
|
|
bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0);
|
|
|
|
if (mGizmoHovering && !AltPressed && !CtrlPressed)
|
|
{
|
|
mGizmoTransforming = true;
|
|
mpEditor->Gizmo()->StartTransform();
|
|
mpEditor->BeginGizmoTransform();
|
|
}
|
|
}
|
|
|
|
void CSceneViewport::OnMouseRelease(QMouseEvent *pEvent)
|
|
{
|
|
if (pEvent->button() == Qt::LeftButton)
|
|
{
|
|
// Stop gizmo transform
|
|
if (mGizmoTransforming)
|
|
{
|
|
CGizmo *pGizmo = mpEditor->Gizmo();
|
|
pGizmo->EndTransform();
|
|
mpEditor->EndGizmoTransform();
|
|
mGizmoTransforming = false;
|
|
}
|
|
|
|
// Object selection/deselection
|
|
else
|
|
emit ViewportClick(mRayIntersection, pEvent);
|
|
}
|
|
}
|
|
|
|
// ************ MENU ACTIONS ************
|
|
void CSceneViewport::OnToggleSelect()
|
|
{
|
|
if (mpMenuNode->IsSelected())
|
|
mpEditor->DeselectNode(mpMenuNode);
|
|
else
|
|
mpEditor->SelectNode(mpMenuNode);
|
|
}
|
|
|
|
void CSceneViewport::OnSelectConnected()
|
|
{
|
|
QList<u32> InstanceIDs;
|
|
bool SearchOutgoing = (sender() == mpSelectConnectedOutgoingAction || sender() == mpSelectConnectedAllAction);
|
|
bool SearchIncoming = (sender() == mpSelectConnectedIncomingAction || sender() == mpSelectConnectedAllAction);
|
|
FindConnectedObjects(static_cast<CScriptNode*>(mpMenuNode)->Instance()->InstanceID(), SearchOutgoing, SearchIncoming, InstanceIDs);
|
|
|
|
QList<CSceneNode*> Nodes;
|
|
foreach (u32 ID, InstanceIDs)
|
|
Nodes << mpScene->NodeForInstanceID(ID);
|
|
|
|
bool ShouldClear = ((qApp->keyboardModifiers() & Qt::ControlModifier) == 0);
|
|
mpEditor->BatchSelectNodes(Nodes, ShouldClear, "Select Connected");
|
|
}
|
|
|
|
void CSceneViewport::OnHideSelection()
|
|
{
|
|
for (CSelectionIterator It(mpEditor->Selection()); It; ++It)
|
|
It->SetVisible(false);
|
|
}
|
|
|
|
void CSceneViewport::OnHideUnselected()
|
|
{
|
|
for (CSceneIterator It(mpScene, eScriptNode | eLightNode); !It.DoneIterating(); ++It)
|
|
if (!It->IsSelected())
|
|
It->SetVisible(false);
|
|
}
|
|
|
|
void CSceneViewport::OnHideNode()
|
|
{
|
|
mpMenuNode->SetVisible(false);
|
|
}
|
|
|
|
void CSceneViewport::OnHideType()
|
|
{
|
|
static_cast<CScriptNode*>(mpMenuNode)->Template()->SetVisible(false);
|
|
}
|
|
|
|
void CSceneViewport::OnHideLayer()
|
|
{
|
|
static_cast<CScriptNode*>(mpMenuNode)->Instance()->Layer()->SetVisible(false);
|
|
}
|
|
|
|
void CSceneViewport::OnUnhideAll()
|
|
{
|
|
CSceneIterator it(mpScene, eScriptNode | eLightNode, true);
|
|
|
|
while (!it.DoneIterating())
|
|
{
|
|
if (!it->IsVisible())
|
|
{
|
|
if (it->NodeType() == eLightNode)
|
|
it->SetVisible(true);
|
|
|
|
else
|
|
{
|
|
CScriptNode *pScript = static_cast<CScriptNode*>(*it);
|
|
|
|
if (!pScript->MarkedVisible())
|
|
pScript->SetVisible(true);
|
|
|
|
else
|
|
{
|
|
pScript->Template()->SetVisible(true);
|
|
pScript->Instance()->Layer()->SetVisible(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
++it;
|
|
}
|
|
}
|
|
|
|
void CSceneViewport::OnContextMenuClose()
|
|
{
|
|
mpContextMenu = nullptr;
|
|
mpMenuNode = nullptr;
|
|
}
|