# 🦴 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>