7#include <assimp/anim.h>
8#include <assimp/matrix4x4.h>
9#include <assimp/quaternion.h>
10#include <assimp/scene.h>
11#include <assimp/vector3.h>
19void decomposeBind(
const aiMatrix4x4 &
m,
float &
px,
float &
py,
float &
pz,
float &
qx,
float &
qy,
20 float &
qz,
float &
qw,
float &
sx,
float &
sy,
float &
sz) {
36float ticksToSeconds(
const aiAnimation *anim,
double ticks) {
37 double tps = anim->mTicksPerSecond;
38 if (tps <= 1e-8) tps = 25.0;
39 return static_cast<float>(ticks / tps);
44void AnimImporter::collectBones(
const aiNode *
node,
int parent, AnimSkeleton *skeleton,
45 std::vector<const aiNode *> &
order) {
47 const std::string
name =
node->mName.C_Str();
48 const std::string boneName =
name.empty() ? (
"node_" + std::to_string(
order.size())) :
name;
49 const int id = skeleton->addBone(boneName,
parent);
53 decomposeBind(
node->mTransformation,
px,
py,
pz,
qx,
qy,
qz,
qw,
sx,
sy,
sz);
54 skeleton->setBindPosition(
id,
px,
py,
pz);
55 skeleton->setBindRotation(
id,
qx,
qy,
qz,
qw);
56 skeleton->setBindScale(
id,
sx,
sy,
sz);
58 for (
unsigned i = 0; i <
node->mNumChildren; ++i) {
59 collectBones(
node->mChildren[i],
id, skeleton,
order);
65 throw Exception(
"AnimImporter.loadSkeleton: invalid scene");
68 std::vector<const aiNode *>
order;
69 collectBones(
scene->mRootNode, -1, skeleton,
order);
70 if (skeleton->getBoneCount() == 0) {
72 throw Exception(
"AnimImporter.loadSkeleton: no bones");
79 if (!
scene || !skeleton) {
80 throw Exception(
"AnimImporter.loadClip: null scene/skeleton");
83 throw Exception(
"AnimImporter.loadClip: invalid anim index %d", animIndex);
85 const aiAnimation *anim =
scene->mAnimations[animIndex];
86 auto *
clip =
new AnimClip(anim->mName.length ? anim->mName.C_Str() :
"anim");
89 clip->setSampleRate(30.f);
91 for (
unsigned c = 0;
c < anim->mNumChannels; ++
c) {
92 const aiNodeAnim *channel = anim->mChannels[
c];
93 if (!channel)
continue;
94 const int bone = skeleton->
findBone(channel->mNodeName.C_Str());
95 if (
bone < 0)
continue;
97 for (
unsigned i = 0; i < channel->mNumPositionKeys; ++i) {
98 const aiVectorKey &k = channel->mPositionKeys[i];
99 clip->addPositionKey(
bone, ticksToSeconds(anim, k.mTime), k.mValue.x, k.mValue.y,
102 for (
unsigned i = 0; i < channel->mNumRotationKeys; ++i) {
103 const aiQuatKey &k = channel->mRotationKeys[i];
104 clip->addRotationKey(
bone, ticksToSeconds(anim, k.mTime), k.mValue.x, k.mValue.y,
105 k.mValue.z, k.mValue.w);
107 for (
unsigned i = 0; i < channel->mNumScalingKeys; ++i) {
108 const aiVectorKey &k = channel->mScalingKeys[i];
109 clip->addScaleKey(
bone, ticksToSeconds(anim, k.mTime), k.mValue.x, k.mValue.y,
117 if (!
scene)
return 0;
118 return static_cast<int>(
scene->mNumAnimations);
123 const aiAnimation *
a =
scene->mAnimations[animIndex];
124 return a->mName.length ?
a->mName.C_Str() : std::string(
"anim") + std::to_string(animIndex);
std::array< float, 4 > rotation
std::array< float, 3 > position
std::weak_ptr< PrimitiveScene > scene
EVENGINE_API_FOUNDATION public API.
Keyframed skeletal animation clip (local TRS tracks per bone). Script type: AnimClip.
void setDuration(float seconds)
Sets the duration.
static int getAnimationCount(const aiScene *scene)
Assimp scene helpers (AnimImporterAssimp.cpp; requires model3d).
static AnimClip * loadClip(const aiScene *scene, const AnimSkeleton *skeleton, int animIndex=0)
Load clip by index; maps node-name channels onto skeleton bones.
static std::string getAnimationName(const aiScene *scene, int animIndex)
Returns the animation name.
static AnimSkeleton * loadSkeleton(const aiScene *scene)
Build skeleton from the scene node hierarchy (depth-first).
3D bone hierarchy + bind-pose local TRS for skeletal animation. Independent of ik::Skeleton3D (FABRIK...
int findBone(const std::string &name) const
Finds bone.