Translation gizmo transform functionality implemented

This commit is contained in:
parax0 2015-08-19 21:01:58 -04:00
parent 08dbdb337a
commit 63c8351dcf
23 changed files with 567 additions and 107 deletions

44
Common/CPlane.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "CPlane.h"
CPlane::CPlane()
{
mNormal = CVector3f::skUp;
mDist = 0.f;
}
CPlane::CPlane(const CVector3f& normal, float dist)
{
mNormal = normal;
mDist = dist;
}
CPlane::CPlane(const CVector3f& normal, const CVector3f& origin)
{
Redefine(normal, origin);
}
void CPlane::Redefine(const CVector3f& normal, const CVector3f& origin)
{
mNormal = normal;
mDist = -normal.Dot(origin);
}
CVector3f CPlane::Normal() const
{
return mNormal;
}
float CPlane::Dist() const
{
return mDist;
}
void CPlane::SetNormal(const CVector3f& normal)
{
mNormal = normal;
}
void CPlane::SetDist(float dist)
{
mDist = dist;
}

23
Common/CPlane.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef CPLANE_H
#define CPLANE_H
#include "CVector3f.h"
class CPlane
{
CVector3f mNormal;
float mDist;
public:
CPlane();
CPlane(const CVector3f& normal, float dist);
CPlane(const CVector3f& normal, const CVector3f& origin);
void Redefine(const CVector3f& normal, const CVector3f& origin);
CVector3f Normal() const;
float Dist() const;
void SetNormal(const CVector3f& normal);
void SetDist(float dist);
};
#endif // CPLANE_H

View File

@ -18,6 +18,47 @@ CQuaternion::CQuaternion(float _x, float _y, float _z, float _w)
w = _w;
}
CVector3f CQuaternion::XAxis()
{
return (*this * CVector3f::skUnitX);
}
CVector3f CQuaternion::YAxis()
{
return (*this * CVector3f::skUnitY);
}
CVector3f CQuaternion::ZAxis()
{
return (*this * CVector3f::skUnitZ);
}
CQuaternion CQuaternion::Inverse()
{
float fNorm = (w * w) + (x * x) + (y * y) + (z * z);
if (fNorm > 0.f)
{
float fInvNorm = 1.f / fNorm;
return CQuaternion(-x * fInvNorm, -y * fInvNorm, -z * fInvNorm, w * fInvNorm);
}
else
return CQuaternion::skZero;
}
// ************ OPERATORS ************
CVector3f CQuaternion::operator*(const CVector3f& vec) const
{
CVector3f uv, uuv;
CVector3f qvec(x, y, z);
uv = qvec.Cross(vec);
uuv = qvec.Cross(uv);
uv *= (2.0f * w);
uuv *= 2.0f;
return vec + uv + uuv;
}
CQuaternion CQuaternion::operator*(const CQuaternion& other) const
{
CQuaternion out;
@ -84,3 +125,4 @@ CQuaternion CQuaternion::FromAxisAngle(float angle, CVector3f axis)
}
CQuaternion CQuaternion::skIdentity = CQuaternion(0.f, 0.f, 0.f, 1.f);
CQuaternion CQuaternion::skZero = CQuaternion(0.f, 0.f, 0.f, 0.f);

View File

@ -11,7 +11,13 @@ public:
CQuaternion();
CQuaternion(float _x, float _y, float _z, float _w);
CVector3f XAxis();
CVector3f YAxis();
CVector3f ZAxis();
CQuaternion Inverse();
// Operators
CVector3f operator*(const CVector3f& vec) const;
CQuaternion operator*(const CQuaternion& other) const;
void operator *= (const CQuaternion& other);
@ -20,6 +26,7 @@ public:
static CQuaternion FromAxisAngle(float angle, CVector3f axis);
static CQuaternion skIdentity;
static CQuaternion skZero;
};
#endif // CQUATERNION_H

View File

@ -279,12 +279,15 @@ 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);
const CVector3f CVector3f::skUnitX = CVector3f(1.f, 0.f, 0.f);
const CVector3f CVector3f::skUnitY = CVector3f(0.f, 1.f, 0.f);
const CVector3f CVector3f::skUnitZ = CVector3f(0.f, 0.f, 1.f);
const CVector3f CVector3f::skRight = CVector3f::skUnitX;
const CVector3f CVector3f::skLeft = -CVector3f::skUnitX;
const CVector3f CVector3f::skForward = CVector3f::skUnitY;
const CVector3f CVector3f::skBack = -CVector3f::skUnitY;
const CVector3f CVector3f::skUp = CVector3f::skUnitZ;
const CVector3f CVector3f::skDown = -CVector3f::skUnitZ;
// ************ OTHER ************
std::ostream& operator<<(std::ostream& o, const CVector3f& Vector)

View File

@ -75,10 +75,13 @@ public:
static const CVector3f skZero;
static const CVector3f skOne;
static const CVector3f skInfinite;
static const CVector3f skForward;
static const CVector3f skBack;
static const CVector3f skUnitX;
static const CVector3f skUnitY;
static const CVector3f skUnitZ;
static const CVector3f skRight;
static const CVector3f skLeft;
static const CVector3f skForward;
static const CVector3f skBack;
static const CVector3f skUp;
static const CVector3f skDown;

11
Common/ETransformSpace.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef ETRANSFORMSPACE
#define ETRANSFORMSPACE
enum ETransformSpace
{
eWorldTransform,
eLocalTransform
};
#endif // ETRANSFORMSPACE

View File

@ -3,6 +3,11 @@
namespace Math
{
float Abs(float v)
{
return fabs(v);
}
float Pow(float Base, float Exponent)
{
return pow(Base, Exponent);
@ -15,6 +20,23 @@ float Distance(const CVector3f& A, const CVector3f& B)
Pow(B.z - A.z, 2.f) );
}
std::pair<bool,float> RayPlaneIntersecton(const CRay& ray, const CPlane& plane)
{
// Code based on ray/plane intersect code from Ogre
// https://bitbucket.org/sinbad/ogre/src/197116fd2ac62c57cdeed1666f9866c3dddd4289/OgreMain/src/OgreMath.cpp?at=default#OgreMath.cpp-350
// Are ray and plane parallel?
float denom = plane.Normal().Dot(ray.Direction());
if (Abs(denom) < FLT_EPSILON)
return std::pair<bool,float>(false, 0.f);
// Not parallel
float nom = plane.Normal().Dot(ray.Origin()) + plane.Dist();
float t = -(nom / denom);
return std::pair<bool,float>(t >= 0.f, t);
}
std::pair<bool,float> RayBoxIntersection(const CRay& Ray, const CAABox& Box)
{
// Code slightly modified from Ogre

View File

@ -3,6 +3,7 @@
#include "CAABox.h"
#include "CRay.h"
#include "CPlane.h"
#include "CVector3f.h"
#include "SRayIntersection.h"
#include <utility>
@ -10,10 +11,14 @@
namespace Math
{
float Abs(float v);
float Pow(float Base, float Exponent);
float Distance(const CVector3f& A, const CVector3f& B);
std::pair<bool,float> RayPlaneIntersecton(const CRay& ray, const CPlane& plane);
std::pair<bool,float> RayBoxIntersection(const CRay& Ray, const CAABox& Box);
std::pair<bool,float> RayLineIntersection(const CRay& ray, const CVector3f& pointA,

View File

@ -140,10 +140,6 @@ void CRenderer::RenderBuckets(CCamera& Camera)
mTransparentBucket.Sort(Camera);
mTransparentBucket.Draw(mOptions);
mTransparentBucket.Clear();
// Clear depth buffer to enable more rendering passes
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
}
void CRenderer::RenderBloom()
@ -331,6 +327,12 @@ void CRenderer::EndFrame()
gDrawCount = 0;
}
void CRenderer::ClearDepthBuffer()
{
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
}
// ************ PRIVATE ************
void CRenderer::InitFramebuffer()
{

View File

@ -72,6 +72,7 @@ public:
void AddTransparentMesh(IRenderable *pRenderable, u32 AssetID, CAABox& AABox, ERenderCommand Command);
void BeginFrame();
void EndFrame();
void ClearDepthBuffer();
// Private
private:

View File

@ -157,7 +157,6 @@ void CSceneManager::SetActiveArea(CGameArea* _area)
{
CScriptObject *pObj = pGenLayer->ObjectByIndex(o);
CScriptNode *Node = AddScriptObject(pObj);
Node->BuildLightList(mpArea);
// Add to map
mScriptNodeMap[pObj->InstanceID()] = Node;
@ -165,9 +164,12 @@ void CSceneManager::SetActiveArea(CGameArea* _area)
}
PickEnvironmentObjects();
// Ensure script nodes have valid positions
// Ensure script nodes have valid positions + build light lists
for (auto it = mScriptNodeMap.begin(); it != mScriptNodeMap.end(); it++)
{
it->second->GeneratePosition();
it->second->BuildLightList(mpArea);
}
u32 NumLightLayers = mpArea->GetLightLayerCount();
CGraphics::sAreaAmbientColor = CColor::skBlack;

View File

@ -134,7 +134,8 @@ SOURCES += \
UI/WScanPreviewPanel.cpp \
UI/WIntegralSpinBox.cpp \
UI/CAboutDialog.cpp \
UI/CGizmo.cpp
UI/CGizmo.cpp \
Common/CPlane.cpp
HEADERS += \
Common/AnimUtil.h \
@ -283,7 +284,9 @@ HEADERS += \
UI/CAboutDialog.h \
UI/CGizmo.h \
Core/IRenderable.h \
Core/SRenderablePtr.h
Core/SRenderablePtr.h \
Common/ETransformSpace.h \
Common/CPlane.h
FORMS += \
UI/CWorldEditorWindow.ui \

View File

@ -185,15 +185,29 @@ void CSceneNode::DrawBoundingBox()
}
// ************ TRANSFORM ************
void CSceneNode::Translate(const CVector3f& Translation)
void CSceneNode::Translate(const CVector3f& translation, ETransformSpace transformSpace)
{
mPosition += Translation;
switch (transformSpace)
{
case eWorldTransform:
mPosition += translation;
break;
case eLocalTransform:
mPosition += mRotation * translation;
break;
}
MarkTransformChanged();
}
void CSceneNode::Scale(const CVector3f& Scale)
void CSceneNode::Rotate(const CQuaternion& rotation, ETransformSpace transformSpace)
{
mScale *= Scale;
mRotation *= rotation;
MarkTransformChanged();
}
void CSceneNode::Scale(const CVector3f& scale, ETransformSpace transformSpace)
{
mScale *= scale;
MarkTransformChanged();
}
@ -209,9 +223,9 @@ void CSceneNode::UpdateTransform()
void CSceneNode::ForceRecalculateTransform()
{
_mCachedTransform = CTransform4f::skIdentity;
_mCachedTransform.Scale(GetAbsoluteScale());
_mCachedTransform.Rotate(GetAbsoluteRotation());
_mCachedTransform.Translate(GetAbsolutePosition());
_mCachedTransform.Scale(AbsoluteScale());
_mCachedTransform.Rotate(AbsoluteRotation());
_mCachedTransform.Translate(AbsolutePosition());
_mCachedAABox = mLocalAABox.Transformed(_mCachedTransform);
// Sync with children - only needed if caller hasn't marked transform changed already
@ -259,47 +273,47 @@ CSceneManager* CSceneNode::Scene()
return mpScene;
}
CVector3f CSceneNode::GetPosition() const
CVector3f CSceneNode::LocalPosition() const
{
return mPosition;
}
CVector3f CSceneNode::GetAbsolutePosition() const
CVector3f CSceneNode::AbsolutePosition() const
{
CVector3f ret = mPosition;
if ((mpParent) && (InheritsPosition()))
ret += mpParent->GetAbsolutePosition();
ret += mpParent->AbsolutePosition();
return ret;
}
CQuaternion CSceneNode::GetRotation() const
CQuaternion CSceneNode::LocalRotation() const
{
return mRotation;
}
CQuaternion CSceneNode::GetAbsoluteRotation() const
CQuaternion CSceneNode::AbsoluteRotation() const
{
CQuaternion ret = mRotation;
if ((mpParent) && (InheritsRotation()))
ret *= mpParent->GetAbsoluteRotation();
ret *= mpParent->AbsoluteRotation();
return ret;
}
CVector3f CSceneNode::GetScale() const
CVector3f CSceneNode::LocalScale() const
{
return mScale;
}
CVector3f CSceneNode::GetAbsoluteScale() const
CVector3f CSceneNode::AbsoluteScale() const
{
CVector3f ret = mScale;
if ((mpParent) && (InheritsScale()))
ret *= mpParent->GetAbsoluteScale();
ret *= mpParent->AbsoluteScale();
return ret;
}

View File

@ -3,13 +3,14 @@
#include <Core/IRenderable.h>
#include "ENodeType.h"
#include <Common/CVector3f.h>
#include <Common/CQuaternion.h>
#include <Common/CAABox.h>
#include <Common/CQuaternion.h>
#include <Common/CRay.h>
#include <Common/CRayCollisionTester.h>
#include <Common/types.h>
#include <Common/CTransform4f.h>
#include <Common/CVector3f.h>
#include <Common/ETransformSpace.h>
#include <Common/types.h>
#include <Core/ERenderOptions.h>
#include <Resource/CLight.h>
#include <Resource/CGameArea.h>
@ -68,8 +69,9 @@ public:
void DrawBoundingBox();
// Transform
void Translate(const CVector3f& Translation);
void Scale(const CVector3f& Scale);
void Translate(const CVector3f& translation, ETransformSpace transformSpace);
void Rotate(const CQuaternion& rotation, ETransformSpace transformSpace);
void Scale(const CVector3f& scale, ETransformSpace transformSpace);
void UpdateTransform();
void ForceRecalculateTransform();
void MarkTransformChanged();
@ -79,12 +81,12 @@ public:
std::string Name() const;
CSceneNode* Parent() const;
CSceneManager* Scene();
CVector3f GetPosition() const;
CVector3f GetAbsolutePosition() const;
CQuaternion GetRotation() const;
CQuaternion GetAbsoluteRotation() const;
CVector3f GetScale() const;
CVector3f GetAbsoluteScale() const;
CVector3f LocalPosition() const;
CVector3f AbsolutePosition() const;
CQuaternion LocalRotation() const;
CQuaternion AbsoluteRotation() const;
CVector3f LocalScale() const;
CVector3f AbsoluteScale() const;
CAABox AABox();
CVector3f CenterPoint();
bool MarkedVisible() const;

View File

@ -45,7 +45,7 @@ CScriptNode::CScriptNode(CSceneManager *pScene, CSceneNode *pParent, CScriptObje
{
mpVolumePreviewNode = new CModelNode(pScene, this, pVolumeModel);
mpVolumePreviewNode->SetInheritance(true, (VolumeShape == 1), false);
mpVolumePreviewNode->Scale(mpInstance->GetVolume());
mpVolumePreviewNode->Scale(mpInstance->GetVolume(), eWorldTransform);
mpVolumePreviewNode->ForceAlphaEnabled(true);
}
}
@ -266,7 +266,7 @@ void CScriptNode::GeneratePosition()
const SLink& link = (mpInstance->NumInLinks() > 0 ? mpInstance->InLink(0) : mpInstance->OutLink(0));
CScriptNode *pNode = mpScene->ScriptNodeByID(link.ObjectID);
pNode->GeneratePosition();
mPosition = pNode->GetAbsolutePosition();
mPosition = pNode->AbsolutePosition();
mPosition.z += (pNode->AABox().Size().z / 2.f);
mPosition.z += (AABox().Size().z / 2.f);
mPosition.z += 2.f;

View File

@ -105,24 +105,31 @@ void CEditorGLWidget::mousePressEvent(QMouseEvent *pEvent)
// 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
else if (pEvent->button() == Qt::LeftButton)
mButtonsPressed |= eLeftButton;
else
{
if (pEvent->button() == Qt::LeftButton)
mButtonsPressed |= eLeftButton;
emit MouseClick(pEvent);
}
mLastMousePos = pEvent->globalPos();
}
void CEditorGLWidget::mouseReleaseEvent(QMouseEvent *pEvent)
{
bool fromMouseInput = IsMouseInputActive();
if (pEvent->button() == Qt::LeftButton) mButtonsPressed &= ~eLeftButton;
if (pEvent->button() == Qt::MidButton) mButtonsPressed &= ~eMiddleButton;
if (pEvent->button() == Qt::RightButton) mButtonsPressed &= ~eRightButton;
// Make cursor visible and emit mouse click event if middle/right mouse buttons are both released
// Make cursor visible if needed
if (!IsMouseInputActive())
{
SetCursorVisible(true);
emit MouseClick(pEvent);
}
// Emit mouse release event if we didn't just exit mouse input (or regardless on left click)
if (!fromMouseInput || (pEvent->button() == Qt::LeftButton))
emit MouseRelease(pEvent);
}
void CEditorGLWidget::keyPressEvent(QKeyEvent *pEvent)

View File

@ -54,6 +54,7 @@ signals:
void Render(CCamera& Camera);
void PostRender();
void MouseClick(QMouseEvent *pEvent);
void MouseRelease(QMouseEvent *pEvent);
void MouseDrag(QMouseEvent *pEvent);
private:

View File

@ -2,6 +2,8 @@
#include <Common/Math.h>
#include <Core/CRenderer.h>
#include <Core/CResCache.h>
#include <Core/CDrawUtil.h>
#include <iostream>
CGizmo::CGizmo()
{
@ -15,9 +17,10 @@ CGizmo::CGizmo()
mPosition = CVector3f::skZero;
mRotation = CQuaternion::skIdentity;
mScale = CVector3f::skOne;
mDeltaPosition = CVector3f::skZero;
mDeltaTranslation = CVector3f::skZero;
mDeltaRotation = CQuaternion::skIdentity;
mDeltaScale = CVector3f::skOne;
mSetOffset = false;
mFlipScaleX = false;
mFlipScaleY = false;
mFlipScaleZ = false;
@ -109,11 +112,10 @@ void CGizmo::UpdateForCamera(const CCamera &camera)
mBillboardRotation = CQuaternion::FromAxisAngle(angle, axis);
}
bool CGizmo::IntersectsRay(const CRay &ray)
bool CGizmo::CheckSelectedAxes(const CRay &ray)
{
// todo: fix raycasting for rotate gizmo; currently it can hit the invisible back side of the model
CRay localRay = ray.Transformed(mTransform.Inverse());
float threshold = 0.02f * mGizmoSize * mCameraDist;
// Do raycast on each model
SModelPart *pPart = mpCurrentParts;
@ -126,7 +128,12 @@ bool CGizmo::IntersectsRay(const CRay &ray)
for (u32 iPart = 0; iPart < mNumCurrentParts; iPart++)
{
if (!pPart->enableRayCast) continue;
if (!pPart->enableRayCast)
{
pPart++;
continue;
}
CModel *pModel = pPart->pModel;
// Ray/Model AABox test - allow buffer room because lines are small
@ -141,7 +148,7 @@ bool CGizmo::IntersectsRay(const CRay &ray)
for (u32 iSurf = 0; iSurf < pModel->GetSurfaceCount(); iSurf++)
{
// Skip surface/box check
// Skip surface/box check - since we use lines the boxes might be too small
SSurface *pSurf = pModel->GetSurface(iSurf);
std::pair<bool,float> surfCheck = pSurf->IntersectsRay(localRay, 0.05f);
@ -183,11 +190,129 @@ bool CGizmo::IntersectsRay(const CRay &ray)
return true;
}
u32 CGizmo::NumSelectedAxes()
{
u32 out = 0;
for (u32 iAxis = 1; iAxis < 8; iAxis <<= 1)
if (mSelectedAxes & (EGizmoAxes) iAxis) out++;
return out;
}
void CGizmo::ResetSelectedAxes()
{
mSelectedAxes = eNone;
}
void CGizmo::StartTransform()
{
mSetOffset = false;
mTotalTranslation = CVector3f::skZero;
mTotalRotation = CQuaternion::skIdentity;
mTotalScale = CVector3f::skOne;
}
bool CGizmo::TransformFromInput(const CRay& ray, const CCamera& camera)
{
if (mMode == eTranslate)
{
// Create translate plane
CVector3f axisA, axisB;
u32 numAxes = NumSelectedAxes();
if (numAxes == 1)
{
if (mSelectedAxes & eX) axisB = mRotation.XAxis();
else if (mSelectedAxes & eY) axisB = mRotation.YAxis();
else axisB = mRotation.ZAxis();
CVector3f gizmoToCamera = (mPosition - camera.Position()).Normalized();
axisA = axisB.Cross(gizmoToCamera);
}
else if (numAxes == 2)
{
axisA = (mSelectedAxes & eX ? mRotation.XAxis() : mRotation.YAxis());
axisB = (mSelectedAxes & eZ ? mRotation.ZAxis() : mRotation.YAxis());
}
CVector3f planeNormal = axisA.Cross(axisB);
mTranslatePlane.Redefine(planeNormal, mPosition);
// Do translate
std::pair<bool,float> result = Math::RayPlaneIntersecton(ray, mTranslatePlane);
if (result.first)
{
CVector3f hit = ray.PointOnRay(result.second);
CVector3f localDelta = mRotation.Inverse() * (hit - mPosition);
// Calculate new position
CVector3f newPos = mPosition;
if (mSelectedAxes & eX) newPos += mRotation.XAxis() * localDelta.x;
if (mSelectedAxes & eY) newPos += mRotation.YAxis() * localDelta.y;
if (mSelectedAxes & eZ) newPos += mRotation.ZAxis() * localDelta.z;
// Check relativity of new pos to camera to reduce issue where the gizmo might
// go flying off into the distance if newPosToCamera is parallel to the plane
CVector3f newPosToCamera = (newPos - camera.Position()).Normalized();
float dot = Math::Abs(planeNormal.Dot(newPosToCamera));
if (dot < 0.02f) return false;
// Set offset
if (!mSetOffset)
{
mTranslateOffset = mPosition - newPos;
mDeltaTranslation = CVector3f::skZero;
mSetOffset = true;
return false;
}
// Apply translation
else
{
mDeltaTranslation = mRotation.Inverse() * (newPos - mPosition + mTranslateOffset);
mTotalTranslation += mDeltaTranslation;
mPosition = newPos + mTranslateOffset;
return true;
}
}
else
{
mDeltaTranslation = CVector3f::skZero;
return false;
}
}
return false;
}
void CGizmo::EndTransform()
{
}
CGizmo::EGizmoMode CGizmo::Mode()
{
return mMode;
}
CVector3f CGizmo::Position()
{
return mPosition;
}
CVector3f CGizmo::DeltaTranslation()
{
return mDeltaTranslation;
}
CVector3f CGizmo::TotalTranslation()
{
return mTotalTranslation;
}
void CGizmo::SetMode(EGizmoMode mode)
{
mMode = mode;
@ -197,16 +322,22 @@ void CGizmo::SetMode(EGizmoMode mode)
case eTranslate:
mpCurrentParts = smTranslateModels;
mNumCurrentParts = 9;
mDeltaRotation = CQuaternion::skIdentity;
mDeltaScale = CVector3f::skOne;
break;
case eRotate:
mpCurrentParts = smRotateModels;
mNumCurrentParts = 4;
mDeltaTranslation = CVector3f::skZero;
mDeltaScale = CVector3f::skOne;
break;
case eScale:
mpCurrentParts = smScaleModels;
mNumCurrentParts = 10;
mDeltaTranslation = CVector3f::skZero;
mDeltaRotation = CQuaternion::skIdentity;
break;
}
}
@ -216,9 +347,9 @@ void CGizmo::SetPosition(const CVector3f& position)
mPosition = position;
}
void CGizmo::ResetSelectedAxes()
void CGizmo::SetOrientation(const CQuaternion& orientation)
{
mSelectedAxes = eNone;
mRotation = orientation;
}
// ************ PRIVATE STATIC ************

View File

@ -1,8 +1,9 @@
#ifndef CGIZMO_H
#define CGIZMO_H
#include <Common/CVector3f.h>
#include <Common/CPlane.h>
#include <Common/CQuaternion.h>
#include <Common/CVector3f.h>
#include <Common/EnumUtil.h>
#include <Core/CCamera.h>
#include <Core/CToken.h>
@ -63,15 +64,24 @@ private:
CTransform4f mTransform;
CVector3f mPosition;
CVector3f mDeltaTranslation;
CVector3f mTotalTranslation;
CQuaternion mRotation;
CVector3f mScale;
CVector3f mDeltaPosition;
CQuaternion mDeltaRotation;
CQuaternion mTotalRotation;
CVector3f mScale;
CVector3f mDeltaScale;
CVector3f mTotalScale;
bool mFlipScaleX;
bool mFlipScaleY;
bool mFlipScaleZ;
CPlane mTranslatePlane;
CVector3f mLastTranslatePosition;
CVector3f mTranslateOffset;
bool mSetOffset;
struct SModelPart
{
EGizmoAxes modelAxes;
@ -104,12 +114,20 @@ public:
void IncrementSize();
void DecrementSize();
void UpdateForCamera(const CCamera& camera);
bool IntersectsRay(const CRay& ray);
bool CheckSelectedAxes(const CRay& ray);
u32 NumSelectedAxes();
void ResetSelectedAxes();
void StartTransform();
bool TransformFromInput(const CRay& ray, const CCamera& camera);
void EndTransform();
EGizmoMode Mode();
CVector3f Position();
CVector3f DeltaTranslation();
CVector3f TotalTranslation();
void SetMode(EGizmoMode mode);
void SetPosition(const CVector3f& position);
void ResetSelectedAxes();
void SetOrientation(const CQuaternion& orientation);
// Protected
protected:

View File

@ -6,6 +6,7 @@
#include <iostream>
#include <QOpenGLContext>
#include <QFontMetrics>
#include <QComboBox>
#include <Core/Log.h>
#include "WDraggableSpinBox.h"
@ -32,6 +33,9 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
mpHoverNode = nullptr;
mDrawSky = true;
mShowGizmo = false;
mGizmoHovering = false;
mGizmoTransforming = false;
mUpdateUILater = false;
mFrameCount = 0;
mFPSTimer.Start();
@ -47,7 +51,6 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
delete pOldTitleBar;
// Initialize UI stuff
ui->ModifyTabContents->SetEditor(this);
ui->InstancesTabContents->SetEditor(this, mpSceneManager);
@ -55,17 +58,27 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
ui->CamSpeedSpinBox->SetDefaultValue(1.0);
ResetHover();
mTransformSpace = eWorldTransform;
QComboBox *pTransformCombo = new QComboBox(this);
pTransformCombo->setMinimumWidth(75);
pTransformCombo->addItem("World");
pTransformCombo->addItem("Local");
ui->MainToolBar->insertWidget(0, pTransformCombo);
// Initialize offscreen actions
addAction(ui->ActionIncrementGizmo);
addAction(ui->ActionDecrementGizmo);
// Connect signals and slots
connect(pTransformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(SetTransformSpace(int)));
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()
@ -135,28 +148,58 @@ void CWorldEditor::ViewportRayCast(CRay Ray)
{
if (!ui->MainViewport->IsMouseInputActive())
{
// Gizmo ray check
mGizmoHovering = mGizmo.IntersectsRay(Ray);
// Scene ray check
SRayIntersection Result = mpSceneManager->SceneRayCast(Ray);
if (Result.Hit)
if (!mGizmoTransforming)
{
if (mpHoverNode)
mpHoverNode->SetMouseHovering(false);
// Gizmo hover check
if (mShowGizmo && !mSelectedNodes.empty())
mGizmoHovering = mGizmo.CheckSelectedAxes(Ray);
else
{
mGizmoHovering = false;
mGizmo.ResetSelectedAxes();
}
mpHoverNode = Result.pNode;
mpHoverNode->SetMouseHovering(true);
// Scene ray check
SRayIntersection Result = mpSceneManager->SceneRayCast(Ray);
mHoverPoint = Ray.PointOnRay(Result.Distance);
if (Result.Hit)
{
if (mpHoverNode)
mpHoverNode->SetMouseHovering(false);
mpHoverNode = Result.pNode;
mpHoverNode->SetMouseHovering(true);
mHoverPoint = Ray.PointOnRay(Result.Distance);
}
else
ResetHover();
}
else
ResetHover();
{
bool moved = mGizmo.TransformFromInput(Ray, ui->MainViewport->Camera());
if (moved)
{
CVector3f delta = mGizmo.DeltaTranslation();
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
{
(*it)->Translate(delta, mTransformSpace);
(*it)->BuildLightList(this->mpArea);
}
RecalculateSelectionBounds();
mUpdateUILater = true;
}
}
}
else
{
mGizmo.ResetSelectedAxes();
if (!mGizmoTransforming)
{
mGizmoHovering = false;
mGizmo.ResetSelectedAxes();
}
ResetHover();
}
}
@ -229,44 +272,73 @@ void CWorldEditor::ClearSelection()
// ************ SLOTS ************
void CWorldEditor::ViewportMouseDrag(QMouseEvent *pEvent)
{
// todo: gizmo translate/rotate/scale implementation
}
void CWorldEditor::ViewportMouseClick(QMouseEvent *pEvent)
{
// Process left click (button press)
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)
{
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)
// Gizmo transform stop
if (mGizmoTransforming)
{
// No valid node selected - do nothing
if (!ValidNode)
return;
DeselectNode(mpHoverNode);
mGizmoTransforming = false;
}
// Other - select object
else
// Object selection/deselection
else if (!ui->MainViewport->IsMouseInputActive())
{
// Control not pressed - clear existing selection
if (!CtrlPressed)
ClearSelection();
bool ValidNode = ((mpHoverNode) && (mpHoverNode->NodeType() != eStaticNode));
bool AltPressed = ((pEvent->modifiers() & Qt::AltModifier) != 0);
bool CtrlPressed = ((pEvent->modifiers() & Qt::ControlModifier) != 0);
// Add hover node to selection
if (ValidNode)
SelectNode(mpHoverNode);
// 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();
}
UpdateSelectionUI();
}
// Later, possibly expand to context menu creation for right-click
// todo: context menu creation on right-click goes here
}
// ************ SLOTS ************
@ -298,8 +370,10 @@ void CWorldEditor::ViewportRender(CCamera& Camera)
if (mShowGizmo && (mSelectedNodes.size() > 0))
{
mpRenderer->ClearDepthBuffer();
Camera.LoadMatrices();
mGizmo.UpdateForCamera(Camera);
if (!mGizmoTransforming) mGizmo.UpdateForCamera(Camera);
mGizmo.AddToRenderer(mpRenderer);
if (mGizmo.Mode() == CGizmo::eRotate)
@ -318,6 +392,12 @@ void CWorldEditor::ViewportPostRender()
// Update UI with raycast results
UpdateCursor();
UpdateStatusBar();
if (mUpdateUILater)
{
UpdateSelectionUI();
mUpdateUILater = false;
}
}
void CWorldEditor::SetViewportSize(int Width, int Height)
@ -325,6 +405,22 @@ void CWorldEditor::SetViewportSize(int Width, int Height)
mpRenderer->SetViewportSize(Width, Height);
}
void CWorldEditor::SetTransformSpace(int space)
{
switch (space)
{
case 0:
mTransformSpace = eWorldTransform;
mGizmo.SetOrientation(CQuaternion::skIdentity);
break;
case 1:
mTransformSpace = eLocalTransform;
if (!mSelectedNodes.empty())
mGizmo.SetOrientation(mSelectedNodes.front()->AbsoluteRotation());
break;
}
}
// ************ PRIVATE ************
void CWorldEditor::RecalculateSelectionBounds()
{
@ -396,13 +492,21 @@ void CWorldEditor::UpdateSelectionUI()
ui->SelectionInfoLabel->setText(SelectionText);
// Update transform
CVector3f pos = (!mSelectedNodes.empty() ? mSelectedNodes.front()->GetAbsolutePosition() : CVector3f::skZero);
CVector3f pos = (!mSelectedNodes.empty() ? mSelectedNodes.front()->AbsolutePosition() : CVector3f::skZero);
ui->XSpinBox->setValue(pos.x);
ui->YSpinBox->setValue(pos.y);
ui->ZSpinBox->setValue(pos.z);
// Update gizmo
mGizmo.SetPosition(pos);
if (!mGizmoTransforming)
{
mGizmo.SetPosition(pos);
if ((mTransformSpace == eLocalTransform) && !mSelectedNodes.empty())
mGizmo.SetOrientation(mSelectedNodes.front()->AbsoluteRotation());
else
mGizmo.SetOrientation(CQuaternion::skIdentity);
}
}
// ************ ACTIONS ************

View File

@ -9,6 +9,7 @@
#include <Common/CTimer.h>
#include <Common/EKeyInputs.h>
#include <Common/SRayIntersection.h>
#include <Common/ETransformSpace.h>
#include <Core/CRenderer.h>
#include <Core/CSceneManager.h>
#include <Core/CToken.h>
@ -25,6 +26,7 @@ class CWorldEditor : public QMainWindow
CRenderer *mpRenderer;
CSceneManager *mpSceneManager;
CGizmo mGizmo;
ETransformSpace mTransformSpace;
CCamera mCamera;
CGameArea *mpArea;
CWorld *mpWorld;
@ -34,6 +36,8 @@ class CWorldEditor : public QMainWindow
bool mDrawSky;
bool mShowGizmo;
bool mGizmoHovering;
bool mGizmoTransforming;
bool mUpdateUILater;
CVector3f mHoverPoint;
CSceneNode *mpHoverNode;
@ -64,7 +68,9 @@ public slots:
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;

View File

@ -440,13 +440,16 @@
<addaction name="menuWindow"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="Toolbar">
<widget class="QToolBar" name="FileToolBar">
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="windowTitle">
<string>toolBar_2</string>
</property>
<property name="movable">
<bool>false</bool>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
@ -593,13 +596,19 @@
</layout>
</widget>
</widget>
<widget class="QToolBar" name="toolBar">
<widget class="QToolBar" name="MainToolBar">
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="windowTitle">
<string>toolBar</string>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>