diff --git a/Common/CVector3f.cpp b/Common/CVector3f.cpp index 381aa7f0..8104000f 100644 --- a/Common/CVector3f.cpp +++ b/Common/CVector3f.cpp @@ -279,6 +279,12 @@ const float& CVector3f::operator[](long index) const const CVector3f CVector3f::skZero = CVector3f(0.f); const CVector3f CVector3f::skOne = CVector3f(1.f); const CVector3f CVector3f::skInfinite = CVector3f(FLT_MAX); +const CVector3f CVector3f::skForward = CVector3f(0.f, 1.f, 0.f); +const CVector3f CVector3f::skBack = CVector3f(0.f, -1.f, 0.f); +const CVector3f CVector3f::skRight = CVector3f( 1.f, 0.f, 0.f); +const CVector3f CVector3f::skLeft = CVector3f(-1.f, 0.f, 0.f); +const CVector3f CVector3f::skUp = CVector3f(0.f, 0.f, 1.f); +const CVector3f CVector3f::skDown = CVector3f(0.f, 0.f, -1.f); // ************ OTHER ************ std::ostream& operator<<(std::ostream& o, const CVector3f& Vector) diff --git a/Common/CVector3f.h b/Common/CVector3f.h index 9b81c511..565fb07b 100644 --- a/Common/CVector3f.h +++ b/Common/CVector3f.h @@ -75,6 +75,12 @@ public: static const CVector3f skZero; static const CVector3f skOne; static const CVector3f skInfinite; + static const CVector3f skForward; + static const CVector3f skBack; + static const CVector3f skRight; + static const CVector3f skLeft; + static const CVector3f skUp; + static const CVector3f skDown; // Other friend std::ostream& operator<<(std::ostream& o, const CVector3f& Vector); diff --git a/Core/CRenderer.cpp b/Core/CRenderer.cpp index 58d9e6b6..be0e8c49 100644 --- a/Core/CRenderer.cpp +++ b/Core/CRenderer.cpp @@ -125,6 +125,7 @@ void CRenderer::SetViewportSize(u32 Width, u32 Height) void CRenderer::RenderBuckets(CCamera& Camera) { if (!mInitialized) Init(); + mSceneFramebuffer.Bind(); // Set backface culling if (mOptions & eEnableBackfaceCull) glEnable(GL_CULL_FACE); @@ -141,6 +142,7 @@ void CRenderer::RenderBuckets(CCamera& Camera) mTransparentBucket.Clear(); // Clear depth buffer to enable more rendering passes + glDepthMask(GL_TRUE); glClear(GL_DEPTH_BUFFER_BIT); } @@ -172,6 +174,7 @@ void CRenderer::RenderBloom() glViewport(0, 0, BloomWidth, BloomHeight); glClearColor(0.f, 0.f, 0.f, 0.f); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_FALSE); CGraphics::SetIdentityMVP(); CGraphics::UpdateMVPBlock(); @@ -308,10 +311,6 @@ void CRenderer::BeginFrame() void CRenderer::EndFrame() { - // Post-processing - if (mBloomMode != eNoBloom) - RenderBloom(); - // Render result to screen glBindFramebuffer(GL_FRAMEBUFFER, mDefaultFramebuffer); InitFramebuffer(); diff --git a/Core/IRenderable.h b/Core/IRenderable.h index c77e933f..814efe25 100644 --- a/Core/IRenderable.h +++ b/Core/IRenderable.h @@ -13,8 +13,8 @@ public: IRenderable() {} virtual ~IRenderable() {} virtual void AddToRenderer(CRenderer *pRenderer) = 0; - virtual void Draw(ERenderOptions options) = 0; - virtual void DrawAsset(ERenderOptions, u32) {} + virtual void Draw(ERenderOptions options) {} + virtual void DrawAsset(ERenderOptions options, u32 asset) {} virtual void DrawSelection() {} }; diff --git a/EditorAssets/SelectMode.png b/EditorAssets/SelectMode.png new file mode 100644 index 00000000..2997b2c9 Binary files /dev/null and b/EditorAssets/SelectMode.png differ diff --git a/Icons.qrc b/Icons.qrc index 6d32fc62..ec4384db 100644 --- a/Icons.qrc +++ b/Icons.qrc @@ -27,5 +27,6 @@ EditorAssets/Modify.png EditorAssets/Unlink.png EditorAssets/World.png + EditorAssets/SelectMode.png diff --git a/UI/CGizmo.cpp b/UI/CGizmo.cpp new file mode 100644 index 00000000..4f8f5e9a --- /dev/null +++ b/UI/CGizmo.cpp @@ -0,0 +1,221 @@ +#include "CGizmo.h" +#include +#include + +CGizmo::CGizmo() +{ + LoadModels(); + + mMode = eRotate; + mSelectedAxes = eNone; + mGizmoSize = 1.f; + mCameraDist = 0.f; + + mPosition = CVector3f::skZero; + mRotation = CQuaternion::skIdentity; + mScale = CVector3f::skOne; + mDeltaPosition = CVector3f::skZero; + mDeltaRotation = CQuaternion::skIdentity; + mDeltaScale = CVector3f::skOne; + mFlipScaleX = false; + mFlipScaleY = false; + mFlipScaleZ = false; +} + +CGizmo::~CGizmo() +{ +} + +void CGizmo::AddToRenderer(CRenderer *pRenderer) +{ + // Transform is updated every frame even if the user doesn't modify the gizmo + // in order to account for scale changes based on camera distance + UpdateTransform(); + + // Determine which SModelPart array to use + SModelPart *pParts; + u32 numParts; + + if (mMode == eTranslate) { + pParts = smTranslateModels; + numParts = 6; + } else if (mMode == eRotate) { + pParts = smRotateModels; + numParts = 4; + } else if (mMode == eScale) { + pParts = smScaleModels; + numParts = 7; + } + + // Add all parts to renderer + for (u32 iPart = 0; iPart < numParts; iPart++) + { + CModel *pModel = pParts->pModel; + + // Determine whether to use the mat set for regular (0) or highlight (1) + bool isHighlighted = (mSelectedAxes & pParts->modelAxes) == pParts->modelAxes; + u32 setID = (isHighlighted ? 1 : 0); + + // Add to renderer... + if (pModel->HasTransparency(setID)) + pRenderer->AddTransparentMesh(this, iPart, pModel->AABox().Transformed(mTransform), eDrawAsset); + else + pRenderer->AddOpaqueMesh(this, iPart, pModel->AABox().Transformed(mTransform), eDrawAsset); + + pParts++; + } +} + +void CGizmo::DrawAsset(ERenderOptions options, u32 asset) +{ + CGraphics::sMVPBlock.ModelMatrix = mTransform.ToMatrix4f(); + CGraphics::UpdateMVPBlock(); + + // Determine which SModelPart array to use + SModelPart *pParts; + u32 numParts; + + if (mMode == eTranslate) { + pParts = smTranslateModels; + numParts = 6; + } else if (mMode == eRotate) { + pParts = smRotateModels; + numParts = 4; + } else if (mMode == eScale) { + pParts = smScaleModels; + numParts = 7; + } + + if (asset >= numParts) return; + + // Draw model + bool isHighlighted = (mSelectedAxes & pParts[asset].modelAxes) == pParts[asset].modelAxes; + u32 setID = (isHighlighted ? 1 : 0); + pParts[asset].pModel->Draw((ERenderOptions) 0, setID); +} + +void CGizmo::DrawRotationOutline() +{ + CGraphics::sMVPBlock.ModelMatrix = mBillboardTransform.ToMatrix4f(); + CGraphics::UpdateMVPBlock(); + smRotateClipOutline.pModel->Draw((ERenderOptions) 0, 0); +} + +void CGizmo::IncrementSize() +{ + static const float skIncAmount = 1.3f; + static const float skMaxSize = powf(skIncAmount, 4); + + mGizmoSize *= skIncAmount; + if (mGizmoSize > skMaxSize) mGizmoSize = skMaxSize; +} + +void CGizmo::DecrementSize() +{ + static const float skDecAmount = (1.f / 1.3f); + static const float skMinSize = powf(skDecAmount, 4); + + mGizmoSize *= skDecAmount; + if (mGizmoSize < skMinSize) mGizmoSize = skMinSize; +} + +void CGizmo::UpdateForCamera(const CCamera &camera) +{ + CVector3f camPos = camera.Position(); + mCameraDist = mPosition.Distance(camPos); + mFlipScaleX = camPos.x < mPosition.x; + mFlipScaleY = camPos.y < mPosition.y; + mFlipScaleZ = camPos.z < mPosition.z; + + // todo: make this cleaner... + CVector3f billDir = (mPosition - camPos).Normalized(); + CVector3f axis = CVector3f::skForward.Cross(billDir); + float angle = acos(CVector3f::skForward.Dot(billDir)); + angle = 180 + (angle * 180 / 3.14159265358979323846f); + mBillboardRotation = CQuaternion::FromAxisAngle(angle, axis); +} + +CGizmo::EGizmoMode CGizmo::Mode() +{ + return mMode; +} + +void CGizmo::SetMode(EGizmoMode mode) +{ + mMode = mode; +} + +void CGizmo::SetPosition(const CVector3f& position) +{ + mPosition = position; +} + +// ************ PRIVATE STATIC ************ +void CGizmo::LoadModels() +{ + if (!smModelsLoaded) + { + smTranslateModels[CGIZMO_TRANSLATE_X] = SModelPart(eX, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoX.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_Y] = SModelPart(eY, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoY.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_Z] = SModelPart(eZ, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_XY] = SModelPart(eXY, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoXY.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_XZ] = SModelPart(eXZ, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoXZ.CMDL")); + smTranslateModels[CGIZMO_TRANSLATE_YZ] = SModelPart(eYZ, (CModel*) gResCache.GetResource("../resources/editor/TranslateGizmoYZ.CMDL")); + + smRotateModels[CGIZMO_ROTATE_X] = SModelPart(eX, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoX.CMDL")); + smRotateModels[CGIZMO_ROTATE_Y] = SModelPart(eY, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoY.CMDL")); + smRotateModels[CGIZMO_ROTATE_Z] = SModelPart(eZ, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoZ.CMDL")); + smRotateModels[CGIZMO_ROTATE_XYZ] = SModelPart(eXYZ, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoXYZ.CMDL")); + smRotateClipOutline = SModelPart(eNone, (CModel*) gResCache.GetResource("../resources/editor/RotateGizmoClipOutline.CMDL")); + + smScaleModels[CGIZMO_SCALE_X] = SModelPart(eX, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoX.CMDL")); + smScaleModels[CGIZMO_SCALE_Y] = SModelPart(eY, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoY.CMDL")); + smScaleModels[CGIZMO_SCALE_Z] = SModelPart(eZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoZ.CMDL")); + smScaleModels[CGIZMO_SCALE_XY] = SModelPart(eXY, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoXY.CMDL")); + smScaleModels[CGIZMO_SCALE_XZ] = SModelPart(eXZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoXZ.CMDL")); + smScaleModels[CGIZMO_SCALE_YZ] = SModelPart(eYZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoYZ.CMDL")); + smScaleModels[CGIZMO_SCALE_XYZ] = SModelPart(eXYZ, (CModel*) gResCache.GetResource("../resources/editor/ScaleGizmoXYZ.CMDL")); + + smModelsLoaded = true; + } +} + +// ************ PROTECTED ************ +void CGizmo::UpdateTransform() +{ + // Scale is recalculated every frame because it changes frequently, based on camera distance + // Rotation and position values are just saved directly + mScale = mGizmoSize * (mCameraDist / 10.f); + + // Scale also factors in delta scale + axis flip if mode is Scale. + if (mMode == eScale) + { + mScale *= mDeltaScale; + + if (mFlipScaleX) mScale.x = -mScale.x; + if (mFlipScaleY) mScale.y = -mScale.y; + if (mFlipScaleZ) mScale.z = -mScale.z; + } + + // Create transform + mTransform = CTransform4f::skIdentity; + mTransform.Scale(mScale); + mTransform.Rotate(mRotation); + mTransform.Translate(mPosition); + + // Create billboard transform for rotation gizmo + if (mMode == eRotate) + { + mBillboardTransform = CTransform4f::skIdentity; + mBillboardTransform.Scale(mScale); + mBillboardTransform.Rotate(mBillboardRotation); + mBillboardTransform.Translate(mPosition); + } +} + +// ************ STATIC MEMBER INITIALIZATION ************ +bool CGizmo::smModelsLoaded = false; +CGizmo::SModelPart CGizmo::smTranslateModels[6]; +CGizmo::SModelPart CGizmo::smRotateModels[4]; +CGizmo::SModelPart CGizmo::smScaleModels[7]; +CGizmo::SModelPart CGizmo::smRotateClipOutline; diff --git a/UI/CGizmo.h b/UI/CGizmo.h new file mode 100644 index 00000000..a1acd33d --- /dev/null +++ b/UI/CGizmo.h @@ -0,0 +1,113 @@ +#ifndef CGIZMO_H +#define CGIZMO_H + +#include +#include +#include +#include +#include +#include +#include + +#define CGIZMO_TRANSLATE_X 0 +#define CGIZMO_TRANSLATE_Y 1 +#define CGIZMO_TRANSLATE_Z 2 +#define CGIZMO_TRANSLATE_XY 3 +#define CGIZMO_TRANSLATE_XZ 4 +#define CGIZMO_TRANSLATE_YZ 5 +#define CGIZMO_ROTATE_X 0 +#define CGIZMO_ROTATE_Y 1 +#define CGIZMO_ROTATE_Z 2 +#define CGIZMO_ROTATE_XYZ 3 +#define CGIZMO_SCALE_X 0 +#define CGIZMO_SCALE_Y 1 +#define CGIZMO_SCALE_Z 2 +#define CGIZMO_SCALE_XY 3 +#define CGIZMO_SCALE_XZ 4 +#define CGIZMO_SCALE_YZ 5 +#define CGIZMO_SCALE_XYZ 6 + +class CGizmo : public IRenderable +{ +public: + enum EGizmoMode + { + eTranslate, eRotate, eScale + }; + + enum EGizmoAxes + { + eNone = 0x0, + eX = 0x1, + eY = 0x2, + eZ = 0x4, + eXY = eX | eY, + eXZ = eX | eZ, + eYZ = eY | eZ, + eXYZ = eX | eY | eZ + }; + +private: + EGizmoMode mMode; + EGizmoAxes mSelectedAxes; + CTransform4f mBillboardTransform; + CQuaternion mBillboardRotation; + float mGizmoSize; + float mCameraDist; + + CTransform4f mTransform; + CVector3f mPosition; + CQuaternion mRotation; + CVector3f mScale; + CVector3f mDeltaPosition; + CQuaternion mDeltaRotation; + CVector3f mDeltaScale; + bool mFlipScaleX; + bool mFlipScaleY; + bool mFlipScaleZ; + + // Static + struct SModelPart + { + EGizmoAxes modelAxes; + CModel *pModel; + CToken modelToken; + + SModelPart() {} + SModelPart(EGizmoAxes axes, CModel *_pModel) : + modelAxes(axes), pModel(_pModel), modelToken(_pModel) {} + }; + + static bool smModelsLoaded; + static SModelPart smTranslateModels[6]; + static SModelPart smRotateModels[4]; + static SModelPart smScaleModels[7]; + static SModelPart smRotateClipOutline; + +public: + CGizmo(); + ~CGizmo(); + void AddToRenderer(CRenderer *pRenderer); + void DrawAsset(ERenderOptions options, u32 asset); + void DrawRotationOutline(); + + void IncrementSize(); + void DecrementSize(); + void UpdateForCamera(const CCamera& camera); + bool IntersectsRay(const CRay& ray); + + EGizmoMode Mode(); + void SetMode(EGizmoMode mode); + void SetPosition(const CVector3f& position); + + // Protected +protected: + void UpdateTransform(); + + // Private Static +private: + static void LoadModels(); +}; +DEFINE_ENUM_FLAGS(CGizmo::EGizmoAxes) + +#endif // CGIZMO_H diff --git a/UI/CWorldEditor.cpp b/UI/CWorldEditor.cpp index 3b6e7c2d..9abb5b6a 100644 --- a/UI/CWorldEditor.cpp +++ b/UI/CWorldEditor.cpp @@ -31,6 +31,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) : mpWorld = nullptr; mpHoverNode = nullptr; mDrawSky = true; + mShowGizmo = false; mFrameCount = 0; mFPSTimer.Start(); @@ -54,6 +55,10 @@ CWorldEditor::CWorldEditor(QWidget *parent) : ui->CamSpeedSpinBox->SetDefaultValue(1.0); ResetHover(); + // Initialize offscreen actions + addAction(ui->ActionIncrementGizmo); + addAction(ui->ActionDecrementGizmo); + // Connect signals and slots connect(ui->CamSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnCameraSpeedChange(double))); connect(ui->MainViewport, SIGNAL(PreRender()), this, SLOT(ViewportPreRender())); @@ -283,6 +288,19 @@ void CWorldEditor::ViewportRender(CCamera& Camera) mpRenderer->RenderBuckets(Camera); mpRenderer->RenderBloom(); + + if (mShowGizmo && (mSelectedNodes.size() > 0)) + { + Camera.LoadMatrices(); + mGizmo.UpdateForCamera(Camera); + mGizmo.AddToRenderer(mpRenderer); + + if (mGizmo.Mode() == CGizmo::eRotate) + mGizmo.DrawRotationOutline(); + + mpRenderer->RenderBuckets(Camera); + } + mpRenderer->EndFrame(); mFrameTimer.Stop(); mFrameCount++; @@ -373,6 +391,9 @@ void CWorldEditor::UpdateSelectionUI() ui->XSpinBox->setValue(pos.x); ui->YSpinBox->setValue(pos.y); ui->ZSpinBox->setValue(pos.z); + + // Update gizmo + mGizmo.SetPosition(pos); } // ************ ACTIONS ************ @@ -522,3 +543,52 @@ void CWorldEditor::on_ActionEditLayers_triggered() Editor.SetArea(mpArea); Editor.exec(); } + +void CWorldEditor::on_ActionSelectObjects_triggered() +{ + mShowGizmo = false; + ui->ActionSelectObjects->setChecked(true); + ui->ActionTranslate->setChecked(false); + ui->ActionRotate->setChecked(false); + ui->ActionScale->setChecked(false); +} + +void CWorldEditor::on_ActionTranslate_triggered() +{ + mShowGizmo = true; + mGizmo.SetMode(CGizmo::eTranslate); + ui->ActionSelectObjects->setChecked(false); + ui->ActionTranslate->setChecked(true); + ui->ActionRotate->setChecked(false); + ui->ActionScale->setChecked(false); +} + +void CWorldEditor::on_ActionRotate_triggered() +{ + mShowGizmo = true; + mGizmo.SetMode(CGizmo::eRotate); + ui->ActionSelectObjects->setChecked(false); + ui->ActionTranslate->setChecked(false); + ui->ActionRotate->setChecked(true); + ui->ActionScale->setChecked(false); +} + +void CWorldEditor::on_ActionScale_triggered() +{ + mShowGizmo = true; + mGizmo.SetMode(CGizmo::eScale); + ui->ActionSelectObjects->setChecked(false); + ui->ActionTranslate->setChecked(false); + ui->ActionRotate->setChecked(false); + ui->ActionScale->setChecked(true); +} + +void CWorldEditor::on_ActionIncrementGizmo_triggered() +{ + mGizmo.IncrementSize(); +} + +void CWorldEditor::on_ActionDecrementGizmo_triggered() +{ + mGizmo.DecrementSize(); +} diff --git a/UI/CWorldEditor.h b/UI/CWorldEditor.h index 717d742b..4b26b0db 100644 --- a/UI/CWorldEditor.h +++ b/UI/CWorldEditor.h @@ -4,6 +4,7 @@ #include #include +#include "CGizmo.h" #include #include #include @@ -23,6 +24,7 @@ class CWorldEditor : public QMainWindow Q_OBJECT CRenderer *mpRenderer; CSceneManager *mpSceneManager; + CGizmo mGizmo; CCamera mCamera; CGameArea *mpArea; CWorld *mpWorld; @@ -30,6 +32,7 @@ class CWorldEditor : public QMainWindow CToken mWorldToken; CTimer mFrameTimer; bool mDrawSky; + bool mShowGizmo; CVector3f mHoverPoint; CSceneNode *mpHoverNode; @@ -91,6 +94,12 @@ 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(); }; #endif // CWORLDEDITOR_H diff --git a/UI/CWorldEditor.ui b/UI/CWorldEditor.ui index 3a5c92f1..ac3ecab7 100644 --- a/UI/CWorldEditor.ui +++ b/UI/CWorldEditor.ui @@ -441,6 +441,9 @@ + + Qt::NoContextMenu + toolBar_2 @@ -591,6 +594,9 @@ + + Qt::NoContextMenu + toolBar @@ -603,6 +609,7 @@ + @@ -631,6 +638,9 @@ Translate + + Ctrl+W + @@ -646,6 +656,9 @@ Rotate + + Ctrl+E + @@ -661,6 +674,9 @@ Scale + + Ctrl+R + @@ -871,6 +887,55 @@ Fake Bloom + + + true + + + true + + + + :/icons/EditorAssets/SelectMode.png:/icons/EditorAssets/SelectMode.png + + + Select Objects + + + Select Objects + + + Ctrl+Q + + + + + Increment Gizmo Size + + + Increment Gizmo Size + + + = + + + Qt::WindowShortcut + + + + + Decrement Gizmo Size + + + Decrement Gizmo Size + + + - + + + Qt::WindowShortcut + + @@ -879,6 +944,11 @@
CEditorGLWidget.h
1
+ + WDraggableSpinBox + QDoubleSpinBox +
WDraggableSpinBox.h
+
WModifyTab QWidget @@ -891,11 +961,6 @@
WorldEditor/WInstancesTab.h
1
- - WDraggableSpinBox - QDoubleSpinBox -
WDraggableSpinBox.h
-
XSpinBox