From dbf002d12af8c1d1ce35b1fe7b86198a5d760512 Mon Sep 17 00:00:00 2001 From: parax0 Date: Tue, 1 Sep 2015 13:05:48 -0400 Subject: [PATCH] Split off lots of editor functionality into new abstract INodeEditor class and viewport functionality into CBasicViewport class; added viewport subclasses and undo/redo system in the World Editor --- Common/Math.cpp | 8 + Common/Math.h | 2 + Core/CCamera.cpp | 15 +- Core/CCamera.h | 7 +- Core/CRenderBucket.cpp | 2 +- Core/CRenderer.cpp | 2 +- Core/CSceneManager.cpp | 7 +- PrimeWorldEditor.pro | 28 +- Scene/CScriptNode.cpp | 6 + ...CEditorGLWidget.cpp => CBasicViewport.cpp} | 190 +++--- UI/{CEditorGLWidget.h => CBasicViewport.h} | 51 +- UI/CGizmo.cpp | 12 +- UI/CGizmo.h | 9 +- UI/CModelEditorViewport.cpp | 95 +++ UI/CModelEditorViewport.h | 30 + UI/CModelEditorWindow.cpp | 94 +-- UI/CModelEditorWindow.h | 13 +- UI/CModelEditorWindow.ui | 16 +- UI/CSceneViewport.cpp | 230 +++++++ UI/CSceneViewport.h | 49 ++ UI/CWorldEditor.cpp | 629 ++++-------------- UI/CWorldEditor.h | 78 +-- UI/CWorldEditor.ui | 111 +--- UI/CWorldEditorWindow.cpp | 6 +- UI/CWorldEditorWindow.ui | 14 +- UI/INodeEditor.cpp | 272 ++++++++ UI/INodeEditor.h | 77 +++ UI/WPropertyEditor.cpp | 2 +- UI/WVectorEditor.cpp | 2 - UI/WorldEditor/WModifyTab.cpp | 2 +- UI/WorldEditor/WModifyTab.h | 2 +- UI/undo/CClearSelectionCommand.cpp | 42 ++ UI/undo/CClearSelectionCommand.h | 20 + UI/undo/CDeselectNodeCommand.cpp | 42 ++ UI/undo/CDeselectNodeCommand.h | 19 + UI/undo/CRotateNodeCommand.cpp | 103 +++ UI/undo/CRotateNodeCommand.h | 34 + UI/undo/CScaleNodeCommand.cpp | 103 +++ UI/undo/CScaleNodeCommand.h | 34 + UI/undo/CSelectNodeCommand.cpp | 42 ++ UI/undo/CSelectNodeCommand.h | 19 + UI/undo/CTranslateNodeCommand.cpp | 92 +++ UI/undo/CTranslateNodeCommand.h | 32 + UI/undo/EUndoCommand.h | 12 + UI/undo/UndoCommands.h | 13 + 45 files changed, 1760 insertions(+), 908 deletions(-) rename UI/{CEditorGLWidget.cpp => CBasicViewport.cpp} (57%) rename UI/{CEditorGLWidget.h => CBasicViewport.h} (64%) create mode 100644 UI/CModelEditorViewport.cpp create mode 100644 UI/CModelEditorViewport.h create mode 100644 UI/CSceneViewport.cpp create mode 100644 UI/CSceneViewport.h create mode 100644 UI/INodeEditor.cpp create mode 100644 UI/INodeEditor.h create mode 100644 UI/undo/CClearSelectionCommand.cpp create mode 100644 UI/undo/CClearSelectionCommand.h create mode 100644 UI/undo/CDeselectNodeCommand.cpp create mode 100644 UI/undo/CDeselectNodeCommand.h create mode 100644 UI/undo/CRotateNodeCommand.cpp create mode 100644 UI/undo/CRotateNodeCommand.h create mode 100644 UI/undo/CScaleNodeCommand.cpp create mode 100644 UI/undo/CScaleNodeCommand.h create mode 100644 UI/undo/CSelectNodeCommand.cpp create mode 100644 UI/undo/CSelectNodeCommand.h create mode 100644 UI/undo/CTranslateNodeCommand.cpp create mode 100644 UI/undo/CTranslateNodeCommand.h create mode 100644 UI/undo/EUndoCommand.h create mode 100644 UI/undo/UndoCommands.h diff --git a/Common/Math.cpp b/Common/Math.cpp index 6c80a2b7..3d2447d5 100644 --- a/Common/Math.cpp +++ b/Common/Math.cpp @@ -1,4 +1,6 @@ #include "Math.h" +#include +#include namespace Math { @@ -336,4 +338,10 @@ std::pair RayTriangleIntersection(const CRay& Ray, return std::pair(true, t); } +CMatrix4f PerspectiveMatrix(float fov, float aspect, float near, float far) +{ + // todo: don't use glm + return CMatrix4f::FromGlmMat4(glm::perspective(fov, aspect, near, far)).Transpose(); +} + } // End namespace diff --git a/Common/Math.h b/Common/Math.h index e5fd8b84..765a5433 100644 --- a/Common/Math.h +++ b/Common/Math.h @@ -32,6 +32,8 @@ std::pair RayTriangleIntersection(const CRay& Ray, const CVector3f& const CVector3f& PointB, const CVector3f& PointC, bool AllowBackfaces = false); +CMatrix4f PerspectiveMatrix(float fov, float aspect, float near, float far); + // Constants static const float skPi = 3.14159265358979323846f; static const float skHalfPi = skPi / 2.f; diff --git a/Core/CCamera.cpp b/Core/CCamera.cpp index ce25638b..b160b91a 100644 --- a/Core/CCamera.cpp +++ b/Core/CCamera.cpp @@ -1,7 +1,7 @@ #include "CCamera.h" #include "CGraphics.h" #include - +#include #include #define HALF_PI 1.570796371f @@ -170,21 +170,25 @@ CVector3f CCamera::Position() const return mPosition; } -CVector3f CCamera::GetDirection() const +CVector3f CCamera::Direction() const { return mDirection; } -float CCamera::GetYaw() const +float CCamera::Yaw() const { return mYaw; } -float CCamera::GetPitch() const +float CCamera::Pitch() const { return mPitch; } +float CCamera::FieldOfView() const +{ + return 55.f; +} const CMatrix4f& CCamera::ViewMatrix() { @@ -298,7 +302,6 @@ void CCamera::CalculateView() void CCamera::CalculateProjection() { - // todo: don't use glm - mCachedProjectionMatrix = CMatrix4f::FromGlmMat4(glm::perspective(55.f, mAspectRatio, 0.1f, 4096.f)).Transpose(); + mCachedProjectionMatrix = Math::PerspectiveMatrix(55.f, mAspectRatio, 0.1f, 4096.f); mProjectionOutdated = false; } diff --git a/Core/CCamera.h b/Core/CCamera.h index 7c6cd595..ecd2cfa7 100644 --- a/Core/CCamera.h +++ b/Core/CCamera.h @@ -49,9 +49,10 @@ public: // Getters CVector3f Position() const; - CVector3f GetDirection() const; - float GetYaw() const; - float GetPitch() const; + CVector3f Direction() const; + float Yaw() const; + float Pitch() const; + float FieldOfView() const; const CMatrix4f& ViewMatrix(); const CMatrix4f& RotationOnlyViewMatrix(); const CMatrix4f& ProjectionMatrix(); diff --git a/Core/CRenderBucket.cpp b/Core/CRenderBucket.cpp index dfc5de72..65a3373c 100644 --- a/Core/CRenderBucket.cpp +++ b/Core/CRenderBucket.cpp @@ -30,7 +30,7 @@ void CRenderBucket::Sort(CCamera& Camera) CCamera *pCamera; bool operator()(SRenderablePtr left, SRenderablePtr right) { CVector3f cPos = pCamera->Position(); - CVector3f cDir = pCamera->GetDirection(); + CVector3f cDir = pCamera->Direction(); CVector3f distL = left.AABox.ClosestPointAlongVector(cDir) - cPos; float dotL = distL.Dot(cDir); diff --git a/Core/CRenderer.cpp b/Core/CRenderer.cpp index 1584274a..c82f569a 100644 --- a/Core/CRenderer.cpp +++ b/Core/CRenderer.cpp @@ -126,7 +126,7 @@ void CRenderer::RenderBuckets(CCamera& Camera) { if (!mInitialized) Init(); mSceneFramebuffer.Bind(); - Camera.LoadMatrices(); + //Camera.LoadMatrices(); // Set backface culling if (mOptions & eEnableBackfaceCull) glEnable(GL_CULL_FACE); diff --git a/Core/CSceneManager.cpp b/Core/CSceneManager.cpp index 8b6571d4..9c36f4dd 100644 --- a/Core/CSceneManager.cpp +++ b/Core/CSceneManager.cpp @@ -200,8 +200,11 @@ void CSceneManager::SetActiveWorld(CWorld* _world) void CSceneManager::ClearScene() { - mpAreaRootNode->Unparent(); - delete mpAreaRootNode; + if (mpAreaRootNode) + { + mpAreaRootNode->Unparent(); + delete mpAreaRootNode; + } mModelNodes.clear(); mStaticNodes.clear(); diff --git a/PrimeWorldEditor.pro b/PrimeWorldEditor.pro index 942262f5..81923686 100644 --- a/PrimeWorldEditor.pro +++ b/PrimeWorldEditor.pro @@ -72,7 +72,6 @@ SOURCES += \ Resource/factory/CWorldLoader.cpp \ Resource/CStringTable.cpp \ Resource/factory/CStringLoader.cpp \ - UI/CEditorGLWidget.cpp \ Core/CGraphics.cpp \ Resource/CFont.cpp \ Resource/factory/CFontLoader.cpp \ @@ -135,7 +134,17 @@ SOURCES += \ UI/WIntegralSpinBox.cpp \ UI/CAboutDialog.cpp \ UI/CGizmo.cpp \ - Common/CPlane.cpp + Common/CPlane.cpp \ + UI/undo/CTranslateNodeCommand.cpp \ + UI/undo/CClearSelectionCommand.cpp \ + UI/undo/CSelectNodeCommand.cpp \ + UI/undo/CDeselectNodeCommand.cpp \ + UI/CBasicViewport.cpp \ + UI/INodeEditor.cpp \ + UI/CSceneViewport.cpp \ + UI/undo/CRotateNodeCommand.cpp \ + UI/undo/CScaleNodeCommand.cpp \ + UI/CModelEditorViewport.cpp HEADERS += \ Common/AnimUtil.h \ @@ -208,7 +217,6 @@ HEADERS += \ Resource/SDependency.h \ Resource/CStringTable.h \ Resource/factory/CStringLoader.h \ - UI/CEditorGLWidget.h \ Core/CGraphics.h \ Resource/CFont.h \ Resource/factory/CFontLoader.h \ @@ -286,7 +294,19 @@ HEADERS += \ Core/IRenderable.h \ Core/SRenderablePtr.h \ Common/ETransformSpace.h \ - Common/CPlane.h + Common/CPlane.h \ + UI/undo/CTranslateNodeCommand.h \ + UI/undo/EUndoCommand.h \ + UI/undo/CClearSelectionCommand.h \ + UI/undo/CSelectNodeCommand.h \ + UI/undo/CDeselectNodeCommand.h \ + UI/undo/UndoCommands.h \ + UI/CBasicViewport.h \ + UI/INodeEditor.h \ + UI/CSceneViewport.h \ + UI/undo/CRotateNodeCommand.h \ + UI/undo/CScaleNodeCommand.h \ + UI/CModelEditorViewport.h FORMS += \ UI/CWorldEditorWindow.ui \ diff --git a/Scene/CScriptNode.cpp b/Scene/CScriptNode.cpp index 36c3d29c..e206fc42 100644 --- a/Scene/CScriptNode.cpp +++ b/Scene/CScriptNode.cpp @@ -108,6 +108,12 @@ void CScriptNode::AddToRenderer(CRenderer *pRenderer) if (mHasVolumePreview) mpVolumePreviewNode->AddToRenderer(pRenderer); + + if (mpInstance->ObjectTypeID() == 0xC) + { + CGraphics::sMVPBlock.ViewMatrix = Transform().Inverse().ToMatrix4f(); + CGraphics::UpdateMVPBlock(); + } } } diff --git a/UI/CEditorGLWidget.cpp b/UI/CBasicViewport.cpp similarity index 57% rename from UI/CEditorGLWidget.cpp rename to UI/CBasicViewport.cpp index 6aaf7ca4..14dbf007 100644 --- a/UI/CEditorGLWidget.cpp +++ b/UI/CBasicViewport.cpp @@ -1,49 +1,31 @@ +#include "CBasicViewport.h" +#include +#include +#include #include -#include -#include -#include -#include -#include -#include "CEditorGLWidget.h" -#include -#include -#include -#include -#include -#include -#include -#include -QTimer CEditorGLWidget::sRefreshTimer; - -CEditorGLWidget::CEditorGLWidget(QWidget *pParent) : - QOpenGLWidget(pParent) +CBasicViewport::CBasicViewport(QWidget *pParent) : + QOpenGLWidget(pParent), + mLastDrawTime(CTimer::GlobalTime()), + mKeysPressed(0), + mButtonsPressed(0), + mCursorState(Qt::ArrowCursor), + mCursorVisible(true) { setMouseTracking(true); - mLastDrawTime = CTimer::GlobalTime(); - mKeysPressed = 0; - mButtonsPressed = 0; - mCursorState = Qt::ArrowCursor; - mCursorVisible = true; mCamera.SetAspectRatio((float) width() / height()); - - connect(&sRefreshTimer, SIGNAL(timeout()), this, SLOT(update())); - - if (!sRefreshTimer.isActive()) - sRefreshTimer.start(0); } -CEditorGLWidget::~CEditorGLWidget() +CBasicViewport::~CBasicViewport() { } -void CEditorGLWidget::initializeGL() +void CBasicViewport::initializeGL() { // Initialize CGraphics CGraphics::Initialize(); // Setting various GL flags - glEnable(GL_DEPTH_TEST); glEnable(GL_PRIMITIVE_RESTART); glPrimitiveRestartIndex(0xFFFF); glDepthFunc(GL_LEQUAL); @@ -55,44 +37,33 @@ void CEditorGLWidget::initializeGL() CMaterial::KillCachedMaterial(); CShader::KillCachedShader(); - // Initialize renderer - emit ViewportResized(width(), height()); + // Initialize size + OnResize(); } -void CEditorGLWidget::paintGL() +void CBasicViewport::paintGL() { - double DeltaTime = CTimer::GlobalTime() - mLastDrawTime; - mLastDrawTime = CTimer::GlobalTime(); + // Prep render + glViewport(0, 0, width(), height()); + glLineWidth(1.f); + glEnable(GL_DEPTH_TEST); - // Camera movement is processed here in order to sync it with the paint event - // This way movement happens exactly once per frame - no more, no less - ProcessInput(DeltaTime); + // Actual rendering is intended to be handled by subclassing CBasicViewport and + // reimplementing Render(). + Paint(); - // Pre-render signal allows for per-frame operations to be performed before the draw happens - emit PreRender(); - - // We emit a signal to indicate it's time to render the viewport instead of doing the rendering here. - // This allows the editor GL widget class to be reused among multiple editors with different rendering needs. - emit Render(mCamera); - - // Post-render signal allows for the frame to be completed with post-processing - emit PostRender(); + // Finally, draw XYZ axes in the corner +// DrawAxes(); } -void CEditorGLWidget::resizeGL(int w, int h) +void CBasicViewport::resizeGL(int w, int h) { mCamera.SetAspectRatio((float) w / h); glViewport(0, 0, w, h); - emit ViewportResized(w, h); + OnResize(); } -void CEditorGLWidget::mouseMoveEvent(QMouseEvent *pEvent) -{ - if ((!IsMouseInputActive()) && (mButtonsPressed & eLeftButton)) - emit MouseDrag(pEvent); -} - -void CEditorGLWidget::mousePressEvent(QMouseEvent *pEvent) +void CBasicViewport::mousePressEvent(QMouseEvent *pEvent) { setFocus(); @@ -100,7 +71,11 @@ void CEditorGLWidget::mousePressEvent(QMouseEvent *pEvent) if (pEvent->button() == Qt::RightButton) mButtonsPressed |= eRightButton; if (IsMouseInputActive()) + { SetCursorVisible(false); + mMouseMoved = false; + mMoveTimer.Restart(); + } // Left click only activates if mouse input is inactive to prevent the user from // clicking on things and creating selection rectangles while the cursor is hidden @@ -109,13 +84,13 @@ void CEditorGLWidget::mousePressEvent(QMouseEvent *pEvent) if (pEvent->button() == Qt::LeftButton) mButtonsPressed |= eLeftButton; - emit MouseClick(pEvent); + OnMouseClick(pEvent); } mLastMousePos = pEvent->globalPos(); } -void CEditorGLWidget::mouseReleaseEvent(QMouseEvent *pEvent) +void CBasicViewport::mouseReleaseEvent(QMouseEvent *pEvent) { bool fromMouseInput = IsMouseInputActive(); if (pEvent->button() == Qt::LeftButton) mButtonsPressed &= ~eLeftButton; @@ -126,12 +101,23 @@ void CEditorGLWidget::mouseReleaseEvent(QMouseEvent *pEvent) if (!IsMouseInputActive()) SetCursorVisible(true); - // Emit mouse release event if we didn't just exit mouse input (or regardless on left click) + // Run mouse release if we didn't just exit mouse input (or regardless on left click) if (!fromMouseInput || (pEvent->button() == Qt::LeftButton)) - emit MouseRelease(pEvent); + OnMouseRelease(pEvent); } -void CEditorGLWidget::keyPressEvent(QKeyEvent *pEvent) +void CBasicViewport::mouseMoveEvent(QMouseEvent *pEvent) +{ + // todo: draggable selection rectangle +} + +void CBasicViewport::wheelEvent(QWheelEvent *pEvent) +{ + // Maybe track a "wheel delta" member variable and let CCamera decide what to do with it? + mCamera.Zoom(pEvent->angleDelta().y() / 6000.f); +} + +void CBasicViewport::keyPressEvent(QKeyEvent *pEvent) { switch (pEvent->key()) { @@ -145,7 +131,7 @@ void CEditorGLWidget::keyPressEvent(QKeyEvent *pEvent) } } -void CEditorGLWidget::keyReleaseEvent(QKeyEvent *pEvent) +void CBasicViewport::keyReleaseEvent(QKeyEvent *pEvent) { switch (pEvent->key()) { @@ -159,13 +145,7 @@ void CEditorGLWidget::keyReleaseEvent(QKeyEvent *pEvent) } } -void CEditorGLWidget::wheelEvent(QWheelEvent *pEvent) -{ - // Maybe track a "wheel delta" member variable and let CCamera decide what to do with it? - mCamera.Zoom(pEvent->angleDelta().y() / 6000.f); -} - -void CEditorGLWidget::focusOutEvent(QFocusEvent*) +void CBasicViewport::focusOutEvent(QFocusEvent*) { // When the widget loses focus, release all input. mButtonsPressed = 0; @@ -173,7 +153,14 @@ void CEditorGLWidget::focusOutEvent(QFocusEvent*) SetCursorVisible(true); } -void CEditorGLWidget::SetCursorState(const QCursor &Cursor) +void CBasicViewport::contextMenuEvent(QContextMenuEvent *pEvent) +{ + // Only allow context menu if we aren't exiting mouse input mode. + if (!mMouseMoved && (mMoveTimer.Time() < 0.5)) + ContextMenu(pEvent); +} + +void CBasicViewport::SetCursorState(const QCursor &Cursor) { mCursorState = Cursor; @@ -181,7 +168,7 @@ void CEditorGLWidget::SetCursorState(const QCursor &Cursor) setCursor(Cursor); } -void CEditorGLWidget::SetCursorVisible(bool visible) +void CBasicViewport::SetCursorVisible(bool visible) { mCursorVisible = visible; @@ -191,35 +178,35 @@ void CEditorGLWidget::SetCursorVisible(bool visible) setCursor(Qt::BlankCursor); } -bool CEditorGLWidget::IsCursorVisible() +bool CBasicViewport::IsCursorVisible() { return mCursorVisible; } -bool CEditorGLWidget::IsMouseInputActive() +bool CBasicViewport::IsMouseInputActive() { static const int skMoveButtons = eMiddleButton | eRightButton; return ((mButtonsPressed & skMoveButtons) != 0); } -bool CEditorGLWidget::IsKeyboardInputActive() +bool CBasicViewport::IsKeyboardInputActive() { static const int skMoveKeys = eQKey | eWKey | eEKey | eAKey | eSKey | eDKey; return ((mKeysPressed & skMoveKeys) != 0); } -CCamera& CEditorGLWidget::Camera() +CCamera& CBasicViewport::Camera() { return mCamera; } -CRay CEditorGLWidget::CastRay() +CRay CBasicViewport::CastRay() { CVector2f MouseCoords = MouseDeviceCoordinates(); return mCamera.CastRay(MouseCoords); } -CVector2f CEditorGLWidget::MouseDeviceCoordinates() +CVector2f CBasicViewport::MouseDeviceCoordinates() { QPoint MousePos = QCursor::pos(); QPoint ThisPos = this->mapToGlobal(pos()); @@ -232,10 +219,18 @@ CVector2f CEditorGLWidget::MouseDeviceCoordinates() return Device; } - -// ************ PRIVATE ************ -void CEditorGLWidget::ProcessInput(double DeltaTime) +double CBasicViewport::LastRenderDuration() { + return mFrameTimer.Time(); +} + +// ************ PUBLIC SLOTS ************ +void CBasicViewport::ProcessInput() +{ + // Process camera input + double DeltaTime = CTimer::GlobalTime() - mLastDrawTime; + mLastDrawTime = CTimer::GlobalTime(); + if (IsMouseInputActive()) { float XMovement = (QCursor::pos().x() - mLastMousePos.x()) * 0.01f; @@ -245,9 +240,42 @@ void CEditorGLWidget::ProcessInput(double DeltaTime) { mCamera.ProcessMouseInput((EKeyInputs) mKeysPressed, (EMouseInputs) mButtonsPressed, XMovement, YMovement); QCursor::setPos(mLastMousePos); + mMouseMoved = true; } } if (IsKeyboardInputActive()) mCamera.ProcessKeyInput((EKeyInputs) mKeysPressed, DeltaTime); + + // Check user input + CheckUserInput(); +} + +void CBasicViewport::Render() +{ + mFrameTimer.Start(); + update(); + mFrameTimer.Stop(); +} + +// ************ PRIVATE ************ +void CBasicViewport::ProcessInput(double DeltaTime) +{ +} + +void CBasicViewport::DrawAxes() +{ + // Draw 64x64 axes in lower-left corner with 8px margins + glViewport(8, height() - 72, 64, 64); + glEnable(GL_DEPTH_TEST); + glClear(GL_DEPTH_BUFFER_BIT); + + CGraphics::sMVPBlock.ViewMatrix = CTransform4f::TranslationMatrix(CVector3f(0,2,0)).ToMatrix4f() * mCamera.RotationOnlyViewMatrix(); + CGraphics::sMVPBlock.ProjectionMatrix = Math::PerspectiveMatrix(mCamera.FieldOfView(), 1.f, 0.1f, 4096.f); + CGraphics::UpdateMVPBlock(); + + glLineWidth(2.f); + CDrawUtil::DrawLine(CVector3f(0,0,0), CVector3f(1,0,0), CColor::skRed); // X + CDrawUtil::DrawLine(CVector3f(0,0,0), CVector3f(0,1,0), CColor::skGreen); // Y + CDrawUtil::DrawLine(CVector3f(0,0,0), CVector3f(0,0,1), CColor::skBlue); // Z } diff --git a/UI/CEditorGLWidget.h b/UI/CBasicViewport.h similarity index 64% rename from UI/CEditorGLWidget.h rename to UI/CBasicViewport.h index 646b490f..9ac6b1fd 100644 --- a/UI/CEditorGLWidget.h +++ b/UI/CBasicViewport.h @@ -11,34 +11,44 @@ #include #include #include +#include -class CEditorGLWidget : public QOpenGLWidget +class CBasicViewport : public QOpenGLWidget { Q_OBJECT - static QTimer sRefreshTimer; +protected: + // Render CCamera mCamera; - QPoint mLastMousePos; + CTimer mFrameTimer; double mLastDrawTime; - QPoint mLeftClickPoint; - int mButtonsPressed; // int container for EMouseInputs flags - int mKeysPressed; // int container for EKeyInputs flags + + // Cursor settings QCursor mCursorState; bool mCursorVisible; + // Input + QPoint mLastMousePos; + bool mMouseMoved; + CTimer mMoveTimer; + int mButtonsPressed; // int container for EMouseInputs flags + int mKeysPressed; // int container for EKeyInputs flags + public: - explicit CEditorGLWidget(QWidget *pParent = 0); - ~CEditorGLWidget(); + explicit CBasicViewport(QWidget *pParent = 0); + ~CBasicViewport(); void initializeGL(); void paintGL(); void resizeGL(int w, int h); - void mouseMoveEvent(QMouseEvent *pEvent); void mousePressEvent(QMouseEvent *pEvent); void mouseReleaseEvent(QMouseEvent *pEvent); + void mouseMoveEvent(QMouseEvent *pEvent); + void wheelEvent(QWheelEvent *pEvent); void keyPressEvent(QKeyEvent *pEvent); void keyReleaseEvent(QKeyEvent *pEvent); - void wheelEvent(QWheelEvent *pEvent); void focusOutEvent(QFocusEvent *pEvent); + void contextMenuEvent(QContextMenuEvent *pEvent); + void SetCursorState(const QCursor& Cursor); void SetCursorVisible(bool visible); bool IsCursorVisible(); @@ -47,18 +57,23 @@ public: CCamera& Camera(); CRay CastRay(); CVector2f MouseDeviceCoordinates(); + double LastRenderDuration(); -signals: - void ViewportResized(int w, int h); - void PreRender(); - void Render(CCamera& Camera); - void PostRender(); - void MouseClick(QMouseEvent *pEvent); - void MouseRelease(QMouseEvent *pEvent); - void MouseDrag(QMouseEvent *pEvent); +public slots: + void ProcessInput(); + void Render(); + +protected slots: + virtual void CheckUserInput() {} + virtual void Paint() {} + virtual void ContextMenu(QContextMenuEvent *pEvent) {} + virtual void OnResize() {} + virtual void OnMouseClick(QMouseEvent *pEvent) {} + virtual void OnMouseRelease(QMouseEvent *pEvent) {} private: void ProcessInput(double DeltaTime); + void DrawAxes(); }; #endif diff --git a/UI/CGizmo.cpp b/UI/CGizmo.cpp index 1e46c63f..2a0d8fee 100644 --- a/UI/CGizmo.cpp +++ b/UI/CGizmo.cpp @@ -476,7 +476,7 @@ bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera) mDeltaScale = mTotalScale / oldScale; - if (!mHasTransformed && (scaleAmount != 0.f)) + if (!mHasTransformed && (scaleAmount != 1.f)) mHasTransformed = true; return true; @@ -491,6 +491,11 @@ void CGizmo::EndTransform() mIsTransforming = false; } +bool CGizmo::IsTransforming() +{ + return mIsTransforming; +} + bool CGizmo::HasTransformed() { return mHasTransformed; @@ -501,6 +506,11 @@ CGizmo::EGizmoMode CGizmo::Mode() return mMode; } +ETransformSpace CGizmo::TransformSpace() +{ + return mTransformSpace; +} + CVector3f CGizmo::Position() { return mPosition; diff --git a/UI/CGizmo.h b/UI/CGizmo.h index 9a8fc882..762b9ae1 100644 --- a/UI/CGizmo.h +++ b/UI/CGizmo.h @@ -41,7 +41,7 @@ class CGizmo : public IRenderable public: enum EGizmoMode { - eTranslate, eRotate, eScale + eTranslate, eRotate, eScale, eOff }; enum EGizmoAxes @@ -68,6 +68,7 @@ private: CVector2f mWrapOffset; bool mEnableCursorWrap; + // Transform CTransform4f mTransform; CTransform4f mBillboardTransform; CTransform4f mScaledTransform; @@ -79,6 +80,7 @@ private: bool mFlipScaleY; bool mFlipScaleZ; + // Delta transform CVector3f mDeltaTranslation; CVector3f mTotalTranslation; CQuaternion mDeltaRotation; @@ -87,15 +89,16 @@ private: CVector3f mDeltaScale; CVector3f mTotalScale; + // Transform helpers CPlane mTranslatePlane; CVector3f mTranslateOffset; float mRotateOffset; float mScaleOffset; bool mSetOffset; - CVector3f mHitPoint; CVector3f mMoveDir; + // Model parts struct SModelPart { EGizmoAxes modelAxes; @@ -133,9 +136,11 @@ public: void StartTransform(); bool TransformFromInput(const CRay& ray, CCamera& camera); void EndTransform(); + bool IsTransforming(); bool HasTransformed(); EGizmoMode Mode(); + ETransformSpace TransformSpace(); CVector3f Position(); CVector3f DeltaTranslation(); CVector3f TotalTranslation(); diff --git a/UI/CModelEditorViewport.cpp b/UI/CModelEditorViewport.cpp new file mode 100644 index 00000000..820239ba --- /dev/null +++ b/UI/CModelEditorViewport.cpp @@ -0,0 +1,95 @@ +#include "CModelEditorViewport.h" +#include + +CModelEditorViewport::CModelEditorViewport(QWidget *pParent) + : CBasicViewport(pParent), + mMode(eDrawMesh), + mpActiveMaterial(nullptr), + mpModelNode(nullptr) +{ + mpRenderer = new CRenderer(); + mpRenderer->SetViewportSize(width(), height()); + mpRenderer->SetClearColor(CColor::skBlack); + mpRenderer->ToggleGrid(true); +} + +CModelEditorViewport::~CModelEditorViewport() +{ + delete mpRenderer; +} + +void CModelEditorViewport::SetNode(CModelNode *pNode) +{ + mpModelNode = pNode; +} + +void CModelEditorViewport::SetActiveMaterial(CMaterial *pMat) +{ + mpActiveMaterial = pMat; +} + +void CModelEditorViewport::SetDrawMode(EDrawMode mode) +{ + mMode = mode; +} + +void CModelEditorViewport::SetClearColor(CColor color) +{ + mpRenderer->SetClearColor(color); +} + +void CModelEditorViewport::Paint() +{ + mpRenderer->BeginFrame(); + mCamera.LoadMatrices(); + + if (!mpModelNode->Model()) + CDrawUtil::DrawGrid(); + + else if (mMode == eDrawMesh) + { + CDrawUtil::DrawGrid(); + mpModelNode->AddToRenderer(mpRenderer); + mpRenderer->RenderBuckets(mCamera); + } + + else if (mMode == eDrawSphere) + { + if (!mpActiveMaterial) return; + glEnable(GL_CULL_FACE); + + CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); + CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; + CGraphics::UpdateMVPBlock(); + CGraphics::SetDefaultLighting(); + CGraphics::UpdateLightBlock(); // Note: vertex block is updated by the material + mpActiveMaterial->SetCurrent(eEnableUVScroll | eEnableBackfaceCull | eEnableOccluders); + + CDrawUtil::DrawSphere(true); + } + + else if (mMode == eDrawSquare) + { + if (!mpActiveMaterial) return; + glDisable(GL_CULL_FACE); + + CGraphics::SetDefaultLighting(); + CGraphics::UpdateLightBlock(); + CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); + + CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; + CGraphics::sMVPBlock.ViewMatrix = CMatrix4f::skIdentity; + CGraphics::sMVPBlock.ProjectionMatrix = CMatrix4f::skIdentity; + CGraphics::UpdateMVPBlock(); + + mpActiveMaterial->SetCurrent(eEnableUVScroll | eEnableOccluders); + CDrawUtil::DrawSquare(); + } + + mpRenderer->EndFrame(); +} + +void CModelEditorViewport::OnResize() +{ + mpRenderer->SetViewportSize(width(), height()); +} diff --git a/UI/CModelEditorViewport.h b/UI/CModelEditorViewport.h new file mode 100644 index 00000000..ebf23b22 --- /dev/null +++ b/UI/CModelEditorViewport.h @@ -0,0 +1,30 @@ +#ifndef CMODELEDITORVIEWPORT_H +#define CMODELEDITORVIEWPORT_H + +#include "CBasicViewport.h" + +class CModelEditorViewport : public CBasicViewport +{ +public: + enum EDrawMode { + eDrawMesh, eDrawSphere, eDrawSquare + }; + +private: + EDrawMode mMode; + CModelNode *mpModelNode; + CMaterial *mpActiveMaterial; + CRenderer *mpRenderer; + +public: + CModelEditorViewport(QWidget *pParent = 0); + ~CModelEditorViewport(); + void SetNode(CModelNode *pNode); + void SetActiveMaterial(CMaterial *pMat); + void SetDrawMode(EDrawMode mode); + void SetClearColor(CColor color); + void Paint(); + void OnResize(); +}; + +#endif // CMODELEDITORVIEWPORT_H diff --git a/UI/CModelEditorWindow.cpp b/UI/CModelEditorWindow.cpp index b3d2a585..98599532 100644 --- a/UI/CModelEditorWindow.cpp +++ b/UI/CModelEditorWindow.cpp @@ -33,15 +33,13 @@ CModelEditorWindow::CModelEditorWindow(QWidget *parent) : mpCurrentPass = nullptr; mIgnoreSignals = false; - mpRenderer = new CRenderer(); - mpRenderer->ToggleGrid(true); - mpRenderer->SetClearColor(CColor(0.3f, 0.3f, 0.3f, 1.f)); + ui->Viewport->SetNode(mpCurrentModelNode); + ui->Viewport->SetClearColor(CColor(0.3f, 0.3f, 0.3f, 1.f)); - CCamera& Camera = ui->PreviewGLWidget->Camera(); - Camera.Snap(CVector3f(0, 3, 1)); - Camera.SetFree(); - Camera.SetMoveSpeed(0.5f); - mDrawMode = eDrawMesh; + CCamera& camera = ui->Viewport->Camera(); + camera.Snap(CVector3f(0, 3, 1)); + camera.SetFree(); + camera.SetMoveSpeed(0.5f); // UI initialization UpdateAnimParamUI(-1); @@ -54,8 +52,8 @@ CModelEditorWindow::CModelEditorWindow(QWidget *parent) : ui->ClearColorPicker->setColor(QColor(76, 76, 76, 255)); // Viewport Signal/Slot setup - connect(ui->PreviewGLWidget, SIGNAL(ViewportResized(int,int)), this, SLOT(SetViewportSize(int,int))); - connect(ui->PreviewGLWidget, SIGNAL(Render(CCamera&)), this, SLOT(PaintViewport(CCamera&))); + connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); + mRefreshTimer.start(0); // Editor UI Signal/Slot setup ui->SetSelectionComboBox->setProperty ("ModelEditorWidgetType", eSetSelectComboBox); @@ -139,10 +137,15 @@ CModelEditorWindow::CModelEditorWindow(QWidget *parent) : CModelEditorWindow::~CModelEditorWindow() { delete mpCurrentModelNode; - delete mpRenderer; delete ui; } +void CModelEditorWindow::RefreshViewport() +{ + ui->Viewport->ProcessInput(); + ui->Viewport->Render(); +} + void CModelEditorWindow::SetActiveModel(CModel *pModel) { mpCurrentModelNode->SetModel(pModel); @@ -194,6 +197,7 @@ void CModelEditorWindow::SetActiveMaterial(int MatIndex) u32 SetIndex = ui->SetSelectionComboBox->currentIndex(); mpCurrentMat = mpCurrentModel->GetMaterialByIndex(SetIndex, MatIndex); + ui->Viewport->SetActiveMaterial(mpCurrentMat); if (!mpCurrentMat) return; //mpCurrentMat->SetTint(CColor(1.f, 0.5f, 0.5f, 1.f)); @@ -572,66 +576,6 @@ void CModelEditorWindow::UpdateUI(int Value) } } -void CModelEditorWindow::PaintViewport(CCamera& Camera) -{ - mpRenderer->BeginFrame(); - - Camera.LoadMatrices(); - - if (!mpCurrentModel) - { - CDrawUtil::DrawGrid(); - } - - else if (mDrawMode == eDrawMesh) - { - CDrawUtil::DrawGrid(); - mpCurrentModelNode->AddToRenderer(mpRenderer); - mpRenderer->RenderBuckets(Camera); - } - - else if (mDrawMode == eDrawSphere) - { - if (!mpCurrentMat) return; - glEnable(GL_CULL_FACE); - - CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); - CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; - CGraphics::UpdateMVPBlock(); - CGraphics::SetDefaultLighting(); - CGraphics::UpdateLightBlock(); // Note: vertex block is updated by the material - mpCurrentMat->SetCurrent(eEnableUVScroll | eEnableBackfaceCull | eEnableOccluders); - - CDrawUtil::DrawSphere(true); - } - - else if (mDrawMode == eDrawSquare) - { - if (!mpCurrentMat) return; - glDisable(GL_CULL_FACE); - - CGraphics::SetDefaultLighting(); - CGraphics::UpdateLightBlock(); - CGraphics::sVertexBlock.COLOR0_Amb = CGraphics::skDefaultAmbientColor.ToVector4f(); - - CGraphics::sMVPBlock.ModelMatrix = CMatrix4f::skIdentity; - CGraphics::sMVPBlock.ViewMatrix = CMatrix4f::skIdentity; - CGraphics::sMVPBlock.ProjectionMatrix = CMatrix4f::skIdentity; - CGraphics::UpdateMVPBlock(); - - mpCurrentMat->SetCurrent(eEnableUVScroll | eEnableOccluders); - CDrawUtil::DrawSquare(); - } - - mpRenderer->EndFrame(); -} - -void CModelEditorWindow::SetViewportSize(int Width, int Height) -{ - mViewportAspectRatio = (float) Width / (float) Height; - mpRenderer->SetViewportSize(Width, Height); -} - // ************ PRIVATE ************ void CModelEditorWindow::ActivateMatEditUI(bool Active) { @@ -793,23 +737,23 @@ void CModelEditorWindow::closeEvent(QCloseEvent*) void CModelEditorWindow::on_MeshPreviewButton_clicked() { - mDrawMode = eDrawMesh; + ui->Viewport->SetDrawMode(CModelEditorViewport::eDrawMesh); } void CModelEditorWindow::on_SpherePreviewButton_clicked() { - mDrawMode = eDrawSphere; + ui->Viewport->SetDrawMode(CModelEditorViewport::eDrawSphere); } void CModelEditorWindow::on_FlatPreviewButton_clicked() { - mDrawMode = eDrawSquare; + ui->Viewport->SetDrawMode(CModelEditorViewport::eDrawSquare); } void CModelEditorWindow::on_ClearColorPicker_colorChanged(const QColor &Color) { CColor NewColor((u8) Color.red(), (u8) Color.green(), (u8) Color.blue(), 255); - mpRenderer->SetClearColor(NewColor); + ui->Viewport->SetClearColor(NewColor); } void CModelEditorWindow::on_actionImport_triggered() diff --git a/UI/CModelEditorWindow.h b/UI/CModelEditorWindow.h index 132c9b79..2b0603e0 100644 --- a/UI/CModelEditorWindow.h +++ b/UI/CModelEditorWindow.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include "CModelEditorViewport.h" namespace Ui { class CModelEditorWindow; @@ -18,12 +20,7 @@ class CModelEditorWindow : public QMainWindow { Q_OBJECT - enum EDrawMode { - eDrawMesh, eDrawSphere, eDrawSquare - }; - Ui::CModelEditorWindow *ui; - CRenderer *mpRenderer; CSceneManager *mpScene; QString mOutputFilename; CModel *mpCurrentModel; @@ -32,8 +29,7 @@ class CModelEditorWindow : public QMainWindow CMaterial *mpCurrentMat; CMaterialPass *mpCurrentPass; bool mIgnoreSignals; - EDrawMode mDrawMode; - float mViewportAspectRatio; + QTimer mRefreshTimer; public: explicit CModelEditorWindow(QWidget *parent = 0); @@ -42,6 +38,7 @@ public: void closeEvent(QCloseEvent *pEvent); public slots: + void RefreshViewport(); void SetActiveMaterial(int MatIndex); void SetActivePass(int PassIndex); void UpdateMaterial(); @@ -52,8 +49,6 @@ public slots: void UpdateMaterial(QColor eColorProperty); void UpdateUI(int Value); void UpdateAnimParamUI(int Mode); - void PaintViewport(CCamera& Camera); - void SetViewportSize(int Width, int Height); private: void ActivateMatEditUI(bool Active); diff --git a/UI/CModelEditorWindow.ui b/UI/CModelEditorWindow.ui index 51e0979e..4bb89193 100644 --- a/UI/CModelEditorWindow.ui +++ b/UI/CModelEditorWindow.ui @@ -2124,7 +2124,7 @@ 0 - + 250 @@ -2480,6 +2480,11 @@ + + WDraggableSpinBox + QDoubleSpinBox +
WDraggableSpinBox.h
+
WColorPicker QWidget @@ -2496,16 +2501,11 @@ 1 - CEditorGLWidget + CModelEditorViewport QWidget -
CEditorGLWidget.h
+
CModelEditorViewport.h
1
- - WDraggableSpinBox - QDoubleSpinBox -
WDraggableSpinBox.h
-
diff --git a/UI/CSceneViewport.cpp b/UI/CSceneViewport.cpp new file mode 100644 index 00000000..1d84350c --- /dev/null +++ b/UI/CSceneViewport.cpp @@ -0,0 +1,230 @@ +#include "CSceneViewport.h" +#include "undo/UndoCommands.h" + +CSceneViewport::CSceneViewport(QWidget *pParent) + : CBasicViewport(pParent), + mpEditor(nullptr), + mpScene(nullptr), + mDrawSky(true), + mGizmoTransforming(false), + mpHoverNode(nullptr), + mHoverPoint(CVector3f::skZero) +{ + mpRenderer = new CRenderer(); + mpRenderer->SetClearColor(CColor::skBlack); + mpRenderer->SetViewportSize(width(), height()); +} + +CSceneViewport::~CSceneViewport() +{ + delete mpRenderer; +} + +void CSceneViewport::SetScene(INodeEditor *pEditor, CSceneManager *pScene) +{ + mpEditor = pEditor; + mpScene = pScene; +} + +void CSceneViewport::SetSkyEnabled(bool b) +{ + mDrawSky = b; +} + +CRenderer* CSceneViewport::Renderer() +{ + return mpRenderer; +} + +CSceneNode* CSceneViewport::HoverNode() +{ + return mpHoverNode; +} + +CVector3f CSceneViewport::HoverPoint() +{ + return mHoverPoint; +} + +void CSceneViewport::CheckGizmoInput(const CRay& ray) +{ + CGizmo *pGizmo = mpEditor->Gizmo(); + + // Gizmo not transforming: Check for gizmo hover + if (!pGizmo->IsTransforming()) + { + if (mpEditor->IsGizmoVisible()) + mGizmoHovering = pGizmo->CheckSelectedAxes(ray); + else + mGizmoHovering = false; + } + + // Gizmo transforming: Run gizmo input with ray/mouse coords + else if (mGizmoTransforming) + { + bool transformed = pGizmo->TransformFromInput(ray, mCamera); + if (transformed) emit GizmoMoved(); + } + + else mGizmoHovering = false; +} + +void CSceneViewport::SceneRayCast(const CRay& ray) +{ + if (mpEditor->Gizmo()->IsTransforming()) + { + ResetHover(); + return; + } + + SRayIntersection result = mpScene->SceneRayCast(ray); + + if (result.Hit) + { + if (mpHoverNode) + mpHoverNode->SetMouseHovering(false); + + mpHoverNode = result.pNode; + mpHoverNode->SetMouseHovering(true); + mHoverPoint = ray.PointOnRay(result.Distance); + } + + else + ResetHover(); +} + +void CSceneViewport::ResetHover() +{ + if (mpHoverNode) mpHoverNode->SetMouseHovering(false); + mpHoverNode = nullptr; + mHoverPoint = CVector3f::skZero; +} + +bool CSceneViewport::IsHoveringGizmo() +{ + return mGizmoHovering; +} + +// ************ PROTECTED SLOTS ************ +void CSceneViewport::CheckUserInput() +{ + if (!underMouse() || IsMouseInputActive()) + { + ResetHover(); + mGizmoHovering = false; + return; + } + + CRay ray = CastRay(); + CheckGizmoInput(ray); + + if (!mpEditor->Gizmo()->IsTransforming()) + SceneRayCast(ray); +} + +void CSceneViewport::Paint() +{ + if (!mpScene) return; + + mpRenderer->BeginFrame(); + + if (mDrawSky) + { + CModel *pSky = mpScene->GetActiveSkybox(); + if (pSky) mpRenderer->RenderSky(pSky, mCamera); + } + + mCamera.LoadMatrices(); + mpScene->AddSceneToRenderer(mpRenderer); + mpRenderer->RenderBuckets(mCamera); + mpRenderer->RenderBloom(); + + if (mpEditor->IsGizmoVisible()) + { + CGizmo *pGizmo = mpEditor->Gizmo(); + mCamera.LoadMatrices(); + + mpRenderer->ClearDepthBuffer(); + pGizmo->UpdateForCamera(mCamera); + pGizmo->AddToRenderer(mpRenderer); + mpRenderer->RenderBuckets(mCamera); + } + + mpRenderer->EndFrame(); +} + +void CSceneViewport::ContextMenu(QContextMenuEvent *pEvent) +{ +} + +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 + { + bool validNode = (mpHoverNode && (mpHoverNode->NodeType() != eStaticNode)); + bool altPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0); + bool ctrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0); + + // Alt: Deselect + if (altPressed) + { + if (!validNode) + return; + + mpEditor->DeselectNode(mpHoverNode); + } + + // Ctrl: Add to selection + else if (ctrlPressed) + { + if (validNode) + mpEditor->SelectNode(mpHoverNode); + } + + // Neither: clear selection + select + else + { + if (!mGizmoHovering) + { + if (validNode) + mpEditor->ClearAndSelectNode(mpHoverNode); + else + mpEditor->ClearSelection(); + } + } + + mpEditor->UpdateSelectionUI(); + } + } + +} diff --git a/UI/CSceneViewport.h b/UI/CSceneViewport.h new file mode 100644 index 00000000..d88ff52e --- /dev/null +++ b/UI/CSceneViewport.h @@ -0,0 +1,49 @@ +#ifndef CSCENEVIEWPORT_H +#define CSCENEVIEWPORT_H + +#include "CBasicViewport.h" +#include "INodeEditor.h" + +class CSceneViewport : public CBasicViewport +{ + Q_OBJECT + + INodeEditor *mpEditor; + CSceneManager *mpScene; + CRenderer *mpRenderer; + + // Render settings + bool mDrawSky; + + // Scene interaction + bool mGizmoHovering; + bool mGizmoTransforming; + CSceneNode *mpHoverNode; + CVector3f mHoverPoint; + +public: + CSceneViewport(QWidget *pParent = 0); + ~CSceneViewport(); + void SetScene(INodeEditor *pEditor, CSceneManager *pScene); + void SetSkyEnabled(bool b); + CRenderer* Renderer(); + CSceneNode* HoverNode(); + CVector3f HoverPoint(); + void CheckGizmoInput(const CRay& ray); + void SceneRayCast(const CRay& ray); + void ResetHover(); + bool IsHoveringGizmo(); + +signals: + void GizmoMoved(); + +protected slots: + void CheckUserInput(); + void Paint(); + void ContextMenu(QContextMenuEvent *pEvent); + void OnResize(); + void OnMouseClick(QMouseEvent *pEvent); + void OnMouseRelease(QMouseEvent *pEvent); +}; + +#endif // CSCENEVIEWPORT_H diff --git a/UI/CWorldEditor.cpp b/UI/CWorldEditor.cpp index 29f4aa0a..d250e59c 100644 --- a/UI/CWorldEditor.cpp +++ b/UI/CWorldEditor.cpp @@ -1,6 +1,6 @@ #include "CWorldEditor.h" #include "ui_CWorldEditor.h" -#include "CEditorGLWidget.h" +#include "CBasicViewport.h" #include #include #include @@ -10,36 +10,27 @@ #include #include "WDraggableSpinBox.h" #include "WVectorEditor.h" +#include "undo/UndoCommands.h" #include "WorldEditor/CLayerEditor.h" #include "WorldEditor/WModifyTab.h" #include "WorldEditor/WInstancesTab.h" CWorldEditor::CWorldEditor(QWidget *parent) : - QMainWindow(parent), + INodeEditor(parent), ui(new Ui::CWorldEditor) { Log::Write("Creating World Editor"); ui->setupUi(this); - mpRenderer = new CRenderer(); - mpRenderer->SetClearColor(CColor::skBlack); - QSize ViewSize = ui->MainViewport->size(); - mpRenderer->SetViewportSize(ViewSize.width(), ViewSize.height()); - - mpSceneManager = new CSceneManager(); - mpArea = nullptr; mpWorld = nullptr; - mpHoverNode = nullptr; - mDrawSky = true; - mShowGizmo = false; mGizmoHovering = false; mGizmoTransforming = false; - mGizmoUIOutdated = true; - mFrameCount = 0; - mFPSTimer.Start(); + // Start refresh timer + connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); + mRefreshTimer.start(0); // Create blank title bar with some space to allow for dragging the dock QWidget *pOldTitleBar = ui->MainDock->titleBarWidget(); @@ -53,38 +44,28 @@ CWorldEditor::CWorldEditor(QWidget *parent) : delete pOldTitleBar; // Initialize UI stuff + ui->MainViewport->SetScene(this, &mScene); ui->ModifyTabContents->SetEditor(this); - ui->InstancesTabContents->SetEditor(this, mpSceneManager); + ui->InstancesTabContents->SetEditor(this, &mScene); ui->MainDock->installEventFilter(this); ui->TransformSpinBox->SetOrientation(Qt::Horizontal); ui->TransformSpinBox->layout()->setContentsMargins(0,0,0,0); ui->CamSpeedSpinBox->SetDefaultValue(1.0); - ResetHover(); - mTranslateSpace = eWorldTransform; - mRotateSpace = eWorldTransform; - - mpTransformSpaceComboBox = new QComboBox(this); - mpTransformSpaceComboBox->setMinimumWidth(75); - mpTransformSpaceComboBox->addItem("World"); - mpTransformSpaceComboBox->addItem("Local"); - ui->MainToolBar->insertWidget(0, mpTransformSpaceComboBox); + mpTransformCombo->setMinimumWidth(75); + ui->MainToolBar->addActions(mGizmoActions); + ui->MainToolBar->addWidget(mpTransformCombo); + ui->menuEdit->addActions(mUndoActions); // Initialize offscreen actions addAction(ui->ActionIncrementGizmo); addAction(ui->ActionDecrementGizmo); // Connect signals and slots - connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f))); + connect(ui->MainViewport, SIGNAL(GizmoMoved()), this, SLOT(OnGizmoMoved())); connect(ui->TransformSpinBox, SIGNAL(ValueChanged(CVector3f)), this, SLOT(OnTransformSpinBoxModified(CVector3f))); - connect(mpTransformSpaceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetTransformSpace(int))); + connect(ui->TransformSpinBox, SIGNAL(EditingDone(CVector3f)), this, SLOT(OnTransformSpinBoxEdited(CVector3f))); connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double))); - connect(ui->MainViewport, SIGNAL(PreRender()), this, SLOT(ViewportPreRender())); - connect(ui->MainViewport, SIGNAL(Render(CCamera&)), this, SLOT(ViewportRender(CCamera&))); - connect(ui->MainViewport, SIGNAL(ViewportResized(int,int)), this, SLOT(SetViewportSize(int,int))); - connect(ui->MainViewport, SIGNAL(frameSwapped()), this, SLOT(ViewportPostRender())); - connect(ui->MainViewport, SIGNAL(MouseClick(QMouseEvent*)), this, SLOT(ViewportMouseClick(QMouseEvent*))); - connect(ui->MainViewport, SIGNAL(MouseRelease(QMouseEvent*)), this, SLOT(ViewportMouseRelease(QMouseEvent*))); } CWorldEditor::~CWorldEditor() @@ -107,7 +88,7 @@ bool CWorldEditor::eventFilter(QObject *pObj, QEvent *pEvent) void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) { - ResetHover(); + ui->MainViewport->ResetHover(); ClearSelection(); ui->ModifyTabContents->ClearUI(); ui->ModifyTabContents->ClearCachedEditors(); @@ -124,8 +105,8 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) mAreaToken = CToken(pArea); mWorldToken = CToken(pWorld); - mpSceneManager->SetActiveWorld(pWorld); - mpSceneManager->SetActiveArea(pArea); + mScene.SetActiveWorld(pWorld); + mScene.SetActiveArea(pArea); // Snap camera to location of area CTransform4f AreaTransform = pArea->GetTransform(); @@ -150,350 +131,21 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea) ui->InstancesTabContents->SetMaster(pMaster); } -void CWorldEditor::ViewportRayCast() -{ - if (!ui->MainViewport->IsMouseInputActive()) - { - // Cast ray - CVector2f mouseCoords = ui->MainViewport->MouseDeviceCoordinates(); - CCamera camera = ui->MainViewport->Camera(); - CRay ray = camera.CastRay(mouseCoords); - - if (!mGizmoTransforming) - { - // Gizmo hover check - if (mShowGizmo && !mSelectedNodes.empty()) - mGizmoHovering = mGizmo.CheckSelectedAxes(ray); - else - { - mGizmoHovering = false; - mGizmo.ResetSelectedAxes(); - } - - // Scene ray check - SRayIntersection Result = mpSceneManager->SceneRayCast(ray); - - if (Result.Hit) - { - if (mpHoverNode) - mpHoverNode->SetMouseHovering(false); - - mpHoverNode = Result.pNode; - mpHoverNode->SetMouseHovering(true); - - mHoverPoint = ray.PointOnRay(Result.Distance); - } - else - ResetHover(); - } - else - { - bool transformed = mGizmo.TransformFromInput(ray, ui->MainViewport->Camera()); - - if (transformed) - { - switch (mGizmo.Mode()) - { - case CGizmo::eTranslate: - { - CVector3f delta = mGizmo.DeltaTranslation(); - - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - { - (*it)->Translate(delta, mTranslateSpace); - (*it)->BuildLightList(this->mpArea); - } - break; - } - - case CGizmo::eRotate: - { - CQuaternion delta = mGizmo.DeltaRotation(); - - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - (*it)->Rotate(delta, mRotateSpace); - - break; - } - - case CGizmo::eScale: - { - CVector3f delta = mGizmo.DeltaScale(); - - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - (*it)->Scale(delta); - - break; - } - } - - RecalculateSelectionBounds(); - mGizmoUIOutdated = true; - } - } - } - else - { - if (!mGizmoTransforming) - { - mGizmoHovering = false; - mGizmo.ResetSelectedAxes(); - } - ResetHover(); - } -} - -CRenderer* CWorldEditor::Renderer() -{ - return mpRenderer; -} - -CSceneManager* CWorldEditor::Scene() -{ - return mpSceneManager; -} - CGameArea* CWorldEditor::ActiveArea() { return mpArea; } -// ************ SELECTION ************ -void CWorldEditor::SelectNode(CSceneNode *pNode) -{ - if (!pNode->IsSelected()) - { - pNode->SetSelected(true); - mSelectedNodes.push_back(pNode); - mSelectionAABox.ExpandBounds(pNode->AABox()); - - if (pNode->NodeType() == eScriptNode) - { - CScriptNode *pScript = static_cast(pNode); - if (pScript->HasPreviewVolume()) - mSelectionAABox.ExpandBounds(pScript->PreviewVolumeAABox()); - } - } - - UpdateSelectionUI(); -} - -void CWorldEditor::DeselectNode(CSceneNode *pNode) -{ - if (pNode->IsSelected()) - { - pNode->SetSelected(false); - - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - { - if (*it == pNode) - { - mSelectedNodes.erase(it); - break; - } - } - } - - RecalculateSelectionBounds(); - UpdateSelectionUI(); -} - -void CWorldEditor::ClearSelection() -{ - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - (*it)->SetSelected(false); - - mSelectedNodes.clear(); - mSelectionAABox = CAABox::skInfinite; - UpdateSelectionUI(); -} - // ************ SLOTS ************ -void CWorldEditor::ViewportMouseDrag(QMouseEvent *pEvent) -{ -} - -void CWorldEditor::ViewportMouseClick(QMouseEvent *pEvent) -{ - bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0); - bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0); - - if (mGizmoHovering && !AltPressed && !CtrlPressed) - { - mGizmoTransforming = true; - mGizmo.StartTransform(); - } -} - -void CWorldEditor::ViewportMouseRelease(QMouseEvent *pEvent) -{ - if (pEvent->button() == Qt::LeftButton) - { - // Gizmo transform stop - if (mGizmoTransforming) - { - mGizmo.EndTransform(); - mGizmoTransforming = false; - mGizmoUIOutdated = true; - } - - // Object selection/deselection - else if (!ui->MainViewport->IsMouseInputActive()) - { - bool ValidNode = ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode)); - bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0); - bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0); - - // Alt pressed - deselect object - if (AltPressed) - { - // No valid node selected - do nothing - if (!ValidNode) - return; - - DeselectNode(mpHoverNode); - } - - // Ctrl pressed - add object to selection - else if (CtrlPressed) - { - // Add hover node to selection - if (ValidNode) - SelectNode(mpHoverNode); - } - - // Neither pressed - else - { - // If the gizmo isn't under the mouse, clear existing selection + select object (if applicable) - if (!mGizmoHovering) - { - ClearSelection(); - - if (ValidNode) - SelectNode(mpHoverNode); - } - } - - UpdateSelectionUI(); - } - } - - // todo: context menu creation on right-click goes here -} - -// ************ SLOTS ************ -void CWorldEditor::ViewportPreRender() -{ - // Perform raycast - if (ui->MainViewport->underMouse()) - ViewportRayCast(); - else - ResetHover(); - - // Start frame - mFrameTimer.Start(); - mpRenderer->BeginFrame(); -} - -void CWorldEditor::ViewportRender(CCamera& Camera) -{ - mpSceneManager->AddSceneToRenderer(mpRenderer); - - if (mDrawSky) - { - CModel *pSky = mpSceneManager->GetActiveSkybox(); - if (pSky) mpRenderer->RenderSky(pSky, Camera); - } - - Camera.LoadMatrices(); - mpRenderer->RenderBuckets(Camera); - mpRenderer->RenderBloom(); - - if (mShowGizmo && (mSelectedNodes.size() > 0)) - { - mpRenderer->ClearDepthBuffer(); - - Camera.LoadMatrices(); - mGizmo.UpdateForCamera(Camera); - mGizmo.AddToRenderer(mpRenderer); - - mpRenderer->RenderBuckets(Camera); - mpRenderer->ClearDepthBuffer(); - } - - mpRenderer->EndFrame(); - mFrameTimer.Stop(); - mFrameCount++; -} - -void CWorldEditor::ViewportPostRender() -{ - // Update UI with raycast results - UpdateCursor(); - UpdateStatusBar(); - - if (mGizmoUIOutdated) UpdateGizmoUI(); -} - -void CWorldEditor::SetViewportSize(int Width, int Height) -{ - mpRenderer->SetViewportSize(Width, Height); -} - -void CWorldEditor::SetTransformSpace(int space) -{ - if (mGizmo.Mode() == CGizmo::eScale) return; - ETransformSpace& transformSpace = (mGizmo.Mode() == CGizmo::eTranslate ? mTranslateSpace : mRotateSpace); - - switch (space) - { - case 0: - transformSpace = eWorldTransform; - mGizmo.SetLocalRotation(CQuaternion::skIdentity); - break; - case 1: - transformSpace = eLocalTransform; - if (!mSelectedNodes.empty()) - mGizmo.SetLocalRotation(mSelectedNodes.front()->AbsoluteRotation()); - break; - } - - mGizmo.SetTransformSpace(transformSpace); -} - -// ************ PRIVATE ************ -void CWorldEditor::RecalculateSelectionBounds() -{ - mSelectionAABox = CAABox::skInfinite; - - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - { - mSelectionAABox.ExpandBounds( (*it)->AABox() ); - - if ((*it)->NodeType() == eScriptNode) - { - CScriptNode *pScript = static_cast(*it); - if (pScript->HasPreviewVolume()) - mSelectionAABox.ExpandBounds(pScript->PreviewVolumeAABox()); - } - } -} - -void CWorldEditor::ResetHover() -{ - if (mpHoverNode) mpHoverNode->SetMouseHovering(false); - mpHoverNode = nullptr; - mHoverPoint = CVector3f::skZero; -} - void CWorldEditor::UpdateCursor() { if (ui->MainViewport->IsCursorVisible()) { - if (mGizmoHovering) + CSceneNode *pHoverNode = ui->MainViewport->HoverNode(); + + if (ui->MainViewport->IsHoveringGizmo()) ui->MainViewport->SetCursorState(Qt::SizeAllCursor); - else if ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode)) + else if ((pHoverNode) && (pHoverNode->NodeType() != eStaticNode)) ui->MainViewport->SetCursorState(Qt::PointingHandCursor); else ui->MainViewport->SetCursorState(Qt::ArrowCursor); @@ -505,10 +157,15 @@ void CWorldEditor::UpdateStatusBar() // Would be cool to do more frequent status bar updates with more info. Unfortunately, this causes lag. QString StatusText = ""; - if (!mGizmoHovering && mpHoverNode) + if (!mGizmoHovering) { - if (mpHoverNode->NodeType() != eStaticNode) - StatusText = QString::fromStdString(mpHoverNode->Name()); + if (ui->MainViewport->underMouse()) + { + CSceneNode *pHoverNode = ui->MainViewport->HoverNode(); + + if (pHoverNode && (pHoverNode->NodeType() != eStaticNode)) + StatusText = QString::fromStdString(pHoverNode->Name()); + } } if (ui->statusbar->currentMessage() != StatusText) @@ -518,15 +175,15 @@ void CWorldEditor::UpdateStatusBar() void CWorldEditor::UpdateSelectionUI() { // Update sidebar - ui->ModifyTabContents->GenerateUI(mSelectedNodes); + ui->ModifyTabContents->GenerateUI(mSelection); // Update selection info text QString SelectionText; - if (mSelectedNodes.size() == 1) - SelectionText = QString::fromStdString(mSelectedNodes.front()->Name()); - else if (mSelectedNodes.size() > 1) - SelectionText = QString("%1 objects selected").arg(mSelectedNodes.size()); + if (mSelection.size() == 1) + SelectionText = QString::fromStdString(mSelection.front()->Name()); + else if (mSelection.size() > 1) + SelectionText = QString("%1 objects selected").arg(mSelection.size()); QFontMetrics Metrics(ui->SelectionInfoLabel->font()); SelectionText = Metrics.elidedText(SelectionText, Qt::ElideRight, ui->SelectionInfoFrame->width() - 10); @@ -552,26 +209,26 @@ void CWorldEditor::UpdateGizmoUI() case CGizmo::eTranslate: if (mGizmoTransforming && mGizmo.HasTransformed()) spinBoxValue = mGizmo.TotalTranslation(); - else if (!mSelectedNodes.empty()) - spinBoxValue = mSelectedNodes.front()->AbsolutePosition(); + else if (!mSelection.empty()) + spinBoxValue = mSelection.front()->AbsolutePosition(); break; case CGizmo::eRotate: if (mGizmoTransforming && mGizmo.HasTransformed()) spinBoxValue = mGizmo.TotalRotation(); - else if (!mSelectedNodes.empty()) - spinBoxValue = mSelectedNodes.front()->AbsoluteRotation().ToEuler(); + else if (!mSelection.empty()) + spinBoxValue = mSelection.front()->AbsoluteRotation().ToEuler(); break; case CGizmo::eScale: if (mGizmoTransforming && mGizmo.HasTransformed()) spinBoxValue = mGizmo.TotalScale(); - else if (!mSelectedNodes.empty()) - spinBoxValue = mSelectedNodes.front()->AbsoluteScale(); + else if (!mSelection.empty()) + spinBoxValue = mSelection.front()->AbsoluteScale(); break; } } - else if (!mSelectedNodes.empty()) spinBoxValue = mSelectedNodes.front()->AbsolutePosition(); + else if (!mSelection.empty()) spinBoxValue = mSelection.front()->AbsolutePosition(); ui->TransformSpinBox->blockSignals(true); ui->TransformSpinBox->SetValue(spinBoxValue); @@ -581,26 +238,37 @@ void CWorldEditor::UpdateGizmoUI() // Update gizmo if (!mGizmoTransforming) { - if (!mSelectedNodes.empty()) - mGizmo.SetPosition(mSelectedNodes.front()->AbsolutePosition()); - - // Determine transform space - ETransformSpace space; - if (mGizmo.Mode() == CGizmo::eTranslate) space = mTranslateSpace; - else if (mGizmo.Mode() == CGizmo::eRotate) space = mRotateSpace; - else space = eLocalTransform; - - // Set gizmo transform space - mGizmo.SetTransformSpace(space); - - if (!mSelectedNodes.empty()) - mGizmo.SetLocalRotation(mSelectedNodes.front()->AbsoluteRotation()); + // Set gizmo transform + if (!mSelection.empty()) + { + mGizmo.SetPosition(mSelection.front()->AbsolutePosition()); + mGizmo.SetLocalRotation(mSelection.front()->AbsoluteRotation()); + } } +} - mGizmoUIOutdated = false; +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) ); } // ************ ACTIONS ************ +void CWorldEditor::RefreshViewport() +{ + if (!mGizmo.IsTransforming()) + mGizmo.ResetSelectedAxes(); + + // Process input + update UI + ui->MainViewport->ProcessInput(); + UpdateCursor(); + UpdateStatusBar(); + UpdateGizmoUI(); + + // Render + ui->MainViewport->Render(); +} + void CWorldEditor::OnCameraSpeedChange(double speed) { static const double skDefaultSpeed = 1.0; @@ -613,22 +281,30 @@ void CWorldEditor::OnCameraSpeedChange(double speed) void CWorldEditor::OnTransformSpinBoxModified(CVector3f value) { + if (mSelection.empty()) return; + switch (mGizmo.Mode()) { - case CGizmo::eTranslate: - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - (*it)->SetPosition(value); - break; + case CGizmo::eTranslate: + { + CVector3f delta = value - mSelection.front()->AbsolutePosition(); + mUndoStack.push(new CTranslateNodeCommand(this, mSelection, delta, mTranslateSpace)); + break; + } - case CGizmo::eRotate: - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - (*it)->SetRotation(value); - break; + case CGizmo::eRotate: + { + CQuaternion delta = CQuaternion::FromEuler(value) * mSelection.front()->AbsoluteRotation().Inverse(); + mUndoStack.push(new CRotateNodeCommand(this, mSelection, CVector3f::skZero, delta, mRotateSpace)); + break; + } - case CGizmo::eScale: - for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++) - (*it)->SetScale(value); - break; + case CGizmo::eScale: + { + CVector3f delta = value / mSelection.front()->AbsoluteScale(); + mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta)); + break; + } } RecalculateSelectionBounds(); @@ -637,33 +313,42 @@ void CWorldEditor::OnTransformSpinBoxModified(CVector3f value) void CWorldEditor::OnTransformSpinBoxEdited(CVector3f) { + ui->TransformSpinBox->blockSignals(true); + setFocus(); + ui->TransformSpinBox->blockSignals(false); + if (mSelection.empty()) return; + + if (mGizmo.Mode() == CGizmo::eTranslate) mUndoStack.push(CTranslateNodeCommand::End()); + else if (mGizmo.Mode() == CGizmo::eRotate) mUndoStack.push(CRotateNodeCommand::End()); + else if (mGizmo.Mode() == CGizmo::eScale) mUndoStack.push(CScaleNodeCommand::End()); + UpdateGizmoUI(); } // These functions are from "Go to slot" in the designer void CWorldEditor::on_ActionDrawWorld_triggered() { - mpSceneManager->SetWorld(ui->ActionDrawWorld->isChecked()); + mScene.SetWorld(ui->ActionDrawWorld->isChecked()); } void CWorldEditor::on_ActionDrawCollision_triggered() { - mpSceneManager->SetCollision(ui->ActionDrawCollision->isChecked()); + mScene.SetCollision(ui->ActionDrawCollision->isChecked()); } void CWorldEditor::on_ActionDrawObjects_triggered() { - mpSceneManager->SetObjects(ui->ActionDrawObjects->isChecked()); + mScene.SetObjects(ui->ActionDrawObjects->isChecked()); } void CWorldEditor::on_ActionDrawLights_triggered() { - mpSceneManager->SetLights(ui->ActionDrawLights->isChecked()); + mScene.SetLights(ui->ActionDrawLights->isChecked()); } void CWorldEditor::on_ActionDrawSky_triggered() { - mDrawSky = ui->ActionDrawSky->isChecked(); + ui->MainViewport->SetSkyEnabled(ui->ActionDrawSky->isChecked()); } void CWorldEditor::on_ActionNoLighting_triggered() @@ -692,7 +377,7 @@ void CWorldEditor::on_ActionWorldLighting_triggered() void CWorldEditor::on_ActionNoBloom_triggered() { - mpRenderer->SetBloom(CRenderer::eNoBloom); + ui->MainViewport->Renderer()->SetBloom(CRenderer::eNoBloom); ui->ActionNoBloom->setChecked(true); ui->ActionBloomMaps->setChecked(false); ui->ActionFakeBloom->setChecked(false); @@ -701,7 +386,7 @@ void CWorldEditor::on_ActionNoBloom_triggered() void CWorldEditor::on_ActionBloomMaps_triggered() { - mpRenderer->SetBloom(CRenderer::eBloomMaps); + ui->MainViewport->Renderer()->SetBloom(CRenderer::eBloomMaps); ui->ActionNoBloom->setChecked(false); ui->ActionBloomMaps->setChecked(true); ui->ActionFakeBloom->setChecked(false); @@ -710,7 +395,7 @@ void CWorldEditor::on_ActionBloomMaps_triggered() void CWorldEditor::on_ActionFakeBloom_triggered() { - mpRenderer->SetBloom(CRenderer::eFakeBloom); + ui->MainViewport->Renderer()->SetBloom(CRenderer::eFakeBloom); ui->ActionNoBloom->setChecked(false); ui->ActionBloomMaps->setChecked(false); ui->ActionFakeBloom->setChecked(true); @@ -719,7 +404,7 @@ void CWorldEditor::on_ActionFakeBloom_triggered() void CWorldEditor::on_ActionBloom_triggered() { - mpRenderer->SetBloom(CRenderer::eBloom); + ui->MainViewport->Renderer()->SetBloom(CRenderer::eBloom); ui->ActionNoBloom->setChecked(false); ui->ActionBloomMaps->setChecked(false); ui->ActionFakeBloom->setChecked(false); @@ -732,17 +417,16 @@ void CWorldEditor::on_ActionZoomOnSelection_triggered() static const float skAreaDistScale = 0.8f; CCamera& Camera = ui->MainViewport->Camera(); - CVector3f CamDir = Camera.GetDirection(); + CVector3f CamDir = Camera.Direction(); CVector3f NewPos; // Zoom on selection - if (mSelectedNodes.size() != 0) + if (mSelection.size() != 0) { - CVector3f Min = mSelectionAABox.Min(); - CVector3f Max = mSelectionAABox.Max(); + CVector3f Min = mSelectionBounds.Min(); + CVector3f Max = mSelectionBounds.Max(); float Dist = ((Max.x - Min.x) + (Max.y - Min.y) + (Max.z - Min.z)) / 3.f; - //float Dist = mSelectionAABox.Min().Distance(mSelectionAABox.Max()); - NewPos = mSelectionAABox.Center() + (CamDir * -(Dist * skDistScale)); + NewPos = mSelectionBounds.Center() + (CamDir * -(Dist * skDistScale)); } // Zoom on area @@ -752,7 +436,6 @@ void CWorldEditor::on_ActionZoomOnSelection_triggered() CVector3f Min = AreaBox.Min(); CVector3f Max = AreaBox.Max(); float Dist = ((Max.x - Min.x) + (Max.y - Min.y) + (Max.z - Min.z)) / 3.f; - //float Dist = AreaBox.Min().Distance(AreaBox.Max()); NewPos = AreaBox.Center() + (CamDir * -(Dist * skAreaDistScale)); } @@ -761,12 +444,12 @@ void CWorldEditor::on_ActionZoomOnSelection_triggered() void CWorldEditor::on_ActionDisableBackfaceCull_triggered() { - mpRenderer->ToggleBackfaceCull(!ui->ActionDisableBackfaceCull->isChecked()); + ui->MainViewport->Renderer()->ToggleBackfaceCull(!ui->ActionDisableBackfaceCull->isChecked()); } void CWorldEditor::on_ActionDisableAlpha_triggered() { - mpRenderer->ToggleAlphaDisabled(ui->ActionDisableAlpha->isChecked()); + ui->MainViewport->Renderer()->ToggleAlphaDisabled(ui->ActionDisableAlpha->isChecked()); } void CWorldEditor::on_ActionEditLayers_triggered() @@ -777,94 +460,6 @@ void CWorldEditor::on_ActionEditLayers_triggered() Editor.exec(); } -void CWorldEditor::on_ActionSelectObjects_triggered() -{ - mShowGizmo = false; - mGizmoUIOutdated = true; - ui->ActionSelectObjects->setChecked(true); - ui->ActionTranslate->setChecked(false); - ui->ActionRotate->setChecked(false); - ui->ActionScale->setChecked(false); - - // Set transform spin box settings - ui->TransformSpinBox->SetSingleStep(0.1); - ui->TransformSpinBox->SetDefaultValue(0.0); - - // Set transform space combo box - mpTransformSpaceComboBox->setEnabled(false); - mpTransformSpaceComboBox->blockSignals(true); - mpTransformSpaceComboBox->setCurrentIndex(0); - mpTransformSpaceComboBox->blockSignals(false); -} - -void CWorldEditor::on_ActionTranslate_triggered() -{ - mShowGizmo = true; - mGizmoUIOutdated = true; - mGizmo.SetMode(CGizmo::eTranslate); - mGizmo.SetTransformSpace(mTranslateSpace); - ui->ActionSelectObjects->setChecked(false); - ui->ActionTranslate->setChecked(true); - ui->ActionRotate->setChecked(false); - ui->ActionScale->setChecked(false); - - // Set transform spin box settings - ui->TransformSpinBox->SetSingleStep(0.1); - ui->TransformSpinBox->SetDefaultValue(0.0); - - // Set transform space combo box - int index = (mTranslateSpace == eWorldTransform ? 0 : 1); - mpTransformSpaceComboBox->setEnabled(true); - mpTransformSpaceComboBox->blockSignals(true); - mpTransformSpaceComboBox->setCurrentIndex(index); - mpTransformSpaceComboBox->blockSignals(false); -} - -void CWorldEditor::on_ActionRotate_triggered() -{ - mShowGizmo = true; - mGizmoUIOutdated = true; - mGizmo.SetMode(CGizmo::eRotate); - mGizmo.SetTransformSpace(mRotateSpace); - ui->ActionSelectObjects->setChecked(false); - ui->ActionTranslate->setChecked(false); - ui->ActionRotate->setChecked(true); - ui->ActionScale->setChecked(false); - - // Set transform spin box settings - ui->TransformSpinBox->SetSingleStep(1.0); - ui->TransformSpinBox->SetDefaultValue(0.0); - - // Set transform space combo box - int index = (mRotateSpace == eWorldTransform ? 0 : 1); - mpTransformSpaceComboBox->setEnabled(true); - mpTransformSpaceComboBox->blockSignals(true); - mpTransformSpaceComboBox->setCurrentIndex(index); - mpTransformSpaceComboBox->blockSignals(false); -} - -void CWorldEditor::on_ActionScale_triggered() -{ - mShowGizmo = true; - mGizmoUIOutdated = true; - mGizmo.SetMode(CGizmo::eScale); - mGizmo.SetTransformSpace(eLocalTransform); - ui->ActionSelectObjects->setChecked(false); - ui->ActionTranslate->setChecked(false); - ui->ActionRotate->setChecked(false); - ui->ActionScale->setChecked(true); - - // Set transform spin box settings - ui->TransformSpinBox->SetSingleStep(0.1); - ui->TransformSpinBox->SetDefaultValue(1.0); - - // Set transform space combo box - force to local - mpTransformSpaceComboBox->setEnabled(false); - mpTransformSpaceComboBox->blockSignals(true); - mpTransformSpaceComboBox->setCurrentIndex(1); - mpTransformSpaceComboBox->blockSignals(false); -} - void CWorldEditor::on_ActionIncrementGizmo_triggered() { mGizmo.IncrementSize(); diff --git a/UI/CWorldEditor.h b/UI/CWorldEditor.h index 649af99e..504845f5 100644 --- a/UI/CWorldEditor.h +++ b/UI/CWorldEditor.h @@ -1,9 +1,13 @@ #ifndef CWORLDEDITOR_H #define CWORLDEDITOR_H -#include -#include +#include "INodeEditor.h" + #include +#include +#include +#include +#include #include "CGizmo.h" #include @@ -21,74 +25,38 @@ namespace Ui { class CWorldEditor; } -class CWorldEditor : public QMainWindow +class CWorldEditor : public INodeEditor { Q_OBJECT - CRenderer *mpRenderer; - CSceneManager *mpSceneManager; - CGizmo mGizmo; - ETransformSpace mTranslateSpace; - ETransformSpace mRotateSpace; - CCamera mCamera; - CGameArea *mpArea; + Ui::CWorldEditor *ui; + CWorld *mpWorld; + CGameArea *mpArea; CToken mAreaToken; CToken mWorldToken; - CTimer mFrameTimer; - bool mDrawSky; - bool mShowGizmo; - bool mGizmoHovering; - bool mGizmoTransforming; - bool mGizmoUIOutdated; - - CVector3f mHoverPoint; - CSceneNode *mpHoverNode; - std::list mSelectedNodes; - CAABox mSelectionAABox; - - QComboBox *mpTransformSpaceComboBox; - - CTimer mFPSTimer; - int mFrameCount; + QTimer mRefreshTimer; public: explicit CWorldEditor(QWidget *parent = 0); ~CWorldEditor(); bool eventFilter(QObject *pObj, QEvent *pEvent); void SetArea(CWorld *pWorld, CGameArea *pArea); - void ViewportRayCast(); - CRenderer* Renderer(); - CSceneManager* Scene(); CGameArea* ActiveArea(); - // Selection - void SelectNode(CSceneNode *pNode); - void DeselectNode(CSceneNode *pNode); - void ClearSelection(); - -public slots: - void ViewportPreRender(); - void ViewportRender(CCamera& Camera); - void ViewportPostRender(); - void ViewportMouseDrag(QMouseEvent *pEvent); - void ViewportMouseClick(QMouseEvent *pEvent); - void ViewportMouseRelease(QMouseEvent *pEvent); - void SetViewportSize(int Width, int Height); - void SetTransformSpace(int space); - -private: - Ui::CWorldEditor *ui; - void RecalculateSelectionBounds(); - void ResetHover(); - void UpdateCursor(); - - // UI - void OnSidebarResize(); + // Update UI + void UpdateGizmoUI(); void UpdateSelectionUI(); void UpdateStatusBar(); - void UpdateGizmoUI(); + +protected: + void GizmoModeChanged(CGizmo::EGizmoMode mode); + +private: + void UpdateCursor(); + void OnSidebarResize(); private slots: + void RefreshViewport(); void OnCameraSpeedChange(double speed); void OnTransformSpinBoxModified(CVector3f value); void OnTransformSpinBoxEdited(CVector3f value); @@ -108,10 +76,6 @@ private slots: void on_ActionDisableBackfaceCull_triggered(); void on_ActionDisableAlpha_triggered(); void on_ActionEditLayers_triggered(); - void on_ActionSelectObjects_triggered(); - void on_ActionTranslate_triggered(); - void on_ActionRotate_triggered(); - void on_ActionScale_triggered(); void on_ActionIncrementGizmo_triggered(); void on_ActionDecrementGizmo_triggered(); }; diff --git a/UI/CWorldEditor.ui b/UI/CWorldEditor.ui index d7c73718..e7ddf2cb 100644 --- a/UI/CWorldEditor.ui +++ b/UI/CWorldEditor.ui @@ -31,7 +31,7 @@ 0 - + 0 @@ -245,8 +245,6 @@ Edit - - @@ -475,10 +473,6 @@ - - - - @@ -490,60 +484,6 @@ Save - - - true - - - - :/icons/EditorAssets/Translate.png:/icons/EditorAssets/Translate.png - - - Translate - - - Translate - - - Ctrl+W - - - - - true - - - - :/icons/EditorAssets/Rotate.png:/icons/EditorAssets/Rotate.png - - - Rotate - - - Rotate - - - Ctrl+E - - - - - true - - - - :/icons/EditorAssets/Scale.png:/icons/EditorAssets/Scale.png - - - Scale - - - Scale - - - Ctrl+R - - Open Model Viewer @@ -573,22 +513,6 @@ Unlink - - - Undo - - - Ctrl+Z - - - - - Redo - - - Ctrl+Y - - true @@ -753,27 +677,6 @@ Fake Bloom - - - true - - - true - - - - :/icons/EditorAssets/SelectMode.png:/icons/EditorAssets/SelectMode.png - - - Select Objects - - - Select Objects - - - Ctrl+Q - - Increment Gizmo Size @@ -804,12 +707,6 @@ - - CEditorGLWidget - QWidget -
CEditorGLWidget.h
- 1 -
WDraggableSpinBox QDoubleSpinBox @@ -833,6 +730,12 @@
WVectorEditor.h
1
+ + CSceneViewport + QWidget +
CSceneViewport.h
+ 1 +
TabWidget diff --git a/UI/CWorldEditorWindow.cpp b/UI/CWorldEditorWindow.cpp index 46e923e5..1670c414 100644 --- a/UI/CWorldEditorWindow.cpp +++ b/UI/CWorldEditorWindow.cpp @@ -11,7 +11,7 @@ #include #include #include -#include "CEditorGLWidget.h" +#include "CBasicViewport.h" CWorldEditorWindow::CWorldEditorWindow(QWidget *parent) : QMainWindow(parent), @@ -30,13 +30,13 @@ CWorldEditorWindow::CWorldEditorWindow(QWidget *parent) : mViewportKeysPressed = 0; mShouldDrawSky = true; - connect(ui->CentralGLWidget, SIGNAL(ViewportResized(int,int)), this, SLOT(SetViewportSize(int,int))); + /*connect(ui->CentralGLWidget, SIGNAL(ViewportResized(int,int)), this, SLOT(SetViewportSize(int,int))); connect(ui->CentralGLWidget, SIGNAL(PaintViewport(double)), this, SLOT(PaintViewport(double))); connect(ui->CentralGLWidget, SIGNAL(MouseClicked(QMouseEvent*)), this, SLOT(OnViewportRayCast(QMouseEvent*))); connect(ui->CentralGLWidget, SIGNAL(MouseMoved(QMouseEvent*, float, float)), this, SLOT(OnViewportMouseMove(QMouseEvent*, float, float))); connect(ui->CentralGLWidget, SIGNAL(KeyPressed(QKeyEvent*)), this, SLOT(OnViewportKeyPress(QKeyEvent*))); connect(ui->CentralGLWidget, SIGNAL(KeyReleased(QKeyEvent*)), this, SLOT(OnViewportKeyRelease(QKeyEvent*))); - connect(ui->CentralGLWidget, SIGNAL(WheelScroll(int)), this, SLOT(OnViewportWheelScroll(int))); + connect(ui->CentralGLWidget, SIGNAL(WheelScroll(int)), this, SLOT(OnViewportWheelScroll(int)));*/ } CWorldEditorWindow::~CWorldEditorWindow() diff --git a/UI/CWorldEditorWindow.ui b/UI/CWorldEditorWindow.ui index 69aa8090..5e08bc4c 100644 --- a/UI/CWorldEditorWindow.ui +++ b/UI/CWorldEditorWindow.ui @@ -28,7 +28,7 @@ 0 - +
@@ -133,8 +133,8 @@ 0 0 - 294 - 654 + 272 + 330 @@ -447,14 +447,6 @@ - - - CEditorGLWidget - QWidget -
CEditorGLWidget.h
- 1 -
-
diff --git a/UI/INodeEditor.cpp b/UI/INodeEditor.cpp new file mode 100644 index 00000000..8860119c --- /dev/null +++ b/UI/INodeEditor.cpp @@ -0,0 +1,272 @@ +#include "INodeEditor.h" +#include "undo/UndoCommands.h" + +INodeEditor::INodeEditor(QWidget *pParent) + : QMainWindow(pParent), + mShowGizmo(false), + mGizmoHovering(false), + mGizmoTransforming(false), + mTranslateSpace(eWorldTransform), + mRotateSpace(eWorldTransform) +{ + // Create undo actions + QAction *pUndoAction = mUndoStack.createUndoAction(this); + QAction *pRedoAction = mUndoStack.createRedoAction(this); + pUndoAction->setShortcut(QKeySequence("Ctrl+Z")); + pRedoAction->setShortcut(QKeySequence("Ctrl+Y")); + mUndoActions.push_back(pUndoAction); + mUndoActions.push_back(pRedoAction); + + // Create gizmo actions + mGizmoActions.append(new QAction(QIcon(":/icons/EditorAssets/SelectMode.png"), "Select Objects", this)); + mGizmoActions.append(new QAction(QIcon(":/icons/EditorAssets/Translate.png"), "Translate", this)); + mGizmoActions.append(new QAction(QIcon(":/icons/EditorAssets/Rotate.png"), "Rotate", this)); + mGizmoActions.append(new QAction(QIcon(":/icons/EditorAssets/Scale.png"), "Scale", this)); + + mGizmoActions[0]->setShortcut(QKeySequence("Ctrl+Q")); + mGizmoActions[1]->setShortcut(QKeySequence("Ctrl+W")); + mGizmoActions[2]->setShortcut(QKeySequence("Ctrl+E")); + mGizmoActions[3]->setShortcut(QKeySequence("Ctrl+R")); + + mpGizmoGroup = new QActionGroup(this); + + foreach (QAction *pAction, mGizmoActions) + { + pAction->setCheckable(true); + mpGizmoGroup->addAction(pAction); + } + + mGizmoActions[0]->setChecked(true); + + // Create transform combo box + mpTransformCombo = new QComboBox(this); + mpTransformCombo->addItem("World"); + mpTransformCombo->addItem("Local"); + + // Connect signals and slots + connect(mGizmoActions[0], SIGNAL(triggered()), this, SLOT(OnSelectObjectsTriggered())); + connect(mGizmoActions[1], SIGNAL(triggered()), this, SLOT(OnTranslateTriggered())); + connect(mGizmoActions[2], SIGNAL(triggered()), this, SLOT(OnRotateTriggered())); + connect(mGizmoActions[3], SIGNAL(triggered()), this, SLOT(OnScaleTriggered())); + connect(mpTransformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnTransformSpaceChanged(int))); +} + +INodeEditor::~INodeEditor() +{ +} + +QUndoStack* INodeEditor::UndoStack() +{ + return &mUndoStack; +} + +CSceneManager* INodeEditor::Scene() +{ + return &mScene; +} + +CGizmo* INodeEditor::Gizmo() +{ + return &mGizmo; +} + +bool INodeEditor::IsGizmoVisible() +{ + return (mShowGizmo && !mSelection.empty()); +} + +void INodeEditor::BeginGizmoTransform() +{ + mGizmoTransforming = true; + + foreach (QAction *pAction, mGizmoActions) + pAction->setEnabled(false); +} + +void INodeEditor::EndGizmoTransform() +{ + mGizmoTransforming = false; + + foreach (QAction *pAction, mGizmoActions) + pAction->setEnabled(true); + + if (mGizmo.Mode() == CGizmo::eTranslate) + mUndoStack.push(CTranslateNodeCommand::End()); + else if (mGizmo.Mode() == CGizmo::eRotate) + mUndoStack.push(CRotateNodeCommand::End()); + else if (mGizmo.Mode() == CGizmo::eScale) + mUndoStack.push(CScaleNodeCommand::End()); +} + +ETransformSpace INodeEditor::CurrentTransformSpace() +{ + switch (mGizmo.Mode()) + { + case CGizmo::eTranslate: return mTranslateSpace; + case CGizmo::eRotate: return mRotateSpace; + case CGizmo::eScale: return eLocalTransform; + default: return eWorldTransform; + } +} + +void INodeEditor::RecalculateSelectionBounds() +{ + mSelectionBounds = CAABox::skInfinite; + + foreach (CSceneNode *pNode, mSelection) + ExpandSelectionBounds(pNode); +} + +void INodeEditor::ExpandSelectionBounds(CSceneNode *pNode) +{ + mSelectionBounds.ExpandBounds(pNode->AABox()); + + if (pNode->NodeType() == eScriptNode) + { + CScriptNode *pScript = static_cast(pNode); + + if (pScript->HasPreviewVolume()) + mSelectionBounds.ExpandBounds(pScript->PreviewVolumeAABox()); + } +} + +void INodeEditor::SelectNode(CSceneNode *pNode) +{ + if (!pNode->IsSelected()) + mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); +} + +void INodeEditor::DeselectNode(CSceneNode *pNode) +{ + if (pNode->IsSelected()) + mUndoStack.push(new CDeselectNodeCommand(this, pNode, mSelection)); +} + +void INodeEditor::ClearSelection() +{ + if (!mSelection.empty()) + mUndoStack.push(new CClearSelectionCommand(this, mSelection)); +} + +void INodeEditor::ClearAndSelectNode(CSceneNode *pNode) +{ + if (mSelection.empty()) + mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); + + else if ((mSelection.size() == 1) && (mSelection.front() == pNode)) + return; + + else + { + mUndoStack.beginMacro("Select"); + mUndoStack.push(new CClearSelectionCommand(this, mSelection)); + mUndoStack.push(new CSelectNodeCommand(this, pNode, mSelection)); + mUndoStack.endMacro(); + } +} + +// ************ PUBLIC SLOTS ************ +void INodeEditor::OnGizmoMoved() +{ + switch (mGizmo.Mode()) + { + case CGizmo::eTranslate: + { + CVector3f delta = mGizmo.DeltaTranslation(); + mUndoStack.push(new CTranslateNodeCommand(this, mSelection, delta, mTranslateSpace)); + break; + } + + case CGizmo::eRotate: + { + CQuaternion delta = mGizmo.DeltaRotation(); + mUndoStack.push(new CRotateNodeCommand(this, mSelection, CVector3f::skZero, delta, mRotateSpace)); + break; + } + + case CGizmo::eScale: + { + CVector3f delta = mGizmo.DeltaScale(); + mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta)); + break; + } + } + + RecalculateSelectionBounds(); + UpdateGizmoUI(); +} + +// ************ PRIVATE SLOTS ************ +void INodeEditor::OnSelectObjectsTriggered() +{ + mGizmo.SetMode(CGizmo::eOff); + mGizmo.SetTransformSpace(eWorldTransform); + mShowGizmo = false; + + mpTransformCombo->setEnabled(false); + mpTransformCombo->blockSignals(true); + mpTransformCombo->setCurrentIndex(0); + mpTransformCombo->blockSignals(false); + + GizmoModeChanged(CGizmo::eOff); + UpdateGizmoUI(); +} + +void INodeEditor::OnTranslateTriggered() +{ + mGizmo.SetMode(CGizmo::eTranslate); + mGizmo.SetTransformSpace(mTranslateSpace); + mShowGizmo = true; + + mpTransformCombo->setEnabled(true); + mpTransformCombo->blockSignals(true); + mpTransformCombo->setCurrentIndex( (mTranslateSpace == eWorldTransform) ? 0 : 1 ); + mpTransformCombo->blockSignals(false); + + GizmoModeChanged(CGizmo::eTranslate); + UpdateGizmoUI(); +} + +void INodeEditor::OnRotateTriggered() +{ + mGizmo.SetMode(CGizmo::eRotate); + mGizmo.SetTransformSpace(mRotateSpace); + mShowGizmo = true; + + mpTransformCombo->setEnabled(true); + mpTransformCombo->blockSignals(true); + mpTransformCombo->setCurrentIndex( (mRotateSpace == eWorldTransform) ? 0 : 1 ); + mpTransformCombo->blockSignals(false); + + GizmoModeChanged(CGizmo::eRotate); + UpdateGizmoUI(); +} + +void INodeEditor::OnScaleTriggered() +{ + mGizmo.SetMode(CGizmo::eScale); + mGizmo.SetTransformSpace(eLocalTransform); + mShowGizmo = true; + + mpTransformCombo->setEnabled(false); + mpTransformCombo->blockSignals(true); + mpTransformCombo->setCurrentIndex(1); + mpTransformCombo->blockSignals(false); + + GizmoModeChanged(CGizmo::eScale); + UpdateGizmoUI(); +} + +void INodeEditor::OnTransformSpaceChanged(int spaceIndex) +{ + if ((mGizmo.Mode() == CGizmo::eScale) || (mGizmo.Mode() == CGizmo::eOff)) return; + + ETransformSpace space = (spaceIndex == 0 ? eWorldTransform : eLocalTransform); + + if (mGizmo.Mode() == CGizmo::eTranslate) + mTranslateSpace = space; + else + mRotateSpace = space; + + mGizmo.SetTransformSpace(space); +} diff --git a/UI/INodeEditor.h b/UI/INodeEditor.h new file mode 100644 index 00000000..7390ba4a --- /dev/null +++ b/UI/INodeEditor.h @@ -0,0 +1,77 @@ +#ifndef INODEEDITOR_H +#define INODEEDITOR_H + +#include +#include +#include +#include +#include +#include + +#include "CGizmo.h" +#include +#include + +class INodeEditor : public QMainWindow +{ + Q_OBJECT + +protected: + // Undo stack + QUndoStack mUndoStack; + QList mUndoActions; + + // Node management + CSceneManager mScene; + QList mSelection; + CAABox mSelectionBounds; + + // Gizmo + CGizmo mGizmo; + bool mShowGizmo; + bool mGizmoHovering; + bool mGizmoTransforming; + ETransformSpace mTranslateSpace; + ETransformSpace mRotateSpace; + + // Gizmo widgets + QActionGroup *mpGizmoGroup; + QList mGizmoActions; + QComboBox *mpTransformCombo; + +public: + explicit INodeEditor(QWidget *pParent = 0); + virtual ~INodeEditor(); + QUndoStack* UndoStack(); + CSceneManager* Scene(); + CGizmo* Gizmo(); + bool IsGizmoVisible(); + void BeginGizmoTransform(); + void EndGizmoTransform(); + + ETransformSpace CurrentTransformSpace(); + void RecalculateSelectionBounds(); + void ExpandSelectionBounds(CSceneNode *pNode); + void SelectNode(CSceneNode *pNode); + void DeselectNode(CSceneNode *pNode); + void ClearSelection(); + void ClearAndSelectNode(CSceneNode *pNode); + + virtual void UpdateGizmoUI() = 0; + virtual void UpdateSelectionUI() = 0; + +public slots: + void OnGizmoMoved(); + +protected: + virtual void GizmoModeChanged(CGizmo::EGizmoMode mode) {} + +private slots: + void OnSelectObjectsTriggered(); + void OnTranslateTriggered(); + void OnRotateTriggered(); + void OnScaleTriggered(); + void OnTransformSpaceChanged(int spaceIndex); +}; + +#endif // INODEEDITOR_H diff --git a/UI/WPropertyEditor.cpp b/UI/WPropertyEditor.cpp index df15fd0a..478a79e7 100644 --- a/UI/WPropertyEditor.cpp +++ b/UI/WPropertyEditor.cpp @@ -154,7 +154,7 @@ void WPropertyEditor::CreateEditor() pLineEdit->setText(QString::fromStdString(pStringCast->Get())); pLineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - pLineEdit->setCursorPosition(0); + //pLineEdit->setCursorPosition(0); mUI.EditorWidget = pLineEdit; break; diff --git a/UI/WVectorEditor.cpp b/UI/WVectorEditor.cpp index 61539e9e..5bc54426 100644 --- a/UI/WVectorEditor.cpp +++ b/UI/WVectorEditor.cpp @@ -61,8 +61,6 @@ void WVectorEditor::SetOrientation(Qt::Orientation orientation) mpLayout->addItem(mpZLayout); mpLayout->setContentsMargins(5,5,5,5); setLayout(mpLayout); - - emit } void WVectorEditor::SetDefaultValue(double value) diff --git a/UI/WorldEditor/WModifyTab.cpp b/UI/WorldEditor/WModifyTab.cpp index 7edc2ee0..758a5ab2 100644 --- a/UI/WorldEditor/WModifyTab.cpp +++ b/UI/WorldEditor/WModifyTab.cpp @@ -38,7 +38,7 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor) mpWorldEditor = pEditor; } -void WModifyTab::GenerateUI(std::list& Selection) +void WModifyTab::GenerateUI(QList& Selection) { WPropertyEditor *pOldEditor = mpCurPropEditor; ClearUI(); diff --git a/UI/WorldEditor/WModifyTab.h b/UI/WorldEditor/WModifyTab.h index a76fecf5..0b12de59 100644 --- a/UI/WorldEditor/WModifyTab.h +++ b/UI/WorldEditor/WModifyTab.h @@ -33,7 +33,7 @@ public: explicit WModifyTab(QWidget *pParent = 0); ~WModifyTab(); void SetEditor(CWorldEditor *pEditor); - void GenerateUI(std::list& Selection); + void GenerateUI(QList& Selection); void ClearUI(); void ClearCachedEditors(); diff --git a/UI/undo/CClearSelectionCommand.cpp b/UI/undo/CClearSelectionCommand.cpp new file mode 100644 index 00000000..89c81b5c --- /dev/null +++ b/UI/undo/CClearSelectionCommand.cpp @@ -0,0 +1,42 @@ +#include "CClearSelectionCommand.h" +#include "../CWorldEditor.h" + +CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList& selection) + : QUndoCommand("Clear Selection"), + mpEditor(pEditor), + mSelectionState(selection), + mpSelection(&selection) +{ +} + +CClearSelectionCommand::~CClearSelectionCommand() +{ +} + +void CClearSelectionCommand::undo() +{ + mpSelection->reserve(mSelectionState.size()); + + foreach (CSceneNode *pNode, mSelectionState) + { + if (!pNode->IsSelected()) + { + pNode->SetSelected(true); + mpSelection->push_back(pNode); + } + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateSelectionUI(); +} + +void CClearSelectionCommand::redo() +{ + foreach (CSceneNode *pNode, *mpSelection) + if (pNode->IsSelected()) + pNode->SetSelected(false); + + mpSelection->clear(); + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateSelectionUI(); +} diff --git a/UI/undo/CClearSelectionCommand.h b/UI/undo/CClearSelectionCommand.h new file mode 100644 index 00000000..f27ccab4 --- /dev/null +++ b/UI/undo/CClearSelectionCommand.h @@ -0,0 +1,20 @@ +#ifndef CCLEARSELECTIONCOMMAND_H +#define CCLEARSELECTIONCOMMAND_H + +#include +#include "../INodeEditor.h" +#include + +class CClearSelectionCommand : public QUndoCommand +{ + INodeEditor *mpEditor; + QList mSelectionState; + QList *mpSelection; +public: + CClearSelectionCommand(INodeEditor *pEditor, QList& selection); + ~CClearSelectionCommand(); + void undo(); + void redo(); +}; + +#endif // CCLEARSELECTIONCOMMAND_H diff --git a/UI/undo/CDeselectNodeCommand.cpp b/UI/undo/CDeselectNodeCommand.cpp new file mode 100644 index 00000000..76433df7 --- /dev/null +++ b/UI/undo/CDeselectNodeCommand.cpp @@ -0,0 +1,42 @@ +#include "CDeselectNodeCommand.h" +#include "../CWorldEditor.h" + +CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection) + : QUndoCommand("Deselect"), + mpEditor(pEditor), + mpNode(pNode), + mpSelection(&selection) +{ +} + +void CDeselectNodeCommand::undo() +{ + if (!mpNode->IsSelected()) + { + mpNode->SetSelected(true); + mpSelection->push_back(mpNode); + } + + mpEditor->ExpandSelectionBounds(mpNode); + mpEditor->UpdateSelectionUI(); +} + +void CDeselectNodeCommand::redo() +{ + if (mpNode->IsSelected()) + { + mpNode->SetSelected(false); + + for (auto it = mpSelection->begin(); it != mpSelection->end(); it++) + { + if (*it == mpNode) + { + mpSelection->erase(it); + break; + } + } + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateSelectionUI(); +} diff --git a/UI/undo/CDeselectNodeCommand.h b/UI/undo/CDeselectNodeCommand.h new file mode 100644 index 00000000..463d25b8 --- /dev/null +++ b/UI/undo/CDeselectNodeCommand.h @@ -0,0 +1,19 @@ +#ifndef CDESELECTNODECOMMAND_H +#define CDESELECTNODECOMMAND_H + +#include +#include "../INodeEditor.h" +#include + +class CDeselectNodeCommand : public QUndoCommand +{ + INodeEditor *mpEditor; + CSceneNode *mpNode; + QList *mpSelection; +public: + CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection); + void undo(); + void redo(); +}; + +#endif // CDESELECTNODECOMMAND_H diff --git a/UI/undo/CRotateNodeCommand.cpp b/UI/undo/CRotateNodeCommand.cpp new file mode 100644 index 00000000..6fd20f9e --- /dev/null +++ b/UI/undo/CRotateNodeCommand.cpp @@ -0,0 +1,103 @@ +#include "CRotateNodeCommand.h" +#include "EUndoCommand.h" +#include "../CWorldEditor.h" + +CRotateNodeCommand::CRotateNodeCommand() + : QUndoCommand("Rotate"), + mpEditor(nullptr), + mCommandEnded(false) +{ +} + +CRotateNodeCommand::CRotateNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& pivot, const CQuaternion& delta, ETransformSpace transformSpace) + : QUndoCommand("Rotate"), + mpEditor(pEditor), + mCommandEnded(false) +{ + mNodeList.reserve(nodes.size()); + + foreach (CSceneNode *pNode, nodes) + { + SNodeRotate rotate; + rotate.pNode = pNode; + rotate.initialPos = pNode->LocalPosition(); + rotate.initialRot = pNode->LocalRotation(); + pNode->Rotate(delta, transformSpace); + rotate.newPos = pNode->LocalPosition(); + rotate.newRot = pNode->LocalRotation(); + mNodeList.push_back(rotate); + } +} + +CRotateNodeCommand::~CRotateNodeCommand() +{ +} + +int CRotateNodeCommand::id() const +{ + return eRotateNodeCmd; +} + +bool CRotateNodeCommand::mergeWith(const QUndoCommand *other) +{ + if (mCommandEnded) return false; + + if (other->id() == eRotateNodeCmd) + { + const CRotateNodeCommand *pCmd = static_cast(other); + + if (pCmd->mCommandEnded) + { + mCommandEnded = true; + return true; + } + + if ((mpEditor == pCmd->mpEditor) && (mNodeList.size() == pCmd->mNodeList.size())) + { + for (u32 iNode = 0; iNode < mNodeList.size(); iNode++) + { + mNodeList[iNode].newPos = pCmd->mNodeList[iNode].newPos; + mNodeList[iNode].newRot = pCmd->mNodeList[iNode].newRot; + } + + return true; + } + } + + return false; +} + +void CRotateNodeCommand::undo() +{ + if (!mpEditor) return; + + foreach (SNodeRotate rotate, mNodeList) + { + rotate.pNode->SetPosition(rotate.initialPos); + rotate.pNode->SetRotation(rotate.initialRot); + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateGizmoUI(); +} + +void CRotateNodeCommand::redo() +{ + if (!mpEditor) return; + + foreach (SNodeRotate rotate, mNodeList) + { + rotate.pNode->SetPosition(rotate.newPos); + rotate.pNode->SetRotation(rotate.newRot); + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateGizmoUI(); +} + +CRotateNodeCommand* CRotateNodeCommand::End() +{ + CRotateNodeCommand *pCmd = new CRotateNodeCommand(); + pCmd->mCommandEnded = true; + return pCmd; +} diff --git a/UI/undo/CRotateNodeCommand.h b/UI/undo/CRotateNodeCommand.h new file mode 100644 index 00000000..7815e58b --- /dev/null +++ b/UI/undo/CRotateNodeCommand.h @@ -0,0 +1,34 @@ +#ifndef CROTATENODECOMMAND_H +#define CROTATENODECOMMAND_H + +#include +#include +#include +#include "../INodeEditor.h" + +class CRotateNodeCommand : public QUndoCommand +{ + struct SNodeRotate + { + CSceneNode *pNode; + CVector3f initialPos; + CQuaternion initialRot; + CVector3f newPos; + CQuaternion newRot; + }; + QList mNodeList; + INodeEditor *mpEditor; + bool mCommandEnded; + +public: + CRotateNodeCommand(); + CRotateNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& pivot, const CQuaternion& delta, ETransformSpace transformSpace); + ~CRotateNodeCommand(); + int id() const; + bool mergeWith(const QUndoCommand *other); + void undo(); + void redo(); + static CRotateNodeCommand* End(); +}; + +#endif // CROTATENODECOMMAND_H diff --git a/UI/undo/CScaleNodeCommand.cpp b/UI/undo/CScaleNodeCommand.cpp new file mode 100644 index 00000000..1a53f77b --- /dev/null +++ b/UI/undo/CScaleNodeCommand.cpp @@ -0,0 +1,103 @@ +#include "CScaleNodeCommand.h" +#include "EUndoCommand.h" +#include "../CWorldEditor.h" + +CScaleNodeCommand::CScaleNodeCommand() + : QUndoCommand("Scale"), + mpEditor(nullptr), + mCommandEnded(false) +{ +} + +CScaleNodeCommand::CScaleNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& pivot, const CVector3f& delta) + : QUndoCommand("Scale"), + mpEditor(pEditor), + mCommandEnded(false) +{ + mNodeList.reserve(nodes.size()); + + foreach (CSceneNode *pNode, nodes) + { + SNodeScale scale; + scale.pNode = pNode; + scale.initialPos = pNode->LocalPosition(); + scale.initialScale = pNode->LocalScale(); + pNode->Scale(delta); + scale.newPos = pNode->LocalPosition(); + scale.newScale = pNode->LocalScale(); + mNodeList.push_back(scale); + } +} + +CScaleNodeCommand::~CScaleNodeCommand() +{ +} + +int CScaleNodeCommand::id() const +{ + return eScaleNodeCmd; +} + +bool CScaleNodeCommand::mergeWith(const QUndoCommand *other) +{ + if (mCommandEnded) return false; + + if (other->id() == eScaleNodeCmd) + { + const CScaleNodeCommand *pCmd = static_cast(other); + + if (pCmd->mCommandEnded) + { + mCommandEnded = true; + return true; + } + + if ((mpEditor == pCmd->mpEditor) && (mNodeList.size() == pCmd->mNodeList.size())) + { + for (u32 iNode = 0; iNode < mNodeList.size(); iNode++) + { + mNodeList[iNode].newPos = pCmd->mNodeList[iNode].newPos; + mNodeList[iNode].newScale = pCmd->mNodeList[iNode].newScale; + } + + return true; + } + } + + return false; +} + +void CScaleNodeCommand::undo() +{ + if (!mpEditor) return; + + foreach (SNodeScale scale, mNodeList) + { + scale.pNode->SetPosition(scale.initialPos); + scale.pNode->SetScale(scale.initialScale); + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateGizmoUI(); +} + +void CScaleNodeCommand::redo() +{ + if (!mpEditor) return; + + foreach (SNodeScale scale, mNodeList) + { + scale.pNode->SetPosition(scale.newPos); + scale.pNode->SetScale(scale.newScale); + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateGizmoUI(); +} + +CScaleNodeCommand* CScaleNodeCommand::End() +{ + CScaleNodeCommand *pCmd = new CScaleNodeCommand(); + pCmd->mCommandEnded = true; + return pCmd; +} diff --git a/UI/undo/CScaleNodeCommand.h b/UI/undo/CScaleNodeCommand.h new file mode 100644 index 00000000..1dbcb528 --- /dev/null +++ b/UI/undo/CScaleNodeCommand.h @@ -0,0 +1,34 @@ +#ifndef CSCALENODECOMMAND_H +#define CSCALENODECOMMAND_H + +#include +#include +#include +#include "../INodeEditor.h" + +class CScaleNodeCommand : public QUndoCommand +{ + struct SNodeScale + { + CSceneNode *pNode; + CVector3f initialPos; + CVector3f initialScale; + CVector3f newPos; + CVector3f newScale; + }; + QList mNodeList; + INodeEditor *mpEditor; + bool mCommandEnded; + +public: + CScaleNodeCommand(); + CScaleNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& pivot, const CVector3f& delta); + ~CScaleNodeCommand(); + int id() const; + bool mergeWith(const QUndoCommand *other); + void undo(); + void redo(); + static CScaleNodeCommand* End(); +}; + +#endif // CScaleNODECOMMAND_H diff --git a/UI/undo/CSelectNodeCommand.cpp b/UI/undo/CSelectNodeCommand.cpp new file mode 100644 index 00000000..540e76bd --- /dev/null +++ b/UI/undo/CSelectNodeCommand.cpp @@ -0,0 +1,42 @@ +#include "CSelectNodeCommand.h" +#include "../CWorldEditor.h" + +CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection) + : QUndoCommand("Select"), + mpEditor(pEditor), + mpNode(pNode), + mpSelection(&selection) +{ +} + +void CSelectNodeCommand::undo() +{ + if (mpNode->IsSelected()) + { + mpNode->SetSelected(false); + + for (auto it = mpSelection->begin(); it != mpSelection->end(); it++) + { + if (*it == mpNode) + { + mpSelection->erase(it); + break; + } + } + } + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateSelectionUI(); +} + +void CSelectNodeCommand::redo() +{ + if (!mpNode->IsSelected()) + { + mpNode->SetSelected(true); + mpSelection->push_back(mpNode); + } + + mpEditor->ExpandSelectionBounds(mpNode); + mpEditor->UpdateSelectionUI(); +} diff --git a/UI/undo/CSelectNodeCommand.h b/UI/undo/CSelectNodeCommand.h new file mode 100644 index 00000000..7b4c7271 --- /dev/null +++ b/UI/undo/CSelectNodeCommand.h @@ -0,0 +1,19 @@ +#ifndef CSELECTNODECOMMAND_H +#define CSELECTNODECOMMAND_H + +#include +#include "../INodeEditor.h" +#include + +class CSelectNodeCommand : public QUndoCommand +{ + INodeEditor *mpEditor; + CSceneNode *mpNode; + QList *mpSelection; +public: + CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList& selection); + void undo(); + void redo(); +}; + +#endif // CSELECTNODECOMMAND_H diff --git a/UI/undo/CTranslateNodeCommand.cpp b/UI/undo/CTranslateNodeCommand.cpp new file mode 100644 index 00000000..ed3590df --- /dev/null +++ b/UI/undo/CTranslateNodeCommand.cpp @@ -0,0 +1,92 @@ +#include "CTranslateNodeCommand.h" +#include "EUndoCommand.h" +#include "../CWorldEditor.h" + +CTranslateNodeCommand::CTranslateNodeCommand() + : QUndoCommand("Translate"), + mpEditor(nullptr), + mCommandEnded(false) +{ +} + +CTranslateNodeCommand::CTranslateNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& delta, ETransformSpace transformSpace) + : QUndoCommand("Translate"), + mpEditor(pEditor), + mCommandEnded(false) +{ + mNodeList.reserve(nodes.size()); + + foreach (CSceneNode *pNode, nodes) + { + SNodeTranslate translate; + translate.pNode = pNode; + translate.initialPos = pNode->LocalPosition(); + pNode->Translate(delta, transformSpace); + translate.newPos = pNode->LocalPosition(); + mNodeList.push_back(translate); + } +} + +CTranslateNodeCommand::~CTranslateNodeCommand() +{ +} + +int CTranslateNodeCommand::id() const +{ + return eTranslateNodeCmd; +} + +bool CTranslateNodeCommand::mergeWith(const QUndoCommand *other) +{ + if (mCommandEnded) return false; + + if (other->id() == eTranslateNodeCmd) + { + const CTranslateNodeCommand *pCmd = static_cast(other); + + if (pCmd->mCommandEnded) + { + mCommandEnded = true; + return true; + } + + if ((mpEditor == pCmd->mpEditor) && (mNodeList.size() == pCmd->mNodeList.size())) + { + for (u32 iNode = 0; iNode < mNodeList.size(); iNode++) + mNodeList[iNode].newPos = pCmd->mNodeList[iNode].newPos; + + return true; + } + } + + return false; +} + +void CTranslateNodeCommand::undo() +{ + if (!mpEditor) return; + + foreach (SNodeTranslate translate, mNodeList) + translate.pNode->SetPosition(translate.initialPos); + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateGizmoUI(); +} + +void CTranslateNodeCommand::redo() +{ + if (!mpEditor) return; + + foreach (SNodeTranslate translate, mNodeList) + translate.pNode->SetPosition(translate.newPos); + + mpEditor->RecalculateSelectionBounds(); + mpEditor->UpdateGizmoUI(); +} + +CTranslateNodeCommand* CTranslateNodeCommand::End() +{ + CTranslateNodeCommand *pCmd = new CTranslateNodeCommand(); + pCmd->mCommandEnded = true; + return pCmd; +} diff --git a/UI/undo/CTranslateNodeCommand.h b/UI/undo/CTranslateNodeCommand.h new file mode 100644 index 00000000..bd6f3bb1 --- /dev/null +++ b/UI/undo/CTranslateNodeCommand.h @@ -0,0 +1,32 @@ +#ifndef CTRANSLATENODECOMMAND_H +#define CTRANSLATENODECOMMAND_H + +#include +#include +#include +#include "../INodeEditor.h" + +class CTranslateNodeCommand : public QUndoCommand +{ + struct SNodeTranslate + { + CSceneNode *pNode; + CVector3f initialPos; + CVector3f newPos; + }; + QList mNodeList; + INodeEditor *mpEditor; + bool mCommandEnded; + +public: + CTranslateNodeCommand(); + CTranslateNodeCommand(INodeEditor *pEditor, const QList& nodes, const CVector3f& delta, ETransformSpace transformSpace); + ~CTranslateNodeCommand(); + int id() const; + bool mergeWith(const QUndoCommand *other); + void undo(); + void redo(); + static CTranslateNodeCommand* End(); +}; + +#endif // CTRANSLATENODECOMMAND_H diff --git a/UI/undo/EUndoCommand.h b/UI/undo/EUndoCommand.h new file mode 100644 index 00000000..4f22114b --- /dev/null +++ b/UI/undo/EUndoCommand.h @@ -0,0 +1,12 @@ +#ifndef EUNDOCOMMAND +#define EUNDOCOMMAND + +enum EUndoCommand +{ + eTranslateNodeCmd, + eRotateNodeCmd, + eScaleNodeCmd +}; + +#endif // EUNDOCOMMAND + diff --git a/UI/undo/UndoCommands.h b/UI/undo/UndoCommands.h new file mode 100644 index 00000000..ca190147 --- /dev/null +++ b/UI/undo/UndoCommands.h @@ -0,0 +1,13 @@ +#ifndef UNDOCOMMANDS +#define UNDOCOMMANDS + +#include "CTranslateNodeCommand.h" +#include "CRotateNodeCommand.h" +#include "CScaleNodeCommand.h" +#include "CSelectNodeCommand.h" +#include "CDeselectNodeCommand.h" +#include "CClearSelectionCommand.h" +#include "EUndoCommand.h" + +#endif // UNDOCOMMANDS +