forked from Redacted/Re3D
Skeletal Animation
This commit is contained in:
@@ -52,7 +52,7 @@ CPMAddPackage(
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/Prerelease-7.zip
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/Prerelease-16.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
|
1130
include/assimp/config.h
Normal file
1130
include/assimp/config.h
Normal file
File diff suppressed because it is too large
Load Diff
16
include/types/animation/AssimpConverter.h
Normal file
16
include/types/animation/AssimpConverter.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
// SkeletalModel
|
||||
namespace SA
|
||||
{
|
||||
class SkeletalModel;
|
||||
}
|
||||
|
||||
// Assimp
|
||||
struct aiScene;
|
||||
|
||||
namespace AssimpConverter
|
||||
{
|
||||
// Converts an Assimp scene to something we can render and animate
|
||||
bool Convert(const aiScene* a_pScene, SA::SkeletalModel& a_OutModel);
|
||||
};
|
125
include/types/animation/skeleton/SkeletalModel.h
Normal file
125
include/types/animation/skeleton/SkeletalModel.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <types/animation/skeleton/types.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
|
||||
namespace SA
|
||||
{
|
||||
class SkeletalModel
|
||||
{
|
||||
public:
|
||||
SkeletalModel();
|
||||
~SkeletalModel();
|
||||
|
||||
void Update(float a_Dt);
|
||||
|
||||
unsigned int GetNumMeshes() const { return m_Meshes.size(); }
|
||||
const sAnimatedMesh& GetMesh(unsigned int i) const { return m_Meshes[i]; }
|
||||
void AddMesh(const sAnimatedMesh& a_Mesh) { m_Meshes.push_back(a_Mesh); }
|
||||
|
||||
void SetGlobalInverseTransform(const LinearAlgebra::Matrix4x4& a_Transform) { m_GlobalInverseTransform = a_Transform; }
|
||||
const LinearAlgebra::Matrix4x4& GetGlobalInverseTransform() const { return m_GlobalInverseTransform; }
|
||||
|
||||
sSkeleton& GetSkeleton() { return m_Skeleton; }
|
||||
const sSkeleton& GetSkeleton() const { return m_Skeleton; }
|
||||
|
||||
sAnimation& GetAnimation() { return m_Animation; }
|
||||
const sAnimation& GetAnimation() const { return m_Animation; }
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
std::vector<sAnimatedMesh> m_Meshes;
|
||||
sSkeleton m_Skeleton;
|
||||
sAnimation m_Animation;
|
||||
LinearAlgebra::Matrix4x4 m_GlobalInverseTransform;
|
||||
|
||||
float m_AnimationTime;
|
||||
|
||||
private:
|
||||
void ReadNodeHierarchy(float AnimationTime, sAnimation& a_Animation, sSkeleton& a_Skeleton, sBone& a_Bone, const LinearAlgebra::Matrix4x4& ParentTransform);
|
||||
void TransformVertices(const sSkeleton& a_Skeleton);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace SA
|
||||
{
|
||||
// Helper functions
|
||||
static LinearAlgebra::Vector3 NodeAnimation_FindInterpolatedPosition(const sNodeAnimation& a_NodeAnimation, float a_AnimationTime);
|
||||
static LinearAlgebra::Quaternion NodeAnimation_FindInterpolatedRotation(const sNodeAnimation& a_NodeAnimation, float a_AnimationTime);
|
||||
template <typename _Ty> static _Ty NodeAnimation_FindInterpolatedValue(std::vector<sNodeAnimationKey<_Ty> > a_Keys, float a_AnimationTime);
|
||||
template <typename _Ty> static unsigned int NodeAnimation_FindIndex(const _Ty& a_Keys, float a_AnimationTime);
|
||||
|
||||
extern const sNodeAnimation* FindNodeAnim(const sAnimation& a_Animation, const std::string& a_NodeName);
|
||||
extern LinearAlgebra::Vector3 InterpolateValue(const LinearAlgebra::Vector3& a_Start, const LinearAlgebra::Vector3& a_End, float a_Factor);
|
||||
extern LinearAlgebra::Quaternion InterpolateValue(const LinearAlgebra::Quaternion& a_Start, const LinearAlgebra::Quaternion& a_End, float a_Factor);
|
||||
extern unsigned int Skeleton_FindBoneIndex(const sSkeleton& a_Skeleton, const std::string& a_Name);
|
||||
extern sBone* Skeleton_FindBone(sSkeleton& a_Skeleton, const std::string& a_Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace SA
|
||||
{
|
||||
LinearAlgebra::Vector3 NodeAnimation_FindInterpolatedPosition(const sNodeAnimation& a_NodeAnimation, float a_AnimationTime)
|
||||
{
|
||||
return NodeAnimation_FindInterpolatedValue(a_NodeAnimation.PositionKeys, a_AnimationTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LinearAlgebra::Quaternion NodeAnimation_FindInterpolatedRotation(const sNodeAnimation& a_NodeAnimation, float a_AnimationTime)
|
||||
{
|
||||
return NodeAnimation_FindInterpolatedValue(a_NodeAnimation.RotationKeys, a_AnimationTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename _Ty>
|
||||
_Ty NodeAnimation_FindInterpolatedValue(std::vector<sNodeAnimationKey<_Ty> > a_Keys, float a_AnimationTime)
|
||||
{
|
||||
if (a_Keys.size() == 1)
|
||||
{
|
||||
return a_Keys[0].Value;
|
||||
}
|
||||
|
||||
unsigned int PositionIndex = NodeAnimation_FindIndex(a_Keys, a_AnimationTime);
|
||||
unsigned int NextPositionIndex = (PositionIndex + 1);
|
||||
//CORE_ASSERT(NextPositionIndex < a_Keys.size());
|
||||
float DeltaTime = a_Keys[NextPositionIndex].Time - a_Keys[PositionIndex].Time;
|
||||
float Factor = (a_AnimationTime - a_Keys[PositionIndex].Time) / DeltaTime;
|
||||
//CORE_ASSERT(Factor >= 0.0f && Factor <= 1.0f);
|
||||
const _Ty& StartPosition = a_Keys[PositionIndex].Value;
|
||||
const _Ty& EndPosition = a_Keys[NextPositionIndex].Value;
|
||||
|
||||
return InterpolateValue(StartPosition, EndPosition, Factor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename _Ty>
|
||||
unsigned int NodeAnimation_FindIndex(const _Ty& a_Keys, float a_AnimationTime)
|
||||
{
|
||||
for (unsigned int i = 0; i < a_Keys.size() - 1; ++i)
|
||||
{
|
||||
if (a_AnimationTime < a_Keys[i + 1].Time)
|
||||
return i;
|
||||
}
|
||||
|
||||
//CORE_ASSERT(false);
|
||||
return -1;
|
||||
}
|
||||
}
|
14
include/types/animation/skeleton/SkeletalModelSerializer.h
Normal file
14
include/types/animation/skeleton/SkeletalModelSerializer.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace SA
|
||||
{
|
||||
class SkeletalModel;
|
||||
|
||||
// Serialization methods
|
||||
extern std::string ModelToData(const SkeletalModel& a_Model);
|
||||
extern bool ModelToData(const SkeletalModel& a_Model, std::string& a_OutData);
|
||||
extern unsigned int ModelToData(const SkeletalModel& a_Model, char* a_pOutData);
|
||||
extern bool DataToModel(const char* a_pData, SkeletalModel& a_OutModel);
|
||||
};
|
73
include/types/animation/skeleton/types.h
Normal file
73
include/types/animation/skeleton/types.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
// STD
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace SA
|
||||
{
|
||||
struct sWeight
|
||||
{
|
||||
unsigned int VertexID;
|
||||
float Weight;
|
||||
};
|
||||
|
||||
struct sBone
|
||||
{
|
||||
std::string Name;
|
||||
|
||||
LinearAlgebra::Matrix4x4 NodeTransform;
|
||||
LinearAlgebra::Matrix4x4 OffsetMatrix; // T-Pose to local bone space
|
||||
LinearAlgebra::Matrix4x4 FinalTransformation;
|
||||
|
||||
unsigned int NumWeights;
|
||||
sWeight* pWeights;
|
||||
|
||||
unsigned int NumChildren;
|
||||
unsigned int* pChildren;
|
||||
};
|
||||
|
||||
struct sAnimatedMesh
|
||||
{
|
||||
unsigned int NumVertices;
|
||||
|
||||
LinearAlgebra::Vector3* pVertices;
|
||||
LinearAlgebra::Vector3* pNormals;
|
||||
|
||||
LinearAlgebra::Vector3* pTransformedVertices;
|
||||
LinearAlgebra::Vector3* pTransformedNormals;
|
||||
|
||||
unsigned int NumIndices;
|
||||
unsigned int* pIndices;
|
||||
};
|
||||
|
||||
struct sSkeleton
|
||||
{
|
||||
std::vector<sBone> Bones;
|
||||
};
|
||||
|
||||
template <typename _Ty>
|
||||
struct sNodeAnimationKey
|
||||
{
|
||||
_Ty Value;
|
||||
float Time;
|
||||
};
|
||||
|
||||
struct sNodeAnimation
|
||||
{
|
||||
std::string Name;
|
||||
|
||||
std::vector<sNodeAnimationKey<LinearAlgebra::Vector3> > PositionKeys;
|
||||
std::vector<sNodeAnimationKey<LinearAlgebra::Quaternion> > RotationKeys;
|
||||
};
|
||||
|
||||
struct sAnimation
|
||||
{
|
||||
std::vector<sNodeAnimation> NodeAnimations;
|
||||
|
||||
float TicksPerSecond;
|
||||
float Duration;
|
||||
};
|
||||
}
|
@@ -7,11 +7,11 @@
|
||||
#include <glad/glad.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/LinearAlgebra/Vector2.h>
|
||||
#include <types/texture.h>
|
||||
|
||||
struct Vertex {
|
||||
float x, y, z;
|
||||
GLfloat u, v;
|
||||
LinearAlgebra::Vector3 normal;
|
||||
};
|
||||
|
||||
class VertexArray {
|
||||
|
195
src/types/animation/AssimpConverter.cpp
Normal file
195
src/types/animation/AssimpConverter.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
#include <types/animation/AssimpConverter.h>
|
||||
|
||||
// Assimp
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
|
||||
// SkeletalModel
|
||||
#include <types/animation/skeleton/SkeletalModel.h>
|
||||
#include <cassert>
|
||||
|
||||
// SkeletalModel
|
||||
using namespace SA;
|
||||
|
||||
namespace AssimpConverter
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Private functions to convert from Assimp data types to glm
|
||||
static LinearAlgebra::Matrix4x4 aiToGlm(const aiMatrix4x4& v)
|
||||
{
|
||||
LinearAlgebra::Matrix4x4 out;
|
||||
assert(sizeof(out) == sizeof(v));
|
||||
memcpy(&out, &v, sizeof(v));
|
||||
return out.Transpose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static LinearAlgebra::Vector3 aiToGlm(const aiVector3D& v)
|
||||
{
|
||||
LinearAlgebra::Vector3 out;
|
||||
assert(sizeof(out) == sizeof(v));
|
||||
memcpy(&out, &v, sizeof(v));
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static LinearAlgebra::Quaternion aiToGlm(const aiQuaternion& v)
|
||||
{
|
||||
return LinearAlgebra::Quaternion(v.w, v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Recursively add all nodes from scene to skeleton
|
||||
static unsigned int AddNodesToSkeleton(aiNode* a_pNode, sSkeleton& a_Skeleton)
|
||||
{
|
||||
unsigned int BoneID = a_Skeleton.Bones.size();
|
||||
{
|
||||
sBone NewBone;
|
||||
NewBone.Name = a_pNode->mName.C_Str();
|
||||
NewBone.NodeTransform = aiToGlm(a_pNode->mTransformation);
|
||||
NewBone.NumChildren = a_pNode->mNumChildren;
|
||||
NewBone.pChildren = new unsigned int[NewBone.NumChildren];
|
||||
NewBone.NumWeights = 0;
|
||||
NewBone.pWeights = NULL;
|
||||
for (unsigned int i = 0; i < NewBone.NumChildren; ++i)
|
||||
NewBone.pChildren[i] = -1;
|
||||
//
|
||||
a_Skeleton.Bones.push_back(NewBone);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < a_pNode->mNumChildren; ++i)
|
||||
{
|
||||
unsigned int ChildBoneID = AddNodesToSkeleton(a_pNode->mChildren[i], a_Skeleton);
|
||||
a_Skeleton.Bones[BoneID].pChildren[i] = ChildBoneID;
|
||||
}
|
||||
|
||||
return BoneID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Adds meshes to AnimatedModel and populates bones in skeleton with weights
|
||||
static void AddMeshesAndBones(const aiScene* a_pScene, SkeletalModel& a_OutModel)
|
||||
{
|
||||
sSkeleton& Skeleton = a_OutModel.GetSkeleton();
|
||||
//
|
||||
for (unsigned int i = 0; i < a_pScene->mNumMeshes; ++i)
|
||||
{
|
||||
aiMesh* pMesh = a_pScene->mMeshes[i];
|
||||
//
|
||||
sAnimatedMesh AnimMesh;
|
||||
AnimMesh.NumVertices = pMesh->mNumVertices;
|
||||
AnimMesh.pVertices = new LinearAlgebra::Vector3[AnimMesh.NumVertices];
|
||||
AnimMesh.pTransformedVertices = new LinearAlgebra::Vector3[AnimMesh.NumVertices];
|
||||
AnimMesh.pNormals = new LinearAlgebra::Vector3[AnimMesh.NumVertices];
|
||||
AnimMesh.pTransformedNormals = new LinearAlgebra::Vector3[AnimMesh.NumVertices];
|
||||
AnimMesh.NumIndices = pMesh->mNumFaces * 3;
|
||||
AnimMesh.pIndices = new unsigned int[AnimMesh.NumIndices];
|
||||
|
||||
memcpy(AnimMesh.pVertices, pMesh->mVertices, AnimMesh.NumVertices*sizeof(aiVector3D));
|
||||
memcpy(AnimMesh.pNormals, pMesh->mNormals, AnimMesh.NumVertices*sizeof(aiVector3D));
|
||||
for (unsigned int i = 0; i < pMesh->mNumBones; ++i)
|
||||
{
|
||||
aiBone* pBone = pMesh->mBones[i];
|
||||
sBone& Bone = *Skeleton_FindBone(Skeleton, pBone->mName.C_Str());
|
||||
Bone.OffsetMatrix = aiToGlm(pBone->mOffsetMatrix);
|
||||
Bone.NumWeights = pBone->mNumWeights;
|
||||
Bone.pWeights = new sWeight[pBone->mNumWeights];
|
||||
for (unsigned int i = 0; i < Bone.NumWeights; ++i)
|
||||
{
|
||||
Bone.pWeights[i].VertexID = pBone->mWeights[i].mVertexId;
|
||||
Bone.pWeights[i].Weight = pBone->mWeights[i].mWeight;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < pMesh->mNumFaces; ++i)
|
||||
{
|
||||
aiFace Face = pMesh->mFaces[i];
|
||||
//CORE_ASSERT(Face.mNumIndices == 3);
|
||||
AnimMesh.pIndices[i * 3 + 0] = Face.mIndices[0];
|
||||
AnimMesh.pIndices[i * 3 + 1] = Face.mIndices[1];
|
||||
AnimMesh.pIndices[i * 3 + 2] = Face.mIndices[2];
|
||||
}
|
||||
|
||||
//
|
||||
a_OutModel.AddMesh(AnimMesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Adds animations to AnimatedModel
|
||||
void AddAnimations(const aiScene* a_pScene, SkeletalModel& a_OutModel)
|
||||
{
|
||||
if (a_pScene->mNumAnimations > 0)
|
||||
{
|
||||
sAnimation& Animation = a_OutModel.GetAnimation();
|
||||
// Only use the first animation
|
||||
aiAnimation* pAnimation = a_pScene->mAnimations[0];
|
||||
for (unsigned int i = 0; i < pAnimation->mNumChannels; ++i)
|
||||
{
|
||||
aiNodeAnim* pChannel = pAnimation->mChannels[i];
|
||||
|
||||
sNodeAnimation NodeAnimation;
|
||||
NodeAnimation.Name = pChannel->mNodeName.C_Str();
|
||||
for (unsigned int i = 0; i < pChannel->mNumPositionKeys; ++i)
|
||||
{
|
||||
sNodeAnimationKey<LinearAlgebra::Vector3> Vec3Key;
|
||||
Vec3Key.Time = (float)pChannel->mPositionKeys[i].mTime;
|
||||
Vec3Key.Value = aiToGlm(pChannel->mPositionKeys[i].mValue);
|
||||
NodeAnimation.PositionKeys.push_back(Vec3Key);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < pChannel->mNumRotationKeys; ++i)
|
||||
{
|
||||
sNodeAnimationKey<LinearAlgebra::Quaternion> QuatKey;
|
||||
QuatKey.Time = (float)pChannel->mRotationKeys[i].mTime;
|
||||
QuatKey.Value = aiToGlm(pChannel->mRotationKeys[i].mValue);
|
||||
NodeAnimation.RotationKeys.push_back(QuatKey);
|
||||
}
|
||||
|
||||
Animation.NodeAnimations.push_back(NodeAnimation);
|
||||
}
|
||||
|
||||
Animation.TicksPerSecond = (float)a_pScene->mAnimations[0]->mTicksPerSecond;
|
||||
Animation.Duration = (float)a_pScene->mAnimations[0]->mDuration;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Converts aiScene to AnimatedModel
|
||||
bool Convert(const aiScene* a_pScene, SkeletalModel& a_OutModel)
|
||||
{
|
||||
if (a_pScene == NULL)
|
||||
return false;
|
||||
|
||||
a_OutModel.SetGlobalInverseTransform(aiToGlm(a_pScene->mRootNode->mTransformation.Inverse()));
|
||||
|
||||
AddNodesToSkeleton(a_pScene->mRootNode, a_OutModel.GetSkeleton());
|
||||
AddMeshesAndBones(a_pScene, a_OutModel);
|
||||
AddAnimations(a_pScene, a_OutModel);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
222
src/types/animation/skeleton/skeletalModel.cpp
Normal file
222
src/types/animation/skeleton/skeletalModel.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include <types/animation/skeleton/SkeletalModel.h>
|
||||
#include <J3ML/LinearAlgebra/Quaternion.h>
|
||||
#include <J3ML/LinearAlgebra/Vector3.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix4x4.h>
|
||||
#include <J3ML/LinearAlgebra/Matrix3x3.h>
|
||||
#include <cstring>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Based on: http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace SA
|
||||
{
|
||||
SkeletalModel::SkeletalModel()
|
||||
: m_AnimationTime(0.0f)
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SkeletalModel::~SkeletalModel()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SkeletalModel::Clear()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_Meshes.size(); ++i)
|
||||
{
|
||||
delete[] m_Meshes[i].pVertices;
|
||||
delete[] m_Meshes[i].pNormals;
|
||||
delete[] m_Meshes[i].pTransformedVertices;
|
||||
delete[] m_Meshes[i].pTransformedNormals;
|
||||
delete[] m_Meshes[i].pIndices;
|
||||
}
|
||||
m_Meshes.clear();
|
||||
|
||||
for (unsigned int i = 0; i < m_Skeleton.Bones.size(); ++i)
|
||||
{
|
||||
delete[] m_Skeleton.Bones[i].pWeights;
|
||||
delete[] m_Skeleton.Bones[i].pChildren;
|
||||
}
|
||||
m_Skeleton.Bones.clear();
|
||||
|
||||
m_Animation.NodeAnimations.clear();
|
||||
m_Animation.Duration = 0.0f;
|
||||
m_Animation.TicksPerSecond = 0.0f;
|
||||
|
||||
m_GlobalInverseTransform = LinearAlgebra::Matrix4x4(1);
|
||||
m_AnimationTime = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SkeletalModel::Update(float a_Dt)
|
||||
{
|
||||
m_AnimationTime = fmodf(m_AnimationTime + a_Dt * m_Animation.TicksPerSecond, m_Animation.Duration);
|
||||
//
|
||||
|
||||
ReadNodeHierarchy(m_AnimationTime, m_Animation, m_Skeleton, m_Skeleton.Bones[0], LinearAlgebra::Matrix4x4(1));
|
||||
TransformVertices(m_Skeleton);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SkeletalModel::ReadNodeHierarchy(float AnimationTime, sAnimation& a_Animation, sSkeleton& a_Skeleton, sBone& a_Bone, const LinearAlgebra::Matrix4x4& ParentTransform)
|
||||
{
|
||||
std::string NodeName(a_Bone.Name);
|
||||
LinearAlgebra::Matrix4x4 NodeTransformation(a_Bone.NodeTransform);
|
||||
const sNodeAnimation* pNewNodeAnim = FindNodeAnim(a_Animation, NodeName);
|
||||
|
||||
if (pNewNodeAnim)
|
||||
{
|
||||
LinearAlgebra::Vector3 Translation = NodeAnimation_FindInterpolatedPosition(*pNewNodeAnim, AnimationTime);
|
||||
LinearAlgebra::Quaternion RotationQ = NodeAnimation_FindInterpolatedRotation(*pNewNodeAnim, AnimationTime);
|
||||
|
||||
// LinearAlgebra::Vector3 Scaling2(1, 1, 1);
|
||||
// LinearAlgebra::Matrix4x4 ScalingM2 = glm::scale(Scaling2);
|
||||
|
||||
LinearAlgebra::Matrix4x4 RotationM2 = RotationQ.ToMatrix4x4();
|
||||
|
||||
LinearAlgebra::Matrix4x4 TranslationM2; TranslationM2.SetTranslatePart(Translation);
|
||||
|
||||
// Combine the above transformations
|
||||
NodeTransformation = TranslationM2 * RotationM2;// * ScalingM2;
|
||||
}
|
||||
|
||||
LinearAlgebra::Matrix4x4 GlobalTransformation = ParentTransform * NodeTransformation;
|
||||
|
||||
unsigned int BoneIndex = Skeleton_FindBoneIndex(a_Skeleton, NodeName);
|
||||
if (BoneIndex != -1)
|
||||
{
|
||||
sBone* pBone = &a_Skeleton.Bones[BoneIndex];
|
||||
pBone->FinalTransformation = m_GlobalInverseTransform * GlobalTransformation * pBone->OffsetMatrix;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < a_Bone.NumChildren; i++)
|
||||
{
|
||||
ReadNodeHierarchy(AnimationTime, a_Animation, a_Skeleton, a_Skeleton.Bones[a_Bone.pChildren[i]], GlobalTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void SkeletalModel::TransformVertices(const sSkeleton& a_Skeleton)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_Meshes.size(); ++i)
|
||||
{
|
||||
// Reset mesh vertices and normals
|
||||
sAnimatedMesh& AnimMesh = m_Meshes[i];
|
||||
memset(AnimMesh.pTransformedVertices, 0, AnimMesh.NumVertices*sizeof(LinearAlgebra::Vector3));
|
||||
memset(AnimMesh.pTransformedNormals, 0, AnimMesh.NumVertices*sizeof(LinearAlgebra::Vector3));
|
||||
|
||||
//
|
||||
for (unsigned int i = 0; i < a_Skeleton.Bones.size(); ++i)
|
||||
{
|
||||
const sBone& Bone = a_Skeleton.Bones[i];
|
||||
//
|
||||
LinearAlgebra::Matrix4x4 Transformation = Bone.FinalTransformation;
|
||||
LinearAlgebra::Matrix3x3 Rotation = Transformation.GetRotatePart();
|
||||
//
|
||||
for (unsigned int i = 0; i < Bone.NumWeights; ++i)
|
||||
{
|
||||
sWeight Weight = Bone.pWeights[i];
|
||||
//
|
||||
LinearAlgebra::Vector3 inVertex = AnimMesh.pVertices[Weight.VertexID];
|
||||
LinearAlgebra::Vector3& outVertex = AnimMesh.pTransformedVertices[Weight.VertexID];
|
||||
LinearAlgebra::Vector4 inVertex4 = {inVertex.x, inVertex.y, inVertex.z, 1};
|
||||
LinearAlgebra::Vector3 t = (Transformation * inVertex) * Weight.Weight;
|
||||
outVertex = outVertex + t;
|
||||
//
|
||||
LinearAlgebra::Vector3 inNormal = AnimMesh.pNormals[Weight.VertexID];
|
||||
LinearAlgebra::Vector3& outNormal = AnimMesh.pTransformedNormals[Weight.VertexID];
|
||||
outNormal = outNormal + (Rotation * inNormal) * Weight.Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize normals
|
||||
for (unsigned int i = 0; i < m_Meshes.size(); ++i)
|
||||
{
|
||||
sAnimatedMesh& AnimMesh = m_Meshes[i];
|
||||
for (unsigned int i = 0; i < AnimMesh.NumVertices; ++i)
|
||||
{
|
||||
AnimMesh.pTransformedNormals[i] = AnimMesh.pTransformedNormals[i].Normalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const sNodeAnimation* FindNodeAnim(const sAnimation& a_Animation, const std::string& a_NodeName)
|
||||
{
|
||||
for (unsigned int i = 0; i < a_Animation.NodeAnimations.size(); ++i)
|
||||
{
|
||||
const sNodeAnimation& NodeAnim = a_Animation.NodeAnimations[i];
|
||||
if (NodeAnim.Name == a_NodeName)
|
||||
{
|
||||
return &NodeAnim;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LinearAlgebra::Vector3 InterpolateValue(const LinearAlgebra::Vector3& a_Start, const LinearAlgebra::Vector3& a_End, float a_Factor)
|
||||
{
|
||||
return a_Start.Lerp(a_End,a_Factor);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LinearAlgebra::Quaternion InterpolateValue(const LinearAlgebra::Quaternion& a_Start, const LinearAlgebra::Quaternion& a_End, float a_Factor)
|
||||
{
|
||||
return a_Start.Slerp(a_End, a_Factor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned int Skeleton_FindBoneIndex(const sSkeleton& a_Skeleton, const std::string& a_Name)
|
||||
{
|
||||
for (unsigned int i = 0; i < a_Skeleton.Bones.size(); ++i)
|
||||
{
|
||||
if (a_Skeleton.Bones[i].Name == a_Name)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
sBone* Skeleton_FindBone(sSkeleton& a_Skeleton, const std::string& a_Name)
|
||||
{
|
||||
return &a_Skeleton.Bones[Skeleton_FindBoneIndex(a_Skeleton, a_Name)];
|
||||
}
|
||||
}
|
314
src/types/animation/skeleton/skeletalModelSerializer.cpp
Normal file
314
src/types/animation/skeleton/skeletalModelSerializer.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
#include <types/animation/skeleton/SkeletalModelSerializer.h>
|
||||
|
||||
#include <types/animation/skeleton/SkeletalModel.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace SA
|
||||
{
|
||||
// Helper function used for reading and writing
|
||||
template <typename _Ty>
|
||||
static void WriteBinary(unsigned int& a_InOutPos, char* a_pDestination, const _Ty& a_Source)
|
||||
{
|
||||
if (a_pDestination != NULL)
|
||||
{
|
||||
memcpy(a_pDestination + a_InOutPos, &a_Source, sizeof(_Ty));
|
||||
}
|
||||
a_InOutPos += sizeof(_Ty);
|
||||
}
|
||||
|
||||
template <typename _Ty>
|
||||
static void ReadBinary(unsigned int& a_InOutPos, _Ty& a_Destination, const char* a_pSource)
|
||||
{
|
||||
memcpy(&a_Destination, a_pSource + a_InOutPos, sizeof(_Ty));
|
||||
a_InOutPos += sizeof(_Ty);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename _Ty>
|
||||
static void WriteBinary(unsigned int& a_InOutPos, char* a_pDestination, const _Ty* a_Source, unsigned int a_NumElements)
|
||||
{
|
||||
if (a_pDestination != NULL)
|
||||
{
|
||||
memcpy(a_pDestination + a_InOutPos, a_Source, sizeof(_Ty) * a_NumElements);
|
||||
}
|
||||
a_InOutPos += sizeof(_Ty) * a_NumElements;
|
||||
}
|
||||
|
||||
template <typename _Ty>
|
||||
static void ReadBinary(unsigned int& a_InOutPos, _Ty*& a_pDestination, const char* a_pSource, unsigned int a_NumElements)
|
||||
{
|
||||
a_pDestination = NULL;
|
||||
if (a_NumElements > 0)
|
||||
{
|
||||
a_pDestination = new _Ty[a_NumElements];
|
||||
memcpy(a_pDestination, a_pSource + a_InOutPos, sizeof(_Ty) * a_NumElements);
|
||||
a_InOutPos += sizeof(_Ty) * a_NumElements;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void WriteString(unsigned int& a_InOutPos, char* a_pDestination, const std::string& a_Source)
|
||||
{
|
||||
unsigned int StringLength = a_Source.size();
|
||||
WriteBinary(a_InOutPos, a_pDestination, StringLength);
|
||||
WriteBinary(a_InOutPos, a_pDestination, a_Source.c_str(), StringLength);
|
||||
}
|
||||
|
||||
static void ReadString(unsigned int& a_InOutPos, std::string& a_Destination, const char* a_pSource)
|
||||
{
|
||||
unsigned int StringLength = 0;
|
||||
ReadBinary(a_InOutPos, StringLength, a_pSource);
|
||||
if (StringLength > 0)
|
||||
{
|
||||
a_Destination.resize(StringLength + 1);
|
||||
memcpy((void*)a_Destination.data(), a_pSource + a_InOutPos, StringLength);
|
||||
a_InOutPos += StringLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename _Ty>
|
||||
static void WriteKeys(unsigned int& a_InOutPos, char* a_pDestination, const std::vector<sNodeAnimationKey<_Ty> >& a_Source)
|
||||
{
|
||||
unsigned int NumKeys = a_Source.size();
|
||||
WriteBinary(a_InOutPos, a_pDestination, NumKeys);
|
||||
for (unsigned int i = 0; i < NumKeys; ++i)
|
||||
{
|
||||
WriteBinary(a_InOutPos, a_pDestination, a_Source[i].Time);
|
||||
WriteBinary(a_InOutPos, a_pDestination, a_Source[i].Value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _Ty>
|
||||
static void ReadKeys(unsigned int& a_InOutPos, std::vector<sNodeAnimationKey<_Ty> >& a_Destination, const char* a_pSource)
|
||||
{
|
||||
unsigned int NumKeys = 0;
|
||||
ReadBinary(a_InOutPos, NumKeys, a_pSource);
|
||||
for (unsigned int i = 0; i < NumKeys; ++i)
|
||||
{
|
||||
sNodeAnimationKey<_Ty> PosKey;
|
||||
ReadBinary(a_InOutPos, PosKey.Time, a_pSource);
|
||||
ReadBinary(a_InOutPos, PosKey.Value, a_pSource);
|
||||
a_Destination.push_back(PosKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern std::string ModelToData(const SkeletalModel& a_Model)
|
||||
{
|
||||
std::string OutData;
|
||||
ModelToData(a_Model, OutData);
|
||||
return OutData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
extern bool ModelToData(const SkeletalModel& a_Model, std::string& a_OutData)
|
||||
{
|
||||
unsigned int DataSize = ModelToData(a_Model, NULL);
|
||||
a_OutData.resize(DataSize);
|
||||
return ModelToData(a_Model, (char*)a_OutData.data()) > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned int ModelToData(const SkeletalModel& a_Model, char* a_pOutData)
|
||||
{
|
||||
unsigned int DataPos = 0;
|
||||
|
||||
int a = 1, b = 2, c = 3;
|
||||
WriteBinary(DataPos, a_pOutData, a);
|
||||
WriteBinary(DataPos, a_pOutData, b);
|
||||
WriteBinary(DataPos, a_pOutData, c);
|
||||
|
||||
// Write meshes
|
||||
unsigned int NumMeshes = a_Model.GetNumMeshes();
|
||||
WriteBinary(DataPos, a_pOutData, NumMeshes);
|
||||
for (unsigned int i = 0; i < NumMeshes; ++i)
|
||||
{
|
||||
const sAnimatedMesh& Mesh = a_Model.GetMesh(i);
|
||||
|
||||
// Write vertices and normals
|
||||
WriteBinary(DataPos, a_pOutData, Mesh.NumVertices);
|
||||
WriteBinary(DataPos, a_pOutData, Mesh.pVertices, Mesh.NumVertices);
|
||||
WriteBinary(DataPos, a_pOutData, Mesh.pNormals, Mesh.NumVertices);
|
||||
|
||||
// Write indices
|
||||
WriteBinary(DataPos, a_pOutData, Mesh.NumIndices);
|
||||
WriteBinary(DataPos, a_pOutData, Mesh.pIndices, Mesh.NumIndices);
|
||||
}
|
||||
|
||||
// Write skeleton
|
||||
unsigned int NumBones = a_Model.GetSkeleton().Bones.size();
|
||||
WriteBinary(DataPos, a_pOutData, NumBones);
|
||||
for (unsigned int i = 0; i < NumBones; ++i)
|
||||
{
|
||||
const sBone& Bone = a_Model.GetSkeleton().Bones[i];
|
||||
|
||||
// Write bone name
|
||||
WriteString(DataPos, a_pOutData, Bone.Name);
|
||||
|
||||
// Write matrices
|
||||
WriteBinary(DataPos, a_pOutData, Bone.NodeTransform);
|
||||
WriteBinary(DataPos, a_pOutData, Bone.OffsetMatrix);
|
||||
|
||||
// Write weights
|
||||
WriteBinary(DataPos, a_pOutData, Bone.NumWeights);
|
||||
for (unsigned int i = 0; i < Bone.NumWeights; ++i)
|
||||
{
|
||||
const sWeight& Weight = Bone.pWeights[i];
|
||||
WriteBinary(DataPos, a_pOutData, Weight.VertexID);
|
||||
WriteBinary(DataPos, a_pOutData, Weight.Weight);
|
||||
}
|
||||
|
||||
// Write children IDs
|
||||
WriteBinary(DataPos, a_pOutData, Bone.NumChildren);
|
||||
WriteBinary(DataPos, a_pOutData, Bone.pChildren, Bone.NumChildren);
|
||||
}
|
||||
|
||||
// Write animation
|
||||
WriteBinary(DataPos, a_pOutData, a_Model.GetAnimation().TicksPerSecond);
|
||||
WriteBinary(DataPos, a_pOutData, a_Model.GetAnimation().Duration);
|
||||
unsigned int NumNodeAnimations = a_Model.GetAnimation().NodeAnimations.size();
|
||||
WriteBinary(DataPos, a_pOutData, NumNodeAnimations);
|
||||
for (unsigned int i = 0; i < NumNodeAnimations; ++i)
|
||||
{
|
||||
const sNodeAnimation Animation = a_Model.GetAnimation().NodeAnimations[i];
|
||||
|
||||
// Write node name
|
||||
WriteString(DataPos, a_pOutData, Animation.Name);
|
||||
|
||||
// Write keyframes
|
||||
WriteKeys(DataPos, a_pOutData, Animation.PositionKeys);
|
||||
WriteKeys(DataPos, a_pOutData, Animation.RotationKeys);
|
||||
}
|
||||
|
||||
WriteBinary(DataPos, a_pOutData, a_Model.GetGlobalInverseTransform());
|
||||
|
||||
return DataPos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool DataToModel(const char* a_pData, SkeletalModel& a_OutModel)
|
||||
{
|
||||
unsigned int DataPos = 0;
|
||||
|
||||
int a = 0, b = 0, c = 0;
|
||||
ReadBinary(DataPos, a, a_pData);
|
||||
ReadBinary(DataPos, b, a_pData);
|
||||
ReadBinary(DataPos, c, a_pData);
|
||||
|
||||
unsigned int NumMeshes = 0;
|
||||
ReadBinary(DataPos, NumMeshes, a_pData);
|
||||
for (unsigned int i = 0; i < NumMeshes; ++i)
|
||||
{
|
||||
sAnimatedMesh Mesh;
|
||||
|
||||
// Read vertices and normals
|
||||
ReadBinary(DataPos, Mesh.NumVertices, a_pData);
|
||||
ReadBinary(DataPos, Mesh.pVertices, a_pData, Mesh.NumVertices);
|
||||
ReadBinary(DataPos, Mesh.pNormals, a_pData, Mesh.NumVertices);
|
||||
if (Mesh.NumVertices > 0)
|
||||
{
|
||||
|
||||
Mesh.pTransformedVertices = new LinearAlgebra::Vector3[Mesh.NumVertices];
|
||||
Mesh.pTransformedNormals = new LinearAlgebra::Vector3[Mesh.NumVertices];
|
||||
}
|
||||
else
|
||||
{
|
||||
Mesh.pTransformedVertices = NULL;
|
||||
Mesh.pTransformedNormals = NULL;
|
||||
}
|
||||
|
||||
// Read indices
|
||||
ReadBinary(DataPos, Mesh.NumIndices, a_pData);
|
||||
ReadBinary(DataPos, Mesh.pIndices, a_pData, Mesh.NumIndices);
|
||||
|
||||
a_OutModel.AddMesh(Mesh);
|
||||
}
|
||||
|
||||
unsigned int NumBones = 0;
|
||||
ReadBinary(DataPos, NumBones, a_pData);
|
||||
for (unsigned int i = 0; i < NumBones; ++i)
|
||||
{
|
||||
sBone Bone;
|
||||
|
||||
// Read bone name
|
||||
ReadString(DataPos, Bone.Name, a_pData);
|
||||
|
||||
// Read matrices
|
||||
ReadBinary(DataPos, Bone.NodeTransform, a_pData);
|
||||
ReadBinary(DataPos, Bone.OffsetMatrix, a_pData);
|
||||
|
||||
// Read weights
|
||||
ReadBinary(DataPos, Bone.NumWeights, a_pData);
|
||||
if (Bone.NumWeights > 0)
|
||||
{
|
||||
Bone.pWeights = new sWeight[Bone.NumWeights];
|
||||
for (unsigned int i = 0; i < Bone.NumWeights; ++i)
|
||||
{
|
||||
sWeight Weight;
|
||||
ReadBinary(DataPos, Weight.VertexID, a_pData);
|
||||
ReadBinary(DataPos, Weight.Weight, a_pData);
|
||||
Bone.pWeights[i] = Weight;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Bone.pWeights = NULL;
|
||||
}
|
||||
|
||||
// Read children IDs
|
||||
ReadBinary(DataPos, Bone.NumChildren, a_pData);
|
||||
ReadBinary(DataPos, Bone.pChildren, a_pData, Bone.NumChildren);
|
||||
|
||||
a_OutModel.GetSkeleton().Bones.push_back(Bone);
|
||||
}
|
||||
|
||||
// Read animation
|
||||
ReadBinary(DataPos, a_OutModel.GetAnimation().TicksPerSecond, a_pData);
|
||||
ReadBinary(DataPos, a_OutModel.GetAnimation().Duration, a_pData);
|
||||
unsigned int NumNodeAnimations = 0;
|
||||
ReadBinary(DataPos, NumNodeAnimations, a_pData);
|
||||
for (unsigned int i = 0; i < NumNodeAnimations; ++i)
|
||||
{
|
||||
sNodeAnimation Animation;
|
||||
|
||||
// Read node name
|
||||
ReadString(DataPos, Animation.Name, a_pData);
|
||||
|
||||
// Read position keys
|
||||
ReadKeys(DataPos, Animation.PositionKeys, a_pData);
|
||||
|
||||
// Read rotation keys
|
||||
ReadKeys(DataPos, Animation.RotationKeys, a_pData);
|
||||
|
||||
a_OutModel.GetAnimation().NodeAnimations.push_back(Animation);
|
||||
}
|
||||
|
||||
LinearAlgebra::Matrix4x4 GlobalInverseTransform;
|
||||
ReadBinary(DataPos, GlobalInverseTransform, a_pData);
|
||||
a_OutModel.SetGlobalInverseTransform(GlobalInverseTransform);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -109,10 +109,10 @@ void Camera::post_render() {
|
||||
this->ticksAlive++;
|
||||
}
|
||||
|
||||
Geometry::Frustum Camera::getFrustum() {
|
||||
Geometry::Camera cam = {this->position,this->fAngle(),this->rAngle(),this->upVector};
|
||||
return Geometry::CreateFrustumFromCamera(cam, engine->window->getSize()[0] / engine->window->getSize()[1], engine->fov, engine->nearPlane, engine->farPlane);
|
||||
}
|
||||
//Geometry::Frustum Camera::getFrustum() {
|
||||
//Geometry::Camera cam = {this->position,this->fAngle(),this->rAngle(),this->upVector};
|
||||
//return Geometry::CreateFrustumFromCamera(cam, engine->window->getSize()[0] / engine->window->getSize()[1], engine->fov, engine->nearPlane, engine->farPlane);
|
||||
//}
|
||||
|
||||
void Camera::hMove(LinearAlgebra::Vector3 a, float vel) {
|
||||
if (cameraMode == CameraMode::FREECAM) {
|
||||
|
Reference in New Issue
Block a user