I have been working on importing bone animation recently, so I made a three-dimensional model similar to minecraft, with some IK methods for testing the import of Assimp animation. The Ouput format is COLLADA (*. Dae), and the tool I used is Blender. On the programming side, my environment is opengl / glm / assimp. I think this information is enough for my problem. One thing, the animation of the model, I just record 7 frames to immediately play the animation of the allip.
Firstly, I think that my transformation, besides the local part of the transformation, is correct, so let the function return only glm::mat4(1.0f) , and the result shows the model of the binding representation (not sure). (see image below)
Secondly, return the value of glm::mat4(1.0f) to bone->localTransform = transform * scaling * glm::mat4(1.0f); , then the model is deformed. (see image below)
Test image and model in a blender:
( bone->localTransform = glm::mat4(1.0f) * scaling * rotate; this is an image underground :()
The code is here:
void MeshModel::UpdateAnimations(float time, std::vector<Bone*>& bones) { for each (Bone* bone in bones) { glm::mat4 rotate = GetInterpolateRotation(time, bone->rotationKeys); glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys); glm::mat4 scaling = GetInterpolateScaling(time, bone->scalingKeys); //bone->localTransform = transform * scaling * glm::mat4(1.0f); //bone->localTransform = glm::mat4(1.0f) * scaling * rotate; //bone->localTransform = glm::translate(glm::mat4(1.0f), glm::vec3(0.5f)); bone->localTransform = glm::mat4(1.0f); } } void MeshModel::UpdateBone(Bone * bone) { glm::mat4 parentTransform = bone->getParentTransform(); bone->nodeTransform = parentTransform * bone->transform // assimp_node->mTransformation * bone->localTransform; // TSR matrix bone->finalTransform = globalInverse * bone->nodeTransform * bone->inverseBindPoseMatrix; // ai_mesh->mBones[i]->mOffsetMatrix for (int i = 0; i < (int)bone->children.size(); i++) { UpdateBone(bone->children[i]); } } glm::mat4 Bone::getParentTransform() { if (this->parent != nullptr) return parent->nodeTransform; else return glm::mat4(1.0f); } glm::mat4 MeshModel::GetInterpolateRotation(float time, std::vector<BoneKey>& keys) { // we need at least two values to interpolate... if ((int)keys.size() == 0) { return glm::mat4(1.0f); } if ((int)keys.size() == 1) { return glm::mat4_cast(keys[0].rotation); } int rotationIndex = FindBestTimeIndex(time, keys); int nextRotationIndex = (rotationIndex + 1); assert(nextRotationIndex < (int)keys.size()); float DeltaTime = (float)(keys[nextRotationIndex].time - keys[rotationIndex].time); float Factor = (time - (float)keys[rotationIndex].time) / DeltaTime; if (Factor < 0.0f) Factor = 0.0f; if (Factor > 1.0f) Factor = 1.0f; assert(Factor >= 0.0f && Factor <= 1.0f); const glm::quat& startRotationQ = keys[rotationIndex].rotation; const glm::quat& endRotationQ = keys[nextRotationIndex].rotation; glm::quat interpolateQ = glm::lerp(endRotationQ, startRotationQ, Factor); interpolateQ = glm::normalize(interpolateQ); return glm::mat4_cast(interpolateQ); } glm::mat4 MeshModel::GetInterpolateTransform(float time, std::vector<BoneKey>& keys) { // we need at least two values to interpolate... if ((int)keys.size() == 0) { return glm::mat4(1.0f); } if ((int)keys.size() == 1) { return glm::translate(glm::mat4(1.0f), keys[0].vector); } int translateIndex = FindBestTimeIndex(time, keys); int nextTranslateIndex = (translateIndex + 1); assert(nextTranslateIndex < (int)keys.size()); float DeltaTime = (float)(keys[nextTranslateIndex].time - keys[translateIndex].time); float Factor = (time - (float)keys[translateIndex].time) / DeltaTime; if (Factor < 0.0f) Factor = 0.0f; if (Factor > 1.0f) Factor = 1.0f; assert(Factor >= 0.0f && Factor <= 1.0f); const glm::vec3& startTranslate = keys[translateIndex].vector; const glm::vec3& endTrabslate = keys[nextTranslateIndex].vector; glm::vec3 delta = endTrabslate - startTranslate; glm::vec3 resultVec = startTranslate + delta * Factor; return glm::translate(glm::mat4(1.0f), resultVec); }
The idea behind the code refers to Matrix calculations for gpu skins and Skeletal animation with Assimp .
In general, I do all the information from the allip to the MeshModel and save it to the bone structure, so I think the information is ok?
Last, my vertex shader code:
#version 330 core #define MAX_BONES_PER_VERTEX 4 in vec3 position; in vec2 texCoord; in vec3 normal; in ivec4 boneID; in vec4 boneWeight; const int MAX_BONES = 100; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform mat4 boneTransform[MAX_BONES]; out vec3 FragPos; out vec3 Normal; out vec2 TexCoords; out float Visibility; const float density = 0.007f; const float gradient = 1.5f; void main() { mat4 boneTransformation = boneTransform[boneID[0]] * boneWeight[0]; boneTransformation += boneTransform[boneID[1]] * boneWeight[1]; boneTransformation += boneTransform[boneID[2]] * boneWeight[2]; boneTransformation += boneTransform[boneID[3]] * boneWeight[3]; vec3 usingPosition = (boneTransformation * vec4(position, 1.0)).xyz; vec3 usingNormal = (boneTransformation * vec4(normal, 1.0)).xyz; vec4 viewPos = view * model * vec4(usingPosition, 1.0); gl_Position = projection * viewPos; FragPos = vec3(model * vec4(usingPosition, 1.0f)); Normal = mat3(transpose(inverse(model))) * usingNormal; TexCoords = texCoord; float distance = length(viewPos.xyz); Visibility = exp(-pow(distance * density, gradient)); Visibility = clamp(Visibility, 0.0f, 1.0f); }
If my question is above, the lack of code or description is vague, please let me know, thanks!
Edit: (1)
In addition, my bone information like this (part of the code extraction):
for (int i = 0; i < (int)nodeAnim->mNumPositionKeys; i++) { BoneKey key; key.time = nodeAnim->mPositionKeys[i].mTime; aiVector3D vec = nodeAnim->mPositionKeys[i].mValue; key.vector = glm::vec3(vec.x, vec.y, vec.z); currentBone->transformKeys.push_back(key); }
had some transform vector, so my code above is glm::mat4 transform = GetInterpolateTransform(time, bone->transformKeys); , Absloutely, gets the same meaning . I'm not sure I did a nomove keyframe animation that provides true or not conversion values โโ(of course, it has 7 keyframes).
Keyframe contents like this (debugging on the head bone):
7 different keyframes, one and the same vector meaning.
Edit: (2)
If you want to test my dae file, I put it in jsfiddle , go in and get it :). Another thing is that in Unity my file works correctly, so I think that maybe there is a problem not in my local conversion, it seems that the problem may be different, such as parentTransform or bone-> transform ... etc .? I also add a local transformation matrix with all the bone, but I canโt understand why COLLADA contains these values โโfor my instant animation ...