mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-18 17:35:43 +00:00
Moved animation-related classes to their own subfolder
This commit is contained in:
52
src/Core/Resource/Animation/CAnimEventData.h
Normal file
52
src/Core/Resource/Animation/CAnimEventData.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef CANIMEVENTDATA
|
||||
#define CANIMEVENTDATA
|
||||
|
||||
#include "Core/Resource/CResource.h"
|
||||
|
||||
class CAnimEventData : public CResource
|
||||
{
|
||||
struct SEvent
|
||||
{
|
||||
u32 mCharacterIndex;
|
||||
CAssetID mAssetRef;
|
||||
};
|
||||
|
||||
std::vector<SEvent> mEvents;
|
||||
|
||||
public:
|
||||
CAnimEventData(CResourceEntry *pEntry = 0)
|
||||
: CResource(pEntry)
|
||||
{
|
||||
}
|
||||
|
||||
CDependencyTree* BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pTree = new CDependencyTree(ID());
|
||||
AddDependenciesToTree(pTree);
|
||||
return pTree;
|
||||
}
|
||||
|
||||
void AddDependenciesToTree(CDependencyTree *pTree) const
|
||||
{
|
||||
for (u32 iEvt = 0; iEvt < mEvents.size(); iEvt++)
|
||||
{
|
||||
const SEvent& rkEvent = mEvents[iEvt];
|
||||
CAssetID ID = rkEvent.mAssetRef;
|
||||
|
||||
if (ID.IsValid() && !pTree->HasDependency(ID))
|
||||
{
|
||||
CAnimEventDependency *pDep = new CAnimEventDependency(ID, rkEvent.mCharacterIndex);
|
||||
pTree->AddChild(pDep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline u32 NumEvents() const { return mEvents.size(); }
|
||||
inline u32 EventCharacterIndex(u32 EventIdx) const { return mEvents[EventIdx].mCharacterIndex; }
|
||||
inline CAssetID EventAssetRef(u32 EventIdx) const { return mEvents[EventIdx].mAssetRef; }
|
||||
|
||||
inline void AddEvent(u32 CharIdx, CAssetID AssetID) { mEvents.push_back( SEvent { CharIdx, AssetID } ); }
|
||||
};
|
||||
|
||||
#endif // CANIMEVENTDATA
|
||||
|
||||
96
src/Core/Resource/Animation/CAnimSet.h
Normal file
96
src/Core/Resource/Animation/CAnimSet.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef CANIMSET_H
|
||||
#define CANIMSET_H
|
||||
|
||||
#include "CAnimation.h"
|
||||
#include "CAnimEventData.h"
|
||||
#include "CSkeleton.h"
|
||||
#include "CSkin.h"
|
||||
#include "Core/Resource/CDependencyGroup.h"
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include "Core/Resource/TResPtr.h"
|
||||
#include "Core/Resource/Model/CModel.h"
|
||||
#include <Common/types.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct SSetCharacter
|
||||
{
|
||||
TString Name;
|
||||
TResPtr<CModel> pModel;
|
||||
TResPtr<CSkin> pSkin;
|
||||
TResPtr<CSkeleton> pSkeleton;
|
||||
|
||||
std::vector<CAssetID> GenericParticles;
|
||||
std::vector<CAssetID> ElectricParticles;
|
||||
std::vector<CAssetID> SwooshParticles;
|
||||
std::vector<CAssetID> SpawnParticles;
|
||||
std::vector<CAssetID> EffectParticles;
|
||||
CAssetID IceModel;
|
||||
CAssetID IceSkin;
|
||||
std::set<u32> UsedAnimationIndices;
|
||||
};
|
||||
|
||||
struct SSetAnimation
|
||||
{
|
||||
TString Name;
|
||||
TResPtr<CAnimation> pAnim;
|
||||
TResPtr<CAnimEventData> pEventData;
|
||||
};
|
||||
|
||||
class CAnimSet : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eAnimSet)
|
||||
friend class CAnimSetLoader;
|
||||
|
||||
std::vector<SSetCharacter> mCharacters;
|
||||
std::vector<SSetAnimation> mAnimations;
|
||||
|
||||
public:
|
||||
CAnimSet(CResourceEntry *pEntry = 0) : CResource(pEntry) {}
|
||||
|
||||
~CAnimSet()
|
||||
{
|
||||
// note: in MP2, event data isn't a standalone resource, so it's owned by the animset; therefore we need to delete it manually
|
||||
if (Game() >= eEchoesDemo)
|
||||
{
|
||||
for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++)
|
||||
{
|
||||
SSetAnimation& rAnim = mAnimations[iAnim];
|
||||
CAnimEventData *pEvents = rAnim.pEventData;
|
||||
ASSERT(pEvents && !pEvents->Entry());
|
||||
rAnim.pEventData = nullptr; // make sure TResPtr destructor doesn't attempt to access
|
||||
delete pEvents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CDependencyTree* BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pTree = new CDependencyTree(ID());
|
||||
|
||||
// Character dependencies
|
||||
for (u32 iChar = 0; iChar < mCharacters.size(); iChar++)
|
||||
{
|
||||
CSetCharacterDependency *pCharTree = CSetCharacterDependency::BuildTree(this, iChar);
|
||||
ASSERT(pCharTree);
|
||||
pTree->AddChild(pCharTree);
|
||||
}
|
||||
|
||||
for (u32 iAnim = 0; iAnim < mAnimations.size(); iAnim++)
|
||||
{
|
||||
CSetAnimationDependency *pAnimTree = CSetAnimationDependency::BuildTree(this, iAnim);
|
||||
ASSERT(pAnimTree);
|
||||
pTree->AddChild(pAnimTree);
|
||||
}
|
||||
|
||||
return pTree;
|
||||
}
|
||||
|
||||
// Accessors
|
||||
inline u32 NumCharacters() const { return mCharacters.size(); }
|
||||
inline u32 NumAnimations() const { return mAnimations.size(); }
|
||||
inline const SSetCharacter* Character(u32 Index) const { ASSERT(Index >= 0 && Index < NumCharacters()); return &mCharacters[Index]; }
|
||||
inline const SSetAnimation* Animation(u32 Index) const { ASSERT(Index >= 0 && Index < NumAnimations()); return &mAnimations[Index]; }
|
||||
};
|
||||
|
||||
#endif // CCHARACTERSET_H
|
||||
67
src/Core/Resource/Animation/CAnimation.cpp
Normal file
67
src/Core/Resource/Animation/CAnimation.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "CAnimation.h"
|
||||
#include <Math/CTransform4f.h>
|
||||
#include <Math/MathUtil.h>
|
||||
|
||||
CAnimation::CAnimation(CResourceEntry *pEntry /*= 0*/)
|
||||
: CResource(pEntry)
|
||||
, mDuration(0.f)
|
||||
, mTickInterval(0.0333333f)
|
||||
, mNumKeys(0)
|
||||
{
|
||||
for (u32 iBone = 0; iBone < 100; iBone++)
|
||||
{
|
||||
mBoneInfo[iBone].TranslationChannelIdx = 0xFF;
|
||||
mBoneInfo[iBone].RotationChannelIdx = 0xFF;
|
||||
mBoneInfo[iBone].ScaleChannelIdx = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
CDependencyTree* CAnimation::BuildDependencyTree() const
|
||||
{
|
||||
CDependencyTree *pTree = new CDependencyTree(ID());
|
||||
pTree->AddDependency(mEventData);
|
||||
return pTree;
|
||||
}
|
||||
|
||||
void CAnimation::EvaluateTransform(float Time, u32 BoneID, CVector3f *pOutTranslation, CQuaternion *pOutRotation, CVector3f *pOutScale) const
|
||||
{
|
||||
const bool kInterpolate = true;
|
||||
if (!pOutTranslation && !pOutRotation && !pOutScale) return;
|
||||
if (mDuration == 0.f) return;
|
||||
|
||||
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 ScaleChannel = mBoneInfo[BoneID].ScaleChannelIdx;
|
||||
u8 RotChannel = mBoneInfo[BoneID].RotationChannelIdx;
|
||||
u8 TransChannel = mBoneInfo[BoneID].TranslationChannelIdx;
|
||||
|
||||
if (ScaleChannel != 0xFF && pOutScale)
|
||||
{
|
||||
const CVector3f& rkLow = mScaleChannels[ScaleChannel][LowKey];
|
||||
const CVector3f& rkHigh = mScaleChannels[ScaleChannel][LowKey + 1];
|
||||
*pOutScale = (kInterpolate ? Math::Lerp<CVector3f>(rkLow, rkHigh, t) : rkLow);
|
||||
}
|
||||
|
||||
if (RotChannel != 0xFF && pOutRotation)
|
||||
{
|
||||
const CQuaternion& rkLow = mRotationChannels[RotChannel][LowKey];
|
||||
const CQuaternion& rkHigh = mRotationChannels[RotChannel][LowKey + 1];
|
||||
*pOutRotation = (kInterpolate ? rkLow.Slerp(rkHigh, t) : rkLow);
|
||||
}
|
||||
|
||||
if (TransChannel != 0xFF && pOutTranslation)
|
||||
{
|
||||
const CVector3f& rkLow = mTranslationChannels[TransChannel][LowKey];
|
||||
const CVector3f& rkHigh = mTranslationChannels[TransChannel][LowKey + 1];
|
||||
*pOutTranslation = (kInterpolate ? Math::Lerp<CVector3f>(rkLow, rkHigh, t) : rkLow);
|
||||
}
|
||||
}
|
||||
|
||||
bool CAnimation::HasTranslation(u32 BoneID) const
|
||||
{
|
||||
return (mBoneInfo[BoneID].TranslationChannelIdx != 0xFF);
|
||||
}
|
||||
47
src/Core/Resource/Animation/CAnimation.h
Normal file
47
src/Core/Resource/Animation/CAnimation.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef CANIMATION_H
|
||||
#define CANIMATION_H
|
||||
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include <Math/CQuaternion.h>
|
||||
#include <Math/CVector3f.h>
|
||||
#include <vector>
|
||||
|
||||
class CAnimation : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eAnimation)
|
||||
friend class CAnimationLoader;
|
||||
|
||||
typedef std::vector<CVector3f> TScaleChannel;
|
||||
typedef std::vector<CQuaternion> TRotationChannel;
|
||||
typedef std::vector<CVector3f> TTranslationChannel;
|
||||
|
||||
float mDuration;
|
||||
float mTickInterval;
|
||||
u32 mNumKeys;
|
||||
|
||||
std::vector<TScaleChannel> mScaleChannels;
|
||||
std::vector<TRotationChannel> mRotationChannels;
|
||||
std::vector<TTranslationChannel> mTranslationChannels;
|
||||
|
||||
struct SBoneChannelInfo
|
||||
{
|
||||
u8 ScaleChannelIdx;
|
||||
u8 RotationChannelIdx;
|
||||
u8 TranslationChannelIdx;
|
||||
};
|
||||
SBoneChannelInfo mBoneInfo[100];
|
||||
|
||||
CAssetID mEventData;
|
||||
|
||||
public:
|
||||
CAnimation(CResourceEntry *pEntry = 0);
|
||||
CDependencyTree* BuildDependencyTree() const;
|
||||
void EvaluateTransform(float Time, u32 BoneID, CVector3f *pOutTranslation, CQuaternion *pOutRotation, CVector3f *pOutScale) 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
|
||||
205
src/Core/Resource/Animation/CAnimationParameters.cpp
Normal file
205
src/Core/Resource/Animation/CAnimationParameters.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
#include "CAnimationParameters.h"
|
||||
#include "CAnimSet.h"
|
||||
#include "Core/GameProject/CResourceStore.h"
|
||||
#include <Common/Log.h>
|
||||
#include <iostream>
|
||||
|
||||
CAnimationParameters::CAnimationParameters()
|
||||
: mGame(ePrime)
|
||||
, mCharIndex(0)
|
||||
, mAnimIndex(0)
|
||||
, mUnknown2(0)
|
||||
, mUnknown3(0)
|
||||
{
|
||||
}
|
||||
|
||||
CAnimationParameters::CAnimationParameters(EGame Game)
|
||||
: mGame(Game)
|
||||
, mCharIndex(0)
|
||||
, mAnimIndex(0)
|
||||
, mUnknown2(0)
|
||||
, mUnknown3(0)
|
||||
{
|
||||
}
|
||||
|
||||
CAnimationParameters::CAnimationParameters(IInputStream& rSCLY, EGame Game)
|
||||
: mGame(Game)
|
||||
, mCharIndex(0)
|
||||
, mAnimIndex(0)
|
||||
, mUnknown2(0)
|
||||
, mUnknown3(0)
|
||||
{
|
||||
if (Game <= eEchoes)
|
||||
{
|
||||
mCharacterID = CAssetID(rSCLY, Game);
|
||||
mCharIndex = rSCLY.ReadLong();
|
||||
mAnimIndex = rSCLY.ReadLong();
|
||||
}
|
||||
|
||||
else if (Game <= eCorruption)
|
||||
{
|
||||
mCharacterID = CAssetID(rSCLY, Game);
|
||||
mAnimIndex = rSCLY.ReadLong();
|
||||
}
|
||||
|
||||
else if (Game == eReturns)
|
||||
{
|
||||
u8 Flags = rSCLY.ReadByte();
|
||||
|
||||
// 0x80 - CharacterAnimationSet is empty.
|
||||
if (Flags & 0x80)
|
||||
{
|
||||
mAnimIndex = -1;
|
||||
mUnknown2 = 0;
|
||||
mUnknown3 = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
mCharacterID = CAssetID(rSCLY, Game);
|
||||
|
||||
// 0x20 - Default Anim is present
|
||||
if (Flags & 0x20)
|
||||
mAnimIndex = rSCLY.ReadLong();
|
||||
else
|
||||
mAnimIndex = -1;
|
||||
|
||||
// 0x40 - Two-value struct is present
|
||||
if (Flags & 0x40)
|
||||
{
|
||||
mUnknown2 = rSCLY.ReadLong();
|
||||
mUnknown3 = rSCLY.ReadLong();
|
||||
}
|
||||
else
|
||||
{
|
||||
mUnknown2 = 0;
|
||||
mUnknown3 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAnimationParameters::Write(IOutputStream& rSCLY)
|
||||
{
|
||||
if (mGame <= eEchoes)
|
||||
{
|
||||
if (mCharacterID.IsValid())
|
||||
{
|
||||
mCharacterID.Write(rSCLY);
|
||||
rSCLY.WriteLong(mCharIndex);
|
||||
rSCLY.WriteLong(mAnimIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
rSCLY.WriteLong(0xFFFFFFFF);
|
||||
rSCLY.WriteLong(0);
|
||||
rSCLY.WriteLong(0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
else if (mGame <= eCorruption)
|
||||
{
|
||||
if (mCharacterID.IsValid())
|
||||
{
|
||||
mCharacterID.Write(rSCLY);
|
||||
rSCLY.WriteLong(mAnimIndex);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
rSCLY.WriteLongLong(CAssetID::skInvalidID64.ToLongLong());
|
||||
rSCLY.WriteLong(0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!mCharacterID.IsValid())
|
||||
rSCLY.WriteByte((u8) 0x80);
|
||||
|
||||
else
|
||||
{
|
||||
u8 Flag = 0;
|
||||
if (mAnimIndex != -1) Flag |= 0x20;
|
||||
if (mUnknown2 != 0 || mUnknown3 != 0) Flag |= 0x40;
|
||||
|
||||
rSCLY.WriteByte(Flag);
|
||||
mCharacterID.Write(rSCLY);
|
||||
|
||||
if (Flag & 0x20)
|
||||
rSCLY.WriteLong(mAnimIndex);
|
||||
|
||||
if (Flag & 0x40)
|
||||
{
|
||||
rSCLY.WriteLong(mUnknown2);
|
||||
rSCLY.WriteLong(mUnknown3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CModel* CAnimationParameters::GetCurrentModel(s32 NodeIndex /*= -1*/)
|
||||
{
|
||||
if (!mCharacterID.IsValid()) return nullptr;
|
||||
|
||||
CAnimSet *pSet = AnimSet();
|
||||
if (!pSet) return nullptr;
|
||||
if (pSet->Type() != eAnimSet) return nullptr;
|
||||
if (NodeIndex == -1) NodeIndex = mCharIndex;
|
||||
|
||||
if (pSet->NumCharacters() <= (u32) NodeIndex) return nullptr;
|
||||
return pSet->Character(NodeIndex)->pModel;
|
||||
}
|
||||
|
||||
TString CAnimationParameters::GetCurrentCharacterName(s32 NodeIndex /*= -1*/)
|
||||
{
|
||||
if (!mCharacterID.IsValid()) return "";
|
||||
|
||||
CAnimSet *pSet = AnimSet();
|
||||
if (!pSet) return "";
|
||||
if (pSet->Type() != eAnimSet) return "";
|
||||
if (NodeIndex == -1) NodeIndex = mCharIndex;
|
||||
|
||||
if (pSet->NumCharacters() <= (u32) NodeIndex) return "";
|
||||
return pSet->Character(NodeIndex)->Name;
|
||||
}
|
||||
|
||||
// ************ ACCESSORS ************
|
||||
u32 CAnimationParameters::Unknown(u32 Index)
|
||||
{
|
||||
// mAnimIndex isn't unknown, but I'm too lazy to move it because there's a lot
|
||||
// of UI stuff that depends on these functions atm for accessing and editing parameters.
|
||||
switch (Index)
|
||||
{
|
||||
case 0: return mAnimIndex;
|
||||
case 1: return mUnknown2;
|
||||
case 2: return mUnknown3;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CAnimationParameters::SetResource(const CAssetID& rkID)
|
||||
{
|
||||
mCharacterID = rkID;
|
||||
mCharIndex = 0;
|
||||
mAnimIndex = 0;
|
||||
|
||||
// Validate ID
|
||||
if (mCharacterID.IsValid())
|
||||
{
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
|
||||
|
||||
if (!pEntry)
|
||||
Log::Error("Invalid resource ID passed to CAnimationParameters: " + rkID.ToString());
|
||||
else if (pEntry->ResourceType() != eAnimSet)
|
||||
Log::Error("Resource with invalid type passed to CAnimationParameters: " + pEntry->CookedAssetPath().GetFileName());
|
||||
}
|
||||
}
|
||||
|
||||
void CAnimationParameters::SetUnknown(u32 Index, u32 Value)
|
||||
{
|
||||
switch (Index)
|
||||
{
|
||||
case 0: mAnimIndex = Value;
|
||||
case 1: mUnknown2 = Value;
|
||||
case 2: mUnknown3 = Value;
|
||||
}
|
||||
}
|
||||
53
src/Core/Resource/Animation/CAnimationParameters.h
Normal file
53
src/Core/Resource/Animation/CAnimationParameters.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef CANIMATIONPARAMETERS_H
|
||||
#define CANIMATIONPARAMETERS_H
|
||||
|
||||
#include "CAnimSet.h"
|
||||
#include "Core/Resource/TResPtr.h"
|
||||
#include "Core/Resource/Model/CModel.h"
|
||||
#include <Common/EGame.h>
|
||||
|
||||
class CAnimationParameters
|
||||
{
|
||||
EGame mGame;
|
||||
CAssetID mCharacterID;
|
||||
|
||||
u32 mCharIndex;
|
||||
u32 mAnimIndex;
|
||||
u32 mUnknown2;
|
||||
u32 mUnknown3;
|
||||
|
||||
public:
|
||||
CAnimationParameters();
|
||||
CAnimationParameters(EGame Game);
|
||||
CAnimationParameters(IInputStream& rSCLY, EGame Game);
|
||||
void Write(IOutputStream& rSCLY);
|
||||
|
||||
CModel* GetCurrentModel(s32 NodeIndex = -1);
|
||||
TString GetCurrentCharacterName(s32 NodeIndex = -1);
|
||||
|
||||
// Accessors
|
||||
inline EGame Version() const { return mGame; }
|
||||
inline CAssetID ID() const { return mCharacterID; }
|
||||
inline CAnimSet* AnimSet() const { return (CAnimSet*) gpResourceStore->LoadResource(mCharacterID, (mGame < eCorruptionProto ? "ANCS" : "CHAR")); }
|
||||
inline u32 CharacterIndex() const { return mCharIndex; }
|
||||
inline u32 AnimIndex() const { return mAnimIndex; }
|
||||
inline void SetCharIndex(u32 Index) { mCharIndex = Index; }
|
||||
inline void SetAnimIndex(u32 Index) { mAnimIndex = Index; }
|
||||
|
||||
u32 Unknown(u32 Index);
|
||||
void SetResource(const CAssetID& rkID);
|
||||
void SetUnknown(u32 Index, u32 Value);
|
||||
|
||||
// Operators
|
||||
inline bool operator==(const CAnimationParameters& rkOther) const
|
||||
{
|
||||
return ( (mGame == rkOther.mGame) &&
|
||||
(mCharacterID == rkOther.mCharacterID) &&
|
||||
(mCharIndex == rkOther.mCharIndex) &&
|
||||
(mAnimIndex == rkOther.mAnimIndex) &&
|
||||
(mUnknown2 == rkOther.mUnknown2) &&
|
||||
(mUnknown3 == rkOther.mUnknown3) );
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CANIMATIONPARAMETERS_H
|
||||
182
src/Core/Resource/Animation/CSkeleton.cpp
Normal file
182
src/Core/Resource/Animation/CSkeleton.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "CSkeleton.h"
|
||||
#include "Core/Render/CBoneTransformData.h"
|
||||
#include "Core/Render/CDrawUtil.h"
|
||||
#include "Core/Render/CGraphics.h"
|
||||
#include <Common/AssertMacro.h>
|
||||
#include <Math/MathUtil.h>
|
||||
|
||||
// ************ CBone ************
|
||||
CBone::CBone(CSkeleton *pSkel)
|
||||
: mpSkeleton(pSkel)
|
||||
, mSelected(false)
|
||||
{
|
||||
}
|
||||
|
||||
void CBone::UpdateTransform(CBoneTransformData& rData, const SBoneTransformInfo& rkParentTransform, CAnimation *pAnim, float Time, bool AnchorRoot)
|
||||
{
|
||||
// Get transform data
|
||||
SBoneTransformInfo TransformInfo;
|
||||
TransformInfo.Position = mLocalPosition;
|
||||
|
||||
if (pAnim)
|
||||
pAnim->EvaluateTransform(Time, mID, &TransformInfo.Position, &TransformInfo.Rotation, &TransformInfo.Scale);
|
||||
|
||||
if (AnchorRoot && IsRoot())
|
||||
TransformInfo.Position = CVector3f::skZero;
|
||||
|
||||
// Apply parent transform
|
||||
TransformInfo.Position = rkParentTransform.Position + (rkParentTransform.Rotation * (rkParentTransform.Scale * TransformInfo.Position));
|
||||
TransformInfo.Rotation = rkParentTransform.Rotation * TransformInfo.Rotation;
|
||||
|
||||
// Calculate transform
|
||||
CTransform4f& rTransform = rData[mID];
|
||||
rTransform.SetIdentity();
|
||||
rTransform.Scale(TransformInfo.Scale);
|
||||
rTransform.Rotate(TransformInfo.Rotation);
|
||||
rTransform.Translate(TransformInfo.Position);
|
||||
rTransform *= mInvBind;
|
||||
|
||||
// Calculate children
|
||||
for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
|
||||
mChildren[iChild]->UpdateTransform(rData, TransformInfo, pAnim, Time, AnchorRoot);
|
||||
}
|
||||
|
||||
CVector3f CBone::TransformedPosition(const CBoneTransformData& rkData) const
|
||||
{
|
||||
return rkData[mID] * Position();
|
||||
}
|
||||
|
||||
CQuaternion CBone::TransformedRotation(const CBoneTransformData &rkData) const
|
||||
{
|
||||
return rkData[mID] * Rotation();
|
||||
}
|
||||
|
||||
bool CBone::IsRoot() const
|
||||
{
|
||||
// In Retro's engine most skeletons have another bone named Skeleton_Root parented directly under the
|
||||
// actual root bone... that bone sometimes acts as the actual root (transforming the entire skeleton),
|
||||
// so we need to account for both
|
||||
return (mpParent == nullptr || mpParent->Parent() == nullptr);
|
||||
}
|
||||
|
||||
// ************ CSkeleton ************
|
||||
const float CSkeleton::skSphereRadius = 0.025f;
|
||||
|
||||
CSkeleton::CSkeleton(CResourceEntry *pEntry /*= 0*/)
|
||||
: CResource(pEntry)
|
||||
, mpRootBone(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CSkeleton::~CSkeleton()
|
||||
{
|
||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||
delete mBones[iBone];
|
||||
}
|
||||
|
||||
CBone* CSkeleton::BoneByID(u32 BoneID) const
|
||||
{
|
||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||
{
|
||||
if (mBones[iBone]->ID() == BoneID)
|
||||
return mBones[iBone];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CBone* CSkeleton::BoneByName(const TString& rkBoneName) const
|
||||
{
|
||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||
{
|
||||
if (mBones[iBone]->Name() == rkBoneName)
|
||||
return mBones[iBone];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 CSkeleton::MaxBoneID() const
|
||||
{
|
||||
u32 ID = 0;
|
||||
|
||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||
{
|
||||
if (mBones[iBone]->ID() > ID)
|
||||
ID = mBones[iBone]->ID();
|
||||
}
|
||||
|
||||
return ID;
|
||||
}
|
||||
|
||||
void CSkeleton::UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot)
|
||||
{
|
||||
ASSERT(rData.NumTrackedBones() >= MaxBoneID());
|
||||
mpRootBone->UpdateTransform(rData, SBoneTransformInfo(), pAnim, Time, AnchorRoot);
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
CBone *pBone = mBones[iBone];
|
||||
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
|
||||
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
|
||||
{
|
||||
CBone *pChild = pBone->ChildByIndex(iChild);
|
||||
CVector3f ChildPos = pkData ? pChild->TransformedPosition(*pkData) : pChild->Position();
|
||||
CDrawUtil::DrawLine(BonePos, ChildPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw bone spheres
|
||||
CTransform4f BaseTransform = CGraphics::sMVPBlock.ModelMatrix;
|
||||
|
||||
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||
{
|
||||
CBone *pBone = mBones[iBone];
|
||||
CVector3f BonePos = pkData ? pBone->TransformedPosition(*pkData) : pBone->Position();
|
||||
|
||||
CTransform4f Transform;
|
||||
Transform.Scale(skSphereRadius);
|
||||
Transform.Translate(BonePos);
|
||||
CGraphics::sMVPBlock.ModelMatrix = Transform * BaseTransform;
|
||||
CGraphics::UpdateMVPBlock();
|
||||
CDrawUtil::DrawSphere(pBone->IsSelected() ? CColor::skRed : CColor::skWhite);
|
||||
}
|
||||
}
|
||||
|
||||
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 = pBone->TransformedPosition(rkData);
|
||||
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;
|
||||
}
|
||||
89
src/Core/Resource/Animation/CSkeleton.h
Normal file
89
src/Core/Resource/Animation/CSkeleton.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#ifndef CSKELETON_H
|
||||
#define CSKELETON_H
|
||||
|
||||
#include "CAnimation.h"
|
||||
#include "Core/Render/FRenderOptions.h"
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include <Common/TString.h>
|
||||
#include <Common/types.h>
|
||||
#include <Math/CRay.h>
|
||||
#include <Math/CVector3f.h>
|
||||
|
||||
class CBoneTransformData;
|
||||
class CBone;
|
||||
|
||||
struct SBoneTransformInfo
|
||||
{
|
||||
CVector3f Position;
|
||||
CQuaternion Rotation;
|
||||
CVector3f Scale;
|
||||
|
||||
SBoneTransformInfo()
|
||||
: Position(CVector3f::skZero), Rotation(CQuaternion::skIdentity), Scale(CVector3f::skOne) {}
|
||||
};
|
||||
|
||||
class CSkeleton : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eSkeleton)
|
||||
friend class CSkeletonLoader;
|
||||
|
||||
CBone *mpRootBone;
|
||||
std::vector<CBone*> mBones;
|
||||
|
||||
static const float skSphereRadius;
|
||||
|
||||
public:
|
||||
CSkeleton(CResourceEntry *pEntry = 0);
|
||||
~CSkeleton();
|
||||
void UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot);
|
||||
CBone* BoneByID(u32 BoneID) const;
|
||||
CBone* BoneByName(const TString& rkBoneName) const;
|
||||
u32 MaxBoneID() const;
|
||||
|
||||
void Draw(FRenderOptions Options, const CBoneTransformData *pkData);
|
||||
std::pair<s32,float> RayIntersect(const CRay& rkRay, const CBoneTransformData& rkData);
|
||||
|
||||
inline u32 NumBones() const { return mBones.size(); }
|
||||
inline CBone* RootBone() const { return mpRootBone; }
|
||||
};
|
||||
|
||||
class CBone
|
||||
{
|
||||
friend class CSkeletonLoader;
|
||||
|
||||
CSkeleton *mpSkeleton;
|
||||
CBone *mpParent;
|
||||
std::vector<CBone*> mChildren;
|
||||
u32 mID;
|
||||
CVector3f mPosition;
|
||||
CVector3f mLocalPosition;
|
||||
CQuaternion mRotation;
|
||||
CQuaternion mLocalRotation;
|
||||
TString mName;
|
||||
CTransform4f mInvBind;
|
||||
bool mSelected;
|
||||
|
||||
public:
|
||||
CBone(CSkeleton *pSkel);
|
||||
void UpdateTransform(CBoneTransformData& rData, const SBoneTransformInfo& rkParentTransform, CAnimation *pAnim, float Time, bool AnchorRoot);
|
||||
CVector3f TransformedPosition(const CBoneTransformData& rkData) const;
|
||||
CQuaternion TransformedRotation(const CBoneTransformData& rkData) const;
|
||||
bool IsRoot() const;
|
||||
|
||||
// Accessors
|
||||
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 CVector3f LocalPosition() const { return mLocalPosition; }
|
||||
inline CQuaternion Rotation() const { return mRotation; }
|
||||
inline CQuaternion LocalRotation() const { return mLocalRotation; }
|
||||
inline TString Name() const { return mName; }
|
||||
inline bool IsSelected() const { return mSelected; }
|
||||
|
||||
inline void SetSelected(bool Selected) { mSelected = Selected; }
|
||||
};
|
||||
|
||||
#endif // CSKELETON_H
|
||||
50
src/Core/Resource/Animation/CSkin.h
Normal file
50
src/Core/Resource/Animation/CSkin.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef CSKIN_H
|
||||
#define CSKIN_H
|
||||
|
||||
#include "Core/Resource/CResource.h"
|
||||
#include "Core/Resource/Model/CVertex.h"
|
||||
|
||||
struct SVertexWeights
|
||||
{
|
||||
TBoneIndices Indices;
|
||||
TBoneWeights Weights;
|
||||
};
|
||||
|
||||
class CSkin : public CResource
|
||||
{
|
||||
DECLARE_RESOURCE_TYPE(eSkin)
|
||||
friend class CSkinLoader;
|
||||
|
||||
struct SVertGroup
|
||||
{
|
||||
SVertexWeights Weights;
|
||||
u32 NumVertices;
|
||||
};
|
||||
std::vector<SVertGroup> mVertGroups;
|
||||
|
||||
public:
|
||||
CSkin(CResourceEntry *pEntry = 0) : CResource(pEntry) {}
|
||||
|
||||
const SVertexWeights& WeightsForVertex(u32 VertIdx)
|
||||
{
|
||||
// Null weights bind everything to the root bone in case there is no matching vertex group
|
||||
static const SVertexWeights skNullWeights = {
|
||||
{ 3, 0, 0, 0 },
|
||||
{ 1.f, 0.f, 0.f, 0.f }
|
||||
};
|
||||
|
||||
u32 Index = 0;
|
||||
|
||||
for (u32 iGrp = 0; iGrp < mVertGroups.size(); iGrp++)
|
||||
{
|
||||
if (VertIdx < Index + mVertGroups[iGrp].NumVertices)
|
||||
return mVertGroups[iGrp].Weights;
|
||||
|
||||
Index += mVertGroups[iGrp].NumVertices;
|
||||
}
|
||||
|
||||
return skNullWeights;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CSKIN_H
|
||||
88
src/Core/Resource/Animation/IMetaAnimation.h
Normal file
88
src/Core/Resource/Animation/IMetaAnimation.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef IMETAANIMATION
|
||||
#define IMETAANIMATION
|
||||
|
||||
#include "CAnimation.h"
|
||||
#include "Core/Resource/TResPtr.h"
|
||||
#include <Common/TString.h>
|
||||
|
||||
enum EMetaAnimationType
|
||||
{
|
||||
eMAT_Play = 0,
|
||||
eMAT_Blend = 1,
|
||||
eMAT_PhaseBlend = 2, // note: structure shared with eMAT_Blend, differences are currently unknown
|
||||
eMAT_Random = 3,
|
||||
eMAT_Sequence = 4
|
||||
};
|
||||
|
||||
// Base MetaAnimation interface
|
||||
class IMetaAnimation
|
||||
{
|
||||
protected:
|
||||
TString mName;
|
||||
EMetaAnimationType mType;
|
||||
|
||||
public:
|
||||
IMetaAnimation(EMetaAnimationType Type)
|
||||
: mType(Type) {}
|
||||
|
||||
virtual ~IMetaAnimation() {}
|
||||
|
||||
// Accessors
|
||||
inline void SetName(const TString& rkName) { mName = rkName; }
|
||||
inline TString Name() const { return mName; }
|
||||
inline EMetaAnimationType Type() const { return mType; }
|
||||
};
|
||||
|
||||
// CMetaAnimationPlay - plays an animation
|
||||
class CMetaAnimationPlay : public IMetaAnimation
|
||||
{
|
||||
protected:
|
||||
TResPtr<CAnimation> mpAnim;
|
||||
|
||||
public:
|
||||
CMetaAnimationPlay(CAnimation *pAnim)
|
||||
: IMetaAnimation(eMAT_Play), mpAnim(pAnim) {}
|
||||
|
||||
inline CAnimation* GetPlayAnimation() const { return mpAnim; }
|
||||
};
|
||||
|
||||
// CMetaAnimationBlend - blend between two animations
|
||||
class CMetaAnimationBlend : public IMetaAnimation
|
||||
{
|
||||
protected:
|
||||
IMetaAnimation *mpAnimA;
|
||||
IMetaAnimation *mpAnimB;
|
||||
|
||||
public:
|
||||
CMetaAnimationBlend(IMetaAnimation *pAnimA, IMetaAnimation *pAnimB)
|
||||
: IMetaAnimation(eMAT_Blend), mpAnimA(pAnimA), mpAnimB(pAnimB) {}
|
||||
|
||||
~CMetaAnimationBlend()
|
||||
{
|
||||
delete mpAnimA;
|
||||
delete mpAnimB;
|
||||
}
|
||||
|
||||
inline IMetaAnimation* BlendAnimationA() const { return mpAnimA; }
|
||||
inline IMetaAnimation* BlendAnimationB() const { return mpAnimB; }
|
||||
};
|
||||
|
||||
// SAnimProbabilityPair - structure used by CMetaAnimationRandom to associate an animation with a probability value
|
||||
struct SAnimProbabilityPair
|
||||
{
|
||||
IMetaAnimation *pAnim;
|
||||
u32 Probability;
|
||||
};
|
||||
|
||||
// CMetaAnimationRandom - play random animation
|
||||
class CMetaAnimationRandom : public IMetaAnimation
|
||||
{
|
||||
protected:
|
||||
std::vector<SAnimProbabilityPair> mProbabilityPairs;
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
#endif // IMETAANIMATION
|
||||
|
||||
Reference in New Issue
Block a user