Rotate gizmo transform functionality implemented

This commit is contained in:
parax0 2015-08-23 21:02:14 -04:00
parent 04b4f36da9
commit 614f73487e
12 changed files with 460 additions and 103 deletions

View File

@ -28,6 +28,26 @@ void CVector2f::Write(COutputStream &Output)
Output.WriteFloat(y);
}
float CVector2f::Magnitude() const
{
return sqrtf(SquaredMagnitude());
}
float CVector2f::SquaredMagnitude() const
{
return Dot(*this);
}
CVector2f CVector2f::Normalized() const
{
return *this / Magnitude();
}
float CVector2f::Dot(const CVector2f& other) const
{
return ((x * other.x) + (y * other.y));
}
CVector2f CVector2f::operator+(const CVector2f& src) const
{
CVector2f out;
@ -142,11 +162,21 @@ bool CVector2f::operator!=(const CVector2f& other) const
return (!(*this == other));
}
CVector2f CVector2f::operator-() const
{
return CVector2f(-x, -y);
}
float& CVector2f::operator[](long index)
{
return (&x)[index];
}
const float& CVector2f::operator[](long index) const
{
return (&x)[index];
}
// ************ STATIC MEMBER INITIALIZATION ************
const CVector2f CVector2f::skZero = CVector2f(0, 0);
const CVector2f CVector2f::skOne = CVector2f(1, 1);

View File

@ -14,6 +14,11 @@ public:
CVector2f(CInputStream& Input);
void Write(COutputStream& Output);
float Magnitude() const;
float SquaredMagnitude() const;
CVector2f Normalized() const;
float Dot(const CVector2f& other) const;
CVector2f operator+(const CVector2f& other) const;
CVector2f operator-(const CVector2f& other) const;
CVector2f operator*(const CVector2f& other) const;
@ -32,7 +37,9 @@ public:
void operator/=(const float other);
bool operator==(const CVector2f& other) const;
bool operator!=(const CVector2f& other) const;
CVector2f operator-() const;
float& operator[](long index);
const float& operator[](long index) const;
// Static Members
static const CVector2f skZero;

View File

@ -65,7 +65,7 @@ float CVector3f::SquaredMagnitude() const
CVector3f CVector3f::Normalized() const
{
return *this / Magnitude();;
return *this / Magnitude();
}
float CVector3f::Dot(const CVector3f& other) const

View File

@ -153,7 +153,14 @@ CRay CCamera::CastRay(CVector2f DeviceCoords)
void CCamera::LoadMatrices()
{
CGraphics::sMVPBlock.ViewMatrix = ViewMatrix();
CGraphics::sMVPBlock.ProjectionMatrix = ProjectionMatrix();
CGraphics::sMVPBlock.ProjectionMatrix = ProjectionMatrix();
CGraphics::UpdateMVPBlock();
}
void CCamera::LoadRotationOnlyMatrices()
{
CGraphics::sMVPBlock.ViewMatrix = RotationOnlyViewMatrix();
CGraphics::sMVPBlock.ProjectionMatrix = ProjectionMatrix();
CGraphics::UpdateMVPBlock();
}
@ -187,6 +194,17 @@ const CMatrix4f& CCamera::ViewMatrix()
return mCachedViewMatrix;
}
const CMatrix4f& CCamera::RotationOnlyViewMatrix()
{
if (mViewOutdated)
CalculateView();
return CMatrix4f(mCachedViewMatrix[0][0], mCachedViewMatrix[0][1], mCachedViewMatrix[0][2], 0.f,
mCachedViewMatrix[1][0], mCachedViewMatrix[1][1], mCachedViewMatrix[1][2], 0.f,
mCachedViewMatrix[2][0], mCachedViewMatrix[2][1], mCachedViewMatrix[2][2], 0.f,
mCachedViewMatrix[3][0], mCachedViewMatrix[3][1], mCachedViewMatrix[3][2], 1.f);
}
const CMatrix4f& CCamera::ProjectionMatrix()
{
if (mProjectionOutdated)

View File

@ -45,6 +45,7 @@ public:
void ProcessMouseInput(EKeyInputs KeyFlags, EMouseInputs MouseFlags, float XMovement, float YMovement);
CRay CastRay(CVector2f DeviceCoords);
void LoadMatrices();
void LoadRotationOnlyMatrices();
// Getters
CVector3f Position() const;
@ -52,6 +53,7 @@ public:
float GetYaw() const;
float GetPitch() const;
const CMatrix4f& ViewMatrix();
const CMatrix4f& RotationOnlyViewMatrix();
const CMatrix4f& ProjectionMatrix();
// Setters

View File

@ -245,6 +245,9 @@ void CRenderer::RenderBloom()
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
CDrawUtil::DrawSquare();
}
// Clean up
glEnable(GL_DEPTH_TEST);
}
void CRenderer::RenderSky(CModel *pSkyboxModel, CVector3f CameraPosition)

View File

@ -201,7 +201,15 @@ void CSceneNode::Translate(const CVector3f& translation, ETransformSpace transfo
void CSceneNode::Rotate(const CQuaternion& rotation, ETransformSpace transformSpace)
{
mRotation *= rotation;
switch (transformSpace)
{
case eWorldTransform:
mRotation = rotation * mRotation;
break;
case eLocalTransform:
mRotation *= rotation;
break;
}
MarkTransformChanged();
}
@ -370,6 +378,30 @@ void CSceneNode::SetName(const std::string& Name)
mName = Name;
}
void CSceneNode::SetPosition(const CVector3f& position)
{
mPosition = position;
MarkTransformChanged();
}
void CSceneNode::SetRotation(const CQuaternion& rotation)
{
mRotation = rotation;
MarkTransformChanged();
}
void CSceneNode::SetRotation(const CVector3f& rotEuler)
{
mRotation = CQuaternion::FromEuler(rotEuler);
MarkTransformChanged();
}
void CSceneNode::SetScale(const CVector3f& scale)
{
mScale = scale;
MarkTransformChanged();
}
void CSceneNode::SetMouseHovering(bool Hovering)
{
mMouseHovering = Hovering;

View File

@ -98,6 +98,10 @@ public:
// Setters
void SetName(const std::string& Name);
void SetPosition(const CVector3f& position);
void SetRotation(const CQuaternion& rotation);
void SetRotation(const CVector3f& rotEuler);
void SetScale(const CVector3f& scale);
void SetMouseHovering(bool Hovering);
void SetSelected(bool Selected);
void SetVisible(bool Visible);

View File

@ -3,6 +3,10 @@
#include <Core/CRenderer.h>
#include <Core/CResCache.h>
#include <Core/CDrawUtil.h>
#include <Core/Log.h>
#include <QApplication>
#include <QDesktopWidget>
#include <iostream>
CGizmo::CGizmo()
@ -11,8 +15,13 @@ CGizmo::CGizmo()
SetMode(eTranslate);
mSelectedAxes = eNone;
mTransformSpace = eWorldTransform;
mGizmoSize = 1.f;
mCameraDist = 0.f;
mIsTransforming = false;
mHasTransformed = false;
mWrapOffset = 0.f;
mEnableCursorWrap = true;
mPosition = CVector3f::skZero;
mRotation = CQuaternion::skIdentity;
@ -43,7 +52,8 @@ void CGizmo::AddToRenderer(CRenderer *pRenderer)
CModel *pModel = pPart->pModel;
// Determine whether to use the mat set for regular (0) or highlight (1)
bool isHighlighted = (mSelectedAxes & pPart->modelAxes) == pPart->modelAxes;
EGizmoAxes partAxes = pPart->modelAxes;
bool isHighlighted = (partAxes != eNone) && ((mSelectedAxes & partAxes) == pPart->modelAxes);
u32 setID = (isHighlighted ? 1 : 0);
// Add to renderer...
@ -58,24 +68,21 @@ void CGizmo::AddToRenderer(CRenderer *pRenderer)
void CGizmo::DrawAsset(ERenderOptions options, u32 asset)
{
CGraphics::sMVPBlock.ModelMatrix = mTransform.ToMatrix4f();
CGraphics::UpdateMVPBlock();
// Determine which SModelPart array to use
if (asset >= mNumCurrentParts) return;
SModelPart *pPart = mpCurrentParts;
// Draw model
bool isHighlighted = (mSelectedAxes & pPart[asset].modelAxes) == pPart[asset].modelAxes;
u32 setID = (isHighlighted ? 1 : 0);
pPart[asset].pModel->Draw((ERenderOptions) 0, setID);
}
void CGizmo::DrawRotationOutline()
{
CGraphics::sMVPBlock.ModelMatrix = mBillboardTransform.ToMatrix4f();
// Set model matrix
CGraphics::sMVPBlock.ModelMatrix = (pPart[asset].isBillboard ? mBillboardTransform.ToMatrix4f() : mTransform.ToMatrix4f());
CGraphics::UpdateMVPBlock();
smRotateClipOutline.pModel->Draw((ERenderOptions) 0, 0);
// Choose material set
EGizmoAxes partAxes = pPart[asset].modelAxes;
bool isHighlighted = (partAxes != eNone) && ((mSelectedAxes & partAxes) == pPart[asset].modelAxes);
u32 setID = (isHighlighted ? 1 : 0);
// Draw model
pPart[asset].pModel->Draw((ERenderOptions) 0, setID);
}
void CGizmo::IncrementSize()
@ -99,15 +106,17 @@ void CGizmo::DecrementSize()
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;
if ((!mIsTransforming) || (mMode != eTranslate))
mCameraDist = mPosition.Distance(camPos);
// todo: make this cleaner...
CVector3f billDir = (mPosition - camPos).Normalized();
CVector3f axis = CVector3f::skForward.Cross(billDir);
float angle = acos(CVector3f::skForward.Dot(billDir));
float angle = acosf(CVector3f::skForward.Dot(billDir));
angle = 180 + (angle * 180 / 3.14159265358979323846f);
mBillboardRotation = CQuaternion::FromAxisAngle(angle, axis);
}
@ -116,6 +125,7 @@ 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());
CRay billRay = ray.Transformed(mBillboardTransform.Inverse());
// Do raycast on each model
SModelPart *pPart = mpCurrentParts;
@ -135,11 +145,12 @@ bool CGizmo::CheckSelectedAxes(const CRay &ray)
}
CModel *pModel = pPart->pModel;
CRay& partRay = (pPart->isBillboard ? billRay : localRay);
// Ray/Model AABox test - allow buffer room because lines are small
CAABox AABox = pModel->AABox();
AABox.ExpandBy(CVector3f::skOne);
bool modelBoxCheck = Math::RayBoxIntersection(localRay, AABox).first;
bool modelBoxCheck = Math::RayBoxIntersection(partRay, AABox).first;
if (modelBoxCheck)
{
@ -150,7 +161,7 @@ bool CGizmo::CheckSelectedAxes(const CRay &ray)
{
// 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);
std::pair<bool,float> surfCheck = pSurf->IntersectsRay(partRay, 0.05f);
if (surfCheck.first)
{
@ -186,8 +197,11 @@ bool CGizmo::CheckSelectedAxes(const CRay &ray)
return (a.dist < b.dist);
});
CRay& partRay = (pPart->isBillboard ? billRay : localRay);
mSelectedAxes = results.front().pPart->modelAxes;
return true;
mHitPoint = mTransform * partRay.PointOnRay(results.front().dist);
return (mSelectedAxes != eNone);
}
u32 CGizmo::NumSelectedAxes()
@ -207,14 +221,43 @@ void CGizmo::ResetSelectedAxes()
void CGizmo::StartTransform()
{
mIsTransforming = true;
mHasTransformed = false;
mWrapOffset = CVector2f::skZero;
mSetOffset = false;
mTotalTranslation = CVector3f::skZero;
mTotalRotation = CQuaternion::skIdentity;
mTotalRotation = CVector3f::skZero;
mCurrentRotation = CQuaternion::skIdentity;
mTotalScale = CVector3f::skOne;
// Set rotation clockwise direction
if (mMode == eRotate)
{
CVector3f axis;
if (mSelectedAxes & eX) axis = mRotation.XAxis();
else if (mSelectedAxes & eY) axis = mRotation.YAxis();
else axis = mRotation.ZAxis();
CVector3f gizmoToHit = (mHitPoint - mPosition).Normalized();
mClockwiseDir = axis.Cross(gizmoToHit);
}
}
bool CGizmo::TransformFromInput(const CRay& ray, const CCamera& camera)
bool CGizmo::TransformFromInput(const CRay& ray, CCamera& camera)
{
// Wrap cursor (this has no effect until the next time this function is called)
if (mEnableCursorWrap && (mMode != eTranslate))
WrapCursor();
// Calculate normalized cursor position
QPoint cursorPos = QCursor::pos();
QRect geom = QApplication::desktop()->screenGeometry();
CVector2f mouseCoords(
(((2.f * cursorPos.x()) / geom.width()) - 1.f),
(1.f - ((2.f * cursorPos.y()) / geom.height()))
);
// Translate
if (mMode == eTranslate)
{
// Create translate plane
@ -273,8 +316,16 @@ bool CGizmo::TransformFromInput(const CRay& ray, const CCamera& camera)
else
{
mDeltaTranslation = mRotation.Inverse() * (newPos - mPosition + mTranslateOffset);
if (!(mSelectedAxes & eX)) mDeltaTranslation.x = 0.f;
if (!(mSelectedAxes & eY)) mDeltaTranslation.y = 0.f;
if (!(mSelectedAxes & eZ)) mDeltaTranslation.z = 0.f;
mTotalTranslation += mDeltaTranslation;
mPosition = newPos + mTranslateOffset;
mPosition += mRotation * mDeltaTranslation;
if (!mHasTransformed && (mDeltaTranslation != CVector3f::skZero))
mHasTransformed = true;
return true;
}
}
@ -286,11 +337,62 @@ bool CGizmo::TransformFromInput(const CRay& ray, const CCamera& camera)
}
}
// Rotate
else if (mMode == eRotate)
{
// Choose rotation axis
CVector3f axis;
if (mSelectedAxes & eX) axis = CVector3f::skUnitX;
else if (mSelectedAxes & eY) axis = CVector3f::skUnitY;
else axis = CVector3f::skUnitZ;
// Convert hit point + clockwise direction into a line in screen space
// Clockwise direction is set in StartTransform(). Is there a cleaner way to calculate the direction?
CMatrix4f VP = camera.ViewMatrix().Transpose() * camera.ProjectionMatrix().Transpose();
CVector2f lineOrigin = (mHitPoint * VP).xy();
CVector2f lineDir = (((mHitPoint + mClockwiseDir) * VP).xy() - lineOrigin).Normalized();
float rotAmount = lineDir.Dot(mouseCoords + mWrapOffset - lineOrigin) * 180.f;
// Set offset
if (!mSetOffset)
{
mRotateOffset = -rotAmount;
mDeltaRotation = CQuaternion::skIdentity;
mSetOffset = true;
return false;
}
// Apply rotation
rotAmount += mRotateOffset;
CQuaternion oldRot = mCurrentRotation;
mCurrentRotation = CQuaternion::FromAxisAngle(rotAmount, axis);
mDeltaRotation = mCurrentRotation * oldRot.Inverse();
if (mTransformSpace == eLocalTransform)
mRotation *= mDeltaRotation;
// Add to total
if (mSelectedAxes & eX) mTotalRotation.x = rotAmount;
else if (mSelectedAxes & eY) mTotalRotation.y = rotAmount;
else mTotalRotation.z = rotAmount;
if (!mHasTransformed && (rotAmount != 0.f))
mHasTransformed = true;
return true;
}
return false;
}
void CGizmo::EndTransform()
{
mIsTransforming = false;
}
bool CGizmo::HasTransformed()
{
return mHasTransformed;
}
CGizmo::EGizmoMode CGizmo::Mode()
@ -313,6 +415,36 @@ CVector3f CGizmo::TotalTranslation()
return mTotalTranslation;
}
CQuaternion CGizmo::Rotation()
{
return mRotation;
}
CQuaternion CGizmo::DeltaRotation()
{
return mDeltaRotation;
}
CVector3f CGizmo::TotalRotation()
{
return mTotalRotation;
}
CVector3f CGizmo::Scale()
{
return mScale;
}
CVector3f CGizmo::DeltaScale()
{
return mDeltaScale;
}
CVector3f CGizmo::TotalScale()
{
return mTotalScale;
}
void CGizmo::SetMode(EGizmoMode mode)
{
mMode = mode;
@ -328,7 +460,7 @@ void CGizmo::SetMode(EGizmoMode mode)
case eRotate:
mpCurrentParts = smRotateModels;
mNumCurrentParts = 4;
mNumCurrentParts = 5;
mDeltaTranslation = CVector3f::skZero;
mDeltaScale = CVector3f::skOne;
break;
@ -342,47 +474,59 @@ void CGizmo::SetMode(EGizmoMode mode)
}
}
void CGizmo::SetTransformSpace(ETransformSpace space)
{
mTransformSpace = space;
}
void CGizmo::SetPosition(const CVector3f& position)
{
mPosition = position;
}
void CGizmo::SetOrientation(const CQuaternion& orientation)
void CGizmo::SetRotation(const CQuaternion& orientation)
{
mRotation = orientation;
}
void CGizmo::EnableCursorWrap(bool wrap)
{
mEnableCursorWrap = wrap;
}
// ************ PRIVATE STATIC ************
void CGizmo::LoadModels()
{
if (!smModelsLoaded)
{
smTranslateModels[CGIZMO_TRANSLATE_X] = SModelPart(eX, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateX.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_Y] = SModelPart(eY, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateY.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_Z] = SModelPart(eZ, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_LINES_XY] = SModelPart(eXY, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesXY.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_LINES_XZ] = SModelPart(eXZ, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesXZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_LINES_YZ] = SModelPart(eYZ, true, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesYZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_POLY_XY] = SModelPart(eXY, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyXY.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_POLY_XZ] = SModelPart(eXZ, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyXZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_POLY_YZ] = SModelPart(eYZ, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyYZ.CMDL"));
Log::Write("Loading transform gizmo models");
smRotateModels[CGIZMO_ROTATE_X] = SModelPart(eX, true, (CModel*) gResCache.GetResource("../resources/editor/RotateX.CMDL"));
smRotateModels[CGIZMO_ROTATE_Y] = SModelPart(eY, true, (CModel*) gResCache.GetResource("../resources/editor/RotateY.CMDL"));
smRotateModels[CGIZMO_ROTATE_Z] = SModelPart(eZ, true, (CModel*) gResCache.GetResource("../resources/editor/RotateZ.CMDL"));
smRotateModels[CGIZMO_ROTATE_XYZ] = SModelPart(eXYZ, false, (CModel*) gResCache.GetResource("../resources/editor/RotateXYZ.CMDL"));
smRotateClipOutline = SModelPart(eNone, false, (CModel*) gResCache.GetResource("../resources/editor/RotateClipOutline.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_X] = SModelPart(eX, true, false, (CModel*) gResCache.GetResource("../resources/editor/TranslateX.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_Y] = SModelPart(eY, true, false, (CModel*) gResCache.GetResource("../resources/editor/TranslateY.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_Z] = SModelPart(eZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/TranslateZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_LINES_XY] = SModelPart(eXY, true, false, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesXY.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_LINES_XZ] = SModelPart(eXZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesXZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_LINES_YZ] = SModelPart(eYZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/TranslateLinesYZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_POLY_XY] = SModelPart(eXY, false, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyXY.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_POLY_XZ] = SModelPart(eXZ, false, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyXZ.CMDL"));
smTranslateModels[CGIZMO_TRANSLATE_POLY_YZ] = SModelPart(eYZ, false, false, (CModel*) gResCache.GetResource("../resources/editor/TranslatePolyYZ.CMDL"));
smScaleModels[CGIZMO_SCALE_X] = SModelPart(eX, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleX.CMDL"));
smScaleModels[CGIZMO_SCALE_Y] = SModelPart(eY, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleY.CMDL"));
smScaleModels[CGIZMO_SCALE_Z] = SModelPart(eZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleZ.CMDL"));
smScaleModels[CGIZMO_SCALE_LINES_XY] = SModelPart(eXY, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesXY.CMDL"));
smScaleModels[CGIZMO_SCALE_LINES_XZ] = SModelPart(eXZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesXZ.CMDL"));
smScaleModels[CGIZMO_SCALE_LINES_YZ] = SModelPart(eYZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesYZ.CMDL"));
smScaleModels[CGIZMO_SCALE_POLY_XY] = SModelPart(eXY, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyXY.CMDL"));
smScaleModels[CGIZMO_SCALE_POLY_XZ] = SModelPart(eXZ, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyXZ.CMDL"));
smScaleModels[CGIZMO_SCALE_POLY_YZ] = SModelPart(eYZ, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyYZ.CMDL"));
smScaleModels[CGIZMO_SCALE_XYZ] = SModelPart(eXYZ, true, (CModel*) gResCache.GetResource("../resources/editor/ScaleXYZ.CMDL"));
smRotateModels[CGIZMO_ROTATE_OUTLINE] = SModelPart(eNone, true, true, (CModel*) gResCache.GetResource("../resources/editor/RotateClipOutline.CMDL"));
smRotateModels[CGIZMO_ROTATE_X] = SModelPart(eX, true, false, (CModel*) gResCache.GetResource("../resources/editor/RotateX.CMDL"));
smRotateModels[CGIZMO_ROTATE_Y] = SModelPart(eY, true, false, (CModel*) gResCache.GetResource("../resources/editor/RotateY.CMDL"));
smRotateModels[CGIZMO_ROTATE_Z] = SModelPart(eZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/RotateZ.CMDL"));
smRotateModels[CGIZMO_ROTATE_XYZ] = SModelPart(eXYZ, false, false, (CModel*) gResCache.GetResource("../resources/editor/RotateXYZ.CMDL"));
smScaleModels[CGIZMO_SCALE_X] = SModelPart(eX, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleX.CMDL"));
smScaleModels[CGIZMO_SCALE_Y] = SModelPart(eY, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleY.CMDL"));
smScaleModels[CGIZMO_SCALE_Z] = SModelPart(eZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleZ.CMDL"));
smScaleModels[CGIZMO_SCALE_LINES_XY] = SModelPart(eXY, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesXY.CMDL"));
smScaleModels[CGIZMO_SCALE_LINES_XZ] = SModelPart(eXZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesXZ.CMDL"));
smScaleModels[CGIZMO_SCALE_LINES_YZ] = SModelPart(eYZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleLinesYZ.CMDL"));
smScaleModels[CGIZMO_SCALE_POLY_XY] = SModelPart(eXY, false, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyXY.CMDL"));
smScaleModels[CGIZMO_SCALE_POLY_XZ] = SModelPart(eXZ, false, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyXZ.CMDL"));
smScaleModels[CGIZMO_SCALE_POLY_YZ] = SModelPart(eYZ, false, false, (CModel*) gResCache.GetResource("../resources/editor/ScalePolyYZ.CMDL"));
smScaleModels[CGIZMO_SCALE_XYZ] = SModelPart(eXYZ, true, false, (CModel*) gResCache.GetResource("../resources/editor/ScaleXYZ.CMDL"));
smModelsLoaded = true;
}
@ -395,10 +539,10 @@ void CGizmo::UpdateTransform()
// 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.
// Scale also factors in total scale + axis flip if mode is Scale.
if (mMode == eScale)
{
mScale *= mDeltaScale;
mScale *= mTotalScale;
if (mFlipScaleX) mScale.x = -mScale.x;
if (mFlipScaleY) mScale.y = -mScale.y;
@ -421,9 +565,40 @@ void CGizmo::UpdateTransform()
}
}
void CGizmo::WrapCursor()
{
QRect geom = QApplication::desktop()->screenGeometry();
QPoint cursorPos = QCursor::pos();
// Horizontal
if (cursorPos.x() == geom.width() - 1)
{
QCursor::setPos(1, cursorPos.y());
mWrapOffset.x += 2.f;
}
else if (cursorPos.x() == 0)
{
QCursor::setPos(geom.width() - 2, cursorPos.y());
mWrapOffset.x -= 2.f;
}
// Vertical
cursorPos = QCursor::pos(); // Grab again to account for changes on horizontal wrap
if (cursorPos.y() == geom.height() - 1)
{
QCursor::setPos(cursorPos.x(), 1);
mWrapOffset.y -= 2.f;
}
else if (cursorPos.y() == 0)
{
QCursor::setPos(cursorPos.x(), geom.height() - 2);
mWrapOffset.y += 2.f;
}
}
// ************ STATIC MEMBER INITIALIZATION ************
bool CGizmo::smModelsLoaded = false;
CGizmo::SModelPart CGizmo::smTranslateModels[9];
CGizmo::SModelPart CGizmo::smRotateModels[4];
CGizmo::SModelPart CGizmo::smRotateModels[5];
CGizmo::SModelPart CGizmo::smScaleModels[10];
CGizmo::SModelPart CGizmo::smRotateClipOutline;

View File

@ -5,6 +5,7 @@
#include <Common/CQuaternion.h>
#include <Common/CVector3f.h>
#include <Common/EnumUtil.h>
#include <Common/ETransformSpace.h>
#include <Core/CCamera.h>
#include <Core/CToken.h>
#include <Core/IRenderable.h>
@ -19,10 +20,11 @@
#define CGIZMO_TRANSLATE_POLY_XY 6
#define CGIZMO_TRANSLATE_POLY_XZ 7
#define CGIZMO_TRANSLATE_POLY_YZ 8
#define CGIZMO_ROTATE_X 0
#define CGIZMO_ROTATE_Y 1
#define CGIZMO_ROTATE_Z 2
#define CGIZMO_ROTATE_XYZ 3
#define CGIZMO_ROTATE_OUTLINE 0
#define CGIZMO_ROTATE_X 1
#define CGIZMO_ROTATE_Y 2
#define CGIZMO_ROTATE_Z 3
#define CGIZMO_ROTATE_XYZ 4
#define CGIZMO_SCALE_X 0
#define CGIZMO_SCALE_Y 1
#define CGIZMO_SCALE_Z 2
@ -57,10 +59,16 @@ public:
private:
EGizmoMode mMode;
EGizmoAxes mSelectedAxes;
ETransformSpace mTransformSpace;
CVector3f mHitPoint;
CTransform4f mBillboardTransform;
CQuaternion mBillboardRotation;
float mGizmoSize;
float mCameraDist;
bool mIsTransforming;
bool mHasTransformed;
CVector2f mWrapOffset;
bool mEnableCursorWrap;
CTransform4f mTransform;
CVector3f mPosition;
@ -68,7 +76,8 @@ private:
CVector3f mTotalTranslation;
CQuaternion mRotation;
CQuaternion mDeltaRotation;
CQuaternion mTotalRotation;
CQuaternion mCurrentRotation;
CVector3f mTotalRotation; // This is a CVector3f because this value displays on the UI and a quat would cause rollover
CVector3f mScale;
CVector3f mDeltaScale;
CVector3f mTotalScale;
@ -77,21 +86,24 @@ private:
bool mFlipScaleZ;
CPlane mTranslatePlane;
CVector3f mLastTranslatePosition;
CVector3f mTranslateOffset;
float mRotateOffset;
bool mSetOffset;
CVector3f mRotateHitPoint;
CVector3f mClockwiseDir;
struct SModelPart
{
EGizmoAxes modelAxes;
bool enableRayCast;
bool isBillboard;
CModel *pModel;
CToken modelToken;
SModelPart() {}
SModelPart(EGizmoAxes axes, bool rayCastOn, CModel *_pModel) :
modelAxes(axes), enableRayCast(rayCastOn), pModel(_pModel), modelToken(_pModel) {}
SModelPart(EGizmoAxes axes, bool rayCastOn, bool billboard, CModel *_pModel) :
modelAxes(axes), enableRayCast(rayCastOn), isBillboard(billboard), pModel(_pModel), modelToken(_pModel) {}
};
SModelPart *mpCurrentParts;
u32 mNumCurrentParts;
@ -99,9 +111,8 @@ private:
// Static
static bool smModelsLoaded;
static SModelPart smTranslateModels[9];
static SModelPart smRotateModels[4];
static SModelPart smRotateModels[5];
static SModelPart smScaleModels[10];
static SModelPart smRotateClipOutline;
public:
CGizmo();
@ -109,7 +120,6 @@ public:
void AddToRenderer(CRenderer *pRenderer);
void DrawAsset(ERenderOptions options, u32 asset);
void DrawRotationOutline();
void IncrementSize();
void DecrementSize();
@ -118,20 +128,30 @@ public:
u32 NumSelectedAxes();
void ResetSelectedAxes();
void StartTransform();
bool TransformFromInput(const CRay& ray, const CCamera& camera);
bool TransformFromInput(const CRay& ray, CCamera& camera);
void EndTransform();
bool HasTransformed();
EGizmoMode Mode();
CVector3f Position();
CVector3f DeltaTranslation();
CVector3f TotalTranslation();
CQuaternion Rotation();
CQuaternion DeltaRotation();
CVector3f TotalRotation();
CVector3f Scale();
CVector3f DeltaScale();
CVector3f TotalScale();
void SetMode(EGizmoMode mode);
void SetTransformSpace(ETransformSpace space);
void SetPosition(const CVector3f& position);
void SetOrientation(const CQuaternion& orientation);
void SetRotation(const CQuaternion& orientation);
void EnableCursorWrap(bool wrap);
// Protected
protected:
void UpdateTransform();
void WrapCursor();
// Private Static
private:

View File

@ -35,7 +35,7 @@ CWorldEditor::CWorldEditor(QWidget *parent) :
mShowGizmo = false;
mGizmoHovering = false;
mGizmoTransforming = false;
mUpdateUILater = false;
mGizmoUIOutdated = true;
mFrameCount = 0;
mFPSTimer.Start();
@ -144,15 +144,20 @@ void CWorldEditor::SetArea(CWorld *pWorld, CGameArea *pArea)
ui->InstancesTabContents->SetMaster(pMaster);
}
void CWorldEditor::ViewportRayCast(CRay Ray)
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);
mGizmoHovering = mGizmo.CheckSelectedAxes(ray);
else
{
mGizmoHovering = false;
@ -160,7 +165,7 @@ void CWorldEditor::ViewportRayCast(CRay Ray)
}
// Scene ray check
SRayIntersection Result = mpSceneManager->SceneRayCast(Ray);
SRayIntersection Result = mpSceneManager->SceneRayCast(ray);
if (Result.Hit)
{
@ -170,26 +175,44 @@ void CWorldEditor::ViewportRayCast(CRay Ray)
mpHoverNode = Result.pNode;
mpHoverNode->SetMouseHovering(true);
mHoverPoint = Ray.PointOnRay(Result.Distance);
mHoverPoint = ray.PointOnRay(Result.Distance);
}
else
ResetHover();
}
else
{
bool moved = mGizmo.TransformFromInput(Ray, ui->MainViewport->Camera());
bool transformed = mGizmo.TransformFromInput(ray, ui->MainViewport->Camera());
if (moved)
if (transformed)
{
CVector3f delta = mGizmo.DeltaTranslation();
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
switch (mGizmo.Mode())
{
(*it)->Translate(delta, mTransformSpace);
(*it)->BuildLightList(this->mpArea);
case CGizmo::eTranslate:
{
CVector3f delta = mGizmo.DeltaTranslation();
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
{
(*it)->Translate(delta, mTransformSpace);
(*it)->BuildLightList(this->mpArea);
}
break;
}
case CGizmo::eRotate:
{
CQuaternion delta = mGizmo.DeltaRotation();
for (auto it = mSelectedNodes.begin(); it != mSelectedNodes.end(); it++)
(*it)->Rotate(delta, mTransformSpace);
break;
}
}
RecalculateSelectionBounds();
mUpdateUILater = true;
mGizmoUIOutdated = true;
}
}
}
@ -293,7 +316,9 @@ void CWorldEditor::ViewportMouseRelease(QMouseEvent *pEvent)
// Gizmo transform stop
if (mGizmoTransforming)
{
mGizmo.EndTransform();
mGizmoTransforming = false;
mGizmoUIOutdated = true;
}
// Object selection/deselection
@ -346,7 +371,7 @@ void CWorldEditor::ViewportPreRender()
{
// Perform raycast
if (ui->MainViewport->underMouse())
ViewportRayCast(ui->MainViewport->CastRay());
ViewportRayCast();
else
ResetHover();
@ -373,13 +398,11 @@ void CWorldEditor::ViewportRender(CCamera& Camera)
mpRenderer->ClearDepthBuffer();
Camera.LoadMatrices();
if (!mGizmoTransforming) mGizmo.UpdateForCamera(Camera);
mGizmo.UpdateForCamera(Camera);
mGizmo.AddToRenderer(mpRenderer);
if (mGizmo.Mode() == CGizmo::eRotate)
mGizmo.DrawRotationOutline();
mpRenderer->RenderBuckets(Camera);
mpRenderer->ClearDepthBuffer();
}
mpRenderer->EndFrame();
@ -393,11 +416,7 @@ void CWorldEditor::ViewportPostRender()
UpdateCursor();
UpdateStatusBar();
if (mUpdateUILater)
{
UpdateSelectionUI();
mUpdateUILater = false;
}
if (mGizmoUIOutdated) UpdateGizmoUI();
}
void CWorldEditor::SetViewportSize(int Width, int Height)
@ -411,14 +430,16 @@ void CWorldEditor::SetTransformSpace(int space)
{
case 0:
mTransformSpace = eWorldTransform;
mGizmo.SetOrientation(CQuaternion::skIdentity);
mGizmo.SetRotation(CQuaternion::skIdentity);
break;
case 1:
mTransformSpace = eLocalTransform;
if (!mSelectedNodes.empty())
mGizmo.SetOrientation(mSelectedNodes.front()->AbsoluteRotation());
mGizmo.SetRotation(mSelectedNodes.front()->AbsoluteRotation());
break;
}
mGizmo.SetTransformSpace(mTransformSpace);
}
// ************ PRIVATE ************
@ -491,22 +512,62 @@ void CWorldEditor::UpdateSelectionUI()
SelectionText = Metrics.elidedText(SelectionText, Qt::ElideRight, ui->SelectionInfoFrame->width() - 10);
ui->SelectionInfoLabel->setText(SelectionText);
// Update transform
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 stuff
UpdateGizmoUI();
}
void CWorldEditor::UpdateGizmoUI()
{
// Update transform XYZ spin boxes
CVector3f spinBoxValue = CVector3f::skZero;
// If the gizmo is transforming, use the total transform amount
// Otherwise, use the first selected node transform, or 0 if no selection
if (mShowGizmo)
{
switch (mGizmo.Mode())
{
case CGizmo::eTranslate:
if (mGizmoTransforming && mGizmo.HasTransformed())
spinBoxValue = mGizmo.TotalTranslation();
else if (!mSelectedNodes.empty())
spinBoxValue = mSelectedNodes.front()->AbsolutePosition();
break;
case CGizmo::eRotate:
if (mGizmoTransforming && mGizmo.HasTransformed())
spinBoxValue = mGizmo.TotalRotation();
else if (!mSelectedNodes.empty())
spinBoxValue = mSelectedNodes.front()->AbsoluteRotation().ToEuler();
break;
case CGizmo::eScale:
if (mGizmoTransforming && mGizmo.HasTransformed())
spinBoxValue = mGizmo.TotalScale();
else if (!mSelectedNodes.empty())
spinBoxValue = mSelectedNodes.front()->AbsoluteScale();
break;
}
}
else if (!mSelectedNodes.empty()) spinBoxValue = mSelectedNodes.front()->AbsolutePosition();
ui->XSpinBox->setValue(spinBoxValue.x);
ui->YSpinBox->setValue(spinBoxValue.y);
ui->ZSpinBox->setValue(spinBoxValue.z);
// Update gizmo
if (!mGizmoTransforming)
{
mGizmo.SetPosition(pos);
if (!mSelectedNodes.empty())
mGizmo.SetPosition(mSelectedNodes.front()->AbsolutePosition());
if ((mTransformSpace == eLocalTransform) && !mSelectedNodes.empty())
mGizmo.SetOrientation(mSelectedNodes.front()->AbsoluteRotation());
mGizmo.SetRotation(mSelectedNodes.front()->AbsoluteRotation());
else
mGizmo.SetOrientation(CQuaternion::skIdentity);
mGizmo.SetRotation(CQuaternion::skIdentity);
}
mGizmoUIOutdated = false;
}
// ************ ACTIONS ************
@ -660,6 +721,7 @@ void CWorldEditor::on_ActionEditLayers_triggered()
void CWorldEditor::on_ActionSelectObjects_triggered()
{
mShowGizmo = false;
mGizmoUIOutdated = true;
ui->ActionSelectObjects->setChecked(true);
ui->ActionTranslate->setChecked(false);
ui->ActionRotate->setChecked(false);
@ -669,6 +731,7 @@ void CWorldEditor::on_ActionSelectObjects_triggered()
void CWorldEditor::on_ActionTranslate_triggered()
{
mShowGizmo = true;
mGizmoUIOutdated = true;
mGizmo.SetMode(CGizmo::eTranslate);
ui->ActionSelectObjects->setChecked(false);
ui->ActionTranslate->setChecked(true);
@ -679,6 +742,7 @@ void CWorldEditor::on_ActionTranslate_triggered()
void CWorldEditor::on_ActionRotate_triggered()
{
mShowGizmo = true;
mGizmoUIOutdated = true;
mGizmo.SetMode(CGizmo::eRotate);
ui->ActionSelectObjects->setChecked(false);
ui->ActionTranslate->setChecked(false);
@ -689,6 +753,7 @@ void CWorldEditor::on_ActionRotate_triggered()
void CWorldEditor::on_ActionScale_triggered()
{
mShowGizmo = true;
mGizmoUIOutdated = true;
mGizmo.SetMode(CGizmo::eScale);
ui->ActionSelectObjects->setChecked(false);
ui->ActionTranslate->setChecked(false);

View File

@ -37,7 +37,7 @@ class CWorldEditor : public QMainWindow
bool mShowGizmo;
bool mGizmoHovering;
bool mGizmoTransforming;
bool mUpdateUILater;
bool mGizmoUIOutdated;
CVector3f mHoverPoint;
CSceneNode *mpHoverNode;
@ -52,7 +52,7 @@ public:
~CWorldEditor();
bool eventFilter(QObject *pObj, QEvent *pEvent);
void SetArea(CWorld *pWorld, CGameArea *pArea);
void ViewportRayCast(CRay Ray);
void ViewportRayCast();
CRenderer* Renderer();
CSceneManager* Scene();
CGameArea* ActiveArea();
@ -82,6 +82,7 @@ private:
void OnSidebarResize();
void UpdateSelectionUI();
void UpdateStatusBar();
void UpdateGizmoUI();
private slots:
void OnCameraSpeedChange(double speed);