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

This commit is contained in:
parax0 2015-09-01 13:05:48 -04:00
parent 281a605586
commit dbf002d12a
45 changed files with 1760 additions and 908 deletions

View File

@ -1,4 +1,6 @@
#include "Math.h"
#include <Common/CMatrix4f.h>
#include <gtc/matrix_transform.hpp>
namespace Math
{
@ -336,4 +338,10 @@ std::pair<bool,float> RayTriangleIntersection(const CRay& Ray,
return std::pair<bool,float>(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

View File

@ -32,6 +32,8 @@ std::pair<bool,float> 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;

View File

@ -1,7 +1,7 @@
#include "CCamera.h"
#include "CGraphics.h"
#include <Common/CQuaternion.h>
#include <Common/Math.h>
#include <gtc/matrix_transform.hpp>
#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;
}

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -200,8 +200,11 @@ void CSceneManager::SetActiveWorld(CWorld* _world)
void CSceneManager::ClearScene()
{
if (mpAreaRootNode)
{
mpAreaRootNode->Unparent();
delete mpAreaRootNode;
}
mModelNodes.clear();
mStaticNodes.clear();

View File

@ -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 \

View File

@ -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();
}
}
}

View File

@ -1,49 +1,31 @@
#include "CBasicViewport.h"
#include <Core/CDrawUtil.h>
#include <Core/CGraphics.h>
#include <Common/Math.h>
#include <GL/glew.h>
#include <gtc/matrix_transform.hpp>
#include <gtx/transform.hpp>
#include <Core/CGraphics.h>
#include <Core/CRenderer.h>
#include <QTimer>
#include "CEditorGLWidget.h"
#include <iostream>
#include <Core/CGraphics.h>
#include <Resource/factory/CTextureDecoder.h>
#include <Common/CTimer.h>
#include <Common/CVector4f.h>
#include <QOpenGLContext>
#include <QPainter>
#include <QOpenGLPaintDevice>
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
}

View File

@ -11,34 +11,44 @@
#include <Core/CRenderer.h>
#include <Resource/CFont.h>
#include <Common/CRay.h>
#include <Common/CTimer.h>
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

View File

@ -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;

View File

@ -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();

View File

@ -0,0 +1,95 @@
#include "CModelEditorViewport.h"
#include <Core/CDrawUtil.h>
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());
}

30
UI/CModelEditorViewport.h Normal file
View File

@ -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

View File

@ -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()

View File

@ -9,6 +9,8 @@
#include <Resource/CFont.h>
#include <Resource/model/CModel.h>
#include <Scene/CModelNode.h>
#include <QTimer>
#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);

View File

@ -2124,7 +2124,7 @@
<number>0</number>
</property>
<item>
<widget class="CEditorGLWidget" name="PreviewGLWidget" native="true">
<widget class="CModelEditorViewport" name="Viewport" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>250</horstretch>
@ -2480,6 +2480,11 @@
</action>
</widget>
<customwidgets>
<customwidget>
<class>WDraggableSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>WDraggableSpinBox.h</header>
</customwidget>
<customwidget>
<class>WColorPicker</class>
<extends>QWidget</extends>
@ -2496,16 +2501,11 @@
<container>1</container>
</customwidget>
<customwidget>
<class>CEditorGLWidget</class>
<class>CModelEditorViewport</class>
<extends>QWidget</extends>
<header>CEditorGLWidget.h</header>
<header>CModelEditorViewport.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>WDraggableSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>WDraggableSpinBox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../Icons.qrc"/>

230
UI/CSceneViewport.cpp Normal file
View File

@ -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();
}
}
}

49
UI/CSceneViewport.h Normal file
View File

@ -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

View File

@ -1,6 +1,6 @@
#include "CWorldEditor.h"
#include "ui_CWorldEditor.h"
#include "CEditorGLWidget.h"
#include "CBasicViewport.h"
#include <gtc/matrix_transform.hpp>
#include <Core/CDrawUtil.h>
#include <iostream>
@ -10,36 +10,27 @@
#include <Core/Log.h>
#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<CScriptNode*>(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<CScriptNode*>(*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,23 +281,31 @@ 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);
{
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);
{
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);
{
CVector3f delta = value / mSelection.front()->AbsoluteScale();
mUndoStack.push(new CScaleNodeCommand(this, mSelection, CVector3f::skZero, delta));
break;
}
}
RecalculateSelectionBounds();
UpdateGizmoUI();
@ -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();

View File

@ -1,9 +1,13 @@
#ifndef CWORLDEDITOR_H
#define CWORLDEDITOR_H
#include <QMainWindow>
#include <QList>
#include "INodeEditor.h"
#include <QComboBox>
#include <QList>
#include <QMainWindow>
#include <QTimer>
#include <QUndoStack>
#include "CGizmo.h"
#include <Common/CRay.h>
@ -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<CSceneNode*> 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();
};

View File

@ -31,7 +31,7 @@
<number>0</number>
</property>
<item>
<widget class="CEditorGLWidget" name="MainViewport" native="true">
<widget class="CSceneViewport" name="MainViewport" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
@ -245,8 +245,6 @@
<property name="title">
<string>Edit</string>
</property>
<addaction name="ActionUndo"/>
<addaction name="ActionRedo"/>
</widget>
<widget class="QMenu" name="menuView">
<property name="title">
@ -475,10 +473,6 @@
<addaction name="ActionLink"/>
<addaction name="ActionUnlink"/>
<addaction name="separator"/>
<addaction name="ActionSelectObjects"/>
<addaction name="ActionTranslate"/>
<addaction name="ActionRotate"/>
<addaction name="ActionScale"/>
</widget>
<action name="ActionOpen">
<property name="text">
@ -490,60 +484,6 @@
<string>Save</string>
</property>
</action>
<action name="ActionTranslate">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/EditorAssets/Translate.png</normaloff>:/icons/EditorAssets/Translate.png</iconset>
</property>
<property name="text">
<string>Translate</string>
</property>
<property name="toolTip">
<string>Translate</string>
</property>
<property name="shortcut">
<string>Ctrl+W</string>
</property>
</action>
<action name="ActionRotate">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/EditorAssets/Rotate.png</normaloff>:/icons/EditorAssets/Rotate.png</iconset>
</property>
<property name="text">
<string>Rotate</string>
</property>
<property name="toolTip">
<string>Rotate</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property>
</action>
<action name="ActionScale">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/EditorAssets/Scale.png</normaloff>:/icons/EditorAssets/Scale.png</iconset>
</property>
<property name="text">
<string>Scale</string>
</property>
<property name="toolTip">
<string>Scale</string>
</property>
<property name="shortcut">
<string>Ctrl+R</string>
</property>
</action>
<action name="actionOpen_model_viewer">
<property name="text">
<string>Open Model Viewer</string>
@ -573,22 +513,6 @@
<string>Unlink</string>
</property>
</action>
<action name="ActionUndo">
<property name="text">
<string>Undo</string>
</property>
<property name="shortcut">
<string>Ctrl+Z</string>
</property>
</action>
<action name="ActionRedo">
<property name="text">
<string>Redo</string>
</property>
<property name="shortcut">
<string>Ctrl+Y</string>
</property>
</action>
<action name="ActionDrawWorld">
<property name="checkable">
<bool>true</bool>
@ -753,27 +677,6 @@
<string>Fake Bloom</string>
</property>
</action>
<action name="ActionSelectObjects">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="../Icons.qrc">
<normaloff>:/icons/EditorAssets/SelectMode.png</normaloff>:/icons/EditorAssets/SelectMode.png</iconset>
</property>
<property name="text">
<string>Select Objects</string>
</property>
<property name="toolTip">
<string>Select Objects</string>
</property>
<property name="shortcut">
<string>Ctrl+Q</string>
</property>
</action>
<action name="ActionIncrementGizmo">
<property name="text">
<string>Increment Gizmo Size</string>
@ -804,12 +707,6 @@
</action>
</widget>
<customwidgets>
<customwidget>
<class>CEditorGLWidget</class>
<extends>QWidget</extends>
<header>CEditorGLWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>WDraggableSpinBox</class>
<extends>QDoubleSpinBox</extends>
@ -833,6 +730,12 @@
<header>WVectorEditor.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CSceneViewport</class>
<extends>QWidget</extends>
<header>CSceneViewport.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>TabWidget</tabstop>

View File

@ -11,7 +11,7 @@
#include <Common/StringUtil.h>
#include <Core/CGraphics.h>
#include <gtc/matrix_transform.hpp>
#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()

View File

@ -28,7 +28,7 @@
<number>0</number>
</property>
<item>
<widget class="CEditorGLWidget" name="CentralGLWidget" native="true"/>
<widget class="QWidget" name="CentralGLWidget" native="true"/>
</item>
</layout>
</widget>
@ -133,8 +133,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>294</width>
<height>654</height>
<width>272</width>
<height>330</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -447,14 +447,6 @@
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>CEditorGLWidget</class>
<extends>QWidget</extends>
<header>CEditorGLWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

272
UI/INodeEditor.cpp Normal file
View File

@ -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<CScriptNode*>(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);
}

77
UI/INodeEditor.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef INODEEDITOR_H
#define INODEEDITOR_H
#include <QMainWindow>
#include <QAction>
#include <QActionGroup>
#include <QComboBox>
#include <QList>
#include <QUndoStack>
#include "CGizmo.h"
#include <Common/ETransformSpace.h>
#include <Core/CSceneManager.h>
class INodeEditor : public QMainWindow
{
Q_OBJECT
protected:
// Undo stack
QUndoStack mUndoStack;
QList<QAction*> mUndoActions;
// Node management
CSceneManager mScene;
QList<CSceneNode*> mSelection;
CAABox mSelectionBounds;
// Gizmo
CGizmo mGizmo;
bool mShowGizmo;
bool mGizmoHovering;
bool mGizmoTransforming;
ETransformSpace mTranslateSpace;
ETransformSpace mRotateSpace;
// Gizmo widgets
QActionGroup *mpGizmoGroup;
QList<QAction*> 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

View File

@ -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;

View File

@ -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)

View File

@ -38,7 +38,7 @@ void WModifyTab::SetEditor(CWorldEditor *pEditor)
mpWorldEditor = pEditor;
}
void WModifyTab::GenerateUI(std::list<CSceneNode*>& Selection)
void WModifyTab::GenerateUI(QList<CSceneNode*>& Selection)
{
WPropertyEditor *pOldEditor = mpCurPropEditor;
ClearUI();

View File

@ -33,7 +33,7 @@ public:
explicit WModifyTab(QWidget *pParent = 0);
~WModifyTab();
void SetEditor(CWorldEditor *pEditor);
void GenerateUI(std::list<CSceneNode*>& Selection);
void GenerateUI(QList<CSceneNode*>& Selection);
void ClearUI();
void ClearCachedEditors();

View File

@ -0,0 +1,42 @@
#include "CClearSelectionCommand.h"
#include "../CWorldEditor.h"
CClearSelectionCommand::CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& 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();
}

View File

@ -0,0 +1,20 @@
#ifndef CCLEARSELECTIONCOMMAND_H
#define CCLEARSELECTIONCOMMAND_H
#include <QUndoCommand>
#include "../INodeEditor.h"
#include <Scene/CSceneNode.h>
class CClearSelectionCommand : public QUndoCommand
{
INodeEditor *mpEditor;
QList<CSceneNode*> mSelectionState;
QList<CSceneNode*> *mpSelection;
public:
CClearSelectionCommand(INodeEditor *pEditor, QList<CSceneNode*>& selection);
~CClearSelectionCommand();
void undo();
void redo();
};
#endif // CCLEARSELECTIONCOMMAND_H

View File

@ -0,0 +1,42 @@
#include "CDeselectNodeCommand.h"
#include "../CWorldEditor.h"
CDeselectNodeCommand::CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& 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();
}

View File

@ -0,0 +1,19 @@
#ifndef CDESELECTNODECOMMAND_H
#define CDESELECTNODECOMMAND_H
#include <QUndoCommand>
#include "../INodeEditor.h"
#include <Scene/CSceneNode.h>
class CDeselectNodeCommand : public QUndoCommand
{
INodeEditor *mpEditor;
CSceneNode *mpNode;
QList<CSceneNode*> *mpSelection;
public:
CDeselectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection);
void undo();
void redo();
};
#endif // CDESELECTNODECOMMAND_H

View File

@ -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<CSceneNode*>& 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<const CRotateNodeCommand*>(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;
}

View File

@ -0,0 +1,34 @@
#ifndef CROTATENODECOMMAND_H
#define CROTATENODECOMMAND_H
#include <QUndoCommand>
#include <QList>
#include <Scene/CSceneNode.h>
#include "../INodeEditor.h"
class CRotateNodeCommand : public QUndoCommand
{
struct SNodeRotate
{
CSceneNode *pNode;
CVector3f initialPos;
CQuaternion initialRot;
CVector3f newPos;
CQuaternion newRot;
};
QList<SNodeRotate> mNodeList;
INodeEditor *mpEditor;
bool mCommandEnded;
public:
CRotateNodeCommand();
CRotateNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& 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

View File

@ -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<CSceneNode*>& 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<const CScaleNodeCommand*>(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;
}

View File

@ -0,0 +1,34 @@
#ifndef CSCALENODECOMMAND_H
#define CSCALENODECOMMAND_H
#include <QUndoCommand>
#include <QList>
#include <Scene/CSceneNode.h>
#include "../INodeEditor.h"
class CScaleNodeCommand : public QUndoCommand
{
struct SNodeScale
{
CSceneNode *pNode;
CVector3f initialPos;
CVector3f initialScale;
CVector3f newPos;
CVector3f newScale;
};
QList<SNodeScale> mNodeList;
INodeEditor *mpEditor;
bool mCommandEnded;
public:
CScaleNodeCommand();
CScaleNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& 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

View File

@ -0,0 +1,42 @@
#include "CSelectNodeCommand.h"
#include "../CWorldEditor.h"
CSelectNodeCommand::CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& 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();
}

View File

@ -0,0 +1,19 @@
#ifndef CSELECTNODECOMMAND_H
#define CSELECTNODECOMMAND_H
#include <QUndoCommand>
#include "../INodeEditor.h"
#include <Scene/CSceneNode.h>
class CSelectNodeCommand : public QUndoCommand
{
INodeEditor *mpEditor;
CSceneNode *mpNode;
QList<CSceneNode*> *mpSelection;
public:
CSelectNodeCommand(INodeEditor *pEditor, CSceneNode *pNode, QList<CSceneNode*>& selection);
void undo();
void redo();
};
#endif // CSELECTNODECOMMAND_H

View File

@ -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<CSceneNode*>& 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<const CTranslateNodeCommand*>(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;
}

View File

@ -0,0 +1,32 @@
#ifndef CTRANSLATENODECOMMAND_H
#define CTRANSLATENODECOMMAND_H
#include <QUndoCommand>
#include <QList>
#include <Scene/CSceneNode.h>
#include "../INodeEditor.h"
class CTranslateNodeCommand : public QUndoCommand
{
struct SNodeTranslate
{
CSceneNode *pNode;
CVector3f initialPos;
CVector3f newPos;
};
QList<SNodeTranslate> mNodeList;
INodeEditor *mpEditor;
bool mCommandEnded;
public:
CTranslateNodeCommand();
CTranslateNodeCommand(INodeEditor *pEditor, const QList<CSceneNode*>& 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

12
UI/undo/EUndoCommand.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef EUNDOCOMMAND
#define EUNDOCOMMAND
enum EUndoCommand
{
eTranslateNodeCmd,
eRotateNodeCmd,
eScaleNodeCmd
};
#endif // EUNDOCOMMAND

13
UI/undo/UndoCommands.h Normal file
View File

@ -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