mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-21 10:49:23 +00:00
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:
@@ -190,7 +190,8 @@ HEADERS += \
|
||||
Resource/Factory/CSkeletonLoader.h \
|
||||
Scene/CCharacterNode.h \
|
||||
Resource/CAnimation.h \
|
||||
Resource/Factory/CAnimationLoader.h
|
||||
Resource/Factory/CAnimationLoader.h \
|
||||
Render/CBoneTransformData.h
|
||||
|
||||
# Source Files
|
||||
SOURCES += \
|
||||
|
||||
26
src/Core/Render/CBoneTransformData.h
Normal file
26
src/Core/Render/CBoneTransformData.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CBONETRANSFORMDATA
|
||||
#define CBONETRANSFORMDATA
|
||||
|
||||
#include "Core/Resource/CSkeleton.h"
|
||||
#include <Common/types.h>
|
||||
#include <Math/CTransform4f.h>
|
||||
#include <vector>
|
||||
|
||||
class CBoneTransformData
|
||||
{
|
||||
std::vector<CTransform4f> mBoneMatrices;
|
||||
|
||||
public:
|
||||
CBoneTransformData() { }
|
||||
CBoneTransformData(CSkeleton *pSkel) { ResizeToSkeleton(pSkel); }
|
||||
inline void ResizeToSkeleton(CSkeleton *pSkel) { mBoneMatrices.resize(pSkel ? pSkel->MaxBoneID() + 1 : 0); }
|
||||
inline CTransform4f& BoneMatrix(u32 BoneID) { return mBoneMatrices[BoneID]; }
|
||||
inline const CTransform4f& BoneMatrix(u32 BoneID) const { return mBoneMatrices[BoneID]; }
|
||||
inline void* Data() { return mBoneMatrices.data(); }
|
||||
inline u32 DataSize() const { return mBoneMatrices.size() * sizeof(CTransform4f); }
|
||||
inline CTransform4f& operator[](u32 BoneIndex) { return BoneMatrix(BoneIndex); }
|
||||
inline const CTransform4f& operator[](u32 BoneIndex) const { return BoneMatrix(BoneIndex); }
|
||||
};
|
||||
|
||||
#endif // CBONETRANSFORMDATA
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/)
|
||||
: CSceneNode(pScene, NodeID, pParent)
|
||||
, mAnimTime(0.f)
|
||||
{
|
||||
SetCharacter(pChar);
|
||||
SetCharSet(pChar);
|
||||
}
|
||||
|
||||
ENodeType CCharacterNode::NodeType()
|
||||
@@ -36,31 +37,53 @@ void CCharacterNode::Draw(FRenderOptions Options, int /*ComponentIndex*/, const
|
||||
{
|
||||
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
||||
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
|
||||
pSkel->UpdateTransform(pAnim, (float) CTimer::GlobalTime(), false);
|
||||
pSkel->Draw(Options);
|
||||
pSkel->UpdateTransform(mTransformData, pAnim, mAnimTime, false);
|
||||
pSkel->Draw(Options, mTransformData);
|
||||
}
|
||||
|
||||
SRayIntersection CCharacterNode::RayNodeIntersectTest(const CRay& /*rkRay*/, u32 /*AssetID*/, const SViewInfo& /*rkViewInfo*/)
|
||||
SRayIntersection CCharacterNode::RayNodeIntersectTest(const CRay& rkRay, u32 /*AssetID*/, const SViewInfo& /*rkViewInfo*/)
|
||||
{
|
||||
// Not currently doing any ray checks on character nodes so don't care about this right now.
|
||||
// Check for bone under ray. Doesn't check for model intersections atm
|
||||
if (mpCharacter)
|
||||
{
|
||||
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
||||
|
||||
if (pSkel)
|
||||
{
|
||||
std::pair<s32,float> Hit = pSkel->RayIntersect(rkRay, mTransformData);
|
||||
|
||||
if (Hit.first != -1)
|
||||
{
|
||||
SRayIntersection Intersect;
|
||||
Intersect.Hit = true;
|
||||
Intersect.ComponentIndex = Hit.first;
|
||||
Intersect.Distance = Hit.second;
|
||||
Intersect.HitPoint = rkRay.PointOnRay(Hit.second);
|
||||
Intersect.pNode = this;
|
||||
return Intersect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SRayIntersection();
|
||||
}
|
||||
|
||||
void CCharacterNode::SetCharacter(CAnimSet *pChar)
|
||||
void CCharacterNode::SetCharSet(CAnimSet *pChar)
|
||||
{
|
||||
mpCharacter = pChar;
|
||||
SetActiveCharSet(0);
|
||||
SetActiveChar(0);
|
||||
|
||||
if (!mpCharacter)
|
||||
mLocalAABox = CAABox::skOne;
|
||||
}
|
||||
|
||||
void CCharacterNode::SetActiveCharSet(u32 CharIndex)
|
||||
void CCharacterNode::SetActiveChar(u32 CharIndex)
|
||||
{
|
||||
mActiveCharSet = CharIndex;
|
||||
|
||||
if (mpCharacter)
|
||||
{
|
||||
mTransformData.ResizeToSkeleton(mpCharacter->NodeSkeleton(CharIndex));
|
||||
mLocalAABox = mpCharacter->NodeModel(CharIndex)->AABox();
|
||||
MarkTransformChanged();
|
||||
}
|
||||
@@ -70,3 +93,8 @@ void CCharacterNode::SetActiveAnim(u32 AnimIndex)
|
||||
{
|
||||
mActiveAnim = AnimIndex;
|
||||
}
|
||||
|
||||
void CCharacterNode::SetAnimTime(float Time)
|
||||
{
|
||||
mAnimTime = Time;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
#define CCHARACTERNODE_H
|
||||
|
||||
#include "CSceneNode.h"
|
||||
#include "Core/Render/CBoneTransformData.h"
|
||||
#include "Core/Resource/CAnimSet.h"
|
||||
|
||||
class CCharacterNode : public CSceneNode
|
||||
{
|
||||
TResPtr<CAnimSet> mpCharacter;
|
||||
CBoneTransformData mTransformData;
|
||||
u32 mActiveCharSet;
|
||||
u32 mActiveAnim;
|
||||
float mAnimTime;
|
||||
|
||||
public:
|
||||
explicit CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar = 0, CSceneNode *pParent = 0);
|
||||
@@ -22,9 +25,10 @@ public:
|
||||
inline u32 ActiveCharSet() const { return mActiveCharSet; }
|
||||
inline u32 ActiveAnim() const { return mActiveAnim; }
|
||||
|
||||
void SetCharacter(CAnimSet *pChar);
|
||||
void SetActiveCharSet(u32 CharIndex);
|
||||
void SetCharSet(CAnimSet *pChar);
|
||||
void SetActiveChar(u32 CharIndex);
|
||||
void SetActiveAnim(u32 AnimIndex);
|
||||
void SetAnimTime(float Time);
|
||||
};
|
||||
|
||||
#endif // CCHARACTERNODE_H
|
||||
|
||||
@@ -80,7 +80,7 @@ SRayIntersection CLightNode::RayNodeIntersectTest(const CRay& rkRay, u32 AssetID
|
||||
|
||||
// Step 1: check whether the ray intersects with the plane the billboard is on
|
||||
CPlane BillboardPlane(-rkViewInfo.pCamera->Direction(), mPosition);
|
||||
std::pair<bool,float> PlaneTest = Math::RayPlaneIntersecton(rkRay, BillboardPlane);
|
||||
std::pair<bool,float> PlaneTest = Math::RayPlaneIntersection(rkRay, BillboardPlane);
|
||||
|
||||
if (PlaneTest.first)
|
||||
{
|
||||
|
||||
@@ -361,7 +361,7 @@ SRayIntersection CScriptNode::RayNodeIntersectTest(const CRay& rkRay, u32 AssetI
|
||||
{
|
||||
// Step 1: check whether the ray intersects with the plane the billboard is on
|
||||
CPlane BillboardPlane(-rkViewInfo.pCamera->Direction(), mPosition);
|
||||
std::pair<bool,float> PlaneTest = Math::RayPlaneIntersecton(rkRay, BillboardPlane);
|
||||
std::pair<bool,float> PlaneTest = Math::RayPlaneIntersection(rkRay, BillboardPlane);
|
||||
|
||||
if (PlaneTest.first)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user