From ef523c23398d0553fbc22b71a9cd94b2e944e8a9 Mon Sep 17 00:00:00 2001 From: parax0 Date: Thu, 7 Apr 2016 12:15:24 -0600 Subject: [PATCH] Added support for loading uncompressed animations and playing them back --- src/Core/Core.pro | 8 +- src/Core/Resource/CAnimSet.h | 12 ++ src/Core/Resource/CAnimation.cpp | 37 ++++++ src/Core/Resource/CAnimation.h | 36 ++++++ src/Core/Resource/CResCache.cpp | 3 + src/Core/Resource/CSkeleton.cpp | 48 +++++++- src/Core/Resource/CSkeleton.h | 22 +++- src/Core/Resource/Factory/CAnimSetLoader.cpp | 7 +- .../Resource/Factory/CAnimationLoader.cpp | 105 ++++++++++++++++++ src/Core/Resource/Factory/CAnimationLoader.h | 19 ++++ src/Core/Resource/Factory/CSkeletonLoader.cpp | 10 +- src/Core/Resource/Factory/CSkeletonLoader.h | 3 +- src/Core/Scene/CCharacterNode.cpp | 10 +- src/Core/Scene/CCharacterNode.h | 3 + .../CharacterEditor/CCharacterEditor.cpp | 23 +++- src/Editor/CharacterEditor/CCharacterEditor.h | 2 + src/FileIO/FileIO.pro | 6 +- src/Math/CQuaternion.cpp | 38 +++++++ src/Math/CQuaternion.h | 2 + src/Math/CTransform4f.cpp | 14 +++ src/Math/CTransform4f.h | 2 + src/Math/MathUtil.h | 7 ++ 22 files changed, 399 insertions(+), 18 deletions(-) create mode 100644 src/Core/Resource/CAnimation.cpp create mode 100644 src/Core/Resource/CAnimation.h create mode 100644 src/Core/Resource/Factory/CAnimationLoader.cpp create mode 100644 src/Core/Resource/Factory/CAnimationLoader.h diff --git a/src/Core/Core.pro b/src/Core/Core.pro index a4ab6824..602679b8 100644 --- a/src/Core/Core.pro +++ b/src/Core/Core.pro @@ -188,7 +188,9 @@ HEADERS += \ Resource/Script/CLink.h \ Resource/CSkeleton.h \ Resource/Factory/CSkeletonLoader.h \ - Scene/CCharacterNode.h + Scene/CCharacterNode.h \ + Resource/CAnimation.h \ + Resource/Factory/CAnimationLoader.h # Source Files SOURCES += \ @@ -269,4 +271,6 @@ SOURCES += \ ScriptExtra/CSplinePathExtra.cpp \ Resource/CSkeleton.cpp \ Resource/Factory/CSkeletonLoader.cpp \ - Scene/CCharacterNode.cpp + Scene/CCharacterNode.cpp \ + Resource/CAnimation.cpp \ + Resource/Factory/CAnimationLoader.cpp diff --git a/src/Core/Resource/CAnimSet.h b/src/Core/Resource/CAnimSet.h index edba658a..25caf205 100644 --- a/src/Core/Resource/CAnimSet.h +++ b/src/Core/Resource/CAnimSet.h @@ -2,6 +2,7 @@ #define CANIMSET_H #include "TResPtr.h" +#include "CAnimation.h" #include "CResource.h" #include "CSkeleton.h" #include "Core/Resource/Model/CModel.h" @@ -26,6 +27,13 @@ class CAnimSet : public CResource }; std::vector mNodes; + struct SAnimation + { + TString Name; + TResPtr pAnim; + }; + std::vector mAnims; + public: CAnimSet() : CResource() {} @@ -33,6 +41,10 @@ public: TString NodeName(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].Name; } CModel* NodeModel(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pModel; } CSkeleton* NodeSkeleton(u32 Index) { if (Index >= mNodes.size()) Index = 0; return mNodes[Index].pSkeleton; } + + u32 NumAnims() const { return mAnims.size(); } + CAnimation* Animation(u32 Index) { if (Index >= mAnims.size()) Index = 0; return mAnims[Index].pAnim; } + TString AnimName(u32 Index) { if (Index >= mAnims.size()) Index = 0; return mAnims[Index].Name; } }; #endif // CCHARACTERSET_H diff --git a/src/Core/Resource/CAnimation.cpp b/src/Core/Resource/CAnimation.cpp new file mode 100644 index 00000000..95b9b914 --- /dev/null +++ b/src/Core/Resource/CAnimation.cpp @@ -0,0 +1,37 @@ +#include "CAnimation.h" +#include +#include + +CAnimation::CAnimation() + : mDuration(0.f) + , mTickInterval(0.0333333f) + , mNumKeys(0) +{ +} + +void CAnimation::EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const +{ + if (mDuration == 0.f) return; + + Time = fmodf(Time, mDuration); + if (Time >= FLT_EPSILON) Time -= FLT_EPSILON; + float t = fmodf(Time, mTickInterval) / mTickInterval; + u32 LowKey = (u32) (Time / mTickInterval); + + u8 RotChannel = mBoneInfo[BoneID].RotationChannelIdx; + u8 TransChannel = mBoneInfo[BoneID].TranslationChannelIdx; + + if (RotChannel != 0xFF) + { + const CQuaternion& rkLow = mRotationChannels[RotChannel][LowKey]; + const CQuaternion& rkHigh = mRotationChannels[RotChannel][LowKey + 1]; + rOut.Rotate( rkLow.Slerp(rkHigh, t) ); + } + + if (TransChannel != 0xFF) + { + const CVector3f& rkLow = mTranslationChannels[TransChannel][LowKey]; + const CVector3f& rkHigh = mTranslationChannels[TransChannel][LowKey + 1]; + rOut.Translate( Math::Lerp(rkLow, rkHigh, t) ); + } +} diff --git a/src/Core/Resource/CAnimation.h b/src/Core/Resource/CAnimation.h new file mode 100644 index 00000000..f461b3c8 --- /dev/null +++ b/src/Core/Resource/CAnimation.h @@ -0,0 +1,36 @@ +#ifndef CANIMATION_H +#define CANIMATION_H + +#include "CResource.h" +#include +#include +#include + +class CAnimation : public CResource +{ + DECLARE_RESOURCE_TYPE(eAnimation) + friend class CAnimationLoader; + + typedef std::vector TRotationChannel; + typedef std::vector TTranslationChannel; + + float mDuration; + float mTickInterval; + u32 mNumKeys; + + std::vector mRotationChannels; + std::vector mTranslationChannels; + + struct SBoneChannelInfo + { + u8 RotationChannelIdx; + u8 TranslationChannelIdx; + }; + std::vector mBoneInfo; + +public: + CAnimation(); + void EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const; +}; + +#endif // CANIMATION_H diff --git a/src/Core/Resource/CResCache.cpp b/src/Core/Resource/CResCache.cpp index cf4cfec0..eb873b24 100644 --- a/src/Core/Resource/CResCache.cpp +++ b/src/Core/Resource/CResCache.cpp @@ -1,5 +1,6 @@ #include "CResCache.h" #include "Core/Resource/Factory/CAreaLoader.h" +#include "Core/Resource/Factory/CAnimationLoader.h" #include "Core/Resource/Factory/CAnimSetLoader.h" #include "Core/Resource/Factory/CCollisionLoader.h" #include "Core/Resource/Factory/CFontLoader.h" @@ -151,6 +152,7 @@ CResource* CResCache::GetResource(CUniqueID ResID, CFourCC Type) else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(Mem); else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(Mem); else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(Mem); + else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(Mem); else SupportedFormat = false; // Log errors @@ -206,6 +208,7 @@ CResource* CResCache::GetResource(const TString& rkResPath) else if (Type == "DCLN") pRes = CCollisionLoader::LoadDCLN(File); else if (Type == "EGMC") pRes = CPoiToWorldLoader::LoadEGMC(File); else if (Type == "CINF") pRes = CSkeletonLoader::LoadCINF(File); + else if (Type == "ANIM") pRes = CAnimationLoader::LoadANIM(File); else SupportedFormat = false; if (!pRes) pRes = new CResource(); // Default for unsupported formats diff --git a/src/Core/Resource/CSkeleton.cpp b/src/Core/Resource/CSkeleton.cpp index 88381318..a015fc51 100644 --- a/src/Core/Resource/CSkeleton.cpp +++ b/src/Core/Resource/CSkeleton.cpp @@ -8,6 +8,43 @@ CBone::CBone(CSkeleton *pSkel) { } +void CBone::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot) +{ + mAnimTransform = CTransform4f::skIdentity; + + if (pAnim) + pAnim->EvaluateTransform(Time, mID, mAnimTransform); + + mAnimTransform.Translate(mPosition); + + if (mpParent) + mAnimTransform = mpParent->AnimTransform() * mAnimTransform; + + if (AnchorRoot && IsRoot()) + mAnimTransform.ZeroTranslation(); + + mAbsPosDirty = true; + + for (u32 iChild = 0; iChild < mChildren.size(); iChild++) + mChildren[iChild]->UpdateTransform(pAnim, Time, AnchorRoot); +} + +bool CBone::IsRoot() const +{ + return (mpParent == nullptr); +} + +CVector3f CBone::AbsolutePosition() const +{ + if (mAbsPosDirty) + { + mAbsolutePosition = (mAnimTransform * CVector3f::skZero); + mAbsPosDirty = false; + } + + return mAbsolutePosition; +} + // ************ CSkeleton ************ CSkeleton::CSkeleton() : mpRootBone(nullptr) @@ -31,6 +68,11 @@ CBone* CSkeleton::BoneByID(u32 BoneID) const return nullptr; } +void CSkeleton::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot) +{ + mpRootBone->UpdateTransform(pAnim, Time, AnchorRoot); +} + void CSkeleton::Draw(FRenderOptions /*Options*/) { for (u32 iBone = 0; iBone < mBones.size(); iBone++) @@ -39,8 +81,8 @@ void CSkeleton::Draw(FRenderOptions /*Options*/) // Draw bone CTransform4f Transform; - Transform.Scale(0.01f); - Transform.Translate(pBone->Position()); + Transform.Scale(0.025f); + Transform.Translate(pBone->AbsolutePosition()); CGraphics::sMVPBlock.ModelMatrix = Transform.ToMatrix4f(); CGraphics::UpdateMVPBlock(); CDrawUtil::DrawSphere(CColor::skWhite); @@ -50,6 +92,6 @@ void CSkeleton::Draw(FRenderOptions /*Options*/) CGraphics::UpdateMVPBlock(); for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++) - CDrawUtil::DrawLine(pBone->Position(), pBone->ChildByIndex(iChild)->Position()); + CDrawUtil::DrawLine(pBone->AbsolutePosition(), pBone->ChildByIndex(iChild)->AbsolutePosition()); } } diff --git a/src/Core/Resource/CSkeleton.h b/src/Core/Resource/CSkeleton.h index 21574313..8fbbca0e 100644 --- a/src/Core/Resource/CSkeleton.h +++ b/src/Core/Resource/CSkeleton.h @@ -1,10 +1,12 @@ #ifndef CSKELETON_H #define CSKELETON_H +#include "CAnimation.h" #include "CResource.h" #include "Core/Render/FRenderOptions.h" #include #include +#include #include class CSkeleton; @@ -20,14 +22,24 @@ class CBone CVector3f mPosition; TString mName; + CTransform4f mAnimTransform; + mutable bool mAbsPosDirty; + mutable CVector3f mAbsolutePosition; + public: CBone(CSkeleton *pSkel); + void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot); + bool IsRoot() const; // Accessors - inline u32 ID() const { return mID; } - inline CVector3f Position() const { return mPosition; } - inline u32 NumChildren() const { return mChildren.size(); } - inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; } + inline u32 ID() const { return mID; } + inline CVector3f Position() const { return mPosition; } + inline CBone* Parent() const { return mpParent; } + inline u32 NumChildren() const { return mChildren.size(); } + inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; } + inline const CTransform4f& AnimTransform() const { return mAnimTransform; } + + CVector3f AbsolutePosition() const; }; class CSkeleton : public CResource @@ -41,7 +53,7 @@ class CSkeleton : public CResource public: CSkeleton(); ~CSkeleton(); - + void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot); CBone* BoneByID(u32 BoneID) const; void Draw(FRenderOptions Options); }; diff --git a/src/Core/Resource/Factory/CAnimSetLoader.cpp b/src/Core/Resource/Factory/CAnimSetLoader.cpp index e473c228..e495e8b1 100644 --- a/src/Core/Resource/Factory/CAnimSetLoader.cpp +++ b/src/Core/Resource/Factory/CAnimSetLoader.cpp @@ -104,11 +104,15 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS) // Unfortunately that's all that's actually supported at the moment. Hope to expand later. // Since there's no size value I have to actually read the rest of the node to reach the next one u32 AnimCount = rANCS.ReadLong(); + bool ReadAnimNames = Loader.pSet->mAnims.empty(); + if (ReadAnimNames) Loader.pSet->mAnims.resize(AnimCount); + for (u32 iAnim = 0; iAnim < AnimCount; iAnim++) { rANCS.Seek(0x4, SEEK_CUR); if (Loader.mVersion == ePrime) rANCS.Seek(0x1, SEEK_CUR); - rANCS.ReadString(); + TString AnimName = rANCS.ReadString(); + if (ReadAnimNames) Loader.pSet->mAnims[iAnim].Name = AnimName; } // PAS Database @@ -165,7 +169,6 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS) u32 UnknownCount2 = rANCS.ReadLong(); rANCS.Seek(UnknownCount2 * 0x1C, SEEK_CUR); } - // Lots of work for data I'm not even using x.x } return Loader.pSet; diff --git a/src/Core/Resource/Factory/CAnimationLoader.cpp b/src/Core/Resource/Factory/CAnimationLoader.cpp new file mode 100644 index 00000000..9aaa2d71 --- /dev/null +++ b/src/Core/Resource/Factory/CAnimationLoader.cpp @@ -0,0 +1,105 @@ +#include "CAnimationLoader.h" +#include + +void CAnimationLoader::ReadUncompressedANIM() +{ + mpAnim->mDuration = mpInput->ReadFloat(); + mpInput->Seek(0x4, SEEK_CUR); // Skip unknown + mpAnim->mTickInterval = mpInput->ReadFloat(); + mpInput->Seek(0x4, SEEK_CUR); // Skip unknown + + mpAnim->mNumKeys = mpInput->ReadLong(); + mpInput->Seek(0x4, SEEK_CUR); // Skip unknown + + // Read bone channel info + u32 NumRotationChannels = 0; + u32 NumTranslationChannels = 0; + + u32 NumRotIndices = mpInput->ReadLong(); + std::vector RotIndices(NumRotIndices); + + for (u32 iRot = 0; iRot < NumRotIndices; iRot++) + { + RotIndices[iRot] = mpInput->ReadByte(); + + if (RotIndices[iRot] != 0xFF) + NumRotationChannels++; + } + + u32 NumTransIndices = mpInput->ReadLong(); + std::vector TransIndices(NumTransIndices); + + for (u32 iTrans = 0; iTrans < NumTransIndices; iTrans++) + { + TransIndices[iTrans] = mpInput->ReadByte(); + + if (TransIndices[iTrans] != 0xFF) + NumTranslationChannels++; + } + + // Set up bone channel info + mpAnim->mBoneInfo.resize(NumRotIndices); + + for (u32 iRot = 0, iTrans = 0; iRot < NumRotIndices; iRot++) + { + u8 RotIdx = RotIndices[iRot]; + mpAnim->mBoneInfo[iRot].RotationChannelIdx = RotIdx; + + if (RotIdx != 0xFF) + { + mpAnim->mBoneInfo[iRot].TranslationChannelIdx = TransIndices[iTrans]; + iTrans++; + } + else + mpAnim->mBoneInfo[iRot].TranslationChannelIdx = 0xFF; + } + + // Read bone transforms + mpInput->Seek(0x4, SEEK_CUR); // Skipping quaternion count + mpAnim->mRotationChannels.resize(NumRotationChannels); + + for (u32 iRot = 0; iRot < NumRotationChannels; iRot++) + { + mpAnim->mRotationChannels[iRot].resize(mpAnim->mNumKeys); + + for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++) + mpAnim->mRotationChannels[iRot][iKey] = CQuaternion(*mpInput); + } + + mpInput->Seek(0x4, SEEK_CUR); // Skipping vector3f count + mpAnim->mTranslationChannels.resize(NumTranslationChannels); + + for (u32 iTrans = 0; iTrans < NumTranslationChannels; iTrans++) + { + mpAnim->mTranslationChannels[iTrans].resize(mpAnim->mNumKeys); + + for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++) + mpAnim->mTranslationChannels[iTrans][iKey] = CVector3f(*mpInput); + } + + // Skip EVNT file +} + +// ************ STATIC ************ +CAnimation* CAnimationLoader::LoadANIM(IInputStream& rANIM) +{ + u32 CompressionType = rANIM.ReadLong(); + + if (CompressionType != 0 && CompressionType != 2) + { + Log::FileError(rANIM.GetSourceString(), "Unknown ANIM compression type: " + TString::HexString(CompressionType, 2)); + return nullptr; + } + + if (CompressionType == 2) + { + Log::FileError(rANIM.GetSourceString(), "Compressed ANIMs not supported yet"); + return nullptr; + } + + CAnimationLoader Loader; + Loader.mpAnim = new CAnimation(); + Loader.mpInput = &rANIM; + Loader.ReadUncompressedANIM(); + return Loader.mpAnim; +} diff --git a/src/Core/Resource/Factory/CAnimationLoader.h b/src/Core/Resource/Factory/CAnimationLoader.h new file mode 100644 index 00000000..96c53c07 --- /dev/null +++ b/src/Core/Resource/Factory/CAnimationLoader.h @@ -0,0 +1,19 @@ +#ifndef CANIMATIONLOADER_H +#define CANIMATIONLOADER_H + +#include "Core/Resource/TResPtr.h" +#include "Core/Resource/CAnimation.h" + +class CAnimationLoader +{ + TResPtr mpAnim; + IInputStream *mpInput; + + CAnimationLoader() {} + void ReadUncompressedANIM(); + +public: + static CAnimation* LoadANIM(IInputStream& rANIM); +}; + +#endif // CANIMATIONLOADER_H diff --git a/src/Core/Resource/Factory/CSkeletonLoader.cpp b/src/Core/Resource/Factory/CSkeletonLoader.cpp index 8c12aa36..0f6584b3 100644 --- a/src/Core/Resource/Factory/CSkeletonLoader.cpp +++ b/src/Core/Resource/Factory/CSkeletonLoader.cpp @@ -3,13 +3,19 @@ #include -CSkeletonLoader::CSkeletonLoader() +void CSkeletonLoader::SetLocalBoneCoords(CBone *pBone) { + for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++) + SetLocalBoneCoords(pBone->ChildByIndex(iChild)); + + if (pBone->mpParent) + pBone->mPosition -= pBone->mpParent->mPosition; } // ************ STATIC ************ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF) { + CSkeletonLoader Loader; CSkeleton *pSkel = new CSkeleton(); u32 NumBones = rCINF.ReadLong(); @@ -71,6 +77,8 @@ CSkeleton* CSkeletonLoader::LoadCINF(IInputStream& rCINF) } } + Loader.SetLocalBoneCoords(pSkel->mpRootBone); + // Skip bone ID array u32 NumBoneIDs = rCINF.ReadLong(); rCINF.Seek(NumBoneIDs * 4, SEEK_CUR); diff --git a/src/Core/Resource/Factory/CSkeletonLoader.h b/src/Core/Resource/Factory/CSkeletonLoader.h index 6091fa58..b74f3e6f 100644 --- a/src/Core/Resource/Factory/CSkeletonLoader.h +++ b/src/Core/Resource/Factory/CSkeletonLoader.h @@ -10,7 +10,8 @@ class CSkeletonLoader TResPtr mpSkeleton; EGame mVersion; - CSkeletonLoader(); + CSkeletonLoader() {} + void SetLocalBoneCoords(CBone *pBone); public: static CSkeleton* LoadCINF(IInputStream& rCINF); diff --git a/src/Core/Scene/CCharacterNode.cpp b/src/Core/Scene/CCharacterNode.cpp index 165248cc..a8b1e6e1 100644 --- a/src/Core/Scene/CCharacterNode.cpp +++ b/src/Core/Scene/CCharacterNode.cpp @@ -1,5 +1,6 @@ #include "CCharacterNode.h" -#include +#include "Core/Render/CRenderer.h" +#include CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/) : CSceneNode(pScene, NodeID, pParent) @@ -34,6 +35,8 @@ void CCharacterNode::AddToRenderer(CRenderer *pRenderer, const SViewInfo& /*rkVi void CCharacterNode::Draw(FRenderOptions Options, int /*ComponentIndex*/, const SViewInfo& /*rkViewInfo*/) { CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet); + CAnimation *pAnim = mpCharacter->Animation(mActiveAnim); + pSkel->UpdateTransform(pAnim, (float) CTimer::GlobalTime(), false); pSkel->Draw(Options); } @@ -62,3 +65,8 @@ void CCharacterNode::SetActiveCharSet(u32 CharIndex) MarkTransformChanged(); } } + +void CCharacterNode::SetActiveAnim(u32 AnimIndex) +{ + mActiveAnim = AnimIndex; +} diff --git a/src/Core/Scene/CCharacterNode.h b/src/Core/Scene/CCharacterNode.h index 0dad0de8..c98e3140 100644 --- a/src/Core/Scene/CCharacterNode.h +++ b/src/Core/Scene/CCharacterNode.h @@ -8,6 +8,7 @@ class CCharacterNode : public CSceneNode { TResPtr mpCharacter; u32 mActiveCharSet; + u32 mActiveAnim; public: explicit CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar = 0, CSceneNode *pParent = 0); @@ -19,9 +20,11 @@ public: 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; } void SetCharacter(CAnimSet *pChar); void SetActiveCharSet(u32 CharIndex); + void SetActiveAnim(u32 AnimIndex); }; #endif // CCHARACTERNODE_H diff --git a/src/Editor/CharacterEditor/CCharacterEditor.cpp b/src/Editor/CharacterEditor/CCharacterEditor.cpp index feb90de9..d388a40b 100644 --- a/src/Editor/CharacterEditor/CCharacterEditor.cpp +++ b/src/Editor/CharacterEditor/CCharacterEditor.cpp @@ -19,15 +19,21 @@ CCharacterEditor::CCharacterEditor(QWidget *parent) rCamera.SetMoveSpeed(0.5f); // Init UI + ui->ToolBar->addSeparator(); + mpCharComboBox = new QComboBox(this); mpCharComboBox->setMinimumWidth(175); - ui->ToolBar->addSeparator(); ui->ToolBar->addWidget(mpCharComboBox); + mpAnimComboBox = new QComboBox(this); + mpAnimComboBox->setMinimumWidth(175); + ui->ToolBar->addWidget(mpAnimComboBox); + connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport())); mRefreshTimer.start(0); connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int))); + connect(mpAnimComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveAnimation(int))); connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open())); } @@ -58,6 +64,16 @@ void CCharacterEditor::Open() SetActiveCharacterIndex(0); mpCharComboBox->blockSignals(false); + + // Set up anim combo box + mpAnimComboBox->blockSignals(true); + mpAnimComboBox->clear(); + + for (u32 iAnim = 0; iAnim < pSet->NumAnims(); iAnim++) + mpAnimComboBox->addItem( TO_QSTRING(pSet->AnimName(iAnim)) ); + + SetActiveAnimation(0); + mpAnimComboBox->blockSignals(false); } gResCache.Clean(); @@ -73,3 +89,8 @@ void CCharacterEditor::SetActiveCharacterIndex(int CharIndex) { mpCharNode->SetActiveCharSet((u32) CharIndex); } + +void CCharacterEditor::SetActiveAnimation(int AnimIndex) +{ + mpCharNode->SetActiveAnim((u32) AnimIndex); +} diff --git a/src/Editor/CharacterEditor/CCharacterEditor.h b/src/Editor/CharacterEditor/CCharacterEditor.h index 6ca1132a..7a0a0cfa 100644 --- a/src/Editor/CharacterEditor/CCharacterEditor.h +++ b/src/Editor/CharacterEditor/CCharacterEditor.h @@ -22,6 +22,7 @@ class CCharacterEditor : public QMainWindow CCharacterNode *mpCharNode; QComboBox *mpCharComboBox; + QComboBox *mpAnimComboBox; QTimer mRefreshTimer; public: @@ -32,6 +33,7 @@ public slots: void Open(); void RefreshViewport(); void SetActiveCharacterIndex(int CharIndex); + void SetActiveAnimation(int AnimIndex); }; #endif // CCHARACTEREDITORWINDOW_H diff --git a/src/FileIO/FileIO.pro b/src/FileIO/FileIO.pro index f2744568..4d973b03 100644 --- a/src/FileIO/FileIO.pro +++ b/src/FileIO/FileIO.pro @@ -41,7 +41,8 @@ HEADERS += \ FileIO.h \ IOUtil.h \ IInputStream.h \ - IOutputStream.h + IOutputStream.h \ + CBitStreamInWrapper.h # Source Files SOURCES += \ @@ -54,4 +55,5 @@ SOURCES += \ CVectorOutStream.cpp \ IOUtil.cpp \ IInputStream.cpp \ - IOutputStream.cpp + IOutputStream.cpp \ + CBitStreamInWrapper.cpp diff --git a/src/Math/CQuaternion.cpp b/src/Math/CQuaternion.cpp index 51816f60..9f629d5b 100644 --- a/src/Math/CQuaternion.cpp +++ b/src/Math/CQuaternion.cpp @@ -20,6 +20,14 @@ CQuaternion::CQuaternion(float _W, float _X, float _Y, float _Z) { } +CQuaternion::CQuaternion(IInputStream& rInput) + : W(rInput.ReadFloat()) + , X(rInput.ReadFloat()) + , Y(rInput.ReadFloat()) + , Z(rInput.ReadFloat()) +{ +} + CVector3f CQuaternion::XAxis() const { return (*this * CVector3f::skUnitX); @@ -48,6 +56,36 @@ CQuaternion CQuaternion::Inverse() const return CQuaternion::skZero; } +CQuaternion CQuaternion::Slerp(const CQuaternion& rkRight, float t) const +{ + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + float CosHalfTheta = (W * rkRight.W) + (X * rkRight.X) + (Y * rkRight.Y) + (Z * rkRight.Z); + + if (Math::Abs(CosHalfTheta) >= 1.f) + return *this; + + float ScalarA, ScalarB; + float SinHalfTheta = Math::Sqrt(1.f - (CosHalfTheta * CosHalfTheta)); + + if (Math::Abs(SinHalfTheta) < 0.001f) + { + ScalarA = 0.5f; + ScalarB = 0.5f; + } + + else + { + float HalfTheta = acosf(CosHalfTheta); + ScalarA = sinf((1.f - t) * HalfTheta) / SinHalfTheta; + ScalarB = sinf(t * HalfTheta) / SinHalfTheta; + } + + return CQuaternion( (W * ScalarA) + (rkRight.W * ScalarB), + (X * ScalarA) + (rkRight.X * ScalarB), + (Y * ScalarA) + (rkRight.Y * ScalarB), + (Z * ScalarA) + (rkRight.Z * ScalarB) ); +} + CVector3f CQuaternion::ToEuler() const { // There is more than one way to do this conversion, based on rotation order. diff --git a/src/Math/CQuaternion.h b/src/Math/CQuaternion.h index fa20128c..5ad6f288 100644 --- a/src/Math/CQuaternion.h +++ b/src/Math/CQuaternion.h @@ -10,11 +10,13 @@ public: CQuaternion(); CQuaternion(float _W, float _X, float _Y, float _Z); + CQuaternion(IInputStream& rInput); CVector3f XAxis() const; CVector3f YAxis() const; CVector3f ZAxis() const; CQuaternion Inverse() const; + CQuaternion Slerp(const CQuaternion& rkRight, float t) const; CVector3f ToEuler() const; // Operators diff --git a/src/Math/CTransform4f.cpp b/src/Math/CTransform4f.cpp index 6242c633..f76770fe 100644 --- a/src/Math/CTransform4f.cpp +++ b/src/Math/CTransform4f.cpp @@ -100,6 +100,13 @@ void CTransform4f::Scale(float XScale, float YScale, float ZScale) Scale(CVector3f(XScale, YScale, ZScale)); } +void CTransform4f::ZeroTranslation() +{ + m[0][3] = 0.f; + m[1][3] = 0.f; + m[2][3] = 0.f; +} + CTransform4f CTransform4f::MultiplyIgnoreTranslation(const CTransform4f& rkMtx) const { CTransform4f Out; @@ -153,6 +160,13 @@ CTransform4f CTransform4f::NoTranslation() const m[2][0], m[2][1], m[2][2], 0.f); } +CTransform4f CTransform4f::TranslationOnly() const +{ + return CTransform4f(1.f, 0.f, 0.f, m[0][3], + 0.f, 1.f, 0.f, m[1][3], + 0.f, 0.f, 1.f, m[2][3]); +} + CTransform4f CTransform4f::RotationOnly() const { return CTransform4f::FromMatrix4f(Inverse().ToMatrix4f().Transpose()); diff --git a/src/Math/CTransform4f.h b/src/Math/CTransform4f.h index a0b0b778..1e54218f 100644 --- a/src/Math/CTransform4f.h +++ b/src/Math/CTransform4f.h @@ -37,10 +37,12 @@ public: void Rotate(float XRot, float YRot, float ZRot); void Scale(CVector3f Scale); void Scale(float XScale, float YScale, float ZScale); + void ZeroTranslation(); CTransform4f MultiplyIgnoreTranslation(const CTransform4f& rkMtx) const; CTransform4f Inverse() const; CTransform4f QuickInverse() const; CTransform4f NoTranslation() const; + CTransform4f TranslationOnly() const; CTransform4f RotationOnly() const; // Conversion diff --git a/src/Math/MathUtil.h b/src/Math/MathUtil.h index 1c1be75d..03fc9bd7 100644 --- a/src/Math/MathUtil.h +++ b/src/Math/MathUtil.h @@ -22,6 +22,13 @@ float DegreesToRadians(float Deg); float RadiansToDegrees(float Rad); +template +Type Lerp(const Type& rkA, const Type& rkB, float t) +{ + Type Diff = rkB - rkA; + return rkA + (Diff * t); +} + std::pair RayPlaneIntersecton(const CRay& rkRay, const CPlane& rkPlane); std::pair RayBoxIntersection(const CRay& rkRay, const CAABox& rkBox);