# FBX読み込み処理

## 全体構造（ロード〜描画の流れ）

ざっくりした流れはこうなっています。

```text
[FBXファイル]
    │  (ufbxでロード済み)
    ▼
[ufbx_scene]
    │
    ├─ ExpandAllNodes(scene)   … CPU側メッシュ展開・スキン情報抽出・AABB計算
    │
    ├─ CreateGpuBuffers()      … VB / IB 作成
    │
    └─ CreateEffectsAndTextures(fbx_path, scene)
         └─ テクスチャや BasicEffect の初期化

(ここまでがロード時)

描画時:
    └─ Draw(world, view, proj)
        ├─ CPU スキニング (必要なら)
        ├─ VB を UpdateSubresource で更新
        └─ IA 設定 → BasicEffect → DrawIndexed
```

`<span class="editor-theme-code">UfbxStaticModel</span>`<span style="white-space: pre-wrap;"> のメッシュ関係で主役になるフィールドは（名前だけ）：</span>

```text
mesh_.vertices_        : VertexPNT2 の配列（展開済み頂点）
mesh_.indices_         : uint32_t の配列（三角形インデックス）
mesh_.parts_           : MeshPart の配列（マテリアルごとのサブメッシュ）
mesh_.influences_      : VertexInfluence の配列（頂点ごとのボーン情報）
mesh_.bind_vertices_   : バインドポーズの頂点（＝元の姿）
mesh_.skinned_vertices_: スキニング後の頂点（CPUスキニング結果）
draw_.vb_ / draw_.ib_  : D3D11のVB/IB
draw_.fx_              : BasicEffect
draw_.states_          : CommonStates
draw_.layout_          : 入力レイアウト
```

---

## データ構造イメージ

### 頂点とインデックス

```text
VertexPNT2:
    pos : float3   (頂点位置)
    nrm : float3   (法線)
    uv  : float2   (テクスチャ座標)

頂点バッファ (mesh_.vertices_)
    index: 0   1   2   3   4   5   ...
           +---+---+---+---+---+---+
           | V | V | V | V | V | V |
           +---+---+---+---+---+---+

インデックスバッファ (mesh_.indices_)
    0,1,2 / 3,4,5 / ...
    → TRIANGLELIST で描画
```

### 頂点ごとのボーン影響 VertexInfluence

```text
VertexInfluence:
    uint16_t bone[4];
    float    weight[4];

1頂点 = 最大4本のボーンが影響する
```

```
mesh_.influences_[v]  ← 頂点 v に対するボーン情報
```

### MeshPart（サブメッシュ）

```text
MeshPart:
    const ufbx_material* mat;  // 元FBXのマテリアル
    uint32_t start_index;      // mesh_.indices_ のどこから
    uint32_t index_count;      // 何個インデックスを描くか
    ComPtr<ID3D11ShaderResourceView> srv; // diffuse テクスチャ
```

概念図：

```text
mesh_.indices_:
    [0] [1] [2] [3] [4] [5] [6] [7] ...

MeshPart 0:
    start_index = 0
    index_count = 300

MeshPart 1:
    start_index = 300
    index_count = 150
```

---

## ExpandAllNodes(scene): メッシュ展開

### 1. 初期化

```cpp
mesh_.vertices_.clear();
mesh_.indices_.clear();
mesh_.parts_.clear();
mesh_.influences_.clear();
mesh_.bind_vertices_.clear();
mesh_.skinned_vertices_.clear();
```

AABB 用の min/max も初期化。

```text
bb_min = ( +∞, +∞, +∞ )
bb_max = ( -∞, -∞, -∞ )
```

---

### 2. シーン中の全ノードを走査

```cpp
for each node in scene->nodes:
    mesh = node->mesh
    if !mesh: continue
    …
```

イメージ：

```text
ufbx_scene
    ├─ node0 (meshなし)
    ├─ node1 (mesh A)
    ├─ node2 (mesh B)
    └─ ...
```

`<span class="editor-theme-code">mesh</span>`<span style="white-space: pre-wrap;"> を持っているノードだけメッシュ展開の対象にします。</span>

---

### 3. スキンウェイト抽出 (infl\_per\_vtx)

メッシュがスキンを持っている場合：

```cpp
infl_per_vtx.resize(mesh->num_vertices);
skin = mesh->skin_deformers.data[0];
clusters = skin->clusters;
vtx_list = skin->vertices;
w_list   = skin->weights;
```

ここでやっていること：

```text
FBXのデータ:
    - skin->vertices  : 各頂点に対して "どの weight が付いているか" の範囲
    - skin->weights   : (cluster_index, weight) の配列
    - clusters        : cluster_index → どのボーン(node)か
```

アルゴリズム（概念）：

```text
for each vertex v:
    acc[v] = 空のリスト

for each "skinned vertex" sv in vtx_list:
    v = sv.vertex（実際の頂点番号）
    for k in その頂点に対応する全weight:
        w = weights[weight_begin + k]
        ci = w.cluster_index
        cl = clusters[ci]
        bone_node = cl->bone_node

        // スケルトン側に登録済みならボーン番号を取得
        bone_index = skeleton_.Data().bone_index_of_[bone_node]

        acc[v].push_back( (bone_index, weight) )
```

図にすると：

```text
+---------------------------+
          | ufbx_skin_deformer (skin) |
          +---------------------------+
                | clusters[]        | vertices[] / weights[]
                v                   v
        [ cluster0 ] → bone_node0   vertex0 → (ci, weight)...
        [ cluster1 ] → bone_node1   vertex1 → ...
                |
                └─ bone_node をキーに
                   skeleton_.Data().bone_index_of_ を引く
```

その後、各頂点について

1. `<span class="editor-theme-code">acc[v]</span>`<span style="white-space: pre-wrap;"> を「weight の大きい順にソート」</span>
2. 上位 4 本に絞る
3. 重みを正規化（合計 ≒ 1.0）
4. `<span class="editor-theme-code">VertexInfluence</span>`<span style="white-space: pre-wrap;"> に詰めて </span>`<span class="editor-theme-code">infl_per_vtx[v]</span>`<span style="white-space: pre-wrap;"> に保存</span>

---

### 4. UV セットの決定

```cpp
const ufbx_vertex_vec2* base_uv = nullptr;
if (mesh->vertex_uv.exists) base_uv = &mesh->vertex_uv;
else if (mesh->uv_sets.count > 0 && mesh->uv_sets.data[0].vertex_uv.exists)
    base_uv = &mesh->uv_sets.data[0].vertex_uv;
```

あとでテクスチャとUVセットの名前を見て、`<span class="editor-theme-code">ResolveUVByName()</span>`<span style="white-space: pre-wrap;"> で差し替えもします。</span>

---

### 5. フェイスをマテリアルごとにグループ化

```cpp
std::unordered_map<uint32_t, std::vector<uint32_t>> faces_by_mat;

for each face fi in mesh->faces:
    mi = (mesh->face_material.count > 0) ? mesh->face_material.data[fi] : 0;
    faces_by_mat[mi].push_back(fi);
```

概念：

```text
faces_by_mat:
    mat_index 0 → [faceID 1, 5, 8, ...]
    mat_index 1 → [faceID 0, 2, 3, ...]
```

これを後で MeshPart ごとに展開します。

---

### 6. マテリアルごとに MeshPart 作成＆頂点展開

```cpp
for each (mat_index, face_list) in faces_by_mat:
    MeshPart part;
    part.start_index = mesh_.indices_.size();

    // node / mesh から ufbx_material* を引く
    if (node->materials.count > mat_index) part.mat = node->materials.data[mat_index];
    else if (mesh->materials.count > mat_index) part.mat = mesh->materials.data[mat_index];
```

UV セットの最終決定：

```cpp
tex_for_uv = GetDiffuseTexture(part.mat);
uvv = base_uv;
if (tex_for_uv && tex_for_uv->uv_set.length > 0)
    uvv = ResolveUVByName(mesh, tex_for_uv->uv_set);
```

その後、`<span class="editor-theme-code">face_list</span>`<span style="white-space: pre-wrap;"> の各フェイスを「扇形分割」で三角形にする：</span>

```cpp
for each face f in face_list:
    indices: f.index_begin ... f.index_begin + f.num_indices - 1

    for k = 0 .. (f.num_indices - 3):
        corners[0] = f.index_begin + 0;
        corners[1] = f.index_begin + (k+1);
        corners[2] = f.index_begin + (k+2);
        → この3つで1三角形
```

各 corner について

- <span style="white-space: pre-wrap;">頂点番号 </span>`<span class="editor-theme-code">vtx = mesh->vertex_indices[corner]</span>`
- <span style="white-space: pre-wrap;">位置 </span>`<span class="editor-theme-code">P</span>`：`<span class="editor-theme-code">mesh->vertex_position</span>`<span style="white-space: pre-wrap;"> から取得</span>
- <span style="white-space: pre-wrap;">法線 </span>`<span class="editor-theme-code">N</span>`：`<span class="editor-theme-code">mesh->vertex_normal</span>`<span style="white-space: pre-wrap;"> から取得＋正規化</span>
- <span style="white-space: pre-wrap;">UV </span>`<span class="editor-theme-code">T</span>`<span style="white-space: pre-wrap;">：決定済み </span>`<span class="editor-theme-code">uvv</span>`<span style="white-space: pre-wrap;"> から取得し、</span>`<span class="editor-theme-code">T.y = 1 - v</span>`

そして

```cpp
VertexPNT2 vtx_out = { P, N, T };

VertexInfluence vi;
if (!infl_per_vtx.empty())
    vi = infl_per_vtx[vtx];

mesh_.influences_.push_back(vi);
mesh_.indices_.push_back( mesh_.vertices_.size() );
mesh_.vertices_.push_back( vtx_out );
```

図示すると：

```text
FBX mesh
    ├─ faces[]
    │    ├─ face0 (n角形)
    │    └─ face1 ...
    ├─ vertex_position
    ├─ vertex_normal
    └─ vertex_uv / uv_sets

      ▼ 展開

mesh_.vertices_  (VertexPNT2)
mesh_.indices_   (三角形リスト)
mesh_.influences_(VertexInfluence)
mesh_.parts_     (マテリアル単位)
```

<span style="white-space: pre-wrap;">最後に </span>`<span class="editor-theme-code">part.index_count</span>`<span style="white-space: pre-wrap;"> を計算し、&gt;0 なら </span>`<span class="editor-theme-code">mesh_.parts_.push_back(part)</span>`。

---

### 7. シーン半径（scene\_radius\_）の計算

展開中に AABB を更新しているので、それを使って「おおよその半径」を求めます。

```cpp
ext = (bb_max - bb_min) * 0.5f;
r = max(|ext.x|, |ext.y|, |ext.z|);
if (r < 1e-3f) r = 1.0f;

skeleton_.Data().scene_radius_ = r;
```

これはボーンのデバッグ描画時の軸の長さ決定に使っています。

---

### 8. バインド頂点とスキニング頂点の準備

```cpp
mesh_.bind_vertices_    = mesh_.vertices_;  // バインドポーズ
mesh_.skinned_vertices_ = mesh_.vertices_;  // 初期値：同じ
```

<span style="white-space: pre-wrap;">描画時の CPU スキニングでは </span>`<span class="editor-theme-code">bind_vertices_</span>`<span style="white-space: pre-wrap;"> を入力として </span>`<span class="editor-theme-code">skinned_vertices_</span>`<span style="white-space: pre-wrap;"> を更新します。</span>

---

## CreateGpuBuffers(): VB/IB の作成

<span style="white-space: pre-wrap;">単純に </span>`<span class="editor-theme-code">mesh_.vertices_</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">mesh_.indices_</span>`<span style="white-space: pre-wrap;"> から </span>`<span class="editor-theme-code">ID3D11Buffer</span>`<span style="white-space: pre-wrap;"> を作成しています。</span>

```text
[CPU] mesh_.vertices_  ----CreateBuffer---->  draw_.vb_
[CPU] mesh_.indices_   ----CreateBuffer---->  draw_.ib_
```

特にスキニング対応のための特殊なことはしておらず、`<span class="editor-theme-code">Usage = DEFAULT</span>`<span style="white-space: pre-wrap;"> の普通のVB/IBです。</span>

---

## CreateEffectsAndTextures(): エフェクト&amp;テクスチャ

ここでやっていること：

1. `<span class="editor-theme-code">CommonStates</span>`<span style="white-space: pre-wrap;"> と </span>`<span class="editor-theme-code">BasicEffect</span>`<span style="white-space: pre-wrap;"> を new</span>
2. BasicEffect にライティング設定を行う
3. BasicEffect から VS バイトコードを取り出し、`<span class="editor-theme-code">VertexPNT2</span>`<span style="white-space: pre-wrap;"> に合わせた InputLayout を作成</span>
4. FBX ファイルのディレクトリを求める
5. `<span class="editor-theme-code">mesh_.parts_</span>`<span style="white-space: pre-wrap;"> を走査して diffuse テクスチャを読む</span>

テクスチャの読み方は二通り：

```text
1) tex->content に埋め込みバイナリがある
    → CreateWICTextureFromMemory()

2) tex->filename にパスがある
    → FBXファイルのディレクトリと結合して CreateWICTextureFromFile()
```

<span style="white-space: pre-wrap;">成功すれば </span>`<span class="editor-theme-code">MeshPart::srv</span>`<span style="white-space: pre-wrap;"> に SRV を保持します。</span>

---

## Draw(world, view, proj): CPUスキニング＋描画

### 1. 前提チェック

VB/IB, BasicEffect, layout, indices が揃っているかをチェック。

---

### 2. CPU スキニング

```cpp
if (!mesh_.influences_.empty() && !mesh_.bind_vertices_.empty()) {
    skeleton_.SkinMatrices().resize(skeleton_.Bones().size());

    for i in 0 .. Bones()-1:
        W   = CurrWorld()[i]     // 現在のボーン姿勢 (ワールド)
        G2B = geom_bind_world    // ジオメトリ→ボーン
        skin_mats[i] = G2B * W   // スキン行列
```

スキン行列ののち、`<span class="editor-theme-code">ApplySkinCPU()</span>`<span style="white-space: pre-wrap;"> を呼び出し：</span>

```text
入力:
    skin_mats        : ボーン数
    mesh_.influences_: 頂点数
    mesh_.bind_vertices_: 頂点数

出力:
    mesh_.skinned_vertices_: 頂点数 (更新される)
```

中身は

```text
for each vertex v:
    P = 0, N = 0
    for k=0..3:
        if weight[k] > 0:
            B = skin_mats[bone[k]]
            P += (B * bind_pos) * weight
            N += (B * bind_nrm) * weight
    if any:
        正規化した N と P を skinned_vertices_[v] に書き戻し
    else:
        bind_vertices_[v] をそのままコピー
```

最後に

```cpp
ctx->UpdateSubresource(draw_.vb_.Get(), 0, nullptr,
                       &mesh_.skinned_vertices_[0], 0, 0);
```

で GPU のVBを更新。

---

### 3. IA &amp; ラスタ設定 → MeshPartごとに描画

```cpp
ctx->IASetInputLayout(draw_.layout_.Get());
ctx->IASetVertexBuffers(..., draw_.vb_.Get(), ...);
ctx->IASetIndexBuffer(draw_.ib_.Get(), DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(TRIANGLELIST);

ctx->OMSetDepthStencilState(DepthDefault);
ctx->RSSetState(CullCounterClockwise);
ctx->PSSetSamplers(0, 1, LinearClamp);
```

行列設定：

```cpp
fx->SetWorld(world);
fx->SetView(view);
fx->SetProjection(proj);
```

ブレンドステートは

```text
テクスチャあり    → NonPremultiplied
テクスチャなし    → Opaque
```

を使い分けています。

最後に

```cpp
for each MeshPart part in mesh_.parts_:
    has_tex = (part.srv != nullptr)
    fx->SetTextureEnabled(has_tex);
    if (has_tex) fx->SetTexture(part.srv.Get());
    fx->Apply(ctx);
    ctx->DrawIndexed(part.index_count, part.start_index, 0);
```

---

## まとめ（要点）

- `<span class="editor-theme-code">ExpandAllNodes()</span>`<span style="white-space: pre-wrap;"> で「FBX → 自前メッシュ構造」へ変換</span>
    - 頂点 (`<span class="editor-theme-code">VertexPNT2</span>`)、インデックス、MeshPart、ボーンウェイト (`<span class="editor-theme-code">VertexInfluence</span>`)、AABB を作成
- `<span class="editor-theme-code">CreateGpuBuffers()</span>`<span style="white-space: pre-wrap;"> で VB/IB を確保（この段階ではバインドポーズ形状）</span>
- `<span class="editor-theme-code">CreateEffectsAndTextures()</span>`<span style="white-space: pre-wrap;"> で BasicEffect と テクスチャ（SRV）を用意</span>
- `<span class="editor-theme-code">Draw()</span>`<span style="white-space: pre-wrap;"> で</span>
    - `<span class="editor-theme-code">FbxSkeleton</span>`<span style="white-space: pre-wrap;"> からボーン姿勢をもらい、スキン行列を計算</span>
    - <span style="white-space: pre-wrap;">CPUスキニングで </span>`<span class="editor-theme-code">bind_vertices_ → skinned_vertices_</span>`<span style="white-space: pre-wrap;"> に変換</span>
    - VB を UpdateSubresource → MeshPartごとに BasicEffect で描画

<span style="white-space: pre-wrap;">この構造が、今後 </span>`<span class="editor-theme-code">FbxMesh</span>`<span style="white-space: pre-wrap;"> に切り出すときの「設計単位」になります。</span>  
`<span class="editor-theme-code">ExpandAllNodes / CreateGpuBuffers / CreateEffectsAndTextures / Draw</span>`<span style="white-space: pre-wrap;"> あたりがそのまま </span>`<span class="editor-theme-code">FbxMesh</span>`<span style="white-space: pre-wrap;"> に移るイメージです。</span>