Fixed animation loader bug, added CBoneTransformData to separate animation transforms away from CSkeleton, added skeleton raycasting, added a bunch of animation playback controls to the character editor

This commit is contained in:
parax0
2016-04-10 06:49:42 -06:00
parent dfdbed24c4
commit feace9e38c
23 changed files with 508 additions and 98 deletions

View File

@@ -18,10 +18,11 @@ void CAnimation::EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) c
{
if (mDuration == 0.f) return;
Time = fmodf(Time, mDuration);
if (Time >= mDuration) Time = mDuration;
if (Time >= FLT_EPSILON) Time -= FLT_EPSILON;
float t = fmodf(Time, mTickInterval) / mTickInterval;
u32 LowKey = (u32) (Time / mTickInterval);
if (LowKey == (mNumKeys - 1)) LowKey = mNumKeys - 2;
u8 RotChannel = mBoneInfo[BoneID].RotationChannelIdx;
u8 TransChannel = mBoneInfo[BoneID].TranslationChannelIdx;

View File

@@ -32,6 +32,10 @@ public:
CAnimation();
void EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const;
bool HasTranslation(u32 BoneID) const;
inline float Duration() const { return mDuration; }
inline u32 NumKeys() const { return mNumKeys; }
inline float TickInterval() const { return mTickInterval; }
};
#endif // CANIMATION_H

View File

@@ -1,6 +1,8 @@
#include "CSkeleton.h"
#include "Core/Render/CBoneTransformData.h"
#include "Core/Render/CDrawUtil.h"
#include "Core/Render/CGraphics.h"
#include <Math/MathUtil.h>
// ************ CBone ************
CBone::CBone(CSkeleton *pSkel)
@@ -8,26 +10,25 @@ CBone::CBone(CSkeleton *pSkel)
{
}
void CBone::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot)
void CBone::UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot)
{
mAnimTransform = CTransform4f::skIdentity;
CTransform4f& rTransform = rData[mID];
rTransform.SetIdentity();
if (pAnim)
pAnim->EvaluateTransform(Time, mID, mAnimTransform);
pAnim->EvaluateTransform(Time, mID, rTransform);
if (!pAnim || !pAnim->HasTranslation(mID))
mAnimTransform.Translate(mPosition);
rTransform.Translate(mPosition);
if (mpParent)
mAnimTransform = mpParent->AnimTransform() * mAnimTransform;
rTransform = rData[mpParent->ID()] * rTransform;
if (AnchorRoot && IsRoot())
mAnimTransform.ZeroTranslation();
mAbsPosDirty = true;
rTransform.ZeroTranslation();
for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
mChildren[iChild]->UpdateTransform(pAnim, Time, AnchorRoot);
mChildren[iChild]->UpdateTransform(rData, pAnim, Time, AnchorRoot);
}
bool CBone::IsRoot() const
@@ -35,18 +36,9 @@ bool CBone::IsRoot() const
return (mpParent == nullptr);
}
CVector3f CBone::AbsolutePosition() const
{
if (mAbsPosDirty)
{
mAbsolutePosition = (mAnimTransform * CVector3f::skZero);
mAbsPosDirty = false;
}
return mAbsolutePosition;
}
// ************ CSkeleton ************
const float CSkeleton::skSphereRadius = 0.025f;
CSkeleton::CSkeleton()
: mpRootBone(nullptr)
{
@@ -69,21 +61,35 @@ CBone* CSkeleton::BoneByID(u32 BoneID) const
return nullptr;
}
void CSkeleton::UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot)
u32 CSkeleton::MaxBoneID() const
{
mpRootBone->UpdateTransform(pAnim, Time, AnchorRoot);
u32 ID = 0;
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
{
if (mBones[iBone]->ID() > ID)
ID = mBones[iBone]->ID();
}
return ID;
}
void CSkeleton::Draw(FRenderOptions /*Options*/)
void CSkeleton::UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot)
{
mpRootBone->UpdateTransform(rData, pAnim, Time, AnchorRoot);
}
void CSkeleton::Draw(FRenderOptions /*Options*/, const CBoneTransformData& rkData)
{
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
{
CBone *pBone = mBones[iBone];
const CTransform4f& rkBoneTransform = rkData[pBone->ID()];
// Draw bone
CTransform4f Transform;
Transform.Scale(0.025f);
Transform.Translate(pBone->AbsolutePosition());
Transform.Scale(skSphereRadius);
Transform.Translate(rkBoneTransform.ExtractTranslation());
CGraphics::sMVPBlock.ModelMatrix = Transform;
CGraphics::UpdateMVPBlock();
CDrawUtil::DrawSphere(CColor::skWhite);
@@ -93,6 +99,29 @@ void CSkeleton::Draw(FRenderOptions /*Options*/)
CGraphics::UpdateMVPBlock();
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
CDrawUtil::DrawLine(pBone->AbsolutePosition(), pBone->ChildByIndex(iChild)->AbsolutePosition());
{
const CTransform4f& rkChildTransform = rkData[pBone->ChildByIndex(iChild)->ID()];
CDrawUtil::DrawLine(rkBoneTransform.ExtractTranslation(), rkChildTransform.ExtractTranslation());
}
}
}
std::pair<s32,float> CSkeleton::RayIntersect(const CRay& rkRay, const CBoneTransformData& rkData)
{
std::pair<s32,float> Out(-1, FLT_MAX);
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
{
CBone *pBone = mBones[iBone];
CVector3f BonePos = rkData[pBone->ID()].ExtractTranslation();
std::pair<bool,float> Intersect = Math::RaySphereIntersection(rkRay, BonePos, skSphereRadius);
if (Intersect.first && Intersect.second < Out.second)
{
Out.first = pBone->ID();
Out.second = Intersect.second;
}
}
return Out;
}

View File

@@ -6,9 +6,10 @@
#include "Core/Render/FRenderOptions.h"
#include <Common/TString.h>
#include <Common/types.h>
#include <Math/CTransform4f.h>
#include <Math/CRay.h>
#include <Math/CVector3f.h>
class CBoneTransformData;
class CSkeleton;
class CBone
@@ -22,24 +23,19 @@ 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);
void UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot);
bool IsRoot() const;
// Accessors
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;
inline CSkeleton* Skeleton() const { return mpSkeleton; }
inline CBone* Parent() const { return mpParent; }
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 TString Name() const { return mName; }
};
class CSkeleton : public CResource
@@ -50,12 +46,19 @@ class CSkeleton : public CResource
CBone *mpRootBone;
std::vector<CBone*> mBones;
static const float skSphereRadius;
public:
CSkeleton();
~CSkeleton();
void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot);
void UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot);
CBone* BoneByID(u32 BoneID) const;
void Draw(FRenderOptions Options);
u32 MaxBoneID() const;
void Draw(FRenderOptions Options, const CBoneTransformData& rkData);
std::pair<s32,float> RayIntersect(const CRay& rkRay, const CBoneTransformData& rkData);
inline u32 NumBones() const { return mBones.size(); }
};
#endif // CSKELETON_H

View File

@@ -94,7 +94,7 @@ void CAnimationLoader::ReadCompressedANIM()
// Read key flags
u32 NumKeys = mpInput->ReadLong();
mpAnim->mNumKeys = NumKeys - 1;
mpAnim->mNumKeys = NumKeys;
mKeyFlags.resize(NumKeys);
{
CBitStreamInWrapper BitStream(mpInput);
@@ -176,9 +176,9 @@ void CAnimationLoader::ReadCompressedAnimationData()
}
// Read keys
for (u32 iKey = 0; iKey < mpAnim->mNumKeys; iKey++)
for (u32 iKey = 0; iKey < mpAnim->mNumKeys - 1; iKey++)
{
bool KeyPresent = mKeyFlags[iKey];
bool KeyPresent = mKeyFlags[iKey+1];
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
{
@@ -187,6 +187,8 @@ void CAnimationLoader::ReadCompressedAnimationData()
// Read rotation
if (rChan.NumRotationKeys > 0)
{
// Note if KeyPresent is false, this isn't the correct value of WSign.
// However, we're going to recreate this key later via interpolation, so it doesn't matter what value we use here.
bool WSign = (KeyPresent ? BitStream.ReadBit() : false);
if (KeyPresent)
@@ -234,12 +236,12 @@ void CAnimationLoader::ReadCompressedAnimationData()
{
u32 KeyIndex = FirstIndex + iMissed + 1;
u32 RelKeyIndex = (KeyIndex - FirstIndex);
float Interp = (float) RelKeyIndex / (float) RelLastIndex;
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
{
bool HasTranslationKeys = mCompressedChannels[iChan].NumTranslationKeys > 0;
bool HasRotationKeys = mCompressedChannels[iChan].NumRotationKeys > 0;
float Interp = (float) RelKeyIndex / (float) RelLastIndex;
if (HasRotationKeys)
{