Added support for bone selection in the character editor

This commit is contained in:
parax0 2016-05-01 21:01:15 -06:00
parent ed16d05136
commit 709087d2fe
10 changed files with 108 additions and 7 deletions

View File

@ -8,6 +8,7 @@
// ************ CBone ************ // ************ CBone ************
CBone::CBone(CSkeleton *pSkel) CBone::CBone(CSkeleton *pSkel)
: mpSkeleton(pSkel) : mpSkeleton(pSkel)
, mSelected(false)
{ {
} }
@ -115,12 +116,23 @@ void CSkeleton::UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, fl
void CSkeleton::Draw(FRenderOptions /*Options*/, const CBoneTransformData *pkData) void CSkeleton::Draw(FRenderOptions /*Options*/, const CBoneTransformData *pkData)
{ {
glBlendFunc(GL_ONE, GL_ZERO);
// Draw all child links first to minimize model matrix swaps. // Draw all child links first to minimize model matrix swaps.
for (u32 iBone = 0; iBone < mBones.size(); iBone++) for (u32 iBone = 0; iBone < mBones.size(); iBone++)
{ {
CBone *pBone = mBones[iBone]; CBone *pBone = mBones[iBone];
CVector3f BonePos = pkData ? pBone->TransformedPosition(*pkData) : pBone->Position(); CVector3f BonePos = pkData ? pBone->TransformedPosition(*pkData) : pBone->Position();
// Draw the bone's local XYZ axes for selected bones
if (pBone->IsSelected())
{
CQuaternion BoneRot = pkData ? pBone->TransformedRotation(*pkData) : pBone->Rotation();
CDrawUtil::DrawLine(BonePos, BonePos + BoneRot.XAxis(), CColor::skRed);
CDrawUtil::DrawLine(BonePos, BonePos + BoneRot.YAxis(), CColor::skGreen);
CDrawUtil::DrawLine(BonePos, BonePos + BoneRot.ZAxis(), CColor::skBlue);
}
// Draw child links // Draw child links
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++) for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
{ {
@ -143,7 +155,7 @@ void CSkeleton::Draw(FRenderOptions /*Options*/, const CBoneTransformData *pkDat
Transform.Translate(BonePos); Transform.Translate(BonePos);
CGraphics::sMVPBlock.ModelMatrix = Transform * BaseTransform; CGraphics::sMVPBlock.ModelMatrix = Transform * BaseTransform;
CGraphics::UpdateMVPBlock(); CGraphics::UpdateMVPBlock();
CDrawUtil::DrawSphere(CColor::skWhite); CDrawUtil::DrawSphere(pBone->IsSelected() ? CColor::skRed : CColor::skWhite);
} }
} }

View File

@ -61,6 +61,7 @@ class CBone
CQuaternion mLocalRotation; CQuaternion mLocalRotation;
TString mName; TString mName;
CTransform4f mInvBind; CTransform4f mInvBind;
bool mSelected;
public: public:
CBone(CSkeleton *pSkel); CBone(CSkeleton *pSkel);
@ -80,6 +81,9 @@ public:
inline CQuaternion Rotation() const { return mRotation; } inline CQuaternion Rotation() const { return mRotation; }
inline CQuaternion LocalRotation() const { return mLocalRotation; } inline CQuaternion LocalRotation() const { return mLocalRotation; }
inline TString Name() const { return mName; } inline TString Name() const { return mName; }
inline bool IsSelected() const { return mSelected; }
inline void SetSelected(bool Selected) { mSelected = Selected; }
}; };
#endif // CSKELETON_H #endif // CSKELETON_H

View File

@ -1,6 +1,7 @@
#include "CCharacterEditor.h" #include "CCharacterEditor.h"
#include "ui_CCharacterEditor.h" #include "ui_CCharacterEditor.h"
#include "Editor/UICommon.h" #include "Editor/UICommon.h"
#include <Common/Assert.h>
#include <Math/MathUtil.h> #include <Math/MathUtil.h>
#include <QFileDialog> #include <QFileDialog>
#include <QTreeView> #include <QTreeView>
@ -10,6 +11,7 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
, ui(new Ui::CCharacterEditor) , ui(new Ui::CCharacterEditor)
, mpScene(new CScene()) , mpScene(new CScene())
, mpCharNode(new CCharacterNode(mpScene, -1)) , mpCharNode(new CCharacterNode(mpScene, -1))
, mpSelectedBone(nullptr)
, mAnimTime(0.f) , mAnimTime(0.f)
, mPlayAnim(true) , mPlayAnim(true)
, mLoopAnim(true) , mLoopAnim(true)
@ -38,7 +40,8 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
mRefreshTimer.start(0); mRefreshTimer.start(0);
connect(ui->Viewport, SIGNAL(HoverBoneChanged(u32)), this, SLOT(HoverBoneChanged(u32))); connect(ui->Viewport, SIGNAL(HoverBoneChanged(u32)), this, SLOT(OnViewportHoverBoneChanged(u32)));
connect(ui->Viewport, SIGNAL(ViewportClick(QMouseEvent*)), this, SLOT(OnViewportClick()));
connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open())); connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open()));
connect(ui->ActionShowSkeleton, SIGNAL(toggled(bool)), this, SLOT(ToggleSkeletonVisible(bool))); connect(ui->ActionShowSkeleton, SIGNAL(toggled(bool)), this, SLOT(ToggleSkeletonVisible(bool)));
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int))); connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
@ -54,6 +57,8 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
QList<int> SplitterSizes; QList<int> SplitterSizes;
SplitterSizes << width() * 0.2 << width() * 0.8; SplitterSizes << width() * 0.2 << width() * 0.8;
ui->splitter->setSizes(SplitterSizes); ui->splitter->setSizes(SplitterSizes);
connect(ui->SkeletonHierarchyTreeView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), this, SLOT(OnSkeletonTreeSelectionChanged(QModelIndex)));
} }
CCharacterEditor::~CCharacterEditor() CCharacterEditor::~CCharacterEditor()
@ -114,6 +119,16 @@ CAnimation* CCharacterEditor::CurrentAnimation() const
return nullptr; return nullptr;
} }
void CCharacterEditor::SetSelectedBone(CBone *pBone)
{
if (pBone != mpSelectedBone)
{
if (mpSelectedBone) mpSelectedBone->SetSelected(false);
mpSelectedBone = pBone;
if (mpSelectedBone) mpSelectedBone->SetSelected(true);
}
}
// ************ PUBLIC SLOTS ************ // ************ PUBLIC SLOTS ************
void CCharacterEditor::Open() void CCharacterEditor::Open()
{ {
@ -127,6 +142,10 @@ void CCharacterEditor::Open()
mpCharNode->SetCharSet(mpSet); mpCharNode->SetCharSet(mpSet);
setWindowTitle("Prime World Editor - Character Editor: " + TO_QSTRING(mpSet->Source())); setWindowTitle("Prime World Editor - Character Editor: " + TO_QSTRING(mpSet->Source()));
// Clear selected bone
ui->SkeletonHierarchyTreeView->selectionModel()->clear();
SetSelectedBone(nullptr);
// Set up character combo box // Set up character combo box
mpCharComboBox->blockSignals(true); mpCharComboBox->blockSignals(true);
mpCharComboBox->clear(); mpCharComboBox->clear();
@ -169,7 +188,7 @@ void CCharacterEditor::RefreshViewport()
ui->Viewport->Render(); ui->Viewport->Render();
} }
void CCharacterEditor::HoverBoneChanged(u32 BoneID) void CCharacterEditor::OnViewportHoverBoneChanged(u32 BoneID)
{ {
if (BoneID == 0xFFFFFFFF) if (BoneID == 0xFFFFFFFF)
ui->StatusBar->clearMessage(); ui->StatusBar->clearMessage();
@ -177,6 +196,32 @@ void CCharacterEditor::HoverBoneChanged(u32 BoneID)
ui->StatusBar->showMessage(QString("Bone %1: %2").arg(BoneID).arg( TO_QSTRING(mpSet->NodeSkeleton(mCurrentChar)->BoneByID(BoneID)->Name()) )); ui->StatusBar->showMessage(QString("Bone %1: %2").arg(BoneID).arg( TO_QSTRING(mpSet->NodeSkeleton(mCurrentChar)->BoneByID(BoneID)->Name()) ));
} }
void CCharacterEditor::OnViewportClick()
{
u32 HoverBoneID = ui->Viewport->HoverBoneID();
CSkeleton *pSkel = (mpSet ? mpSet->NodeSkeleton(mCurrentChar) : nullptr);
CBone *pBone = (pSkel ? pSkel->BoneByID(HoverBoneID) : nullptr);
if (!pBone || !pBone->IsSelected())
{
if (pBone)
{
QModelIndex NewBoneIndex = mSkeletonModel.IndexForBone(pBone);
ui->SkeletonHierarchyTreeView->selectionModel()->setCurrentIndex(NewBoneIndex, QItemSelectionModel::ClearAndSelect);
}
else
ui->SkeletonHierarchyTreeView->selectionModel()->clear();
SetSelectedBone(pBone);
}
}
void CCharacterEditor::OnSkeletonTreeSelectionChanged(const QModelIndex& rkIndex)
{
CBone *pBone = mSkeletonModel.BoneForIndex(rkIndex);
SetSelectedBone(pBone);
}
void CCharacterEditor::SetActiveCharacterIndex(int CharIndex) void CCharacterEditor::SetActiveCharacterIndex(int CharIndex)
{ {
mCurrentChar = CharIndex; mCurrentChar = CharIndex;

View File

@ -21,6 +21,7 @@ class CCharacterEditor : public QMainWindow
Ui::CCharacterEditor *ui; Ui::CCharacterEditor *ui;
CScene *mpScene; CScene *mpScene;
CCharacterNode *mpCharNode; CCharacterNode *mpCharNode;
CBone *mpSelectedBone;
CSkeletonHierarchyModel mSkeletonModel; CSkeletonHierarchyModel mSkeletonModel;
QComboBox *mpCharComboBox; QComboBox *mpCharComboBox;
@ -44,17 +45,20 @@ public:
~CCharacterEditor(); ~CCharacterEditor();
void UpdateAnimTime(); void UpdateAnimTime();
CAnimation* CurrentAnimation() const; CAnimation* CurrentAnimation() const;
void SetSelectedBone(CBone *pBone);
public slots: public slots:
void Open(); void Open();
void ToggleSkeletonVisible(bool Visible); void ToggleSkeletonVisible(bool Visible);
void RefreshViewport(); void RefreshViewport();
void HoverBoneChanged(u32 BoneID); void OnViewportHoverBoneChanged(u32 BoneID);
void OnViewportClick();
void OnSkeletonTreeSelectionChanged(const QModelIndex& rkIndex);
void SetActiveCharacterIndex(int CharIndex); void SetActiveCharacterIndex(int CharIndex);
void SetActiveAnimation(int AnimIndex); void SetActiveAnimation(int AnimIndex);
void SetAnimTime(int Time); void SetAnimTime(int Time);
void SetAnimTime(float Time); void SetAnimTime(float Time);
void TogglePlay(); void TogglePlay();
void ToggleLoop(bool Loop); void ToggleLoop(bool Loop);
void AnimSpeedSpinBoxChanged(double NewVal); void AnimSpeedSpinBoxChanged(double NewVal);

View File

@ -31,7 +31,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum> <enum>QAbstractItemView::SingleSelection</enum>
</property> </property>
<property name="verticalScrollMode"> <property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>

View File

@ -64,3 +64,11 @@ void CCharacterEditorViewport::OnResize()
{ {
mpRenderer->SetViewportSize(width(), height()); mpRenderer->SetViewportSize(width(), height());
} }
void CCharacterEditorViewport::OnMouseClick(QMouseEvent *pEvent)
{
if (pEvent->button() == Qt::LeftButton)
{
emit ViewportClick(pEvent);
}
}

View File

@ -21,9 +21,13 @@ public:
void CheckUserInput(); void CheckUserInput();
void Paint(); void Paint();
void OnResize(); void OnResize();
void OnMouseClick(QMouseEvent *pEvent);
inline u32 HoverBoneID() const { return mHoverBone; }
signals: signals:
void HoverBoneChanged(u32 BoneID); void HoverBoneChanged(u32 BoneID);
void ViewportClick(QMouseEvent *pEvent);
}; };
#endif // CCHARACTEREDITORVIEWPORT_H #endif // CCHARACTEREDITORVIEWPORT_H

View File

@ -76,6 +76,27 @@ QVariant CSkeletonHierarchyModel::data(const QModelIndex& rkIndex, int Role) con
return QVariant::Invalid; return QVariant::Invalid;
} }
CBone* CSkeletonHierarchyModel::BoneForIndex(const QModelIndex& rkIndex) const
{
return (CBone*) (rkIndex.internalPointer());
}
QModelIndex CSkeletonHierarchyModel::IndexForBone(CBone *pBone) const
{
CBone *pParent = pBone->Parent();
if (!pParent) return index(0, 0, QModelIndex());
QModelIndex ParentIndex = IndexForBone(pParent);
for (u32 iChild = 0; iChild < pParent->NumChildren(); iChild++)
{
if (pParent->ChildByIndex(iChild) == pBone)
return index(iChild, 0, ParentIndex);
}
return QModelIndex();
}
void CSkeletonHierarchyModel::SetSkeleton(CSkeleton *pSkel) void CSkeletonHierarchyModel::SetSkeleton(CSkeleton *pSkel)
{ {
if (mpSkeleton != pSkel) if (mpSkeleton != pSkel)

View File

@ -15,6 +15,9 @@ public:
int rowCount(const QModelIndex& rkParent) const; int rowCount(const QModelIndex& rkParent) const;
int columnCount(const QModelIndex& rkParent) const; int columnCount(const QModelIndex& rkParent) const;
QVariant data(const QModelIndex& rkIndex, int Role) const; QVariant data(const QModelIndex& rkIndex, int Role) const;
CBone* BoneForIndex(const QModelIndex& rkIndex) const;
QModelIndex IndexForBone(CBone *pBone) const;
void SetSkeleton(CSkeleton *pSkel); void SetSkeleton(CSkeleton *pSkel);
}; };

View File

@ -5,7 +5,7 @@
#include <math.h> #include <math.h>
CQuaternion::CQuaternion() CQuaternion::CQuaternion()
: W(0.f) : W(1.f)
, X(0.f) , X(0.f)
, Y(0.f) , Y(0.f)
, Z(0.f) , Z(0.f)