# 機能設計書 77-BatchedMesh

## 概要

本ドキュメントは、Three.jsライブラリにおけるBatchedMesh（バッチメッシュ）機能の設計を記述する。BatchedMeshは、複数の異なるジオメトリを単一のドローコールでレンダリングするマルチドローバッチ機能を提供し、InstancedMeshよりも柔軟性の高いGPU最適化を実現する。

### 本機能の処理概要

BatchedMeshは、異なるジオメトリを持つ複数のインスタンスを単一のメッシュにまとめ、WebGLのマルチドロー拡張機能を活用して効率的にレンダリングする。InstancedMeshが同一ジオメトリに限定されるのに対し、BatchedMeshは異なるジオメトリ（立方体、球、円柱など）を混在させることができる。

**業務上の目的・背景**：ゲームや建築ビジュアライゼーションでは、多種多様なオブジェクトを大量に描画する必要がある。従来のMeshでは各オブジェクトがドローコールを発生させ、InstancedMeshは同一ジオメトリに限定される。BatchedMeshは、異なるジオメトリを単一の結合ジオメトリにまとめ、マルチドロー機能で一括描画することで、ドローコールのオーバーヘッドを大幅に削減する。

**機能の利用シーン**：都市シミュレーション（建物、車、人など多様なオブジェクト）、ゲームの背景オブジェクト、家具や装飾品が多い室内シーン、複数種類のプリミティブを含むシーン。

**主要な処理内容**：
1. 複数ジオメトリの統合管理（addGeometry/setGeometryAt）
2. ジオメトリに対するインスタンスの作成（addInstance）
3. インスタンスごとの変換行列・色・可視性の管理
4. オブジェクト単位の視錐台カリング（perObjectFrustumCulled）
5. 透明度に基づくソート（sortObjects）
6. ジオメトリの動的追加・削除・最適化

**関連システム・外部連携**：WebGLRenderer/WebGPURenderer（マルチドロー機能）、DataTexture（行列・色・インダイレクト用）、FrustumArray（マルチビューカリング用）。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに登録なし |

## 機能種別

3Dオブジェクト生成 / マルチドローバッチ描画

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| maxInstanceCount | number | Yes | 最大インスタンス数 | 正の整数 |
| maxVertexCount | number | Yes | 全ジオメトリの最大頂点数 | 正の整数 |
| maxIndexCount | number | No | 全ジオメトリの最大インデックス数（デフォルト: maxVertexCount*2） | 正の整数 |
| material | Material/Array<Material> | No | バッチメッシュのマテリアル | Material型または配列 |

### 入力データソース

JavaScriptコードからのコンストラクタ呼び出し。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| isBatchedMesh | boolean | 型識別フラグ（常にtrue） |
| perObjectFrustumCulled | boolean | オブジェクト単位カリング（デフォルト: true） |
| sortObjects | boolean | ソート有効化（デフォルト: true） |
| maxInstanceCount | number | 最大インスタンス数 |
| instanceCount | number | 現在のインスタンス数 |
| boundingBox | Box3 | バッチ全体の境界ボックス |
| boundingSphere | Sphere | バッチ全体の境界球 |

### 出力先

3Dシーングラフ、WebGLレンダリングパイプライン（マルチドロー）

## 処理フロー

### 処理シーケンス

```
1. コンストラクタ呼び出し
   └─ Meshの初期化（空のBufferGeometry）
   └─ マトリックステクスチャ、インダイレクトテクスチャの初期化
2. addGeometry() - ジオメトリの追加
   └─ 頂点・インデックスバッファの結合
   └─ ジオメトリIDの返却
3. addInstance() - インスタンスの追加
   └─ ジオメトリIDを参照
   └─ インスタンスIDの返却
4. setMatrixAt/setColorAt - インスタンス属性の設定
5. onBeforeRender() - レンダリング前処理
   └─ 視錐台カリング
   └─ ソート
   └─ マルチドロー配列の構築
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[Meshコンストラクタ]
    B --> C[isBatchedMesh = true]
    C --> D[テクスチャ初期化]
    D --> E[終了]

    F[addGeometry] --> G[ジオメトリ初期化/検証]
    G --> H[頂点データコピー]
    H --> I[インデックスデータコピー]
    I --> J[geometryId返却]

    K[addInstance] --> L{容量チェック}
    L -->|OK| M[インスタンス情報登録]
    L -->|超過| N[エラー]
    M --> O[instanceId返却]

    P[onBeforeRender] --> Q{カリング/ソート必要?}
    Q -->|Yes| R[視錐台カリング]
    R --> S[深度ソート]
    S --> T[マルチドロー配列構築]
    Q -->|No| U[スキップ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-77-1 | ジオメトリ整合性 | 追加するジオメトリは同じ属性構造を持つ必要がある | addGeometry時 |
| BR-77-2 | インデックス一貫性 | すべてのジオメトリがインデックス付きか非インデックスかで統一 | addGeometry時 |
| BR-77-3 | 負スケール非対応 | setMatrixAtに負のスケールを含む行列は非対応 | 常時 |
| BR-77-4 | 容量超過エラー | maxVertexCount/maxIndexCountを超えるとエラー | addGeometry時 |

### 計算ロジック

**視錐台カリング（onBeforeRender内）**：
```javascript
for each instance:
    if (visible && active):
        matrix = getMatrixAt(instanceId)
        sphere = getBoundingSphereAt(geometryId).applyMatrix4(matrix)
        if (frustum.intersectsSphere(sphere)):
            multiDrawStarts[count] = geometryInfo.start
            multiDrawCounts[count] = geometryInfo.count
            indirectArray[count] = instanceId
            count++
```

## データベース操作仕様

データベース操作なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | 最大インスタンス数超過 | maxInstanceCountを増加 |
| - | Error | 頂点/インデックス容量超過 | maxVertexCount/maxIndexCountを増加 |
| - | Error | ジオメトリ属性の不整合 | 同じ属性構造のジオメトリを使用 |
| - | Error | 無効なinstanceId/geometryId | validate関数でチェック |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- マルチドロー機能により単一ドローコールで複数ジオメトリを描画
- perObjectFrustumCulled=trueで不可視オブジェクトをスキップ
- sortObjects=trueで透明度ソートを自動実行
- optimize()でメモリの断片化を解消

## セキュリティ考慮事項

特になし。

## 備考

- BatchedMeshはWebGLのマルチドロー拡張に依存
- customSortでカスタムソート関数を設定可能
- deleteGeometry()はジオメトリを参照する全インスタンスも削除

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

BatchedMeshの内部データ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DataTexture.js | `src/textures/DataTexture.js` | 行列・色・インダイレクト用テクスチャ |
| 1-2 | BufferGeometry.js | `src/core/BufferGeometry.js` | 結合ジオメトリ |

**読解のコツ**: BatchedMeshは複数のDataTextureで行列、色、インダイレクトインデックスを管理する。

#### Step 2: エントリーポイントを理解する

BatchedMeshクラスの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | BatchedMesh.js | `src/objects/BatchedMesh.js` | クラス全体の構造（182-285行目） |

**主要処理フロー**:
1. **192-194行目**: コンストラクタでMeshを初期化
2. **203行目**: isBatchedMeshフラグの設定
2. **211行目**: perObjectFrustumCulledの設定（デフォルトtrue）
3. **220行目**: sortObjectsの設定（デフォルトtrue）
4. **282-283行目**: テクスチャの初期化

#### Step 3: ジオメトリ操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | BatchedMesh.js | `src/objects/BatchedMesh.js` | addGeometry()（627-703行目） |
| 3-2 | BatchedMesh.js | `src/objects/BatchedMesh.js` | setGeometryAt()（714-817行目） |
| 3-3 | BatchedMesh.js | `src/objects/BatchedMesh.js` | deleteGeometry()（826-853行目） |

**主要処理フロー**:
- **629-631行目**: ジオメトリの初期化と検証
- **740-767行目**: 頂点属性のコピー
- **770-793行目**: インデックスのコピー

#### Step 4: レンダリング前処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | BatchedMesh.js | `src/objects/BatchedMesh.js` | onBeforeRender()（1503-1645行目） |

**主要処理フロー**:
- **1507-1511行目**: 変更なし・カリング/ソート不要時の早期リターン
- **1526-1540行目**: 視錐台の準備
- **1543-1579行目**: ソート有効時の処理
- **1606-1637行目**: ソート無効時の処理

### プログラム呼び出し階層図

```
BatchedMesh
    │
    ├─ extends Mesh
    │      └─ extends Object3D
    │
    ├─ _matricesTexture (DataTexture)
    │      └─ Float32Array (4x4行列/インスタンス)
    │
    ├─ _indirectTexture (DataTexture)
    │      └─ Uint32Array (インスタンスインデックス)
    │
    ├─ _colorsTexture (DataTexture, optional)
    │      └─ Float32Array (RGBA/インスタンス)
    │
    ├─ _geometryInfo[] - ジオメトリ情報配列
    │      └─ vertexStart, vertexCount, indexStart, indexCount, ...
    │
    └─ _instanceInfo[] - インスタンス情報配列
           └─ visible, active, geometryIndex

BatchedMesh.onBeforeRender()
    │
    ├─ _frustum.setFromProjectionMatrix()
    ├─ for each instance
    │      ├─ getMatrixAt()
    │      ├─ getBoundingSphereAt()
    │      └─ frustum.intersectsSphere()
    └─ multiDrawStarts/multiDrawCounts 構築
```

### データフロー図

```
[入力]                         [処理]                           [出力]

BufferGeometry ───────────────▶ addGeometry() ─────────────────▶ geometryId
                               │
                               ├─ 属性コピー
                               └─ インデックスコピー

geometryId ───────────────────▶ addInstance() ─────────────────▶ instanceId

Matrix4 ──────────────────────▶ setMatrixAt() ─────────────────▶ _matricesTexture

onBeforeRender()
_instanceInfo[] ──────────────▶ 視錐台カリング ────────────────▶ _multiDrawStarts[]
_geometryInfo[] ──────────────▶ ソート ────────────────────────▶ _multiDrawCounts[]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| BatchedMesh.js | `src/objects/BatchedMesh.js` | ソース | バッチメッシュクラス |
| Mesh.js | `src/objects/Mesh.js` | ソース | 基底クラス |
| DataTexture.js | `src/textures/DataTexture.js` | ソース | 行列・色・インダイレクト用テクスチャ |
| BufferGeometry.js | `src/core/BufferGeometry.js` | ソース | 結合ジオメトリ |
| BufferAttribute.js | `src/core/BufferAttribute.js` | ソース | 頂点属性 |
| Frustum.js | `src/math/Frustum.js` | ソース | 視錐台カリング |
| FrustumArray.js | `src/math/FrustumArray.js` | ソース | マルチビューカリング |
| Box3.js | `src/math/Box3.js` | ソース | 境界ボックス |
| Sphere.js | `src/math/Sphere.js` | ソース | 境界球 |
