I'm trying to implement skeletal animation using assimp and glm. Everything seems to work, except for rotations.
This is the code I use when packing assimp data into my own engine's format. I THINK it should be correct, but since I've already run into problems with matrices (assimp uses row major, glm - column major), I'd rather post this, too:
for(uint32_t l = 0; l < tempChannel.numRotationKeys; ++l){ quatKey tempKey; tempKey.values = glm::quat(scene->mAnimations[i]->mChannels[j]->mRotationKeys[l].mValue.w, scene->mAnimations[i]->mChannels[j]->mRotationKeys[l].mValue.x, scene->mAnimations[i]->mChannels[j]->mRotationKeys[l].mValue.y, scene->mAnimations[i]->mChannels[j]->mRotationKeys[l].mValue.z); And this is the code that performs the actual transformations (quite a bit of it is based on http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html), but modifications (e.g. storing a list, where each child element is placed after a parent and keeps a parent's id, as was suggested here: http://blog.tojicode.com/2011/10/building-game-part-3-skinning-animation.html) have been made. Scaling and location transforms seem to be working fine, but rotations don't:
for(unsigned int i = 0; i < skeleton.size(); ++i){ finalTransforms[i] = calcFinalTransform(time, i); } glm::mat4 SkeletalMeshObject::calcFinalTransform(double animationTime, unsigned int currentNode){ // Find the node where the keyframes for the current bone are stored // Return -1, if not found int nodeId = findNodeAnimation(currentAnimation, skeleton[currentNode].name); glm::mat4 nodeTransform(skeleton[currentNode].offset); // If this node isn't animated, pass the default offset to it if(nodeId >= 0){ unsigned int index; unsigned int nextIndex; // Interpolate scaling glm::vec3 scl; if(animations[currentAnimation].channels[nodeId].scalingKeys.size() == 1){ scl = animations[currentAnimation].channels[nodeId].scalingKeys[0].values; } else { index = findKey(animationTime, KeyType::Scaling, nodeId); nextIndex = index + 1; double delta = animations[currentAnimation].channels[nodeId].scalingKeys[nextIndex].time - animations[currentAnimation].channels[nodeId].scalingKeys[index].time; double factor = (animationTime - animations[currentAnimation].channels[nodeId].scalingKeys[index].time) / delta; scl = glm::mix(animations[currentAnimation].channels[nodeId].scalingKeys[index].values, animations[currentAnimation].channels[nodeId].scalingKeys[nextIndex].values, (float)factor); } // Interpolate rotation glm::quat rot; if(animations[currentAnimation].channels[nodeId].rotationKeys.size() == 1){ rot = animations[currentAnimation].channels[nodeId].rotationKeys[0].values; } else { index = findKey(animationTime, KeyType::Rotation, nodeId); nextIndex = index + 1; double delta = animations[currentAnimation].channels[nodeId].rotationKeys[nextIndex].time - animations[currentAnimation].channels[nodeId].rotationKeys[index].time; double factor = (animationTime - animations[currentAnimation].channels[nodeId].rotationKeys[index].time) / delta; rot = glm::mix(animations[currentAnimation].channels[nodeId].rotationKeys[index].values, animations[currentAnimation].channels[nodeId].rotationKeys[nextIndex].values, (float)factor); } // Interpolate location glm::vec3 loc; if(animations[currentAnimation].channels[nodeId].locationKeys.size() == 1){ loc = animations[currentAnimation].channels[nodeId].locationKeys[0].values; } else { index = findKey(animationTime, KeyType::Location, nodeId); nextIndex = index + 1; double delta = animations[currentAnimation].channels[nodeId].locationKeys[nextIndex].time - animations[currentAnimation].channels[nodeId].locationKeys[index].time; double factor = (animationTime - animations[currentAnimation].channels[nodeId].locationKeys[index].time) / delta; loc = glm::mix(animations[currentAnimation].channels[nodeId].locationKeys[index].values, animations[currentAnimation].channels[nodeId].locationKeys[nextIndex].values, (float)factor); } // Get final transform nodeTransform = glm::translate(loc) * glm::toMat4(rot) * glm::scale(scl); } // Store the node's transform locRotScaleTransforms[currentNode] = nodeTransform; // Get the id of the parent int parentId = skeleton[currentNode].parent; // Hierarchial transform glm::mat4 hierarchyTransform = nodeTransform; while(parentId >= 0){ hierarchyTransform = locRotScaleTransforms[parentId] * hierarchyTransform; parentId = skeleton[parentId].parent; } // Apply inverse bind position matrix hierarchyTransform *= skeleton[currentNode].ibp; return hierarchyTransform; } Here's how the animation looks in blender:
And here's in my engine. Couldn't capture a gif from my window, so I'll describe it. First, it rotates along multiple axes (bones in the original animation were constrained to z axis), and as you can also see, it distorts. The frame seen below is almost at an end of animation, when distortions are most visible: 
What can I do to make it look right?