Added a bunch of new options and tools to the character editor
This commit is contained in:
parent
7880dd34f4
commit
2db740e572
|
@ -179,6 +179,18 @@ void CCamera::SetOrbit(const CAABox& OrbitTarget, float DistScale /*= 4.f*/)
|
|||
}
|
||||
}
|
||||
|
||||
void CCamera::SetOrbitTarget(const CVector3f& rkOrbitTarget)
|
||||
{
|
||||
mOrbitTarget = rkOrbitTarget;
|
||||
|
||||
if (mMode == eOrbitCamera)
|
||||
{
|
||||
mTransformDirty = true;
|
||||
mViewDirty = true;
|
||||
mFrustumPlanesDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CCamera::SetOrbitDistance(float Distance)
|
||||
{
|
||||
mOrbitDistance = Distance;
|
||||
|
|
|
@ -65,6 +65,7 @@ public:
|
|||
void SetMoveMode(ECameraMoveMode Mode);
|
||||
void SetOrbit(const CVector3f& rkOrbitTarget, float Distance);
|
||||
void SetOrbit(const CAABox& rkOrbitTarget, float DistScale = 4.f);
|
||||
void SetOrbitTarget(const CVector3f& rkOrbitTarget);
|
||||
void SetOrbitDistance(float Distance);
|
||||
|
||||
// Inline Accessors
|
||||
|
|
|
@ -117,6 +117,7 @@ void CSkeleton::UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, fl
|
|||
void CSkeleton::Draw(FRenderOptions /*Options*/, const CBoneTransformData *pkData)
|
||||
{
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glLineWidth(1.f);
|
||||
|
||||
// Draw all child links first to minimize model matrix swaps.
|
||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/)
|
||||
: CSceneNode(pScene, NodeID, pParent)
|
||||
, mAnimated(true)
|
||||
, mAnimTime(0.f)
|
||||
{
|
||||
SetCharSet(pChar);
|
||||
|
@ -25,14 +26,15 @@ void CCharacterNode::PostLoad()
|
|||
|
||||
void CCharacterNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& rkViewInfo)
|
||||
{
|
||||
if (!mpCharacter) return;
|
||||
// todo: frustum check. Currently don't have a means of pulling the AABox for the
|
||||
// current animation so this isn't in yet.
|
||||
if (!mpCharacter) return;
|
||||
UpdateTransformData();
|
||||
|
||||
CModel *pModel = mpCharacter->NodeModel(mActiveCharSet);
|
||||
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
||||
|
||||
if (pModel)
|
||||
if (pModel && rkViewInfo.ShowFlags.HasFlag(eShowObjectGeometry))
|
||||
{
|
||||
if (!pModel->HasTransparency(0))
|
||||
pRenderer->AddMesh(this, -1, AABox(), false, eDrawMesh);
|
||||
|
@ -42,9 +44,6 @@ void CCharacterNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& rkView
|
|||
|
||||
if (pSkel)
|
||||
{
|
||||
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
|
||||
pSkel->UpdateTransform(mTransformData, pAnim, mAnimTime, false);
|
||||
|
||||
if (rkViewInfo.ShowFlags.HasFlag(eShowSkeletons))
|
||||
pRenderer->AddMesh(this, -2, AABox(), false, eDrawMesh, eForeground);
|
||||
}
|
||||
|
@ -57,6 +56,7 @@ void CCharacterNode::Draw(FRenderOptions Options, int ComponentIndex, const SVie
|
|||
// Draw skeleton
|
||||
if (ComponentIndex == -2)
|
||||
{
|
||||
LoadModelMatrix();
|
||||
pSkel->Draw(Options, &mTransformData);
|
||||
}
|
||||
|
||||
|
@ -70,9 +70,14 @@ void CCharacterNode::Draw(FRenderOptions Options, int ComponentIndex, const SVie
|
|||
CGraphics::sPixelBlock.LightmapMultiplier = 1.f;
|
||||
CGraphics::sPixelBlock.TevColor = CColor::skWhite;
|
||||
CGraphics::sPixelBlock.TintColor = TintColor(rkViewInfo);
|
||||
LoadModelMatrix();
|
||||
|
||||
// Draw surface OR draw entire model
|
||||
if (mAnimated)
|
||||
CGraphics::LoadBoneTransforms(mTransformData);
|
||||
else
|
||||
CGraphics::LoadIdentityBoneTransforms();
|
||||
|
||||
CModel *pModel = mpCharacter->NodeModel(mActiveCharSet);
|
||||
|
||||
if (ComponentIndex < 0)
|
||||
|
@ -91,6 +96,7 @@ SRayIntersection CCharacterNode::RayNodeIntersectTest(const CRay& rkRay, u32 /*A
|
|||
|
||||
if (pSkel)
|
||||
{
|
||||
UpdateTransformData();
|
||||
std::pair<s32,float> Hit = pSkel->RayIntersect(rkRay, mTransformData);
|
||||
|
||||
if (Hit.first != -1)
|
||||
|
@ -109,10 +115,22 @@ SRayIntersection CCharacterNode::RayNodeIntersectTest(const CRay& rkRay, u32 /*A
|
|||
return SRayIntersection();
|
||||
}
|
||||
|
||||
CVector3f CCharacterNode::BonePosition(u32 BoneID)
|
||||
{
|
||||
UpdateTransformData();
|
||||
CSkeleton *pSkel = (mpCharacter ? mpCharacter->NodeSkeleton(mActiveCharSet) : nullptr);
|
||||
CBone *pBone = (pSkel ? pSkel->BoneByID(BoneID) : nullptr);
|
||||
|
||||
CVector3f Out = AbsolutePosition();
|
||||
if (pBone) Out += pBone->TransformedPosition(mTransformData);
|
||||
return Out;
|
||||
}
|
||||
|
||||
void CCharacterNode::SetCharSet(CAnimSet *pChar)
|
||||
{
|
||||
mpCharacter = pChar;
|
||||
SetActiveChar(0);
|
||||
ConditionalSetDirty();
|
||||
|
||||
if (!mpCharacter)
|
||||
mLocalAABox = CAABox::skOne;
|
||||
|
@ -121,6 +139,7 @@ void CCharacterNode::SetCharSet(CAnimSet *pChar)
|
|||
void CCharacterNode::SetActiveChar(u32 CharIndex)
|
||||
{
|
||||
mActiveCharSet = CharIndex;
|
||||
ConditionalSetDirty();
|
||||
|
||||
if (mpCharacter)
|
||||
{
|
||||
|
@ -134,9 +153,16 @@ void CCharacterNode::SetActiveChar(u32 CharIndex)
|
|||
void CCharacterNode::SetActiveAnim(u32 AnimIndex)
|
||||
{
|
||||
mActiveAnim = AnimIndex;
|
||||
ConditionalSetDirty();
|
||||
}
|
||||
|
||||
void CCharacterNode::SetAnimTime(float Time)
|
||||
// ************ PROTECTED ************
|
||||
void CCharacterNode::UpdateTransformData()
|
||||
{
|
||||
mAnimTime = Time;
|
||||
if (mTransformDataDirty)
|
||||
{
|
||||
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
||||
if (pSkel) pSkel->UpdateTransform(mTransformData, CurrentAnim(), mAnimTime, false);
|
||||
mTransformDataDirty = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,11 @@ class CCharacterNode : public CSceneNode
|
|||
CBoneTransformData mTransformData;
|
||||
u32 mActiveCharSet;
|
||||
u32 mActiveAnim;
|
||||
bool mAnimated;
|
||||
float mAnimTime;
|
||||
|
||||
mutable bool mTransformDataDirty;
|
||||
|
||||
public:
|
||||
explicit CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar = 0, CSceneNode *pParent = 0);
|
||||
|
||||
|
@ -21,14 +24,26 @@ public:
|
|||
virtual void AddToRenderer(CRenderer *pRenderer, const SViewInfo& rkViewInfo);
|
||||
virtual void Draw(FRenderOptions Options, int ComponentIndex, const SViewInfo& rkViewInfo);
|
||||
virtual SRayIntersection RayNodeIntersectTest(const CRay& rkRay, u32 AssetID, const SViewInfo& rkViewInfo);
|
||||
inline CAnimSet* Character() const { return mpCharacter; }
|
||||
inline u32 ActiveCharSet() const { return mActiveCharSet; }
|
||||
inline u32 ActiveAnim() const { return mActiveAnim; }
|
||||
|
||||
CVector3f BonePosition(u32 BoneID);
|
||||
void SetCharSet(CAnimSet *pChar);
|
||||
void SetActiveChar(u32 CharIndex);
|
||||
void SetActiveAnim(u32 AnimIndex);
|
||||
void SetAnimTime(float Time);
|
||||
|
||||
inline CAnimSet* Character() const { return mpCharacter; }
|
||||
inline u32 ActiveCharIndex() const { return mActiveCharSet; }
|
||||
inline u32 ActiveAnimIndex() const { return mActiveAnim; }
|
||||
inline CAnimation* CurrentAnim() const { return (mAnimated && mpCharacter ? mpCharacter->Animation(mActiveAnim) : nullptr); }
|
||||
inline bool IsAnimated() const { return (mAnimated && CurrentAnim() != nullptr); }
|
||||
|
||||
void SetAnimated(bool Animated) { mAnimated = Animated; SetDirty(); }
|
||||
void SetAnimTime(float Time) { mAnimTime = Time; ConditionalSetDirty(); }
|
||||
|
||||
protected:
|
||||
inline bool IsDirty() { return mTransformDataDirty; }
|
||||
inline void SetDirty() { mTransformDataDirty = true; }
|
||||
inline void ConditionalSetDirty() { if (IsAnimated()) SetDirty(); }
|
||||
void UpdateTransformData();
|
||||
};
|
||||
|
||||
#endif // CCHARACTERNODE_H
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
#include <Common/Assert.h>
|
||||
#include <Math/MathUtil.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QTreeView>
|
||||
|
||||
const CVector3f CCharacterEditor::skDefaultOrbitTarget = CVector3f(0,0,1);
|
||||
const float CCharacterEditor::skDefaultOrbitDistance = 4.f;
|
||||
|
||||
CCharacterEditor::CCharacterEditor(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::CCharacterEditor)
|
||||
, mpScene(new CScene())
|
||||
, mpCharNode(new CCharacterNode(mpScene, -1))
|
||||
, mpSelectedBone(nullptr)
|
||||
, mBindPose(false)
|
||||
, mAnimTime(0.f)
|
||||
, mPlayAnim(true)
|
||||
, mLoopAnim(true)
|
||||
|
@ -22,9 +27,9 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
|
|||
ui->Viewport->SetNode(mpCharNode);
|
||||
|
||||
CCamera& rCamera = ui->Viewport->Camera();
|
||||
rCamera.Snap(CVector3f(0, 3, 1));
|
||||
rCamera.SetOrbit(CVector3f(0, 0, 1), 3.f);
|
||||
rCamera.SetMoveSpeed(0.5f);
|
||||
rCamera.SetPitch(-0.3f);
|
||||
rCamera.SetMoveMode(eOrbitCamera);
|
||||
|
||||
// Init UI
|
||||
ui->ToolBar->addSeparator();
|
||||
|
@ -43,13 +48,24 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
|
|||
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->ActionShowGrid, SIGNAL(toggled(bool)), this, SLOT(ToggleGrid(bool)));
|
||||
connect(ui->ActionShowMesh, SIGNAL(toggled(bool)), this, SLOT(ToggleMeshVisible(bool)));
|
||||
connect(ui->ActionShowSkeleton, SIGNAL(toggled(bool)), this, SLOT(ToggleSkeletonVisible(bool)));
|
||||
connect(ui->ActionBindPose, SIGNAL(toggled(bool)), this, SLOT(ToggleBindPose(bool)));
|
||||
connect(ui->ActionOrbit, SIGNAL(toggled(bool)), this, SLOT(ToggleOrbit(bool)));
|
||||
connect(ui->ActionPlay, SIGNAL(triggered()), this, SLOT(TogglePlay()));
|
||||
connect(ui->ActionRewind, SIGNAL(triggered()), this, SLOT(Rewind()));
|
||||
connect(ui->ActionFastForward, SIGNAL(triggered()), this, SLOT(FastForward()));
|
||||
connect(ui->ActionPrevAnim, SIGNAL(triggered()), this, SLOT(PrevAnim()));
|
||||
connect(ui->ActionNextAnim, SIGNAL(triggered()), this, SLOT(NextAnim()));
|
||||
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
|
||||
connect(mpAnimComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveAnimation(int)));
|
||||
|
||||
connect(ui->AnimSlider, SIGNAL(valueChanged(int)), this, SLOT(SetAnimTime(int)));
|
||||
connect(ui->PlayPauseButton, SIGNAL(pressed()), this, SLOT(TogglePlay()));
|
||||
connect(ui->LoopButton, SIGNAL(toggled(bool)), this, SLOT(ToggleLoop(bool)));
|
||||
connect(ui->RewindButton, SIGNAL(pressed()), this, SLOT(Rewind()));
|
||||
connect(ui->FastForwardButton, SIGNAL(pressed()), this, SLOT(FastForward()));
|
||||
connect(ui->AnimSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(AnimSpeedSpinBoxChanged(double)));
|
||||
|
||||
// Init skeleton tree view
|
||||
|
@ -59,23 +75,6 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
|
|||
ui->splitter->setSizes(SplitterSizes);
|
||||
|
||||
connect(ui->SkeletonHierarchyTreeView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnSkeletonTreeSelectionChanged(QModelIndex)));
|
||||
|
||||
// Set up keyboard shortcuts
|
||||
QAction *pTogglePlayAction = new QAction(this);
|
||||
pTogglePlayAction->setShortcut(QKeySequence("Space"));
|
||||
connect(pTogglePlayAction, SIGNAL(triggered()), this, SLOT(TogglePlay()));
|
||||
|
||||
QAction *pPrevAnimAction = new QAction(this);
|
||||
pPrevAnimAction->setShortcut(QKeySequence("R"));
|
||||
connect(pPrevAnimAction, SIGNAL(triggered()), this, SLOT(PrevAnim()));
|
||||
|
||||
QAction *pNextAnimAction = new QAction(this);
|
||||
pNextAnimAction->setShortcut(QKeySequence("F"));
|
||||
connect(pNextAnimAction, SIGNAL(triggered()), this, SLOT(NextAnim()));
|
||||
|
||||
QList<QAction*> ShortcutActions;
|
||||
ShortcutActions << pTogglePlayAction << pPrevAnimAction << pNextAnimAction;
|
||||
addActions(ShortcutActions);
|
||||
}
|
||||
|
||||
CCharacterEditor::~CCharacterEditor()
|
||||
|
@ -91,7 +90,7 @@ void CCharacterEditor::UpdateAnimTime()
|
|||
|
||||
CAnimation *pAnim = CurrentAnimation();
|
||||
|
||||
if (pAnim && mPlayAnim && !ui->AnimSlider->isSliderDown())
|
||||
if (pAnim && mPlayAnim && !mBindPose && !ui->AnimSlider->isSliderDown())
|
||||
{
|
||||
mAnimTime += DeltaTime * mPlaybackSpeed;
|
||||
|
||||
|
@ -128,6 +127,44 @@ void CCharacterEditor::UpdateAnimTime()
|
|||
}
|
||||
}
|
||||
|
||||
void CCharacterEditor::UpdateCameraOrbit()
|
||||
{
|
||||
CSkeleton *pSkel = CurrentSkeleton();
|
||||
|
||||
if (!pSkel)
|
||||
{
|
||||
// Center around character if we have one, otherwise fall back to default orbit.
|
||||
if (mpSet)
|
||||
ui->Viewport->Camera().SetOrbitTarget(mpCharNode->CenterPoint());
|
||||
else
|
||||
ui->Viewport->Camera().SetOrbit(skDefaultOrbitTarget, skDefaultOrbitDistance);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// If we have a selected bone, orbit around that.
|
||||
if (mpSelectedBone)
|
||||
ui->Viewport->Camera().SetOrbitTarget(mpCharNode->BonePosition(mpSelectedBone->ID()));
|
||||
|
||||
// Otherwise, try to find Skeleton_Root. Barring that, we can orbit the root bone.
|
||||
else
|
||||
{
|
||||
CBone *pRoot = pSkel->RootBone();
|
||||
CBone *pSkelRoot = (pRoot ? pRoot->ChildByIndex(0) : pRoot);
|
||||
CVector3f OrbitTarget = (pSkelRoot ? mpCharNode->BonePosition(pSkelRoot->ID()) : mpCharNode->CenterPoint());
|
||||
ui->Viewport->Camera().SetOrbitTarget(OrbitTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CSkeleton* CCharacterEditor::CurrentSkeleton() const
|
||||
{
|
||||
if (mpSet)
|
||||
return mpSet->NodeSkeleton(mCurrentChar);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CAnimation* CCharacterEditor::CurrentAnimation() const
|
||||
{
|
||||
if (mpSet)
|
||||
|
@ -152,10 +189,11 @@ void CCharacterEditor::Open()
|
|||
QString CharFilename = QFileDialog::getOpenFileName(this, "Open Character", "", "Animation Character Set (*.ANCS)");
|
||||
if (CharFilename.isEmpty()) return;
|
||||
|
||||
mpSet = gResCache.GetResource(CharFilename.toStdString());
|
||||
CAnimSet *pSet = (CAnimSet*) gResCache.GetResource(CharFilename.toStdString());
|
||||
|
||||
if (mpSet)
|
||||
if (pSet)
|
||||
{
|
||||
mpSet = pSet;
|
||||
mpCharNode->SetCharSet(mpSet);
|
||||
setWindowTitle("Prime World Editor - Character Editor: " + TO_QSTRING(mpSet->Source()));
|
||||
|
||||
|
@ -189,21 +227,67 @@ void CCharacterEditor::Open()
|
|||
ui->SkeletonHierarchyTreeView->expandAll();
|
||||
ui->SkeletonHierarchyTreeView->resizeColumnToContents(0);
|
||||
|
||||
// Would rather it just clear the selection on load, but it keeps selecting root by itself, so this is my workaround. :/
|
||||
ui->SkeletonHierarchyTreeView->selectionModel()->setCurrentIndex( mSkeletonModel.index(0, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect );
|
||||
// Select first child bone of root (which should be Skeleton_Root) to line up the camera for orbiting.
|
||||
QModelIndex RootIndex = mSkeletonModel.index(0, 0, QModelIndex());
|
||||
ui->SkeletonHierarchyTreeView->selectionModel()->setCurrentIndex( mSkeletonModel.index(0, 0, RootIndex), QItemSelectionModel::ClearAndSelect );
|
||||
|
||||
// Run CCamera::SetOrbit to reset orbit distance.
|
||||
ui->Viewport->Camera().SetOrbit(mpCharNode->AABox(), 4.f);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, "Error", "Couldn't load file: " + CharFilename);
|
||||
}
|
||||
|
||||
gResCache.Clean();
|
||||
}
|
||||
|
||||
void CCharacterEditor::ToggleGrid(bool Enable)
|
||||
{
|
||||
ui->Viewport->SetGridEnabled(Enable);
|
||||
}
|
||||
|
||||
void CCharacterEditor::ToggleMeshVisible(bool Visible)
|
||||
{
|
||||
// eShowObjectGeometry isn't the best fit, but close enough...?
|
||||
ui->Viewport->SetShowFlag(eShowObjectGeometry, Visible);
|
||||
}
|
||||
|
||||
void CCharacterEditor::ToggleSkeletonVisible(bool Visible)
|
||||
{
|
||||
ui->Viewport->SetShowFlag(eShowSkeletons, Visible);
|
||||
}
|
||||
|
||||
void CCharacterEditor::ToggleBindPose(bool Enable)
|
||||
{
|
||||
mpCharNode->SetAnimated(!Enable);
|
||||
|
||||
if (sender() != ui->ActionBindPose)
|
||||
{
|
||||
ui->ActionBindPose->blockSignals(true);
|
||||
ui->ActionBindPose->setChecked(Enable);
|
||||
ui->ActionBindPose->blockSignals(false);
|
||||
}
|
||||
|
||||
if (Enable && mPlayAnim)
|
||||
{
|
||||
SetAnimTime(0.f);
|
||||
}
|
||||
|
||||
ui->AnimSlider->setEnabled(!Enable);
|
||||
mBindPose = Enable;
|
||||
}
|
||||
|
||||
void CCharacterEditor::ToggleOrbit(bool Enable)
|
||||
{
|
||||
ui->Viewport->Camera().SetMoveMode(Enable ? eOrbitCamera : eFreeCamera);
|
||||
}
|
||||
|
||||
void CCharacterEditor::RefreshViewport()
|
||||
{
|
||||
UpdateAnimTime();
|
||||
UpdateCameraOrbit();
|
||||
ui->Viewport->ProcessInput();
|
||||
ui->Viewport->Render();
|
||||
}
|
||||
|
@ -284,9 +368,10 @@ void CCharacterEditor::SetAnimTime(int Time)
|
|||
|
||||
void CCharacterEditor::SetAnimTime(float Time)
|
||||
{
|
||||
if (mBindPose) Time = 0.f;
|
||||
mAnimTime = Time;
|
||||
|
||||
if (ui->AnimSlider != sender())
|
||||
if (ui->AnimSlider != sender() || mBindPose)
|
||||
{
|
||||
int IntTime = (int) (Time * 1000);
|
||||
ui->AnimSlider->setValue(IntTime);
|
||||
|
@ -308,10 +393,19 @@ void CCharacterEditor::SetAnimTime(float Time)
|
|||
|
||||
void CCharacterEditor::TogglePlay()
|
||||
{
|
||||
if (mBindPose) ToggleBindPose(false);
|
||||
|
||||
mPlayAnim = !mPlayAnim;
|
||||
QString NewText = (mPlayAnim ? "Pause" : "Play");
|
||||
ui->PlayPauseButton->setText(NewText);
|
||||
|
||||
if (ui->ActionPlay != sender())
|
||||
{
|
||||
ui->ActionPlay->blockSignals(true);
|
||||
ui->ActionPlay->setChecked(mPlayAnim);
|
||||
ui->ActionPlay->blockSignals(false);
|
||||
}
|
||||
|
||||
CAnimation *pAnim = CurrentAnimation();
|
||||
|
||||
if (pAnim && mPlayAnim)
|
||||
|
@ -331,6 +425,17 @@ void CCharacterEditor::ToggleLoop(bool Loop)
|
|||
ui->LoopButton->setChecked(Loop);
|
||||
}
|
||||
|
||||
void CCharacterEditor::Rewind()
|
||||
{
|
||||
SetAnimTime(0.f);
|
||||
}
|
||||
|
||||
void CCharacterEditor::FastForward()
|
||||
{
|
||||
CAnimation *pAnim = CurrentAnimation();
|
||||
if (pAnim && !mBindPose) SetAnimTime(pAnim->Duration());
|
||||
}
|
||||
|
||||
void CCharacterEditor::AnimSpeedSpinBoxChanged(double NewVal)
|
||||
{
|
||||
mPlaybackSpeed = (float) NewVal;
|
||||
|
|
|
@ -31,7 +31,7 @@ class CCharacterEditor : public QMainWindow
|
|||
TResPtr<CAnimSet> mpSet;
|
||||
u32 mCurrentChar;
|
||||
u32 mCurrentAnim;
|
||||
bool mShowSkeleton;
|
||||
bool mBindPose;
|
||||
|
||||
// Playback Controls
|
||||
double mLastAnimUpdate;
|
||||
|
@ -40,16 +40,26 @@ class CCharacterEditor : public QMainWindow
|
|||
bool mLoopAnim;
|
||||
float mPlaybackSpeed;
|
||||
|
||||
// Constants
|
||||
static const CVector3f skDefaultOrbitTarget;
|
||||
static const float skDefaultOrbitDistance;
|
||||
|
||||
public:
|
||||
explicit CCharacterEditor(QWidget *parent = 0);
|
||||
~CCharacterEditor();
|
||||
void UpdateAnimTime();
|
||||
void UpdateCameraOrbit();
|
||||
CSkeleton* CurrentSkeleton() const;
|
||||
CAnimation* CurrentAnimation() const;
|
||||
void SetSelectedBone(CBone *pBone);
|
||||
|
||||
public slots:
|
||||
void Open();
|
||||
void ToggleGrid(bool Enable);
|
||||
void ToggleMeshVisible(bool Visible);
|
||||
void ToggleSkeletonVisible(bool Visible);
|
||||
void ToggleBindPose(bool Enable);
|
||||
void ToggleOrbit(bool Enable);
|
||||
void RefreshViewport();
|
||||
void OnViewportHoverBoneChanged(u32 BoneID);
|
||||
void OnViewportClick();
|
||||
|
@ -63,6 +73,8 @@ public slots:
|
|||
void SetAnimTime(float Time);
|
||||
void TogglePlay();
|
||||
void ToggleLoop(bool Loop);
|
||||
void Rewind();
|
||||
void FastForward();
|
||||
void AnimSpeedSpinBoxChanged(double NewVal);
|
||||
};
|
||||
|
||||
|
|
|
@ -83,6 +83,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="RewindButton">
|
||||
<property name="text">
|
||||
<string>Rewind</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="PlayPauseButton">
|
||||
<property name="text">
|
||||
|
@ -90,6 +97,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="FastForwardButton">
|
||||
<property name="text">
|
||||
<string>Fast Forward</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
@ -180,7 +194,25 @@
|
|||
</property>
|
||||
<addaction name="ActionOpen"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>Animation</string>
|
||||
</property>
|
||||
<addaction name="ActionShowGrid"/>
|
||||
<addaction name="ActionShowMesh"/>
|
||||
<addaction name="ActionShowSkeleton"/>
|
||||
<addaction name="ActionBindPose"/>
|
||||
<addaction name="ActionOrbit"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="ActionPlay"/>
|
||||
<addaction name="ActionRewind"/>
|
||||
<addaction name="ActionFastForward"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="ActionPrevAnim"/>
|
||||
<addaction name="ActionNextAnim"/>
|
||||
</widget>
|
||||
<addaction name="MenuFile"/>
|
||||
<addaction name="menuView"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="StatusBar"/>
|
||||
<widget class="QToolBar" name="ToolBar">
|
||||
|
@ -195,7 +227,11 @@
|
|||
</attribute>
|
||||
<addaction name="ActionOpen"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="ActionShowGrid"/>
|
||||
<addaction name="ActionShowMesh"/>
|
||||
<addaction name="ActionShowSkeleton"/>
|
||||
<addaction name="ActionBindPose"/>
|
||||
<addaction name="ActionOrbit"/>
|
||||
</widget>
|
||||
<action name="ActionOpen">
|
||||
<property name="icon">
|
||||
|
@ -222,6 +258,121 @@
|
|||
<property name="toolTip">
|
||||
<string>Show Skeleton</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionShowMesh">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Mesh</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Mesh</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionBindPose">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bind Pose</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Bind Pose</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>B</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionOrbit">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Orbit</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Orbit</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Z</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionShowGrid">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Icons.qrc">
|
||||
<normaloff>:/icons/GridLight.png</normaloff>:/icons/GridLight.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show Grid</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>G</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionPrevAnim">
|
||||
<property name="text">
|
||||
<string>Previous Anim</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>R</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionNextAnim">
|
||||
<property name="text">
|
||||
<string>Next Anim</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionPlay">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Play</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Space</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionRewind">
|
||||
<property name="text">
|
||||
<string>Rewind</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+R</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="ActionFastForward">
|
||||
<property name="text">
|
||||
<string>Fast Forward</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+F</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
CCharacterEditorViewport::CCharacterEditorViewport(QWidget *pParent /*= 0*/)
|
||||
: CBasicViewport(pParent)
|
||||
, mpCharNode(nullptr)
|
||||
, mGridEnabled(true)
|
||||
{
|
||||
mpRenderer = new CRenderer();
|
||||
mpRenderer->SetViewportSize(width(), height());
|
||||
mpRenderer->SetClearColor(CColor(0.3f, 0.3f, 0.3f));
|
||||
mpRenderer->ToggleGrid(true);
|
||||
|
||||
mViewInfo.ShowFlags = eShowNone; // The mesh doesn't check any show flags so this just disables the skeleton.
|
||||
mViewInfo.ShowFlags = eShowObjectGeometry; // This enables the mesh and not the skeleton by default
|
||||
mViewInfo.pRenderer = mpRenderer;
|
||||
mViewInfo.pScene = nullptr;
|
||||
mViewInfo.GameMode = false;
|
||||
|
@ -49,7 +50,7 @@ void CCharacterEditorViewport::Paint()
|
|||
{
|
||||
mpRenderer->BeginFrame();
|
||||
mCamera.LoadMatrices();
|
||||
mGrid.AddToRenderer(mpRenderer, mViewInfo);
|
||||
if (mGridEnabled) mGrid.AddToRenderer(mpRenderer, mViewInfo);
|
||||
|
||||
if (mpCharNode)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ class CCharacterEditorViewport : public CBasicViewport
|
|||
CGridRenderable mGrid;
|
||||
CRenderer *mpRenderer;
|
||||
u32 mHoverBone;
|
||||
bool mGridEnabled;
|
||||
|
||||
public:
|
||||
CCharacterEditorViewport(QWidget *pParent = 0);
|
||||
|
@ -24,6 +25,7 @@ public:
|
|||
void OnMouseClick(QMouseEvent *pEvent);
|
||||
|
||||
inline u32 HoverBoneID() const { return mHoverBone; }
|
||||
inline void SetGridEnabled(bool Enable) { mGridEnabled = Enable; }
|
||||
|
||||
signals:
|
||||
void HoverBoneChanged(u32 BoneID);
|
||||
|
|
Loading…
Reference in New Issue