I'm trying to animate a skeletal mesh. The mesh loads with no problems and all is set up correctly. My problem is that when I calculate the matrix for a given keyframe the mesh go nuts.
Here is na image of the problem.
The mesh on the right is using the boné transform as the final matrix. the ne one the left is using the matrix calculated from keyframes. I don't know where i'm going wrong with this.
Here is my code:
The Animation Clip where all keyframes are stored:
Matrix4 AnimationClip::GetTransform(Bone *bone, float deltaTime)
{
Channel* channel = channels[bone->name];
if (channel == nullptr)
return bone->transform;
else
return channel->Update(deltaTime);
}
///////////
Channel::Channel(aiNodeAnim* animNode)
{
name = string(animNode->mNodeName.data);
for (GLuint k = 0; k < animNode->mNumPositionKeys; k++)
{
aiVectorKey vec = animNode->mPositionKeys[k];
positions.push_back(
Keyframe(
(float)vec.mTime,
Vector3(vec.mValue.x, vec.mValue.y, vec.mValue.z)));
}
for (GLuint k = 0; k < animNode->mNumScalingKeys; k++)
{
aiVectorKey vec = animNode->mScalingKeys[k];
scalings.push_back(
Keyframe(
(float)vec.mTime,
Vector3(vec.mValue.x, vec.mValue.y, vec.mValue.z)));
}
for (GLuint k = 0; k < animNode->mNumRotationKeys; k++)
{
aiQuatKey vec = animNode->mRotationKeys[k];
rotations.push_back(
Keyframe(
(float)vec.mTime,
Quaternion(
vec.mValue.x,
vec.mValue.y,
vec.mValue.z,
vec.mValue.w)));
}
}
Matrix4 Channel::Update(float animTime)
{
return
CalculatePosition(animTime) *
CalculateRotation(animTime) *
CalculateScaling(animTime);
}
Matrix4 Channel::CalculatePosition(float animationTime)
{
return
Matrix4::CreateTranslation(
CalcInterpolatedPosition(animationTime));
}
Vector3 Channel::CalcInterpolatedPosition(float animationTime)
{
if (positions.size() == 1)
return positions[0].value;
GLuint positionIndex = FindPosition(animationTime);
GLuint nextPositionIndex = (positionIndex + 1);
float deltaTime =
positions[nextPositionIndex].time - positions[positionIndex].time;
float factor =
(animationTime - (float)positions[positionIndex].time) / deltaTime;
Vector3 startPos = positions[positionIndex].value;
Vector3 endPos = positions[nextPositionIndex].value;
Vector3 delta = endPos - startPos;
return startPos + delta * factor;//Vector3::Lerp(startPos, endPos, factor);
}
GLuint Channel::FindPosition(float AnimationTime)
{
for (GLuint i = 0; i < positions.size() - 1; i++) {
if (AnimationTime < (float)positions[i + 1].time)
return i;
}
return 0;
}
And Here is my Skinned Mesh code where I'm fetching the keyframes:
void SkinnedMesh::BoneTransform(
double delta,
vector& transforms,
AnimationClip *anim)
{
Matrix4 identity_matrix = Matrix4::Identity();
float ticksPerSecond =
anim->ticksPerSecond == 0 ? 25.0f : anim->ticksPerSecond;
double time_in_ticks = delta * ticksPerSecond;
float animation_time =
fmod((float)time_in_ticks, anim->duration);
UpdateTransforms(
animation_time,
anim,
rootBone,
identity_matrix);
transforms.resize(m_num_bones);
for (GLuint i = 0; i < m_num_bones; i++)
transforms[i] =
m_bone_matrices[i].final_world_transform;
}
void SkinnedMesh::UpdateTransforms(
float p_animation_time,
AnimationClip *anim,
Bone *parentBone,
Matrix4& parentTransform)
{
Matrix4 boneTransform =// parentBone->transform;
anim->GetTransform(parentBone, p_animation_time);
Matrix4 global_transform =
parentTransform * boneTransform;
if (m_bone_mapping.find(parentBone->name) !=
m_bone_mapping.end()) // true if node_name exist in bone_mapping
{
GLuint bone_index = m_bone_mapping[parentBone->name];
m_bone_matrices[bone_index].final_world_transform =
m_global_inverse_transform *
global_transform *
m_bone_matrices[bone_index].offset_matrix;
}
for (vector::iterator it =
parentBone->children.begin();
it != parentBone->children.end();
it++) {
UpdateTransforms(
p_animation_time,
anim,
(*it),
global_transform);
}
}
void SkinnedMesh::Render(Shader* shader, AnimationClip *clip)
{
vector transforms;
BoneTransform(
(double)SDL_GetTicks() / 1000.0f,
transforms,
clip);
for (GLuint i = 0; i < transforms.size(); i++)
{
GLfloat values[16];
Matrix4::ValuePointer(transforms[i], values);
string a = "jointTransforms[";
a.append(to_string(i));
a.append("]");
glProgramUniformMatrix4fv(
shader->GetID(),
shader->GetUniformLocation(a.c_str()),
1,
GL_FALSE,
(const GLfloat*)values);
}
for (unsigned int i = 0; i < meshes.size(); i++)
meshes[i]->Render(shader);
}
It seems the matrices are all wrong and therefore deforming the mesh. Can you help me?
EDIT :
The Meh on the right has this code :
Matrix4 AnimationClip::GetTransform(Bone *bone, float deltaTime)
{
Channel* channel = channels[bone->name];
if (channel == nullptr)
return bone->transform;
else
return bone->transform;
}
EDIT 2 :
As daniel_1985 Pointed out my matrices might have to be transposed but I'm in doubt…
this is how I'm Converting Assimp matrices now
Matrix4 Matrix4::ToMatrix4(aiMatrix4x4 mat)
{
Matrix4 result;
result.row0 = Vector4(mat.a1, mat.b1, mat.c1, mat.d1);
result.row1 = Vector4(mat.a2, mat.b2, mat.c2, mat.d2);
result.row2 = Vector4(mat.a3, mat.b3, mat.c3, mat.d3);
result.row3 = Vector4(mat.a4, mat.b4, mat.c4, mat.d4);
return result;
}
this is the result If I set my skinned mesh to its bones transforms
So What am I doing wrong…..Is the matrix being correctly converted or not? Na if it is Why does my mesh get deformaed like this?
I am building my bone hierarchy like this :
void SkinnedMesh::BuildBoneHierarchy(
aiNode* node,
Bone* parentBone)
{
if (parentBone == nullptr)
{
rootBone = new Bone();
rootBone->name = string(node->mName.data);
rootBone->transform =
Matrix4::ToMatrix4(node->mTransformation);
for(GLuint i = 0; i < node->mNumChildren; i++)
BuildBoneHierarchy(
node->mChildren[i],
rootBone);
}
else
{
Bone* bone = new Bone();
bone->name = string(node->mName.data);
bone->transform =
Matrix4::ToMatrix4(node->mTransformation);
parentBone->children.push_back(bone);
for (GLuint i = 0; i < node->mNumChildren; i++)
BuildBoneHierarchy(
node->mChildren[i],
bone);
}
}
UPDATE :
So I've succesfully fixed my skinned mesh for its pose by calculating manually each bone inverse Transform…. Like this :
void Bone::CalcInverseBindTransform(Matrix4 parentBindTransform)
{
Matrix4 bindTransform = parentBindTransform * transform;
inverseTransform = Matrix4::Invert(bindTransform);
for (vector::iterator iter = children.begin();
iter != children.end();
iter++)
(*iter)->CalcInverseBindTransform(bindTransform);
}
In my Update Transforms I'm setting the mult like this:
GLuint bone_index = m_bone_mapping[parentBone->name];
m_bone_matrices[bone_index].final_world_transform =
m_global_inverse_transform *
global_transform *
parentBone->inverseTransform;
But the when I animate it, it goes nuts…. So… What do I need to do now?
No comments:
Post a Comment