mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-25 16:51:38 +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:
parent
dfdbed24c4
commit
feace9e38c
@ -28,6 +28,8 @@ public:
|
|||||||
inline TFlags operator&(int Mask) const { return TFlags(FlagEnum(mValue & Mask)); }
|
inline TFlags operator&(int Mask) const { return TFlags(FlagEnum(mValue & Mask)); }
|
||||||
inline TFlags operator&(u32 Mask) const { return TFlags(FlagEnum(mValue & Mask)); }
|
inline TFlags operator&(u32 Mask) const { return TFlags(FlagEnum(mValue & Mask)); }
|
||||||
inline TFlags operator&(FlagEnum Flag) const { return TFlags(FlagEnum(mValue & Flag)); }
|
inline TFlags operator&(FlagEnum Flag) const { return TFlags(FlagEnum(mValue & Flag)); }
|
||||||
|
|
||||||
|
inline bool HasAnyFlags(TFlags Flags) const { return ((mValue & Flags) != 0); }
|
||||||
};
|
};
|
||||||
#define DECLARE_FLAGS(Enum, FlagTypeName) typedef TFlags<Enum> FlagTypeName;
|
#define DECLARE_FLAGS(Enum, FlagTypeName) typedef TFlags<Enum> FlagTypeName;
|
||||||
|
|
||||||
|
@ -190,7 +190,8 @@ HEADERS += \
|
|||||||
Resource/Factory/CSkeletonLoader.h \
|
Resource/Factory/CSkeletonLoader.h \
|
||||||
Scene/CCharacterNode.h \
|
Scene/CCharacterNode.h \
|
||||||
Resource/CAnimation.h \
|
Resource/CAnimation.h \
|
||||||
Resource/Factory/CAnimationLoader.h
|
Resource/Factory/CAnimationLoader.h \
|
||||||
|
Render/CBoneTransformData.h
|
||||||
|
|
||||||
# Source Files
|
# Source Files
|
||||||
SOURCES += \
|
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;
|
if (mDuration == 0.f) return;
|
||||||
|
|
||||||
Time = fmodf(Time, mDuration);
|
if (Time >= mDuration) Time = mDuration;
|
||||||
if (Time >= FLT_EPSILON) Time -= FLT_EPSILON;
|
if (Time >= FLT_EPSILON) Time -= FLT_EPSILON;
|
||||||
float t = fmodf(Time, mTickInterval) / mTickInterval;
|
float t = fmodf(Time, mTickInterval) / mTickInterval;
|
||||||
u32 LowKey = (u32) (Time / mTickInterval);
|
u32 LowKey = (u32) (Time / mTickInterval);
|
||||||
|
if (LowKey == (mNumKeys - 1)) LowKey = mNumKeys - 2;
|
||||||
|
|
||||||
u8 RotChannel = mBoneInfo[BoneID].RotationChannelIdx;
|
u8 RotChannel = mBoneInfo[BoneID].RotationChannelIdx;
|
||||||
u8 TransChannel = mBoneInfo[BoneID].TranslationChannelIdx;
|
u8 TransChannel = mBoneInfo[BoneID].TranslationChannelIdx;
|
||||||
|
@ -32,6 +32,10 @@ public:
|
|||||||
CAnimation();
|
CAnimation();
|
||||||
void EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const;
|
void EvaluateTransform(float Time, u32 BoneID, CTransform4f& rOut) const;
|
||||||
bool HasTranslation(u32 BoneID) 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
|
#endif // CANIMATION_H
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "CSkeleton.h"
|
#include "CSkeleton.h"
|
||||||
|
#include "Core/Render/CBoneTransformData.h"
|
||||||
#include "Core/Render/CDrawUtil.h"
|
#include "Core/Render/CDrawUtil.h"
|
||||||
#include "Core/Render/CGraphics.h"
|
#include "Core/Render/CGraphics.h"
|
||||||
|
#include <Math/MathUtil.h>
|
||||||
|
|
||||||
// ************ CBone ************
|
// ************ CBone ************
|
||||||
CBone::CBone(CSkeleton *pSkel)
|
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)
|
if (pAnim)
|
||||||
pAnim->EvaluateTransform(Time, mID, mAnimTransform);
|
pAnim->EvaluateTransform(Time, mID, rTransform);
|
||||||
|
|
||||||
if (!pAnim || !pAnim->HasTranslation(mID))
|
if (!pAnim || !pAnim->HasTranslation(mID))
|
||||||
mAnimTransform.Translate(mPosition);
|
rTransform.Translate(mPosition);
|
||||||
|
|
||||||
if (mpParent)
|
if (mpParent)
|
||||||
mAnimTransform = mpParent->AnimTransform() * mAnimTransform;
|
rTransform = rData[mpParent->ID()] * rTransform;
|
||||||
|
|
||||||
if (AnchorRoot && IsRoot())
|
if (AnchorRoot && IsRoot())
|
||||||
mAnimTransform.ZeroTranslation();
|
rTransform.ZeroTranslation();
|
||||||
|
|
||||||
mAbsPosDirty = true;
|
|
||||||
|
|
||||||
for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
|
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
|
bool CBone::IsRoot() const
|
||||||
@ -35,18 +36,9 @@ bool CBone::IsRoot() const
|
|||||||
return (mpParent == nullptr);
|
return (mpParent == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
CVector3f CBone::AbsolutePosition() const
|
|
||||||
{
|
|
||||||
if (mAbsPosDirty)
|
|
||||||
{
|
|
||||||
mAbsolutePosition = (mAnimTransform * CVector3f::skZero);
|
|
||||||
mAbsPosDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mAbsolutePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ************ CSkeleton ************
|
// ************ CSkeleton ************
|
||||||
|
const float CSkeleton::skSphereRadius = 0.025f;
|
||||||
|
|
||||||
CSkeleton::CSkeleton()
|
CSkeleton::CSkeleton()
|
||||||
: mpRootBone(nullptr)
|
: mpRootBone(nullptr)
|
||||||
{
|
{
|
||||||
@ -69,21 +61,35 @@ CBone* CSkeleton::BoneByID(u32 BoneID) const
|
|||||||
return nullptr;
|
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++)
|
for (u32 iBone = 0; iBone < mBones.size(); iBone++)
|
||||||
{
|
{
|
||||||
CBone *pBone = mBones[iBone];
|
CBone *pBone = mBones[iBone];
|
||||||
|
const CTransform4f& rkBoneTransform = rkData[pBone->ID()];
|
||||||
|
|
||||||
// Draw bone
|
// Draw bone
|
||||||
CTransform4f Transform;
|
CTransform4f Transform;
|
||||||
Transform.Scale(0.025f);
|
Transform.Scale(skSphereRadius);
|
||||||
Transform.Translate(pBone->AbsolutePosition());
|
Transform.Translate(rkBoneTransform.ExtractTranslation());
|
||||||
CGraphics::sMVPBlock.ModelMatrix = Transform;
|
CGraphics::sMVPBlock.ModelMatrix = Transform;
|
||||||
CGraphics::UpdateMVPBlock();
|
CGraphics::UpdateMVPBlock();
|
||||||
CDrawUtil::DrawSphere(CColor::skWhite);
|
CDrawUtil::DrawSphere(CColor::skWhite);
|
||||||
@ -93,6 +99,29 @@ void CSkeleton::Draw(FRenderOptions /*Options*/)
|
|||||||
CGraphics::UpdateMVPBlock();
|
CGraphics::UpdateMVPBlock();
|
||||||
|
|
||||||
for (u32 iChild = 0; iChild < pBone->NumChildren(); iChild++)
|
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 "Core/Render/FRenderOptions.h"
|
||||||
#include <Common/TString.h>
|
#include <Common/TString.h>
|
||||||
#include <Common/types.h>
|
#include <Common/types.h>
|
||||||
#include <Math/CTransform4f.h>
|
#include <Math/CRay.h>
|
||||||
#include <Math/CVector3f.h>
|
#include <Math/CVector3f.h>
|
||||||
|
|
||||||
|
class CBoneTransformData;
|
||||||
class CSkeleton;
|
class CSkeleton;
|
||||||
|
|
||||||
class CBone
|
class CBone
|
||||||
@ -22,24 +23,19 @@ class CBone
|
|||||||
CVector3f mPosition;
|
CVector3f mPosition;
|
||||||
TString mName;
|
TString mName;
|
||||||
|
|
||||||
CTransform4f mAnimTransform;
|
|
||||||
mutable bool mAbsPosDirty;
|
|
||||||
mutable CVector3f mAbsolutePosition;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CBone(CSkeleton *pSkel);
|
CBone(CSkeleton *pSkel);
|
||||||
void UpdateTransform(CAnimation *pAnim, float Time, bool AnchorRoot);
|
void UpdateTransform(CBoneTransformData& rData, CAnimation *pAnim, float Time, bool AnchorRoot);
|
||||||
bool IsRoot() const;
|
bool IsRoot() const;
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
inline u32 ID() const { return mID; }
|
inline CSkeleton* Skeleton() const { return mpSkeleton; }
|
||||||
inline CVector3f Position() const { return mPosition; }
|
|
||||||
inline CBone* Parent() const { return mpParent; }
|
inline CBone* Parent() const { return mpParent; }
|
||||||
inline u32 NumChildren() const { return mChildren.size(); }
|
inline u32 NumChildren() const { return mChildren.size(); }
|
||||||
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
|
inline CBone* ChildByIndex(u32 Index) const { return mChildren[Index]; }
|
||||||
inline const CTransform4f& AnimTransform() const { return mAnimTransform; }
|
inline u32 ID() const { return mID; }
|
||||||
|
inline CVector3f Position() const { return mPosition; }
|
||||||
CVector3f AbsolutePosition() const;
|
inline TString Name() const { return mName; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class CSkeleton : public CResource
|
class CSkeleton : public CResource
|
||||||
@ -50,12 +46,19 @@ class CSkeleton : public CResource
|
|||||||
CBone *mpRootBone;
|
CBone *mpRootBone;
|
||||||
std::vector<CBone*> mBones;
|
std::vector<CBone*> mBones;
|
||||||
|
|
||||||
|
static const float skSphereRadius;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CSkeleton();
|
CSkeleton();
|
||||||
~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;
|
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
|
#endif // CSKELETON_H
|
||||||
|
@ -94,7 +94,7 @@ void CAnimationLoader::ReadCompressedANIM()
|
|||||||
|
|
||||||
// Read key flags
|
// Read key flags
|
||||||
u32 NumKeys = mpInput->ReadLong();
|
u32 NumKeys = mpInput->ReadLong();
|
||||||
mpAnim->mNumKeys = NumKeys - 1;
|
mpAnim->mNumKeys = NumKeys;
|
||||||
mKeyFlags.resize(NumKeys);
|
mKeyFlags.resize(NumKeys);
|
||||||
{
|
{
|
||||||
CBitStreamInWrapper BitStream(mpInput);
|
CBitStreamInWrapper BitStream(mpInput);
|
||||||
@ -176,9 +176,9 @@ void CAnimationLoader::ReadCompressedAnimationData()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read keys
|
// 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++)
|
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
|
||||||
{
|
{
|
||||||
@ -187,6 +187,8 @@ void CAnimationLoader::ReadCompressedAnimationData()
|
|||||||
// Read rotation
|
// Read rotation
|
||||||
if (rChan.NumRotationKeys > 0)
|
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);
|
bool WSign = (KeyPresent ? BitStream.ReadBit() : false);
|
||||||
|
|
||||||
if (KeyPresent)
|
if (KeyPresent)
|
||||||
@ -234,12 +236,12 @@ void CAnimationLoader::ReadCompressedAnimationData()
|
|||||||
{
|
{
|
||||||
u32 KeyIndex = FirstIndex + iMissed + 1;
|
u32 KeyIndex = FirstIndex + iMissed + 1;
|
||||||
u32 RelKeyIndex = (KeyIndex - FirstIndex);
|
u32 RelKeyIndex = (KeyIndex - FirstIndex);
|
||||||
|
float Interp = (float) RelKeyIndex / (float) RelLastIndex;
|
||||||
|
|
||||||
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
|
for (u32 iChan = 0; iChan < mCompressedChannels.size(); iChan++)
|
||||||
{
|
{
|
||||||
bool HasTranslationKeys = mCompressedChannels[iChan].NumTranslationKeys > 0;
|
bool HasTranslationKeys = mCompressedChannels[iChan].NumTranslationKeys > 0;
|
||||||
bool HasRotationKeys = mCompressedChannels[iChan].NumRotationKeys > 0;
|
bool HasRotationKeys = mCompressedChannels[iChan].NumRotationKeys > 0;
|
||||||
float Interp = (float) RelKeyIndex / (float) RelLastIndex;
|
|
||||||
|
|
||||||
if (HasRotationKeys)
|
if (HasRotationKeys)
|
||||||
{
|
{
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/)
|
CCharacterNode::CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar /*= 0*/, CSceneNode *pParent /*= 0*/)
|
||||||
: CSceneNode(pScene, NodeID, pParent)
|
: CSceneNode(pScene, NodeID, pParent)
|
||||||
|
, mAnimTime(0.f)
|
||||||
{
|
{
|
||||||
SetCharacter(pChar);
|
SetCharSet(pChar);
|
||||||
}
|
}
|
||||||
|
|
||||||
ENodeType CCharacterNode::NodeType()
|
ENodeType CCharacterNode::NodeType()
|
||||||
@ -36,31 +37,53 @@ void CCharacterNode::Draw(FRenderOptions Options, int /*ComponentIndex*/, const
|
|||||||
{
|
{
|
||||||
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
CSkeleton *pSkel = mpCharacter->NodeSkeleton(mActiveCharSet);
|
||||||
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
|
CAnimation *pAnim = mpCharacter->Animation(mActiveAnim);
|
||||||
pSkel->UpdateTransform(pAnim, (float) CTimer::GlobalTime(), false);
|
pSkel->UpdateTransform(mTransformData, pAnim, mAnimTime, false);
|
||||||
pSkel->Draw(Options);
|
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();
|
return SRayIntersection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCharacterNode::SetCharacter(CAnimSet *pChar)
|
void CCharacterNode::SetCharSet(CAnimSet *pChar)
|
||||||
{
|
{
|
||||||
mpCharacter = pChar;
|
mpCharacter = pChar;
|
||||||
SetActiveCharSet(0);
|
SetActiveChar(0);
|
||||||
|
|
||||||
if (!mpCharacter)
|
if (!mpCharacter)
|
||||||
mLocalAABox = CAABox::skOne;
|
mLocalAABox = CAABox::skOne;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCharacterNode::SetActiveCharSet(u32 CharIndex)
|
void CCharacterNode::SetActiveChar(u32 CharIndex)
|
||||||
{
|
{
|
||||||
mActiveCharSet = CharIndex;
|
mActiveCharSet = CharIndex;
|
||||||
|
|
||||||
if (mpCharacter)
|
if (mpCharacter)
|
||||||
{
|
{
|
||||||
|
mTransformData.ResizeToSkeleton(mpCharacter->NodeSkeleton(CharIndex));
|
||||||
mLocalAABox = mpCharacter->NodeModel(CharIndex)->AABox();
|
mLocalAABox = mpCharacter->NodeModel(CharIndex)->AABox();
|
||||||
MarkTransformChanged();
|
MarkTransformChanged();
|
||||||
}
|
}
|
||||||
@ -70,3 +93,8 @@ void CCharacterNode::SetActiveAnim(u32 AnimIndex)
|
|||||||
{
|
{
|
||||||
mActiveAnim = AnimIndex;
|
mActiveAnim = AnimIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCharacterNode::SetAnimTime(float Time)
|
||||||
|
{
|
||||||
|
mAnimTime = Time;
|
||||||
|
}
|
||||||
|
@ -2,13 +2,16 @@
|
|||||||
#define CCHARACTERNODE_H
|
#define CCHARACTERNODE_H
|
||||||
|
|
||||||
#include "CSceneNode.h"
|
#include "CSceneNode.h"
|
||||||
|
#include "Core/Render/CBoneTransformData.h"
|
||||||
#include "Core/Resource/CAnimSet.h"
|
#include "Core/Resource/CAnimSet.h"
|
||||||
|
|
||||||
class CCharacterNode : public CSceneNode
|
class CCharacterNode : public CSceneNode
|
||||||
{
|
{
|
||||||
TResPtr<CAnimSet> mpCharacter;
|
TResPtr<CAnimSet> mpCharacter;
|
||||||
|
CBoneTransformData mTransformData;
|
||||||
u32 mActiveCharSet;
|
u32 mActiveCharSet;
|
||||||
u32 mActiveAnim;
|
u32 mActiveAnim;
|
||||||
|
float mAnimTime;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CCharacterNode(CScene *pScene, u32 NodeID, CAnimSet *pChar = 0, CSceneNode *pParent = 0);
|
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 ActiveCharSet() const { return mActiveCharSet; }
|
||||||
inline u32 ActiveAnim() const { return mActiveAnim; }
|
inline u32 ActiveAnim() const { return mActiveAnim; }
|
||||||
|
|
||||||
void SetCharacter(CAnimSet *pChar);
|
void SetCharSet(CAnimSet *pChar);
|
||||||
void SetActiveCharSet(u32 CharIndex);
|
void SetActiveChar(u32 CharIndex);
|
||||||
void SetActiveAnim(u32 AnimIndex);
|
void SetActiveAnim(u32 AnimIndex);
|
||||||
|
void SetAnimTime(float Time);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CCHARACTERNODE_H
|
#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
|
// Step 1: check whether the ray intersects with the plane the billboard is on
|
||||||
CPlane BillboardPlane(-rkViewInfo.pCamera->Direction(), mPosition);
|
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)
|
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
|
// Step 1: check whether the ray intersects with the plane the billboard is on
|
||||||
CPlane BillboardPlane(-rkViewInfo.pCamera->Direction(), mPosition);
|
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)
|
if (PlaneTest.first)
|
||||||
{
|
{
|
||||||
|
@ -231,9 +231,7 @@ CRay CBasicViewport::CastRay()
|
|||||||
|
|
||||||
CVector2f CBasicViewport::MouseDeviceCoordinates()
|
CVector2f CBasicViewport::MouseDeviceCoordinates()
|
||||||
{
|
{
|
||||||
QPoint MousePos = QCursor::pos();
|
QPoint MousePos = mapFromGlobal(QCursor::pos());
|
||||||
QPoint ThisPos = this->mapToGlobal(pos());
|
|
||||||
MousePos -= ThisPos;
|
|
||||||
|
|
||||||
CVector2f Device(
|
CVector2f Device(
|
||||||
(((2.f * MousePos.x()) / width()) - 1.f),
|
(((2.f * MousePos.x()) / width()) - 1.f),
|
||||||
|
@ -317,7 +317,7 @@ bool CGizmo::TransformFromInput(const CRay& rkRay, CCamera& rCamera)
|
|||||||
mTranslatePlane.Redefine(PlaneNormal, mPosition);
|
mTranslatePlane.Redefine(PlaneNormal, mPosition);
|
||||||
|
|
||||||
// Do translate
|
// Do translate
|
||||||
std::pair<bool,float> Result = Math::RayPlaneIntersecton(rkRay, mTranslatePlane);
|
std::pair<bool,float> Result = Math::RayPlaneIntersection(rkRay, mTranslatePlane);
|
||||||
|
|
||||||
if (Result.first)
|
if (Result.first)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "CCharacterEditor.h"
|
#include "CCharacterEditor.h"
|
||||||
#include "ui_CCharacterEditor.h"
|
#include "ui_CCharacterEditor.h"
|
||||||
#include "Editor/UICommon.h"
|
#include "Editor/UICommon.h"
|
||||||
|
#include <Math/MathUtil.h>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
||||||
CCharacterEditor::CCharacterEditor(QWidget *parent)
|
CCharacterEditor::CCharacterEditor(QWidget *parent)
|
||||||
@ -8,6 +9,9 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
|
|||||||
, ui(new Ui::CCharacterEditor)
|
, ui(new Ui::CCharacterEditor)
|
||||||
, mpScene(new CScene())
|
, mpScene(new CScene())
|
||||||
, mpCharNode(new CCharacterNode(mpScene, -1))
|
, mpCharNode(new CCharacterNode(mpScene, -1))
|
||||||
|
, mPlayAnim(true)
|
||||||
|
, mLoopAnim(true)
|
||||||
|
, mPlaybackSpeed(1.f)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
@ -32,9 +36,15 @@ CCharacterEditor::CCharacterEditor(QWidget *parent)
|
|||||||
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
|
connect(&mRefreshTimer, SIGNAL(timeout()), this, SLOT(RefreshViewport()));
|
||||||
mRefreshTimer.start(0);
|
mRefreshTimer.start(0);
|
||||||
|
|
||||||
|
connect(ui->Viewport, SIGNAL(HoverBoneChanged(u32)), this, SLOT(HoverBoneChanged(u32)));
|
||||||
|
connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open()));
|
||||||
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
|
connect(mpCharComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveCharacterIndex(int)));
|
||||||
connect(mpAnimComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveAnimation(int)));
|
connect(mpAnimComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SetActiveAnimation(int)));
|
||||||
connect(ui->ActionOpen, SIGNAL(triggered()), this, SLOT(Open()));
|
|
||||||
|
connect(ui->AnimSlider, SIGNAL(valueChanged(int)), this, SLOT(SetAnimTime(int)));
|
||||||
|
connect(ui->PlayPauseButton, SIGNAL(pressed()), this, SLOT(PlayPauseButtonPressed()));
|
||||||
|
connect(ui->LoopButton, SIGNAL(toggled(bool)), this, SLOT(LoopButtonToggled(bool)));
|
||||||
|
connect(ui->AnimSpeedSpinBox, SIGNAL(valueChanged(double)), this, SLOT(AnimSpeedSpinBoxChanged(double)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CCharacterEditor::~CCharacterEditor()
|
CCharacterEditor::~CCharacterEditor()
|
||||||
@ -42,25 +52,66 @@ CCharacterEditor::~CCharacterEditor()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::UpdateAnimTime()
|
||||||
|
{
|
||||||
|
double Time = CTimer::GlobalTime();
|
||||||
|
double DeltaTime = Time - mLastAnimUpdate;
|
||||||
|
mLastAnimUpdate = Time;
|
||||||
|
|
||||||
|
if (mPlayAnim && !ui->AnimSlider->isSliderDown())
|
||||||
|
{
|
||||||
|
mAnimTime += DeltaTime * mPlaybackSpeed;
|
||||||
|
|
||||||
|
CAnimation *pAnim = CurrentAnimation();
|
||||||
|
float AnimLength = (pAnim ? pAnim->Duration() : 0.f);
|
||||||
|
|
||||||
|
if (mAnimTime > AnimLength)
|
||||||
|
{
|
||||||
|
if (mLoopAnim)
|
||||||
|
mAnimTime = fmodf(mAnimTime, AnimLength);
|
||||||
|
else
|
||||||
|
mAnimTime = AnimLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAnimTime < 0.f)
|
||||||
|
{
|
||||||
|
if (mLoopAnim)
|
||||||
|
mAnimTime = AnimLength + fmodf(mAnimTime, AnimLength);
|
||||||
|
else
|
||||||
|
mAnimTime = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetAnimTime(mAnimTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CAnimation* CCharacterEditor::CurrentAnimation() const
|
||||||
|
{
|
||||||
|
if (mpSet)
|
||||||
|
return mpSet->Animation(mCurrentAnim);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// ************ PUBLIC SLOTS ************
|
// ************ PUBLIC SLOTS ************
|
||||||
void CCharacterEditor::Open()
|
void CCharacterEditor::Open()
|
||||||
{
|
{
|
||||||
QString CharFilename = QFileDialog::getOpenFileName(this, "Open Character", "", "Animation Character Set (*.ANCS)");
|
QString CharFilename = QFileDialog::getOpenFileName(this, "Open Character", "", "Animation Character Set (*.ANCS)");
|
||||||
if (CharFilename.isEmpty()) return;
|
if (CharFilename.isEmpty()) return;
|
||||||
|
|
||||||
TResPtr<CAnimSet> pSet = gResCache.GetResource(CharFilename.toStdString());
|
mpSet = gResCache.GetResource(CharFilename.toStdString());
|
||||||
|
|
||||||
if (pSet)
|
if (mpSet)
|
||||||
{
|
{
|
||||||
mpCharNode->SetCharacter(pSet);
|
mpCharNode->SetCharSet(mpSet);
|
||||||
setWindowTitle("Prime World Editor - Character Editor: " + TO_QSTRING(pSet->Source()));
|
setWindowTitle("Prime World Editor - Character Editor: " + TO_QSTRING(mpSet->Source()));
|
||||||
|
|
||||||
// Set up character combo box
|
// Set up character combo box
|
||||||
mpCharComboBox->blockSignals(true);
|
mpCharComboBox->blockSignals(true);
|
||||||
mpCharComboBox->clear();
|
mpCharComboBox->clear();
|
||||||
|
|
||||||
for (u32 iChar = 0; iChar < pSet->NumNodes(); iChar++)
|
for (u32 iChar = 0; iChar < mpSet->NumNodes(); iChar++)
|
||||||
mpCharComboBox->addItem( TO_QSTRING(pSet->NodeName(iChar)) );
|
mpCharComboBox->addItem( TO_QSTRING(mpSet->NodeName(iChar)) );
|
||||||
|
|
||||||
SetActiveCharacterIndex(0);
|
SetActiveCharacterIndex(0);
|
||||||
mpCharComboBox->blockSignals(false);
|
mpCharComboBox->blockSignals(false);
|
||||||
@ -69,8 +120,8 @@ void CCharacterEditor::Open()
|
|||||||
mpAnimComboBox->blockSignals(true);
|
mpAnimComboBox->blockSignals(true);
|
||||||
mpAnimComboBox->clear();
|
mpAnimComboBox->clear();
|
||||||
|
|
||||||
for (u32 iAnim = 0; iAnim < pSet->NumAnims(); iAnim++)
|
for (u32 iAnim = 0; iAnim < mpSet->NumAnims(); iAnim++)
|
||||||
mpAnimComboBox->addItem( TO_QSTRING(pSet->AnimName(iAnim)) );
|
mpAnimComboBox->addItem( TO_QSTRING(mpSet->AnimName(iAnim)) );
|
||||||
|
|
||||||
SetActiveAnimation(0);
|
SetActiveAnimation(0);
|
||||||
mpAnimComboBox->blockSignals(false);
|
mpAnimComboBox->blockSignals(false);
|
||||||
@ -81,16 +132,81 @@ void CCharacterEditor::Open()
|
|||||||
|
|
||||||
void CCharacterEditor::RefreshViewport()
|
void CCharacterEditor::RefreshViewport()
|
||||||
{
|
{
|
||||||
|
UpdateAnimTime();
|
||||||
ui->Viewport->ProcessInput();
|
ui->Viewport->ProcessInput();
|
||||||
ui->Viewport->Render();
|
ui->Viewport->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::HoverBoneChanged(u32 BoneID)
|
||||||
|
{
|
||||||
|
if (BoneID == 0xFFFFFFFF)
|
||||||
|
ui->StatusBar->clearMessage();
|
||||||
|
else
|
||||||
|
ui->StatusBar->showMessage(QString("Bone %1: %2").arg(BoneID).arg( TO_QSTRING(mpSet->NodeSkeleton(mCurrentChar)->BoneByID(BoneID)->Name()) ));
|
||||||
|
}
|
||||||
|
|
||||||
void CCharacterEditor::SetActiveCharacterIndex(int CharIndex)
|
void CCharacterEditor::SetActiveCharacterIndex(int CharIndex)
|
||||||
{
|
{
|
||||||
mpCharNode->SetActiveCharSet((u32) CharIndex);
|
mCurrentChar = CharIndex;
|
||||||
|
mpCharNode->SetActiveChar((u32) CharIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCharacterEditor::SetActiveAnimation(int AnimIndex)
|
void CCharacterEditor::SetActiveAnimation(int AnimIndex)
|
||||||
{
|
{
|
||||||
|
mCurrentAnim = AnimIndex;
|
||||||
mpCharNode->SetActiveAnim((u32) AnimIndex);
|
mpCharNode->SetActiveAnim((u32) AnimIndex);
|
||||||
|
mLastAnimUpdate = CTimer::GlobalTime();
|
||||||
|
|
||||||
|
ui->AnimSlider->blockSignals(true);
|
||||||
|
ui->AnimSlider->setMaximum((int) (mpSet->Animation(AnimIndex)->Duration() * 1000));
|
||||||
|
ui->AnimSlider->blockSignals(false);
|
||||||
|
|
||||||
|
SetAnimTime(0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::SetAnimTime(int Time)
|
||||||
|
{
|
||||||
|
float FloatTime = Time / 1000.f;
|
||||||
|
SetAnimTime(FloatTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::SetAnimTime(float Time)
|
||||||
|
{
|
||||||
|
mAnimTime = Time;
|
||||||
|
|
||||||
|
if (ui->AnimSlider != sender())
|
||||||
|
{
|
||||||
|
int IntTime = (int) (Time * 1000);
|
||||||
|
ui->AnimSlider->setValue(IntTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
mpCharNode->SetAnimTime(Time);
|
||||||
|
|
||||||
|
CAnimation *pAnim = (mpSet ? mpSet->Animation(mCurrentAnim) : nullptr);
|
||||||
|
u32 NumKeys = 1, CurKey = 0;
|
||||||
|
|
||||||
|
if (pAnim)
|
||||||
|
{
|
||||||
|
NumKeys = pAnim->NumKeys();
|
||||||
|
CurKey = Math::Min<u32>((u32) (Time / pAnim->TickInterval()) + 1, NumKeys - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->FrameLabel->setText(QString("Frame %1 / %2").arg(CurKey).arg(NumKeys - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::PlayPauseButtonPressed()
|
||||||
|
{
|
||||||
|
mPlayAnim = !mPlayAnim;
|
||||||
|
QString NewText = (mPlayAnim ? "Pause" : "Play");
|
||||||
|
ui->PlayPauseButton->setText(NewText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::LoopButtonToggled(bool Checked)
|
||||||
|
{
|
||||||
|
mLoopAnim = Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCharacterEditor::AnimSpeedSpinBoxChanged(double NewVal)
|
||||||
|
{
|
||||||
|
mPlaybackSpeed = (float) NewVal;
|
||||||
}
|
}
|
||||||
|
@ -25,15 +25,35 @@ class CCharacterEditor : public QMainWindow
|
|||||||
QComboBox *mpAnimComboBox;
|
QComboBox *mpAnimComboBox;
|
||||||
QTimer mRefreshTimer;
|
QTimer mRefreshTimer;
|
||||||
|
|
||||||
|
TResPtr<CAnimSet> mpSet;
|
||||||
|
u32 mCurrentChar;
|
||||||
|
u32 mCurrentAnim;
|
||||||
|
|
||||||
|
// Playback Controls
|
||||||
|
double mLastAnimUpdate;
|
||||||
|
float mAnimTime;
|
||||||
|
bool mPlayAnim;
|
||||||
|
bool mLoopAnim;
|
||||||
|
float mPlaybackSpeed;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CCharacterEditor(QWidget *parent = 0);
|
explicit CCharacterEditor(QWidget *parent = 0);
|
||||||
~CCharacterEditor();
|
~CCharacterEditor();
|
||||||
|
void UpdateAnimTime();
|
||||||
|
CAnimation* CurrentAnimation() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void Open();
|
void Open();
|
||||||
void RefreshViewport();
|
void RefreshViewport();
|
||||||
|
void HoverBoneChanged(u32 BoneID);
|
||||||
void SetActiveCharacterIndex(int CharIndex);
|
void SetActiveCharacterIndex(int CharIndex);
|
||||||
void SetActiveAnimation(int AnimIndex);
|
void SetActiveAnimation(int AnimIndex);
|
||||||
|
void SetAnimTime(int Time);
|
||||||
|
void SetAnimTime(float Time);
|
||||||
|
|
||||||
|
void PlayPauseButtonPressed();
|
||||||
|
void LoopButtonToggled(bool Checked);
|
||||||
|
void AnimSpeedSpinBoxChanged(double NewVal);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CCHARACTEREDITORWINDOW_H
|
#endif // CCHARACTEREDITORWINDOW_H
|
||||||
|
@ -15,20 +15,113 @@
|
|||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="CentralWidget">
|
<widget class="QWidget" name="CentralWidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="CCharacterEditorViewport" name="Viewport" native="true"/>
|
<widget class="CCharacterEditorViewport" name="Viewport" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>1</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSlider" name="AnimSlider">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="LoopButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Loop</string>
|
||||||
|
</property>
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="PlayPauseButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Pause</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="AnimSpeedLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Speed:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="WDraggableSpinBox" name="AnimSpeedSpinBox">
|
||||||
|
<property name="suffix">
|
||||||
|
<string>x</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>-10.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>10.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>1.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="FrameLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>21</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Frame 0 / 0</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -85,6 +178,11 @@
|
|||||||
<header>Editor/CharacterEditor/CCharacterEditorViewport.h</header>
|
<header>Editor/CharacterEditor/CCharacterEditorViewport.h</header>
|
||||||
<container>1</container>
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>WDraggableSpinBox</class>
|
||||||
|
<extends>QDoubleSpinBox</extends>
|
||||||
|
<header>Editor/Widgets/WDraggableSpinBox.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../Icons.qrc"/>
|
<include location="../Icons.qrc"/>
|
||||||
|
@ -24,6 +24,26 @@ void CCharacterEditorViewport::SetNode(CCharacterNode *pNode)
|
|||||||
mpCharNode = pNode;
|
mpCharNode = pNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CCharacterEditorViewport::CheckUserInput()
|
||||||
|
{
|
||||||
|
u32 HoverBoneID = -1;
|
||||||
|
|
||||||
|
if (underMouse() && !IsMouseInputActive())
|
||||||
|
{
|
||||||
|
CRay Ray = CastRay();
|
||||||
|
SRayIntersection Intersect = mpCharNode->RayNodeIntersectTest(Ray, 0, mViewInfo);
|
||||||
|
|
||||||
|
if (Intersect.Hit)
|
||||||
|
HoverBoneID = Intersect.ComponentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HoverBoneID != mHoverBone)
|
||||||
|
{
|
||||||
|
mHoverBone = HoverBoneID;
|
||||||
|
emit HoverBoneChanged(mHoverBone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CCharacterEditorViewport::Paint()
|
void CCharacterEditorViewport::Paint()
|
||||||
{
|
{
|
||||||
mpRenderer->BeginFrame();
|
mpRenderer->BeginFrame();
|
||||||
|
@ -6,15 +6,22 @@
|
|||||||
|
|
||||||
class CCharacterEditorViewport : public CBasicViewport
|
class CCharacterEditorViewport : public CBasicViewport
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
CCharacterNode *mpCharNode;
|
CCharacterNode *mpCharNode;
|
||||||
CRenderer *mpRenderer;
|
CRenderer *mpRenderer;
|
||||||
|
u32 mHoverBone;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CCharacterEditorViewport(QWidget *pParent = 0);
|
CCharacterEditorViewport(QWidget *pParent = 0);
|
||||||
~CCharacterEditorViewport();
|
~CCharacterEditorViewport();
|
||||||
void SetNode(CCharacterNode *pNode);
|
void SetNode(CCharacterNode *pNode);
|
||||||
|
void CheckUserInput();
|
||||||
void Paint();
|
void Paint();
|
||||||
void OnResize();
|
void OnResize();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void HoverBoneChanged(u32 BoneID);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CCHARACTEREDITORVIEWPORT_H
|
#endif // CCHARACTEREDITORVIEWPORT_H
|
||||||
|
@ -184,6 +184,13 @@ CTransform4f CTransform4f::RotationOnly() const
|
|||||||
return Inverse().Transpose();
|
return Inverse().Transpose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CVector3f CTransform4f::ExtractTranslation() const
|
||||||
|
{
|
||||||
|
CVector3f Test = *this * CVector3f::skZero;
|
||||||
|
Test = Test;
|
||||||
|
return CVector3f(m[0][3], m[1][3], m[2][3]);
|
||||||
|
}
|
||||||
|
|
||||||
// ************ OPERATORS ************
|
// ************ OPERATORS ************
|
||||||
float* CTransform4f::operator[](long Index)
|
float* CTransform4f::operator[](long Index)
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,8 @@ public:
|
|||||||
CTransform4f TranslationOnly() const;
|
CTransform4f TranslationOnly() const;
|
||||||
CTransform4f RotationOnly() const;
|
CTransform4f RotationOnly() const;
|
||||||
|
|
||||||
|
CVector3f ExtractTranslation() const;
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
static CTransform4f TranslationMatrix(CVector3f Translation);
|
static CTransform4f TranslationMatrix(CVector3f Translation);
|
||||||
static CTransform4f RotationMatrix(CQuaternion Rotation);
|
static CTransform4f RotationMatrix(CQuaternion Rotation);
|
||||||
|
@ -37,7 +37,7 @@ float RadiansToDegrees(float Rad)
|
|||||||
return Rad * 180.f / skPi;
|
return Rad * 180.f / skPi;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool,float> RayPlaneIntersecton(const CRay& rkRay, const CPlane& plane)
|
std::pair<bool,float> RayPlaneIntersection(const CRay& rkRay, const CPlane& plane)
|
||||||
{
|
{
|
||||||
// Code based on ray/plane intersect code from Ogre
|
// Code based on ray/plane intersect code from Ogre
|
||||||
// https://bitbucket.org/sinbad/ogre/src/197116fd2ac62c57cdeed1666f9866c3dddd4289/OgreMain/src/OgreMath.cpp?at=default#OgreMath.cpp-350
|
// https://bitbucket.org/sinbad/ogre/src/197116fd2ac62c57cdeed1666f9866c3dddd4289/OgreMain/src/OgreMath.cpp?at=default#OgreMath.cpp-350
|
||||||
@ -246,6 +246,33 @@ std::pair<bool,float> RayLineIntersection(const CRay& rkRay, const CVector3f& rk
|
|||||||
return std::pair<bool,float>(hit, sc);
|
return std::pair<bool,float>(hit, sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<bool,float> RaySphereIntersection(const CRay& rkRay, const CVector3f& rkSpherePos, float SphereRadius, bool AllowBackfaces /*= false*/)
|
||||||
|
{
|
||||||
|
std::pair<bool,float> Out(false, 0.f);
|
||||||
|
float SquaredRadius = (SphereRadius * SphereRadius);
|
||||||
|
|
||||||
|
// Test for ray origin inside sphere
|
||||||
|
if (!AllowBackfaces && rkRay.Origin().SquaredDistance(rkSpherePos) <= SquaredRadius)
|
||||||
|
return Out;
|
||||||
|
|
||||||
|
CVector3f RayToSphere = rkSpherePos - rkRay.Origin();
|
||||||
|
float CenterDist = RayToSphere.Dot(rkRay.Direction());
|
||||||
|
|
||||||
|
if (CenterDist >= 0.f)
|
||||||
|
{
|
||||||
|
float RayToSphereDistSquared = RayToSphere.SquaredMagnitude();
|
||||||
|
float DSquared = RayToSphereDistSquared - (CenterDist * CenterDist);
|
||||||
|
|
||||||
|
if (DSquared >= 0.f && DSquared <= SquaredRadius)
|
||||||
|
{
|
||||||
|
Out.first = true;
|
||||||
|
Out.second = CenterDist - Sqrt(SquaredRadius - DSquared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Out;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<bool,float> RayTriangleIntersection(const CRay& rkRay,
|
std::pair<bool,float> RayTriangleIntersection(const CRay& rkRay,
|
||||||
const CVector3f& rkVtxA, const CVector3f& rkVtxB,
|
const CVector3f& rkVtxA, const CVector3f& rkVtxB,
|
||||||
const CVector3f& rkVtxC, bool AllowBackfaces)
|
const CVector3f& rkVtxC, bool AllowBackfaces)
|
||||||
|
@ -22,6 +22,18 @@ float DegreesToRadians(float Deg);
|
|||||||
|
|
||||||
float RadiansToDegrees(float Rad);
|
float RadiansToDegrees(float Rad);
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
Type Max(const Type& rkA, const Type& rkB)
|
||||||
|
{
|
||||||
|
return (rkA > rkB ? rkA : rkB);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
Type Min(const Type& rkA, const Type& rkB)
|
||||||
|
{
|
||||||
|
return (rkA < rkB ? rkA : rkB);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
Type Lerp(const Type& rkA, const Type& rkB, float t)
|
Type Lerp(const Type& rkA, const Type& rkB, float t)
|
||||||
{
|
{
|
||||||
@ -29,13 +41,16 @@ Type Lerp(const Type& rkA, const Type& rkB, float t)
|
|||||||
return rkA + (Diff * t);
|
return rkA + (Diff * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool,float> RayPlaneIntersecton(const CRay& rkRay, const CPlane& rkPlane);
|
std::pair<bool,float> RayPlaneIntersection(const CRay& rkRay, const CPlane& rkPlane);
|
||||||
|
|
||||||
std::pair<bool,float> RayBoxIntersection(const CRay& rkRay, const CAABox& rkBox);
|
std::pair<bool,float> RayBoxIntersection(const CRay& rkRay, const CAABox& rkBox);
|
||||||
|
|
||||||
std::pair<bool,float> RayLineIntersection(const CRay& rkRay, const CVector3f& rkPointA,
|
std::pair<bool,float> RayLineIntersection(const CRay& rkRay, const CVector3f& rkPointA,
|
||||||
const CVector3f& rkPointB, float Threshold = 0.02f);
|
const CVector3f& rkPointB, float Threshold = 0.02f);
|
||||||
|
|
||||||
|
std::pair<bool,float> RaySphereIntersection(const CRay& rkRay, const CVector3f& rkSpherePos,
|
||||||
|
float SphereRadius, bool AllowBackfaces = false);
|
||||||
|
|
||||||
std::pair<bool,float> RayTriangleIntersection(const CRay& rkRay, const CVector3f& rkPointA,
|
std::pair<bool,float> RayTriangleIntersection(const CRay& rkRay, const CVector3f& rkPointA,
|
||||||
const CVector3f& rkPointB, const CVector3f& rkPointC,
|
const CVector3f& rkPointB, const CVector3f& rkPointC,
|
||||||
bool AllowBackfaces = false);
|
bool AllowBackfaces = false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user