🌀 Animation(アニメーション)
FBX ファイル内のアニメーションは、スタック (ufbx_anim_stack) と呼ばれる レイヤー (ufbx_anim_layer) の集合として表現されます。
各スタックは、ファイル内の 1 つのアニメーションクリップ(または「テイク」)に対応します。
ufbx ではアニメーションを ufbx_anim デスクリプタを通して扱います。
これにより、「複数レイヤーを合成したスタック」か「単一レイヤー」かを
統一されたインターフェースで選択・評価できます。
🔹 ufbx_anim インスタンスを取得できる場所
ufbx_scene.anim:シーンのデフォルトアニメーションスタックufbx_anim_stack.anim:合成済みアニメーションスタックufbx_anim_layer.anim:個別のアニメーションレイヤーufbx_create_anim():カスタムアニメーションデスクリプタを作成
🎛 評価(Evaluation)
ufbx はファイル内のアニメーションカーブを直接扱えますが、
それらを手動で解釈するのは非常に複雑です。
主な理由は次の通りです:
- ⏩ 時間が非線形な キュービック補間カーブ
- 🔄 回転順序を持つ オイラー角回転カーブ
- ⚙️ プリ/ポスト回転、ピボット、オフセット などの複雑なノード変換
- 🎨 アニメーションレイヤーの合成
こうした複雑さを避けるため、
これらを内部で処理してくれる ufbx の「評価ユーティリティ」を使用することが推奨されます。
特に、下記の「アニメーションのベイク(baking)」は、
複雑な FBX アニメーションを扱いやすい形式に変換する良い出発点です。
🌍 シーン全体の評価(Scene Evaluation)
最も簡単な方法は ufbx_evaluate_scene() を使うことです。
これは指定した時刻におけるアニメーションをすべて適用し、
新しい ufbx_scene を生成する「重い」関数です。
結果のシーンは通常の ufbx_scene と同様に扱えます。
🔧 アニメーションのベイク(Animation Baking)
FBX ファイル内のアニメーションは ufbx_bake_anim() を使って
より単純な形式に「ベイク」できます。
この関数は、トランスフォームアニメーションを
線形補間のトラック(translation/quaternion rotation/scale)に変換します。
トランスフォーム以外のプロパティも線形補間キーとしてベイクされます。
ベイクアルゴリズムは単純な再サンプリングではなく、
キーフレームの頻度などを考慮して効率的に処理します。
ただし、キュービック補間やオイラー回転は
クォータニオンに再サンプリングする必要があります。
🎚 サンプリング設定
ufbx_bake_opts.resample_rate:再サンプリングレートの設定ufbx_bake_opts.minimum_sample_rate:高頻度キーをスキップして二重サンプリングを防止
💻 C 言語例
void bake_animation(ufbx_scene *scene, ufbx_anim *anim)
{
ufbx_baked_anim *bake = ufbx_bake_anim(scene, anim, NULL, NULL);
assert(bake);
for (size_t i = 0; i < bake->nodes.count; i++) {
ufbx_baked_node *bake_node = &bake->nodes.data[i];
ufbx_node *scene_node = scene->nodes.data[bake_node->typed_id];
printf(" node %s:\n", scene_node->name.data);
printf(" translation: %zu keys\n", bake_node->translation_keys.count);
printf(" rotation: %zu keys\n", bake_node->rotation_keys.count);
printf(" scale: %zu keys\n", bake_node->scale_keys.count);
}
ufbx_free_baked_anim(bake);
}
void bake_animations(ufbx_scene *scene)
{
for (size_t i = 0; i < scene->anim_stacks.count; i++) {
ufbx_anim_stack *stack = scene->anim_stacks.data[i];
printf("stack %s:\n", stack->name.data);
bake_animation(scene, stack->anim);
}
}💻 C++ 例
void bake_animation(ufbx_scene *scene, ufbx_anim *anim)
{
ufbx_baked_anim *bake = ufbx_bake_anim(scene, anim, NULL, NULL);
assert(bake);
for (const ufbx_baked_node &bake_node : bake->nodes) {
ufbx_node *scene_node = scene->nodes[bake_node.typed_id];
printf(" node %s:\n", scene_node->name.data);
printf(" translation: %zu keys\n", bake_node.translation_keys.count);
printf(" rotation: %zu keys\n", bake_node.rotation_keys.count);
printf(" scale: %zu keys\n", bake_node.scale_keys.count);
}
ufbx_free_baked_anim(bake);
}
void bake_animations(ufbx_scene *scene)
{
for (ufbx_anim_stack *stack : scene->anim_stacks) {
printf("stack %s:\n", stack->name.data);
bake_animation(scene, stack->anim);
}
}💻 Rust 例
fn bake_animation(scene: &ufbx::Scene, anim: &ufbx::Anim) {
let bake = ufbx::bake_anim(scene, anim, ufbx::BakeOpts::default())
.expect("expected to bake animation");
for bake_node in &bake.nodes {
let scene_node = &scene.nodes[bake_node.typed_id as usize];
println!(" node {}:", scene_node.element.name);
println!(" translation: {} keys", bake_node.translation_keys.len());
println!(" rotation: {} keys", bake_node.rotation_keys.len());
println!(" scale: {} keys", bake_node.scale_keys.len());
}
}
fn bake_animations(scene: &ufbx::Scene) {
for stack in &scene.anim_stacks {
println!("stack {}:", stack.element.name);
bake_animation(scene, &stack.anim);
}
}⚙️ トランスフォーム・プロパティの評価
ufbx は個々の要素を特定の時刻で評価するための低レベル API も提供しています。
関数名 | 内容 |
|---|---|
| ノードの位置・回転(クォータニオン)・スケールを評価 |
| ブレンドシェイプのウェイトを評価 |
| 任意の FBX プロパティ値を評価 |
| 要素全体のプロパティをまとめて評価 |
さらに低レベルの関数:
関数名 | 内容 |
|---|---|
/
|
を評価 |
| 単一の
を評価 |
No Comments