-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improvements in the Assimp importer (#41)
* remove hardcoded root node name * change importer to process bones apart from mesh * improvements in the assimp loader * fix code style * remove unused code * remove unused param * cleanup unecessary code * removed boneNodes * improve readability in bone hierarchy processing * extract material processor * remove empty line * Update deploy.yml * AnimationProcessor * fix test * fix tests * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix test * fix tests * Update deploy.yml * Simplified importer code, and allow loading textures from embedded files * improve material processor code * extracts the bone processor * extract meshprocessor * add unit test to bone processor * Include the mesh processor test * fix after cpp check insight * improve based on cpp check
- Loading branch information
1 parent
2b4b91e
commit 2e7de27
Showing
16 changed files
with
1,027 additions
and
600 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#include "AnimationProcessor.h" | ||
|
||
AnimationProcessor::AnimationProcessor(Ogre::SkeletonPtr skeleton): skeleton(skeleton) {} | ||
|
||
void AnimationProcessor::processAnimations(const aiScene* scene) { | ||
for(auto i = 0u; i < scene->mNumAnimations; i++) { | ||
aiAnimation* animation = scene->mAnimations[i]; | ||
processAnimation(animation, scene); | ||
} | ||
} | ||
|
||
void AnimationProcessor::processAnimation(aiAnimation* animation, const aiScene* scene) { | ||
// Create the animation | ||
Ogre::Animation* ogreAnimation = skeleton->createAnimation(animation->mName.C_Str(), animation->mDuration/10.0f); | ||
// Process the animation channels | ||
for(auto i = 0u; i < animation->mNumChannels; i++) { | ||
aiNodeAnim* nodeAnim = animation->mChannels[i]; | ||
processAnimationChannel(nodeAnim, ogreAnimation, scene, i); | ||
} | ||
} | ||
|
||
|
||
void AnimationProcessor::processAnimationChannel(aiNodeAnim* nodeAnim, Ogre::Animation* animation, const aiScene* scene, unsigned int channelIndex) { | ||
// Create the animation track | ||
Ogre::Bone* bone = skeleton->getBone(nodeAnim->mNodeName.C_Str()); | ||
Ogre::NodeAnimationTrack* track = animation->createNodeTrack(bone->getHandle(), bone); | ||
|
||
// Create a map to store keyframes by time | ||
std::map<double, std::tuple<Ogre::Vector3, Ogre::Quaternion, Ogre::Vector3>> keyframes; | ||
|
||
// Process the position keys | ||
for(auto i = 0u; i < nodeAnim->mNumPositionKeys; i++) { | ||
aiVectorKey positionKey = nodeAnim->mPositionKeys[i]; | ||
Ogre::Vector3 position(positionKey.mValue.x, positionKey.mValue.y, positionKey.mValue.z); | ||
|
||
// Get the bone's T-pose position | ||
auto boneTPosePosition = bone->getPosition(); | ||
|
||
// Convert the position from local space to model space | ||
position = position - boneTPosePosition; | ||
|
||
keyframes[positionKey.mTime] = std::make_tuple( | ||
position, | ||
Ogre::Quaternion::IDENTITY, | ||
Ogre::Vector3::UNIT_SCALE | ||
); | ||
} | ||
|
||
// Process the rotation keys | ||
for(auto i = 0u; i < nodeAnim->mNumRotationKeys; i++) { | ||
aiQuatKey rotationKey = nodeAnim->mRotationKeys[i]; | ||
Ogre::Quaternion boneTPoseRotation = bone->getOrientation(); | ||
Ogre::Quaternion rot(rotationKey.mValue.w, rotationKey.mValue.x, rotationKey.mValue.y, rotationKey.mValue.z); | ||
rot = boneTPoseRotation.Inverse() * rot; // Convert from local space to model space | ||
rot.normalise(); // Normalize the quaternion | ||
if (keyframes.find(rotationKey.mTime) == keyframes.end()) { | ||
keyframes[rotationKey.mTime] = std::make_tuple( | ||
Ogre::Vector3::ZERO, | ||
rot, | ||
Ogre::Vector3::UNIT_SCALE | ||
); | ||
} else { | ||
std::get<1>(keyframes[rotationKey.mTime]) = rot; | ||
} | ||
} | ||
|
||
// Process the scaling keys | ||
for(auto i = 0u; i < nodeAnim->mNumScalingKeys; i++) { | ||
aiVectorKey scalingKey = nodeAnim->mScalingKeys[i]; | ||
Ogre::Vector3 scale(scalingKey.mValue.x, scalingKey.mValue.y, scalingKey.mValue.z); | ||
if (keyframes.find(scalingKey.mTime) == keyframes.end()) { | ||
keyframes[scalingKey.mTime] = std::make_tuple( | ||
Ogre::Vector3::ZERO, | ||
Ogre::Quaternion::IDENTITY, | ||
scale | ||
); | ||
} else { | ||
std::get<2>(keyframes[scalingKey.mTime]) = scale; | ||
} | ||
} | ||
|
||
// Now create the keyframes in the track | ||
for(auto& [time, transform] : keyframes) { | ||
Ogre::TransformKeyFrame* keyFrame = track->createNodeKeyFrame(time/10.0f); | ||
keyFrame->setTranslate(std::get<0>(transform)); | ||
keyFrame->setRotation(std::get<1>(transform)); | ||
keyFrame->setScale(std::get<2>(transform)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include <Ogre.h> | ||
#include <assimp/scene.h> | ||
|
||
class AnimationProcessor | ||
{ | ||
public: | ||
AnimationProcessor(Ogre::SkeletonPtr skeleton); | ||
void processAnimations(const aiScene* scene); | ||
|
||
private: | ||
void processAnimation(aiAnimation* animation, const aiScene* scene); | ||
void processAnimationChannel(aiNodeAnim* nodeAnim, Ogre::Animation* animation, const aiScene* scene, unsigned int channelIndex); | ||
Ogre::SkeletonPtr skeleton; | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#include <gtest/gtest.h> | ||
#include <gmock/gmock.h> | ||
#include "AnimationProcessor.h" | ||
|
||
// Test if processAnimations processes all animations | ||
TEST(AnimationProcessorTest, ProcessAllAnimations) { | ||
auto ogreRoot = std::make_unique<Ogre::Root>(); | ||
auto mockSkeleton= Ogre::SkeletonManager::getSingleton().create("MockSkeleton",Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true); | ||
AnimationProcessor processor(mockSkeleton); | ||
|
||
aiScene scene; | ||
scene.mNumAnimations = 2; | ||
scene.mAnimations = new aiAnimation*[2]; | ||
scene.mAnimations[0] = new aiAnimation; | ||
scene.mAnimations[1] = new aiAnimation; | ||
scene.mAnimations[0]->mName = aiString( std::string( "Animation1")); | ||
scene.mAnimations[1]->mName = aiString( std::string( "Animation2")); | ||
scene.mAnimations[0]->mNumChannels=0; | ||
scene.mAnimations[1]->mNumChannels=0; | ||
|
||
processor.processAnimations(&scene); | ||
|
||
EXPECT_EQ(mockSkeleton->getNumAnimations(), 2); | ||
EXPECT_EQ(mockSkeleton->getAnimation(0)->getName(), "Animation1"); | ||
EXPECT_EQ(mockSkeleton->getAnimation(1)->getName(), "Animation2"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#include "BoneProcessor.h" | ||
|
||
void BoneProcessor::processBoneHierarchy(aiBone* bone) { | ||
// Process the bone node | ||
processBoneNode(bone); | ||
|
||
// Recursively process children bones | ||
for(auto i = 0u; i < bone->mNode->mNumChildren; i++) { | ||
aiNode* childNode = bone->mNode->mChildren[i]; | ||
if(aiBonesMap.find(childNode->mName.C_Str()) != aiBonesMap.end()) { | ||
processBoneHierarchy(aiBonesMap[childNode->mName.C_Str()]); | ||
} | ||
} | ||
} | ||
|
||
void BoneProcessor::processBones(Ogre::SkeletonPtr skeleton, const aiScene *scene) { | ||
this->skeleton = skeleton; | ||
for(auto i = 0u; i < scene->mNumMeshes; i++) { | ||
aiMesh* mesh = scene->mMeshes[i]; | ||
for(auto j = 0u; j < mesh->mNumBones; j++) { | ||
aiBone* bone = mesh->mBones[j]; | ||
// Check if the bone already exists | ||
if(!skeleton->hasBone(bone->mName.C_Str())) { | ||
aiBonesMap[bone->mName.C_Str()] = bone; | ||
createBone(bone->mName.C_Str()); | ||
} | ||
} | ||
} | ||
|
||
// Some models have bone animations but not all the bones are related to the meshes | ||
for(auto i = 0u; i < scene->mNumAnimations; i++) { | ||
aiAnimation* anim = scene->mAnimations[i]; | ||
for(auto j = 0u; j < anim->mNumChannels; j++) { | ||
aiNodeAnim* nodeAnim = anim->mChannels[j]; | ||
createBone(nodeAnim->mNodeName.C_Str()); | ||
} | ||
} | ||
|
||
// Process the root bones first | ||
for(auto i = 0u; i < scene->mNumMeshes; i++) { | ||
aiMesh* mesh = scene->mMeshes[i]; | ||
for(auto j = 0u; j < mesh->mNumBones; j++) { | ||
aiBone* bone = mesh->mBones[j]; | ||
if(bone->mNode && bone->mNode->mParent && !skeleton->hasBone(bone->mNode->mParent->mName.C_Str())) { | ||
createBone(bone->mNode->mParent->mName.C_Str()); | ||
processBoneHierarchy(bone); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void BoneProcessor::createBone(const std::string& boneName) { | ||
// Check if the bone already exists | ||
if(!skeleton->hasBone(boneName)) { | ||
// If the bone does not exist, create it | ||
skeleton->createBone(boneName); | ||
} | ||
} | ||
|
||
void BoneProcessor::processBoneNode(aiBone* bone) { | ||
// Convert the aiBone's offset matrix to an Ogre::Matrix4 | ||
Ogre::Matrix4 offsetMatrix( | ||
bone->mOffsetMatrix.a1, bone->mOffsetMatrix.a2, bone->mOffsetMatrix.a3, bone->mOffsetMatrix.a4, | ||
bone->mOffsetMatrix.b1, bone->mOffsetMatrix.b2, bone->mOffsetMatrix.b3, bone->mOffsetMatrix.b4, | ||
bone->mOffsetMatrix.c1, bone->mOffsetMatrix.c2, bone->mOffsetMatrix.c3, bone->mOffsetMatrix.c4, | ||
bone->mOffsetMatrix.d1, bone->mOffsetMatrix.d2, bone->mOffsetMatrix.d3, bone->mOffsetMatrix.d4 | ||
); | ||
|
||
// Invert the offset matrix to get the global transformation of the bone | ||
Ogre::Matrix4 globalTransform = offsetMatrix.inverse(); | ||
|
||
// If the bone has a parent, multiply the global transformation of the bone with the inverse of the global transformation of the parent to get the local transformation | ||
if(bone->mNode->mParent && bone->mNode->mParent->mName.length) { | ||
Ogre::Bone* parentBone = skeleton->getBone(bone->mNode->mParent->mName.C_Str()); | ||
if(parentBone) { | ||
Ogre::Matrix4 parentGlobalTransform = parentBone->_getFullTransform().inverse(); | ||
globalTransform = parentGlobalTransform * globalTransform; | ||
} | ||
} | ||
|
||
// Convert the Ogre::Matrix4 to an Ogre::Affine3 | ||
Ogre::Affine3 affine(globalTransform); | ||
|
||
// Decompose the offset matrix into position, scale, and orientation | ||
Ogre::Vector3 position, scale; | ||
Ogre::Quaternion orientation; | ||
affine.decomposition(position, scale, orientation); | ||
|
||
// Retrieve the bone (it should already exist) | ||
Ogre::Bone* ogreBone = skeleton->getBone(bone->mName.C_Str()); | ||
|
||
// Set the bone's position, orientation, and scale | ||
ogreBone->setPosition(position); | ||
ogreBone->setOrientation(orientation); | ||
ogreBone->setScale(scale); | ||
|
||
// Add the bone to the parent bone, if it exists | ||
if(bone->mNode->mParent && bone->mNode->mParent->mName.length) { | ||
Ogre::Bone* parentBone = skeleton->getBone(bone->mNode->mParent->mName.C_Str()); | ||
if(parentBone) { | ||
// Check if ogreBone is already a child of parentBone | ||
if (!std::any_of(parentBone->getChildren().begin(), parentBone->getChildren().end(), | ||
[&ogreBone](const auto& childNode) { | ||
return childNode->getName() == ogreBone->getName(); | ||
})) { | ||
parentBone->addChild(ogreBone); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
#pragma once | ||
|
||
#include <Ogre.h> | ||
#include <assimp/scene.h> | ||
|
||
class BoneProcessor { | ||
public: | ||
void processBones(Ogre::SkeletonPtr skeleton, const aiScene* scene); | ||
|
||
private: | ||
void createBone(const std::string& boneName); | ||
void processBoneHierarchy(aiBone* bone); | ||
void processBoneNode(aiBone *bone); | ||
|
||
Ogre::SkeletonPtr skeleton; | ||
std::map<std::string, aiBone*> aiBonesMap; | ||
}; |
Oops, something went wrong.