Added support for bone selection in the character editor
This commit is contained in:
parent
ed16d05136
commit
709087d2fe
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue