# Elements

# Elements（要素） — ufbx ドキュメント日本語訳

---

## 概要

ufbx のほぼすべては、`<span class="editor-theme-code">struct ufbx_element</span>`<span style="white-space: pre-wrap;"> のような「基底クラス」風の表現を持つ </span>****要素（element）****<span style="white-space: pre-wrap;"> で構成されます。要素は </span>`<span class="editor-theme-code">name</span>`<span style="white-space: pre-wrap;">（名前）や、FBX のキー/値プロパティのための </span>`<span class="editor-theme-code">props</span>`（プロパティ集合）といった共通プロパティを含みます。

`<span class="editor-theme-code">ufbx_node</span>`、`<span class="editor-theme-code">ufbx_mesh</span>`、`<span class="editor-theme-code">ufbx_material</span>`<span style="white-space: pre-wrap;"> といった「派生」型は、無名 </span>`<span class="editor-theme-code">union</span>`<span style="white-space: pre-wrap;"> として </span>`<span class="editor-theme-code">ufbx_element</span>`<span style="white-space: pre-wrap;"> ヘッダを埋め込んでおり、</span>`<span class="editor-theme-code">ufbx_element</span>`<span style="white-space: pre-wrap;"> へキャストしたり、</span>`<span class="editor-theme-code">ufbx_element</span>`<span style="white-space: pre-wrap;"> の共通プロパティへ直接アクセスできます。</span>

---

## 例（C）

```c++
void list_nodes(ufbx_scene *scene)
{
    for (size_t i = 0; i < scene->nodes.count; i++) {
        ufbx_node *node = scene->nodes.data[i];

        printf("Node: '%s'\n", node->element.name.data);

        // 同等の省略形:
        printf("Node: '%s'\n", node->name.data);
    }
}
```

---

## Element IDs（要素ID）

`<span class="editor-theme-code">ufbx_scene</span>`<span style="white-space: pre-wrap;"> は利便性のためにネストしたポインタ構造をとりますが、識別子（ID）を使えると便利なことがよくあります：</span>

- `<span class="editor-theme-code">ufbx_element.element_id</span>`：シーン全体で一意な連番インデックス（`<span class="editor-theme-code">scene.elements</span>`<span style="white-space: pre-wrap;"> 内）</span>
- `<span class="editor-theme-code">ufbx_element.typed_id</span>`：同一種別内での連番インデックス（例：`<span class="editor-theme-code">scene.nodes</span>`<span style="white-space: pre-wrap;"> 内）</span>

これらのインデックスを使えば、`<span class="editor-theme-code">ufbx_scene</span>`<span style="white-space: pre-wrap;"> 内の配列をインデックス指定で取り出せます。</span>

### C

```c
assert(node == scene->nodes.data[node->typed_id]);
assert(node == (ufbx_node*)scene->elements.data[node->element_id]);

assert(mesh == scene->meshes.data[mesh->typed_id]);
assert(mesh == (ufbx_mesh*)scene->elements.data[mesh->element_id]);
```

### C++

```c++
assert(node == scene->nodes[node->typed_id]);
assert(node == (ufbx_node*)scene->elements[node->element_id]);
assert(mesh == scene->meshes[mesh->typed_id]);
assert(mesh == (ufbx_mesh*)scene->elements[mesh->element_id]);
```

### Rust

```rust
use std::ptr;

assert!(ptr::eq(node, scene.nodes[node.element.typed_id]));
assert!(ptr::eq(&node.element, scene.elements[node.element.element_id]));

assert!(ptr::eq(mesh, scene.meshes[mesh.element.typed_id]));
assert!(ptr::eq(&mesh.element, scene.elements[mesh.element.element_id]));
```

> これらのインデックスは、****同じファイルを複数回読み込む範囲では安定****していますが、****ファイルを再エクスポートしただけでも****必ずしも安定とは限りません。

---

## Properties（プロパティ）

<span style="white-space: pre-wrap;">FBX は各要素に対して汎用的なキー/値のプロパティを持っており、ufbx はそれを </span>`<span class="editor-theme-code">ufbx_element.props</span>`<span style="white-space: pre-wrap;"> 経由で公開します。多くの場合 ufbx はこれらを内部でフィールドへ解釈します（例：</span>`<span class="editor-theme-code">ufbx_node.local_transform</span>`、`<span class="editor-theme-code">ufbx_light.intensity</span>`<span style="white-space: pre-wrap;">）。ただし、以下のようなケースでは </span>`<span class="editor-theme-code">ufbx_props</span>`<span style="white-space: pre-wrap;"> を直接使うとよいでしょう：</span>

- `<span class="editor-theme-code">ufbx_anim_curve</span>`<span style="white-space: pre-wrap;"> を用いたアニメーションカーブの手動解釈</span>
- ユーザー定義のカスタムプロパティ
- 非標準なマテリアル定義の取り扱い

ufbx は FBX の値を直感的に見えるよう多くの補正を行いますが、`<span class="editor-theme-code">ufbx_props</span>`<span style="white-space: pre-wrap;"> を直接読む場合には </span>****FBX 固有のクセ****<span style="white-space: pre-wrap;"> に注意してください。例えばライトには "Intensity"（強度）というプロパティがあり、FBX はしばしば DCC ツールで入力した値の </span>****100 倍****<span style="white-space: pre-wrap;"> を格納します。ufbx はこのクセを緩和するため </span>****値を 100 で割る****<span style="white-space: pre-wrap;"> 補正を試みますが、FBX プロパティを直接読むと期待と異なる結果になることがあります。</span>

### C 例（Intensity を比較表示）

```c
void print_intensity(ufbx_light *light)
{
    // `light->props` は `light->element.props` の省略形
    ufbx_prop *prop = ufbx_find_prop(&light->props, "Intensity");
    assert(prop);

    printf("ufbx_light.intensity: %.2f\n", light->intensity);
    printf("ufbx_props.Intensity: %.2f\n", prop->value_real);
}
```

### C++ 例

```c++
void print_intensity(ufbx_light *light)
{
    // `light->props` は `light->element.props` の省略形
    ufbx_prop *prop = ufbx_find_prop(&light->props, "Intensity");
    assert(prop);

    printf("ufbx_light.intensity: %.2f\n", light->intensity);
    printf("ufbx_props.Intensity: %.2f\n", prop->value_real);
}
```

### Rust 例

```rust
fn print_intensity(light: &ufbx::Light) {
    let prop = light.element.props
        .find_prop("Intensity")
        .expect("expected to find 'Intensity'");

    println!("ufbx_light.intensity: {:.2}", light.intensity);
    // 注意：Rust バインディングの表現は実装に依存する場合があります
    println!("ufbx_props.Intensity: {:.2}", prop.value_vec4.x);
}
```

#### 例（Blender で強度 2.0 のライトを持つシーン）

```
ufbx_light.intensity: 2.00
ufbx_props.Intensity: 200.00
```

---

## 脚注

1. <span style="white-space: pre-wrap;">FBX のファイルフォーマットではこれらを </span>****Objects****<span style="white-space: pre-wrap;"> と呼びますが、3D モデルの文脈では “object” という語が多義的すぎるため、ufbx では </span>****elements****<span style="white-space: pre-wrap;"> と呼んでいます。</span>

---

## クレジット / ライセンス

<p class="callout warning">本文・コード例は ufbx の公開ライセンス（MIT / Unlicense）に基づき翻訳・再構成しています。必要に応じてクレジット表記を併記してください：  
`<span class="editor-theme-code">© 2020 Samuli Raivio — Original docs under Unlicense/MIT. Japanese translation by <YzLearning>.</span>`</p>

- 原文： ufbx docs “Elements” ページ
- ライセンス： MIT / Unlicense（二重ライセンス）
- <span style="white-space: pre-wrap;">推奨クレジット： </span>`<span class="editor-theme-code">© 2020 Samuli Raivio — Original docs under Unlicense/MIT.</span>`
- 訳者：［YzLearnig］（必要に応じて記入）

# Nodes（ノード）

## 概要

****Nodes（ノード）****（`<span class="editor-theme-code">ufbx_node</span>`<span style="white-space: pre-wrap;">）は、FBX ファイルの </span>****シーングラフ（Scene Graph）****<span style="white-space: pre-wrap;"> を構成する要素です。</span>  
ノード自体は、変換情報（`<span class="editor-theme-code">ufbx_node.local_transform</span>`）と階層構造（`<span class="editor-theme-code">ufbx_node.parent</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">ufbx_node.children[]</span>`）を保持します。

<span style="white-space: pre-wrap;">ノードは </span>****属性（attribute）****<span style="white-space: pre-wrap;"> によって機能が拡張されます。たとえば、</span>`<span class="editor-theme-code">ufbx_mesh</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">ufbx_light</span>`<span style="white-space: pre-wrap;"> などです。</span>  
1つの属性が複数のノードに参照されることもあり、同じメッシュを異なるトランスフォームでインスタンス化することができます。

ノードは共通属性を直接保持しており、たとえば：

- `<span class="editor-theme-code">ufbx_node.mesh</span>`
- `<span class="editor-theme-code">ufbx_node.light</span>`

などを通じてアクセスできます。  
<span style="white-space: pre-wrap;">より珍しい属性は </span>`<span class="editor-theme-code">ufbx_node.attrib</span>`<span style="white-space: pre-wrap;"> に格納されており、</span>`<span class="editor-theme-code">ufbx_as_bone()</span>`<span style="white-space: pre-wrap;"> のようなヘルパ関数で具体的な型へキャスト（または </span>`<span class="editor-theme-code">NULL</span>`）できます。

逆に、「ノードがどの属性を持つか」を列挙する代わりに、「ある属性がどのノードで使用されているか」を調べることもできます。  
各属性（例：`<span class="editor-theme-code">ufbx_mesh</span>`<span style="white-space: pre-wrap;">）には </span>`<span class="editor-theme-code">ufbx_element.instances[]</span>`<span style="white-space: pre-wrap;"> フィールドがあり、それを参照しているすべてのノードを取得できます。</span>

---

## Transforms（変換）

ノードのローカル変換は、****平行移動（translation）・回転（rotation）・スケール（scale）****<span style="white-space: pre-wrap;"> の組み合わせで表されます。</span>  
<span style="white-space: pre-wrap;">これらは </span>`<span class="editor-theme-code">ufbx_node.local_transform</span>`<span style="white-space: pre-wrap;"> に格納され、</span>****親ノードに対する相対的な変換****<span style="white-space: pre-wrap;"> を表します。</span>

ノードはさらに以下のような便利フィールドも持っています：

- `<span class="editor-theme-code">ufbx_node.node_to_parent</span>`（親ノード座標系への行列）
- `<span class="editor-theme-code">ufbx_node.node_to_world</span>`（ワールド座標系への行列）

FBX の内部変換は非常に複雑ですが、ufbx ではこれを隠蔽するための機能を数多く備えています。  
`<span class="editor-theme-code">ufbx_node</span>`<span style="white-space: pre-wrap;"> のフィールドや </span>`<span class="editor-theme-code">ufbx_evaluate_transform()</span>`、`<span class="editor-theme-code">ufbx_bake_anim()</span>`<span style="white-space: pre-wrap;"> などを利用すれば、複雑さを意識せずに扱えます。</span>

（FBX の内部的な変換構造については「Node Transforms」セクションで詳細に説明されています。）

---

## Coordinate Spaces（座標系）

FBX ファイルは任意の座標系（軸向きや単位スケール）で保存されている場合があります。  
たとえば、前方／右／上方向の軸や単位長（1.0 の意味）が異なるケースです。

これに対応するため、`<span class="editor-theme-code">ufbx_scene.settings</span>`<span style="white-space: pre-wrap;"> から </span>****軸（axes）****<span style="white-space: pre-wrap;"> と </span>****単位メートル値（unit\_meters）****<span style="white-space: pre-wrap;"> を取得できます。</span>

また、`<span class="editor-theme-code">ufbx_load_opts.target_axes</span>`<span style="white-space: pre-wrap;"> および </span>`<span class="editor-theme-code">ufbx_load_opts.target_unit_meters</span>`<span style="white-space: pre-wrap;"> を設定することで、</span>  
読み込んだシーンを希望の座標系に変換することも可能です。

<span style="white-space: pre-wrap;">変換方法は </span>`<span class="editor-theme-code">ufbx_load_opts.space_conversion</span>`<span style="white-space: pre-wrap;"> によって指定します：</span>

<table id="bkmrk-%E5%AE%9A%E6%95%B0%E5%90%8D%E5%86%85%E5%AE%B9ufbx_space_conv"><colgroup><col></col><col></col></colgroup><tbody><tr><th>定数名

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">UFBX_SPACE_CONVERSION_TRANSFORM_ROOT</span>`

</td><td>ルートノードで空間変換を行う

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS</span>`

</td><td>各ノードのトランスフォームを補正する

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY</span>`

</td><td>ジオメトリにスケールを焼き込み、軸変換は ADJUST\_TRANSFORMS と同様に行う

</td></tr></tbody></table>

ufbx では、`<span class="editor-theme-code">ufbx_load_opts.handedness_conversion_axis</span>`<span style="white-space: pre-wrap;"> を使用して </span>****左右座標系（右手／左手）****<span style="white-space: pre-wrap;"> の変換も可能です。</span>  
通常、FBX は右手座標系が主流なので、右手系を使用する場合は不要です。  
左手座標系でシーンをロードする場合は、ミラー変換が必要になります。

また、カメラ（ローカル +X 向き）やライト（デフォルトではローカル -Y 向き）の軸を修正する機能もあります。  
<span style="white-space: pre-wrap;">これには </span>`<span class="editor-theme-code">ufbx_load_opts.target_camera_axes</span>`<span style="white-space: pre-wrap;"> および </span>`<span class="editor-theme-code">ufbx_load_opts.target_light_axes</span>`<span style="white-space: pre-wrap;"> を使用します。</span>

---

## Coordinate Spaces in Files（ファイル内の座標系）

FBX の座標系やエクスポータの違いは、多くのユーザーを混乱させてきました。  
（例：「FBX scale 100」「FBX scale 0.01」で検索するとよく出てきます。）

これは ufbx にも影響しますが、いくつかの方法で軽減可能です。

- ****Blender****<span style="white-space: pre-wrap;">：通常 Z-up メートル単位で動作しますが、FBX 書き出し時に </span>****Y-up / cm単位（100倍）****<span style="white-space: pre-wrap;"> に変換します。</span>  
    `<span class="editor-theme-code">UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS</span>`<span style="white-space: pre-wrap;"> を使うと、この100倍スケールを打ち消し、不要なスケーリングを取り除けます。</span>  
    一方、`<span class="editor-theme-code">UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY</span>`<span style="white-space: pre-wrap;"> はジオメトリ自体を0.01倍にスケールして正しい形状を維持しますが、中間ノードにスケール要素が残ります。</span>
- ****Maya****：多くの場合 cm 単位がネイティブです。  
    <span style="white-space: pre-wrap;">こちらでは </span>`<span class="editor-theme-code">UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY</span>`<span style="white-space: pre-wrap;"> の方が適しており、ノードスケールを変更せずにシーンを正しいスケールへ変換できます。</span>

結論として、****すべてのケースで完全に一貫した変換方法は存在しません。****  
ユーザーに変換方法を選択させるオプションを提供するのが望ましいです。

また、軽量ロード（`<span class="editor-theme-code">ufbx_load_opts.ignore_all_content = true</span>`）を使ってシーンを一度読み込み、  
`<span class="editor-theme-code">ufbx_metadata.exporter</span>`<span style="white-space: pre-wrap;"> を確認することで、エクスポータ（Blender / Maya 等）を特定し、</span>  
最適な変換方法を事前設定することも可能です。

> 💡 Blender でエクスポートする場合は、****「Apply Scalings」を “FBX Units Scale” に設定****するのが推奨です。  
> これにより、追加スケールなしのメートル単位（unit\_meters = 1.0）でエクスポートされます。

---

## サンプルコード

```c
// シーンをメートル単位・右手Y-upに変換してロード
ufbx_load_opts opts = { 0 };
opts.target_axes = ufbx_axes_right_handed_y_up;
opts.target_unit_meters = 1.0f;
opts.target_camera_axes = ufbx_axes_right_handed_y_up;
opts.target_light_axes = ufbx_axes_right_handed_y_up;

if (prefer_blender) {
    opts.space_conversion = UFBX_SPACE_CONVERSION_ADJUST_TRANSFORMS;
} else {
    opts.space_conversion = UFBX_SPACE_CONVERSION_MODIFY_GEOMETRY;
}
```

---

## Geometry Transforms（ジオメトリ変換）

<span style="white-space: pre-wrap;">FBX では </span>****ジオメトリ変換（geometry transform）****<span style="white-space: pre-wrap;"> と呼ばれる特殊な変換をサポートしています。</span>  
これはノード直下のメッシュなどにのみ適用され、子ノードには継承されません。  
多くのシーングラフではこの仕組みを直接サポートしていないため、ufbx は代替方法を提供します。

### ジオメトリ変換の利用

<span style="white-space: pre-wrap;">ジオメトリ変換は </span>`<span class="editor-theme-code">ufbx_node.geometry_transform</span>`<span style="white-space: pre-wrap;"> に格納されます。</span>  
また、補助的な行列：

- `<span class="editor-theme-code">ufbx_node.geometry_to_node</span>`
- `<span class="editor-theme-code">ufbx_node.geometry_to_world</span>`

もあり、特に静的メッシュをワールド座標で扱う際に便利です。

### ジオメトリ変換を除去する

非静的シーンでのジオメトリ変換は扱いが難しいため、  
`<span class="editor-theme-code">ufbx_load_opts.geometry_transform_handling</span>`<span style="white-space: pre-wrap;"> によりロード時に削除することも可能です。</span>

<table id="bkmrk-%E5%AE%9A%E6%95%B0%E5%90%8D%E5%86%85%E5%AE%B9ufbx_geometry_t"><colgroup><col></col><col></col></colgroup><tbody><tr><th>定数名

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">UFBX_GEOMETRY_TRANSFORM_HANDLING_HELPER_NODES</span>`

</td><td>補助ノードを挿入して対応（確実だがノード数が増える）

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY</span>`

</td><td>ジオメトリ変換を頂点データに焼き込み（きれいだが制約あり）

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY_NO_FALLBACK</span>`

</td><td>補助ノードを絶対に作らない（ただし変換誤差が発生する）

</td></tr></tbody></table>

---

## Inherit Modes（継承モード）

FBX では非標準的な変換継承も可能です。  
<span style="white-space: pre-wrap;">これは </span>`<span class="editor-theme-code">ufbx_node.inherit_mode</span>`<span style="white-space: pre-wrap;"> によって示されます。</span>

<table id="bkmrk-%E3%83%A2%E3%83%BC%E3%83%89%E5%86%85%E5%AE%B9ufbx_inherit_mo"><colgroup><col></col><col></col></colgroup><tbody><tr><th>モード

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">UFBX_INHERIT_MODE_IGNORE_PARENT_SCALE</span>`

</td><td>親のスケールを無視

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_INHERIT_MODE_COMPONENTWISE_SCALE</span>`

</td><td>スケールと回転を独立して合成

</td></tr></tbody></table>

ufbx では、ロード時にこれらを標準的なシーングラフへ変換するオプションを用意しています：

<table id="bkmrk-%E5%AE%9A%E6%95%B0%E5%86%85%E5%AE%B9ufbx_inherit_mod"><colgroup><col></col><col></col></colgroup><tbody><tr><th>定数

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">UFBX_INHERIT_MODE_HANDLING_PRESERVE</span>`

</td><td>継承モードをそのまま保持（正確だが複雑）

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_INHERIT_MODE_HANDLING_HELPER_NODES</span>`

</td><td>補助ノードを追加して対応

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_INHERIT_MODE_HANDLING_COMPENSATE</span>`

</td><td>子ノードのスケールを逆補正（できない場合は補助ノード）

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_INHERIT_MODE_HANDLING_IGNORE</span>`

</td><td>非標準継承をすべて無視（単純だが不正確）

</td></tr></tbody></table>

---

## Pivots（ピボット）

FBX ノードでは、****回転ピボット****<span style="white-space: pre-wrap;"> と </span>****スケールピボット****<span style="white-space: pre-wrap;"> を個別に設定できます。</span>  
ufbx では、デフォルトでこれらの効果をノードの平行移動へ焼き込みます。

回転ピボットとスケールピボットが同一である場合、  
`<span class="editor-theme-code">ufbx_load_opts.pivot_handling = UFBX_PIVOT_HANDLING_ADJUST_TO_PIVOT</span>`  
を設定することで、ピボットをジオメトリ変換へ変換することも可能です。  
この場合は、`<span class="editor-theme-code">ufbx_load_opts.geometry_transform_handling</span>`<span style="white-space: pre-wrap;"> も併せて指定します（例：</span>`<span class="editor-theme-code">UFBX_GEOMETRY_TRANSFORM_HANDLING_MODIFY_GEOMETRY</span>`）。

---

## その他のプロパティ

ノードには変換以外にも以下のプロパティがあります：

- ****可視性****：`<span class="editor-theme-code">ufbx_node.visible</span>`<span style="white-space: pre-wrap;"> でノードを非表示にできる</span>
- ****マテリアルの上書き****：`<span class="editor-theme-code">ufbx_node.materials[]</span>`<span style="white-space: pre-wrap;"> でインスタンスごとに別マテリアルを適用できる</span>

---

## 備考

<span style="white-space: pre-wrap;">FBX 仕様ではこれらを </span>****“Geometric” transforms（ジオメトリック変換）****<span style="white-space: pre-wrap;"> と呼びますが、</span>  
<span style="white-space: pre-wrap;">ufbx では明確化のため </span>****geometry transforms****<span style="white-space: pre-wrap;"> と呼んでいます。</span>

---

# Meshes（メッシュ） — ufbx ドキュメント日本語訳

## 概要

****Meshes（メッシュ）****（`<span class="editor-theme-code">ufbx_mesh</span>`）は、****ポリゴンジオメトリデータ****<span style="white-space: pre-wrap;"> を保持する要素です。</span>

ufbx では次の用語を使用します：

<table id="bkmrk-%E7%94%A8%E8%AA%9E%E8%AA%AC%E6%98%8Evertex%EF%BC%88%E9%A0%82%E7%82%B9%EF%BC%89%E4%BD%8D%E7%BD%AE%E6%83%85%E5%A0%B1%E3%82%92%E6%8C%81"><colgroup><col></col><col></col></colgroup><tbody><tr><th>用語

</th><th>説明

</th></tr><tr><td>****Vertex（頂点）****

</td><td>位置情報を持つ頂点。3Dモデリングソフトで選択可能な頂点に相当。

</td></tr><tr><td>****Index（インデックス）****

</td><td>頂点にUV・法線・カラーなどの属性を組み合わせたもの。

</td></tr><tr><td>****Face（面）****

</td><td>1枚の平面（三角形・四角形・N-gon）を構成するインデックスの範囲。

</td></tr></tbody></table>

同じ頂点が複数の面から参照されることがあります。  
<span style="white-space: pre-wrap;">各参照では異なるインデックスを持ち、これにより </span>****同一頂点に異なるUVや法線を設定****<span style="white-space: pre-wrap;"> できます。</span>

---

## メッシュのデータ構造

メッシュデータは各種属性として格納されます：

- `<span class="editor-theme-code">ufbx_mesh.vertex_position</span>`<span style="white-space: pre-wrap;"> — 頂点座標</span>
- `<span class="editor-theme-code">ufbx_mesh.vertex_normal</span>`<span style="white-space: pre-wrap;"> — 頂点法線</span>
- `<span class="editor-theme-code">ufbx_mesh.vertex_uv</span>`<span style="white-space: pre-wrap;"> — 頂点UV座標</span>

<span style="white-space: pre-wrap;">各属性には独自の </span>****インデックス配列****（`<span class="editor-theme-code">ufbx_vertex_attrib.indices[]</span>`<span style="white-space: pre-wrap;">）と </span>****値配列****（`<span class="editor-theme-code">ufbx_vertex_attrib.values[]</span>`）があり、  
インデックス指定で値を取得できます：

```c
data[indices[index]]
```

<span style="white-space: pre-wrap;">あるいはヘルパー関数 </span>`<span class="editor-theme-code">ufbx_get_vertex_vec3()</span>`<span style="white-space: pre-wrap;"> や、</span>  
<span style="white-space: pre-wrap;">C++／Rust の </span>`<span class="editor-theme-code">attrib[index]</span>`<span style="white-space: pre-wrap;"> 構文でもアクセス可能です。</span>

---

## 描画例

以下は仮想的な即時モードポリゴンAPIを使ってメッシュを描画する例です。

```c
void draw_polygons(ufbx_mesh *mesh)
{
    for (ufbx_face face : mesh->faces) {
        begin_polygon();

        // ポリゴンの各コーナーをループ
        for (uint32_t corner = 0; corner < face.num_indices; corner++) {

            // 各コーナーに対応するインデックス
            uint32_t index = face.index_begin + corner;

            // 頂点属性を取得
            ufbx_vec3 position = mesh->vertex_position[index];
            ufbx_vec3 normal   = mesh->vertex_normal[index];
            ufbx_vec2 uv       = mesh->vertex_uv[index];

            polygon_corner(position, normal, uv);
        }

        end_polygon();
    }
}
```

---

## Materials（マテリアル）

1つのFBXメッシュには、****異なる部位に複数のマテリアル****<span style="white-space: pre-wrap;"> が割り当てられる場合があります。</span>  
`<span class="editor-theme-code">ufbx_mesh.face_material[]</span>`<span style="white-space: pre-wrap;"> には、面ごとのマテリアルインデックスが格納されており、</span>  
<span style="white-space: pre-wrap;">これを使って </span>`<span class="editor-theme-code">ufbx_mesh.materials[]</span>`<span style="white-space: pre-wrap;"> にアクセスできます。</span>

ただし、****正確な結果を得るには** `<strong class="editor-theme-bold editor-theme-code">ufbx_node.materials[]</strong>` **を使うのが推奨****です。  
理由は、同じメッシュが異なるマテリアルでインスタンス化されることがあるためです。

ゲームエンジンでは、マテリアル境界ごとにメッシュを分割する必要がある場合があります。  
<span style="white-space: pre-wrap;">ufbx ではこれを容易にするために </span>`<span class="editor-theme-code">ufbx_mesh.material_parts[]</span>`<span style="white-space: pre-wrap;"> を提供しています。</span>  
これには、各マテリアルごとの****面数・三角形数・面リスト****が含まれます。  
マテリアルが存在しない場合でも、便宜上1つのマテリアルパートが存在します。

---

## 例：GPU用フォーマットへの変換

<span style="white-space: pre-wrap;">以下は、メッシュデータを </span>****GPU向けインデックス付きフォーマット****<span style="white-space: pre-wrap;"> に変換する例です。</span>  
使用しているヘルパー関数：

- `<span class="editor-theme-code">ufbx_triangulate_face()</span>`：面を三角形化してインデックス配列を生成
- `<span class="editor-theme-code">ufbx_generate_indices()</span>`：頂点配列を重複排除し、インデックスバッファを生成

```cpp
// GPU向け頂点構造体
// 実際にはよりコンパクトな型を使うべきです。
// `ufbx_real` はデフォルトで64bitです。
struct Vertex {
    ufbx_vec3 position;
    ufbx_vec3 normal;
    ufbx_vec2 uv;
};

void convert_mesh_part(ufbx_mesh *mesh, ufbx_mesh_part *part)
{
    std::vector<Vertex> vertices;
    std::vector<uint32_t> tri_indices;
    tri_indices.resize(mesh->max_face_triangles * 3);

    // このマテリアルを使用している各面を処理
    for (uint32_t face_index : part->face_indices) {
        ufbx_face face = mesh->faces[face_index];

        // 面を三角形化
        uint32_t num_tris = ufbx_triangulate_face(
            tri_indices.data(), tri_indices.size(), mesh, face);

        // 各三角形の頂点を取得
        for (size_t i = 0; i < num_tris * 3; i++) {
            uint32_t index = tri_indices[i];

            Vertex v;
            v.position = mesh->vertex_position[index];
            v.normal   = mesh->vertex_normal[index];
            v.uv       = mesh->vertex_uv[index];
            vertices.push_back(v);
        }
    }

    assert(vertices.size() == part->num_triangles * 3);

    // 頂点ストリームを生成
    ufbx_vertex_stream streams[1] = {
        { vertices.data(), vertices.size(), sizeof(Vertex) },
    };
    std::vector<uint32_t> indices;
    indices.resize(part->num_triangles * 3);

    // 頂点重複を削除し、インデックスバッファを生成
    size_t num_vertices = ufbx_generate_indices(
        streams, 1, indices.data(), indices.size(), nullptr, nullptr);

    vertices.resize(num_vertices);

    create_vertex_buffer(vertices.data(), vertices.size());
    create_index_buffer(indices.data(), indices.size());
}
```

---

## Attributes（属性）

上記の属性に加えて、FBXメッシュには他の属性も存在します。  
<span style="white-space: pre-wrap;">多くの属性は </span>****頂点ごと（またはインデックスごと）****<span style="white-space: pre-wrap;"> に定義されていますが、</span>  
<span style="white-space: pre-wrap;">一部は </span>****面単位・エッジ単位****<span style="white-space: pre-wrap;"> のデータも含まれます。</span>

エッジはオプションで、`<span class="editor-theme-code">ufbx_mesh.edges[]</span>`<span style="white-space: pre-wrap;"> で2つのインデックス間を定義します。</span>

---

### <span style="white-space: pre-wrap;">頂点（インデックス単位、最大 </span>`<span class="editor-theme-code">ufbx_mesh.num_indices</span>`）

<table id="bkmrk-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E5%86%85%E5%AE%B9ufbx_mesh.ver"><colgroup><col></col><col></col></colgroup><tbody><tr><th>フィールド

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_position</span>`

</td><td>頂点座標

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_normal</span>`

</td><td>法線ベクトル

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_tangent</span>`

</td><td>接線方向（Tangent）UV.x

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_bitangent</span>`

</td><td>接線空間UV.y

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_uv</span>`

</td><td>UV座標（第1セット）

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_color</span>`

</td><td>頂点カラー（第1セット）

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.vertex_crease</span>`

</td><td>サブディビジョン用クリース値

</td></tr></tbody></table>

---

### <span style="white-space: pre-wrap;">面（最大 </span>`<span class="editor-theme-code">ufbx_mesh.num_faces</span>`）

<table id="bkmrk-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E5%86%85%E5%AE%B9ufbx_mesh.fac"><colgroup><col></col><col></col></colgroup><tbody><tr><th>フィールド

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.face_material</span>`

</td><td>面ごとのマテリアル

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.face_group</span>`

</td><td>ポリゴングループ

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.face_smoothing</span>`

</td><td>スムーズシェーディングフラグ

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.face_hole</span>`

</td><td>穴として扱うかどうか

</td></tr></tbody></table>

---

### <span style="white-space: pre-wrap;">エッジ（最大 </span>`<span class="editor-theme-code">ufbx_mesh.num_edges</span>`）

<table id="bkmrk-%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E5%86%85%E5%AE%B9ufbx_mesh.edg"><colgroup><col></col><col></col></colgroup><tbody><tr><th>フィールド

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.edge_smoothing</span>`

</td><td>法線生成用スムーズフラグ

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.edge_visibility</span>`

</td><td>編集用のエッジ表示フラグ

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_mesh.edge_crease</span>`

</td><td>サブディビジョン用エッジクリース

</td></tr></tbody></table>

---

# 🌀 Animation（アニメーション）

FBX ファイル内のアニメーションは、****スタック (**`<strong class="editor-theme-bold editor-theme-code">ufbx_anim_stack</strong>`**)****<span style="white-space: pre-wrap;"> と呼ばれる </span>****レイヤー (**`<strong class="editor-theme-bold editor-theme-code">ufbx_anim_layer</strong>`**)****<span style="white-space: pre-wrap;"> の集合として表現されます。</span>  
各スタックは、ファイル内の 1 つのアニメーションクリップ（または「テイク」）に対応します。

****ufbx****<span style="white-space: pre-wrap;"> ではアニメーションを </span>`<span class="editor-theme-code">ufbx_anim</span>`<span style="white-space: pre-wrap;"> デスクリプタを通して扱います。</span>  
これにより、「複数レイヤーを合成したスタック」か「単一レイヤー」かを  
統一されたインターフェースで選択・評価できます。

### <span style="white-space: pre-wrap;">🔹 </span>`<span class="editor-theme-code">ufbx_anim</span>`<span style="white-space: pre-wrap;"> インスタンスを取得できる場所</span>

- `<span class="editor-theme-code">ufbx_scene.anim</span>`：シーンのデフォルトアニメーションスタック
- `<span class="editor-theme-code">ufbx_anim_stack.anim</span>`：合成済みアニメーションスタック
- `<span class="editor-theme-code">ufbx_anim_layer.anim</span>`：個別のアニメーションレイヤー
- `<span class="editor-theme-code">ufbx_create_anim()</span>`：カスタムアニメーションデスクリプタを作成

---

## 🎛 評価（Evaluation）

**ufbx**<span style="white-space: pre-wrap;"> はファイル内のアニメーションカーブを直接扱えますが、</span>  
それらを手動で解釈するのは非常に複雑です。

主な理由は次の通りです：

- <span style="white-space: pre-wrap;">⏩ 時間が非線形な </span>****キュービック補間カーブ****
- <span style="white-space: pre-wrap;">🔄 回転順序を持つ </span>****オイラー角回転カーブ****
- <span style="white-space: pre-wrap;">⚙️ </span>****プリ／ポスト回転、ピボット、オフセット****<span style="white-space: pre-wrap;"> などの複雑なノード変換</span>
- <span style="white-space: pre-wrap;">🎨 </span>****アニメーションレイヤーの合成****

こうした複雑さを避けるため、  
<span style="white-space: pre-wrap;">これらを内部で処理してくれる </span>**ufbx**<span style="white-space: pre-wrap;"> の「評価ユーティリティ」を使用することが推奨されます。</span>

特に、下記の「アニメーションのベイク（baking）」は、  
複雑な FBX アニメーションを扱いやすい形式に変換する良い出発点です。

---

## 🌍 シーン全体の評価（Scene Evaluation）

<span style="white-space: pre-wrap;">最も簡単な方法は </span>**`<strong class="editor-theme-bold editor-theme-code">ufbx_evaluate_scene()</strong>`**<span style="white-space: pre-wrap;"> を使うことです。</span>  
これは指定した時刻におけるアニメーションをすべて適用し、  
<span style="white-space: pre-wrap;">新しい </span>`<span class="editor-theme-code">ufbx_scene</span>`<span style="white-space: pre-wrap;"> を生成する「重い」関数です。</span>

<span style="white-space: pre-wrap;">結果のシーンは通常の </span>`<span class="editor-theme-code">ufbx_scene</span>`<span style="white-space: pre-wrap;"> と同様に扱えます。</span>

---

## 🔧 アニメーションのベイク（Animation Baking）

<span style="white-space: pre-wrap;">FBX ファイル内のアニメーションは </span>**`<strong class="editor-theme-bold editor-theme-code">ufbx_bake_anim()</strong>`**<span style="white-space: pre-wrap;"> を使って</span>  
より単純な形式に「ベイク」できます。

この関数は、トランスフォームアニメーションを  
線形補間のトラック（translation／quaternion rotation／scale）に変換します。  
トランスフォーム以外のプロパティも線形補間キーとしてベイクされます。

ベイクアルゴリズムは単純な再サンプリングではなく、  
キーフレームの頻度などを考慮して効率的に処理します。  
ただし、****キュービック補間****や****オイラー回転****は  
クォータニオンに再サンプリングする必要があります。

### 🎚 サンプリング設定

- `<span class="editor-theme-code">ufbx_bake_opts.resample_rate</span>`：再サンプリングレートの設定
- `<span class="editor-theme-code">ufbx_bake_opts.minimum_sample_rate</span>`：高頻度キーをスキップして二重サンプリングを防止

---

### 💻 C 言語例

```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++ 例

```cpp
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 例

```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**<span style="white-space: pre-wrap;"> は個々の要素を特定の時刻で評価するための低レベル API も提供しています。</span>

<table id="bkmrk-%E9%96%A2%E6%95%B0%E5%90%8D%E5%86%85%E5%AE%B9ufbx_evaluate_t"><colgroup><col></col><col></col></colgroup><tbody><tr><th>関数名

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">ufbx_evaluate_transform()</span>`

</td><td>ノードの位置・回転（クォータニオン）・スケールを評価

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_evaluate_blend_weight()</span>`

</td><td>ブレンドシェイプのウェイトを評価

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_evaluate_prop()</span>`

</td><td>任意の FBX プロパティ値を評価

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_evaluate_props()</span>`

</td><td>要素全体のプロパティをまとめて評価

</td></tr></tbody></table>

さらに低レベルの関数：

<table id="bkmrk-%E9%96%A2%E6%95%B0%E5%90%8D%E5%86%85%E5%AE%B9ufbx_evaluate_a"><colgroup><col></col><col></col></colgroup><tbody><tr><th>関数名

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">ufbx_evaluate_anim_value_real()</span>`

<span style="white-space: pre-wrap;"> / </span>

`<span class="editor-theme-code">ufbx_evaluate_anim_value_vec3()</span>`

</td><td>`<span class="editor-theme-code">ufbx_anim_value</span>`

<span style="white-space: pre-wrap;"> を評価</span>

</td></tr><tr><td>`<span class="editor-theme-code">ufbx_evaluate_curve()</span>`

</td><td><span style="white-space: pre-wrap;">単一の </span>

`<span class="editor-theme-code">ufbx_anim_curve</span>`

<span style="white-space: pre-wrap;"> を評価</span>

</td></tr></tbody></table>

# 🦴 Deformers（デフォーマー）

FBX では、****メッシュ変形（Mesh deformation）****<span style="white-space: pre-wrap;"> は </span>**Deformer**<span style="white-space: pre-wrap;"> を通じて実装されています。</span>  
****ufbx****<span style="white-space: pre-wrap;"> は以下の 3 種類の FBX デフォーマーをサポートしています。</span>

- <span style="white-space: pre-wrap;">🦴 </span>`<span class="editor-theme-code">ufbx_skin_deformer</span>`  
    <span style="white-space: pre-wrap;"> → ボーンに頂点を重み付きで結びつける「スキニング」</span>
- <span style="white-space: pre-wrap;">😶‍🌫️ </span>`<span class="editor-theme-code">ufbx_blend_deformer</span>`  
    <span style="white-space: pre-wrap;"> → ブレンドシェイプ（モーフターゲット）による頂点オフセット</span>
- <span style="white-space: pre-wrap;">💾 </span>`<span class="editor-theme-code">ufbx_cache_deformer</span>`  
    <span style="white-space: pre-wrap;"> → ディスクキャッシュからメッシュデータを置き換えるデフォーマー</span>

これらのデフォーマーはメッシュに接続されています。  
例：`<span class="editor-theme-code">ufbx_mesh.skin_deformers[]</span>`<span style="white-space: pre-wrap;"> や </span>`<span class="editor-theme-code">ufbx_mesh.blend_deformers[]</span>`。  
もし正確な適用順序が必要な場合は、  
`<span class="editor-theme-code">ufbx_mesh.all_deformers[]</span>`<span style="white-space: pre-wrap;"> ですべてのデフォーマーを型なしリストで取得できます。</span>

---

## 🦾 Skin Deformer（スキンデフォーマー）

FBX のスケルトンは、通常のノード（`<span class="editor-theme-code">ufbx_node</span>`）で構成され、  
多くの場合ボーン属性（`<span class="editor-theme-code">ufbx_bone</span>`）を持ちます。  
ボーン属性はスキニングに必須ではありませんが、可視化やスケルトン検出に役立ちます。

### 📐 クラスタ（Cluster）

<span style="white-space: pre-wrap;">ボーンによる影響は </span>****クラスタ (**`<strong class="editor-theme-bold editor-theme-code">ufbx_skin_cluster</strong>`**)****<span style="white-space: pre-wrap;"> で定義されます。</span>  
クラスタはメッシュをボーンに結びつけ、  
`<span class="editor-theme-code">ufbx_skin_cluster.geometry_to_bone</span>`<span style="white-space: pre-wrap;"> は</span>  
メッシュ空間からボーン空間への変換を表します。  
これはいわゆる「逆バインド行列（inverse bind matrix）」です。

<span style="white-space: pre-wrap;">影響を受ける頂点は </span>`<span class="editor-theme-code">ufbx_skin_cluster.vertices[]</span>`<span style="white-space: pre-wrap;"> と</span>  
<span style="white-space: pre-wrap;">対応する </span>`<span class="editor-theme-code">ufbx_skin_cluster.weights[]</span>`<span style="white-space: pre-wrap;"> に格納されています。</span>

---

### 🧮 頂点ごとの重み情報

どのボーンが各頂点に影響するかを解析するのはよくある処理です。  
<span style="white-space: pre-wrap;">そこで </span>**ufbx**<span style="white-space: pre-wrap;"> は便利な簡略アクセスとして：</span>

- `<span class="editor-theme-code">ufbx_skin_deformer.vertices[]</span>`
- `<span class="editor-theme-code">ufbx_skin_deformer.weights[]</span>`

を提供しています。  
これらは****影響度の高い順****にソートされているため、  
もし 4 ～ 8 つのウェイトしか対応しない場合は  
<span style="white-space: pre-wrap;">上位 </span>`<span class="editor-theme-code">N</span>`<span style="white-space: pre-wrap;"> 個を取るだけで十分です。</span>

---

### 💻 C 言語サンプル

```c
#define MAX_WEIGHTS 4

typedef struct Vertex {
    ufbx_vec3 position;
    ufbx_vec3 normal;
    float weights[MAX_WEIGHTS];
    uint32_t bones[MAX_WEIGHTS];
} Vertex;

Vertex get_skinned_vertex(ufbx_mesh *mesh, ufbx_skin_deformer *skin, size_t index)
{
    Vertex v = { 0 };
    v.position = ufbx_get_vertex_vec3(&mesh->vertex_position, index);
    v.normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, index);

    uint32_t vertex = mesh->vertex_indices.data[index];
    ufbx_skin_vertex skin_vertex = skin->vertices.data[vertex];
    size_t num_weights = skin_vertex.num_weights;
    if (num_weights > MAX_WEIGHTS) num_weights = MAX_WEIGHTS;

    float total_weight = 0.0f;
    for (size_t i = 0; i < num_weights; i++) {
        ufbx_skin_weight skin_weight = skin->weights.data[skin_vertex.weight_begin + i];
        v.bones[i] = skin_weight.cluster_index;
        v.weights[i] = (float)skin_weight.weight;
        total_weight += (float)skin_weight.weight;
    }

    // FBX では重みの正規化が保証されていないため再正規化する
    for (size_t i = 0; i < num_weights; i++) {
        v.weights[i] /= total_weight;
    }

    return v;
}
```

---

### 💻 C++ 版

```cpp
#define MAX_WEIGHTS 4

struct Vertex {
    ufbx_vec3 position;
    ufbx_vec3 normal;
    float weights[MAX_WEIGHTS];
    uint32_t bones[MAX_WEIGHTS];
};

Vertex get_skinned_vertex(ufbx_mesh *mesh, ufbx_skin_deformer *skin, size_t index)
{
    Vertex v = { 0 };
    v.position = mesh->vertex_position[index];
    v.normal = mesh->vertex_normal[index];

    uint32_t vertex = mesh->vertex_indices[index];
    ufbx_skin_vertex skin_vertex = skin->vertices[vertex];
    size_t num_weights = skin_vertex.num_weights;
    if (num_weights > MAX_WEIGHTS) num_weights = MAX_WEIGHTS;

    float total_weight = 0.0f;
    for (size_t i = 0; i < num_weights; i++) {
        ufbx_skin_weight skin_weight = skin->weights[skin_vertex.weight_begin + i];
        v.bones[i] = skin_weight.cluster_index;
        v.weights[i] = (float)skin_weight.weight;
        total_weight += (float)skin_weight.weight;
    }

    for (size_t i = 0; i < num_weights; i++) {
        v.weights[i] /= total_weight;
    }

    return v;
}
```

---

### 💻 Rust 版

```rust
const MAX_WEIGHTS: usize = 4;

#[derive(Clone, Copy, Default)]
struct Vertex {
    position: ufbx::Vec3,
    normal: ufbx::Vec3,
    weights: [f32; MAX_WEIGHTS],
    bones: [u32; MAX_WEIGHTS],
}

fn get_skinned_vertex(mesh: &ufbx::Mesh, skin: &ufbx::SkinDeformer, index: usize) -> Vertex {
    let mut v = Vertex{
        position: mesh.vertex_position[index],
        normal: mesh.vertex_normal[index],
        ..Default::default()
    };

    let vertex = mesh.vertex_indices[index] as usize;
    let skin_vertex = skin.vertices[vertex];
    let num_weights = (skin_vertex.num_weights as usize).min(MAX_WEIGHTS);

    let mut total_weight: f32 = 0.0;
    for i in 0..num_weights {
        let skin_weight = skin.weights[skin_vertex.weight_begin as usize + i];
        v.bones[i] = skin_weight.cluster_index;
        v.weights[i] = skin_weight.weight as f32;
        total_weight += skin_weight.weight as f32;
    }

    for i in 0..num_weights {
        v.weights[i] /= total_weight;
    }

    v
}
```

---

## ⚙️ Skinning Modes（スキニングモード）

<span style="white-space: pre-wrap;">FBX は </span>`<span class="editor-theme-code">ufbx_skin_deformer.skinning_method</span>`<span style="white-space: pre-wrap;"> により複数のスキニング方式をサポートします。</span>  
基本的には無視しても問題ありませんが、必要なら以下の通りです。

<table id="bkmrk-%E5%AE%9A%E6%95%B0%E5%86%85%E5%AE%B9ufbx_skinning_me"><colgroup><col></col><col></col></colgroup><tbody><tr><th>定数

</th><th>内容

</th></tr><tr><td>`<span class="editor-theme-code">UFBX_SKINNING_METHOD_RIGID</span>`

</td><td>単一ボーン固定（補間なし）

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_SKINNING_METHOD_LINEAR</span>`

</td><td>一般的な線形ブレンドスキニング

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_SKINNING_METHOD_DUAL_QUATERNION</span>`

</td><td>デュアルクォータニオンスキニング

</td></tr><tr><td>`<span class="editor-theme-code">UFBX_SKINNING_METHOD_BLENDED_DQ_LINEAR</span>`

</td><td>線形とデュアルクォータニオンを補間するモード（

`<span class="editor-theme-code">ufbx_skin_vertex.dq_weight</span>`

<span style="white-space: pre-wrap;"> により制御）</span>

</td></tr></tbody></table>

---

## 🎭 Blend Deformer（ブレンドデフォーマー）

****ブレンドシェイプ（モーフターゲット）****<span style="white-space: pre-wrap;"> は</span>  
****ブレンドチャンネル (**`<strong class="editor-theme-bold editor-theme-code">ufbx_blend_channel</strong>`**)****<span style="white-space: pre-wrap;"> によって制御されます。</span>

FBX 形式では「中間ブレンドキー（in-between keyframes）」をサポートしており、  
`<span class="editor-theme-code">ufbx_blend_channel.keyframes[]</span>`<span style="white-space: pre-wrap;"> にキーが定義されています。</span>

もしそれを扱わない場合は、便利なフィールド：

- `<span class="editor-theme-code">ufbx_blend_channel.target_shape</span>`  
    <span style="white-space: pre-wrap;"> → 最後のキー（ターゲットシェイプ）を直接参照</span>

を使用できます。

各ブレンドシェイプ（`<span class="editor-theme-code">ufbx_blend_shape</span>`）は頂点の部分集合に対してオフセットを持ちます：

- `<span class="editor-theme-code">ufbx_blend_shape.offset_vertices[]</span>`：影響を受ける頂点インデックス
- `<span class="editor-theme-code">ufbx_blend_shape.position_offsets[]</span>`：対応する頂点位置のオフセット
- `<span class="editor-theme-code">ufbx_blend_shape.normal_offsets[]</span>`：法線オフセット（多くの場合は未使用またはゼロ）

また、便利関数として以下も用意されています：

- `<span class="editor-theme-code">ufbx_get_blend_shape_offset_index()</span>`
- `<span class="editor-theme-code">ufbx_get_blend_shape_vertex_offset()</span>`

---

### 💻 C 例

```c
#define MAX_BLENDS 4

typedef struct Vertex {
    ufbx_vec3 position;
    ufbx_vec3 normal;
    ufbx_vec3 blend_offsets[MAX_BLENDS];
} Vertex;

Vertex get_blend_vertex(ufbx_mesh *mesh, ufbx_blend_deformer *deformer, size_t index)
{
    Vertex v = { 0 };
    v.position = ufbx_get_vertex_vec3(&mesh->vertex_position, index);
    v.normal = ufbx_get_vertex_vec3(&mesh->vertex_normal, index);

    uint32_t vertex = mesh->vertex_indices.data[index];
    size_t num_blends = deformer->channels.count;
    if (num_blends > MAX_BLENDS) num_blends = MAX_BLENDS;

    for (size_t i = 0; i < num_blends; i++) {
        ufbx_blend_channel *channel = deformer->channels.data[i];
        ufbx_blend_shape *shape = channel->target_shape;
        assert(shape);
        v.blend_offsets[i] = ufbx_get_blend_shape_vertex_offset(shape, vertex); 
    }

    return v;
}
```

---

### 💻 C++ 例

```cpp
#define MAX_BLENDS 4

struct Vertex {
    ufbx_vec3 position;
    ufbx_vec3 normal;
    ufbx_vec3 blend_offsets[MAX_BLENDS];
};

Vertex get_blend_vertex(ufbx_mesh *mesh, ufbx_blend_deformer *deformer, size_t index)
{
    Vertex v = { };
    v.position = mesh->vertex_position[index];
    v.normal = mesh->vertex_normal[index];

    uint32_t vertex = mesh->vertex_indices[index];
    size_t num_blends = deformer->channels.count;
    if (num_blends > MAX_BLENDS) num_blends = MAX_BLENDS;

    for (size_t i = 0; i < num_blends; i++) {
        ufbx_blend_channel *channel = deformer->channels[i];
        ufbx_blend_shape *shape = channel->target_shape;
        assert(shape);
        v.blend_offsets[i] = ufbx_get_blend_shape_vertex_offset(shape, vertex); 
    }

    return v;
}
```

---

### 💻 Rust 例

```rust
const MAX_BLENDS: usize = 4;

#[derive(Clone, Copy, Default)]
struct Vertex {
    position: ufbx::Vec3,
    normal: ufbx::Vec3,
    blend_offsets: [ufbx::Vec3; MAX_BLENDS],
}

fn get_blend_vertex(mesh: &ufbx::Mesh, deformer: &ufbx::BlendDeformer, index: usize) -> Vertex {
    let mut v = Vertex{
        position: mesh.vertex_position[index],
        normal: mesh.vertex_normal[index],
        ..Default::default()
    };

    let vertex = mesh.vertex_indices[index] as usize;
    let num_blends = (deformer.channels.len() as usize).min(MAX_BLENDS);
    for i in 0..num_blends {
        let channel = &deformer.channels[i];
        let shape = channel.target_shape.as_ref().expect("no blend shape, broken file");
        v.blend_offsets[i] = shape.get_vertex_offset(vertex); 
    }

    v
}
```

---

<span style="white-space: pre-wrap;">📘 </span>****備考****  
このドキュメントは MIT / Public Domain ライセンスのもとで公開された  
[ufbx (c) 2020 Samuli Raivio](https://ufbx.github.io)<span style="white-space: pre-wrap;"> の内容を翻訳・整形したものです。</span>