Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements in the Assimp importer #41

Merged
merged 42 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a4811bd
remove hardcoded root node name
fernandotonon Aug 7, 2023
6e46b6d
change importer to process bones apart from mesh
fernandotonon Aug 10, 2023
ed578b9
improvements in the assimp loader
fernandotonon Aug 10, 2023
ecbc8b2
fix code style
fernandotonon Aug 10, 2023
53832ba
remove unused code
fernandotonon Aug 21, 2023
7796a21
remove unused param
fernandotonon Aug 21, 2023
e74e7ea
cleanup unecessary code
fernandotonon Aug 21, 2023
0ab3798
removed boneNodes
fernandotonon Aug 21, 2023
f32fc36
improve readability in bone hierarchy processing
fernandotonon Aug 21, 2023
84bfcbf
extract material processor
fernandotonon Aug 21, 2023
39afdf5
remove empty line
fernandotonon Aug 21, 2023
4149bc2
Merge branch 'master' into macos
fernandotonon Aug 21, 2023
81ddfeb
Update deploy.yml
fernandotonon Aug 21, 2023
e8c0b5d
AnimationProcessor
fernandotonon Aug 22, 2023
e073f5a
Merge branch 'macos' of https://github.com/fernandotonon/QtMeshEditor…
fernandotonon Aug 22, 2023
8cf41ee
fix test
fernandotonon Aug 22, 2023
98901b3
fix tests
fernandotonon Aug 22, 2023
fb551f7
fix test
fernandotonon Aug 22, 2023
b1f5ed4
fix test
fernandotonon Aug 22, 2023
6a4b63b
fix test
fernandotonon Aug 22, 2023
da53574
fix test
fernandotonon Aug 22, 2023
eb631c4
fix test
fernandotonon Aug 22, 2023
d2d32d1
fix test
fernandotonon Aug 22, 2023
c60e7f6
fix test
fernandotonon Aug 22, 2023
6b1cf4a
fix test
fernandotonon Aug 22, 2023
93c3238
fix test
fernandotonon Aug 22, 2023
4d3ee6c
fix test
fernandotonon Aug 22, 2023
e79bf28
fix test
fernandotonon Aug 22, 2023
c85992e
fix test
fernandotonon Aug 22, 2023
600fb8a
fix test
fernandotonon Aug 22, 2023
e78f798
fix test
fernandotonon Aug 22, 2023
7441ca5
fix tests
fernandotonon Aug 23, 2023
7bf1ab3
Update deploy.yml
fernandotonon Aug 23, 2023
62a9da2
Simplified importer code,
fernandotonon Aug 24, 2023
59fdf16
Merge branch 'macos' of https://github.com/fernandotonon/QtMeshEditor…
fernandotonon Aug 24, 2023
df7410e
improve material processor code
fernandotonon Aug 24, 2023
1ddcd93
extracts the bone processor
fernandotonon Aug 24, 2023
42e1c71
extract meshprocessor
fernandotonon Aug 24, 2023
8f04e9e
add unit test to bone processor
fernandotonon Aug 25, 2023
246e272
Include the mesh processor test
fernandotonon Aug 25, 2023
ee565d7
fix after cpp check insight
fernandotonon Aug 25, 2023
2a1bea9
improve based on cpp check
fernandotonon Aug 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions src/Assimp/AnimationProcessor.cpp
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));
}
}
17 changes: 17 additions & 0 deletions src/Assimp/AnimationProcessor.h
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;
};

26 changes: 26 additions & 0 deletions src/Assimp/AnimationProcessor_test.cpp
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");
}
110 changes: 110 additions & 0 deletions src/Assimp/BoneProcessor.cpp
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);
}
}
}
}
17 changes: 17 additions & 0 deletions src/Assimp/BoneProcessor.h
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;
};
Loading