bool GameLib::loadSkeletalAnimation(std::string filename, SkeletalAnimation& outAnimation) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(filename, aiProcess_ValidateDataStructure); if(scene == nullptr || scene->mNumAnimations == 0) return false; aiAnimation* animation = scene->mAnimations[0]; std::unordered_map<double, SkeletalPose> poses; std::hash<std::string> hash; for(unsigned int i = 0; i < animation->mNumChannels; i++) { aiNodeAnim* node = animation->mChannels[i]; std::string nodeName(node->mNodeName.C_Str()); unsigned int boneId = hash(nodeName); // load positions for(unsigned int j = 0; j < node->mNumPositionKeys; j++) { aiVectorKey key = node->mPositionKeys[j]; SkeletalPose::BonePosition existingPosition = poses[key.mTime].getBonePosition(boneId); existingPosition.position.x = key.mValue.x; existingPosition.position.y = key.mValue.y; existingPosition.position.z = key.mValue.z; poses[key.mTime].setBonePosition(boneId, existingPosition); } // load rotations for(unsigned int j = 0; j < node->mNumRotationKeys; j++) { aiQuatKey key = node->mRotationKeys[j]; SkeletalPose::BonePosition existingPosition = poses[key.mTime].getBonePosition(boneId); // calculate the angle from the quaternion float rad = std::acos(key.mValue.w); float sinRad = std::sin(rad); existingPosition.rotationAxis.x = key.mValue.x / sinRad; existingPosition.rotationAxis.y = key.mValue.y / sinRad; existingPosition.rotationAxis.z = key.mValue.z / sinRad; existingPosition.rotation = rad; poses[key.mTime].setBonePosition(boneId, existingPosition); } // load scalings for(unsigned int j = 0; j < node->mNumScalingKeys; j++) { aiVectorKey key = node->mScalingKeys[j]; SkeletalPose::BonePosition existingPosition = poses[key.mTime].getBonePosition(boneId); existingPosition.scale.x = key.mValue.x; existingPosition.scale.y = key.mValue.y; existingPosition.scale.z = key.mValue.z; poses[key.mTime].setBonePosition(boneId, existingPosition); } } // add poses to animation for(auto posePair : poses) { outAnimation.addPose((float)posePair.first, posePair.second); } return true; }
UNNAMESPACE_BEGIN /*============================================================================== Procedural API/functions. ==============================================================================*/ //------------------------------------------------------------------------------ //! int animationVM( VMState* vm ) { ProceduralAnimation* userData = (ProceduralAnimation*)VM::userData( vm ); SkeletalAnimation* anim = userData->animation(); // Only accept a table as argument. if( !VM::isTable( vm, 1 ) ) { StdErr << "Missing arguments to animation()." << nl; return 0; } // Read parameters. float rate; if( VM::get( vm, 1, "rate", rate ) ) anim->rate( rate ); Vec3f vel; if( VM::get( vm, 1, "velocity", vel ) ) anim->velocity( vel ); Vec3f offset; if( VM::get( vm, 1, "offset", offset ) ) anim->offset( offset ); bool cyclic; if( VM::get( vm, 1, "cyclic", cyclic ) ) anim->cyclic( cyclic ); uint type; if( VM::get( vm, 1, "type", type ) ) anim->type( type ); if( VM::get( vm, 1, "poses" ) ) { anim->reservePoses( VM::getTableSize( vm, -1 ) ); // Read all poses. for( int p = 1; VM::geti( vm, -1, p ); ++p ) { Reff ref = Reff::identity(); if( !VM::get( vm, -1, "p", ref.position() ) ) { ref.position( Vec3f::zero() ); } if( !VM::get( vm, -1, "q", ref.orientation() ) ) { ref.orientation( Quatf::identity() ); } RCP<SkeletalPose> pose = anim->addPose( ref ); pose->reserveBones( VM::getTableSize( vm, -1 ) ); // Read all bones positions in pose. for( int i = 1; VM::geti( vm, -1, i ); ++i ) { pose->addBone( VM::toQuatf( vm, -1 ) ); VM::pop( vm, 1 ); } VM::pop( vm, 1 ); } VM::pop( vm, 1 ); } anim->prepare(); // Fix some corner cases. anim->makeRelative(); return 0; }