# 帳票設計書 2-OBJExporter

## 概要

本ドキュメントは、Three.jsライブラリにおけるOBJExporterの帳票設計書である。OBJExporterは3DオブジェクトをWavefront OBJ形式でエクスポートするためのモジュールであり、頂点位置、法線、UV座標をテキスト形式で出力する。

### 本帳票の処理概要

OBJExporterは、Three.jsのMesh、Line、Points等の3DオブジェクトをシンプルなテキストベースのOBJ形式に変換してエクスポートする機能を提供する。複数のオブジェクトは単一のメッシュにマージされて出力される。

**業務上の目的・背景**：OBJ形式は1980年代から存在する最も広くサポートされた3Dファイルフォーマットの一つであり、ほぼすべての3Dソフトウェアでインポート可能である。シンプルなジオメトリデータの交換や、レガシーシステムとの互換性を確保するために本エクスポーターが必要となる。テキスト形式であるため、デバッグやデータ検証も容易である。

**帳票の利用シーン**：3Dモデリングソフトウェア（Blender、Maya、3ds Max等）へのデータ移行、3Dプリンタ用スライサーソフトへの入力、CADシステムとのデータ交換、教育目的での3Dデータフォーマット学習時に利用される。

**主要な出力内容**：
1. 頂点位置データ（v x y z形式）
2. テクスチャ座標（vt u v形式）
3. 法線ベクトル（vn x y z形式）
4. 面定義（f v/vt/vn形式）
5. ライン定義（l v1 v2...形式）
6. ポイント定義（p v1 v2...形式）
7. オブジェクト名（o name形式）
8. マテリアル参照（usemtl name形式）

**帳票の出力タイミング**：ユーザーがエクスポート操作を実行した時点でparseメソッドを呼び出すことで即座に文字列として出力される。同期処理のため、大規模データでは処理完了まで待機が必要となる。

**帳票の利用者**：3Dコンテンツ制作者、ゲーム開発者、CAD/CAMエンジニア、3Dプリント技術者、教育機関の学生・教員が主な利用者である。

## 帳票種別

3Dモデルデータ出力（テキストファイル）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | Three.js アプリケーション | N/A（ライブラリ使用） | OBJExporter.parse() メソッド呼び出し |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | Wavefront OBJ (.obj) |
| 用紙サイズ | N/A（テキストファイル） |
| 向き | N/A |
| ファイル名 | 任意（アプリケーション側で指定） |
| 出力方法 | 文字列（string）として返却 |
| 文字コード | UTF-8（ASCII互換） |

### OBJ固有設定

| 項目 | 内容 |
|-----|------|
| MTLファイル出力 | 非対応（ジオメトリデータのみ） |
| 座標系 | 右手系（Three.jsデフォルト） |
| スケール | ワールド座標系に変換済み |

## 帳票レイアウト

### レイアウト概要

OBJファイルは行指向のテキストフォーマットで、各行がコマンドとパラメータで構成される。

```
┌─────────────────────────────────────┐
│          オブジェクト定義            │
│    (o objectName)                   │
├─────────────────────────────────────┤
│        マテリアル参照（任意）          │
│    (usemtl materialName)            │
├─────────────────────────────────────┤
│           頂点データ                 │
│    (v x y z)                        │
├─────────────────────────────────────┤
│         テクスチャ座標               │
│    (vt u v)                         │
├─────────────────────────────────────┤
│            法線                     │
│    (vn x y z)                       │
├─────────────────────────────────────┤
│            面定義                   │
│    (f v/vt/vn v/vt/vn v/vt/vn)     │
└─────────────────────────────────────┘
```

### ヘッダー部

OBJフォーマットには明示的なヘッダーは存在しない。ファイルはオブジェクト定義から開始する。

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | o | オブジェクト名 | mesh.name | o {name} |
| 2 | usemtl | マテリアル参照 | mesh.material.name | usemtl {name} |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | v | 頂点位置 | geometry.getAttribute('position') | v {x} {y} {z} | 可変 |
| 2 | vt | テクスチャ座標 | geometry.getAttribute('uv') | vt {u} {v} | 可変 |
| 3 | vn | 法線ベクトル | geometry.getAttribute('normal') | vn {x} {y} {z} | 可変 |
| 4 | f | 面定義 | geometry.getIndex() または連番 | f {v/vt/vn} ... | 可変 |
| 5 | l | ライン定義 | Line オブジェクト | l {v1} {v2} ... | 可変 |
| 6 | p | ポイント定義 | Points オブジェクト | p {v1} {v2} ... | 可変 |

### フッター部

OBJフォーマットには明示的なフッターは存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| object | エクスポート対象のObject3D | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | traverse順 | 深さ優先 |
| 2 | 頂点インデックス | 昇順 |

### 改ページ条件

N/A（単一ファイル出力）

## データベース参照仕様

### 参照テーブル一覧

本エクスポーターはデータベースを使用せず、Three.jsオブジェクトのメモリ上のデータを直接参照する。

| データソース | 用途 | 参照方法 |
|-------------|------|---------|
| Object3D | オブジェクト走査 | traverse メソッド |
| Mesh | メッシュデータ | isMesh プロパティで判定 |
| Line | ラインデータ | isLine プロパティで判定 |
| Points | ポイントデータ | isPoints プロパティで判定 |
| BufferGeometry | ジオメトリデータ | geometry プロパティ |

### テーブル別参照項目詳細

#### BufferGeometry（Mesh用）

| 参照項目（属性名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| position | v（頂点位置） | 必須 | fromBufferAttribute で取得 |
| normal | vn（法線） | 存在時 | ワールド空間に変換 |
| uv | vt（テクスチャ座標） | 存在時 | そのまま出力 |
| index | f（面定義） | 存在時 | null時は連番 |

#### BufferGeometry（Points用）

| 参照項目（属性名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| position | v（頂点位置） | 必須 | ワールド空間に変換 |
| color | v（頂点色） | 存在時 | sRGB変換後、頂点行に追記 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| ワールド頂点位置 | vertex.applyMatrix4(mesh.matrixWorld) | なし | ローカル→ワールド変換 |
| ワールド法線 | normal.applyMatrix3(normalMatrixWorld).normalize() | なし | 法線行列で変換後正規化 |
| 頂点カラー(sRGB) | ColorManagement.workingToColorSpace(color, SRGBColorSpace) | なし | 作業色空間からsRGBへ |
| 面インデックス | indexVertex + indices.getX(i) + 1 | なし | OBJは1始まりインデックス |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[parse呼び出し] --> B[変数初期化]
    B --> C[object.traverse]
    C --> D{オブジェクト種別}
    D -->|isMesh| E[parseMesh]
    D -->|isLine| F[parseLine]
    D -->|isPoints| G[parsePoints]
    E --> H[頂点出力 v]
    H --> I[UV出力 vt]
    I --> J[法線出力 vn]
    J --> K[面出力 f]
    F --> L[頂点出力 v]
    L --> M[ライン出力 l]
    G --> N[頂点出力 v]
    N --> O[カラー付加]
    O --> P[ポイント出力 p]
    K --> Q[インデックス更新]
    M --> Q
    P --> Q
    Q --> C
    C --> R[文字列返却]
    R --> S[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ジオメトリなし | position属性が未定義 | - | 該当オブジェクトをスキップ |
| インデックスなし | geometry.getIndex()がnull | - | 連番インデックスで代替 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 頂点数: 〜数十万 |
| 目標出力時間 | 同期処理のため大規模データでは数秒かかる場合あり |
| 同時出力数上限 | 1（同期処理） |

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

- テキスト形式のため、出力内容は容易に検証可能
- オブジェクト名やマテリアル名がそのまま出力されるため、機密情報を含まないよう注意
- 出力ファイルへのアクセス制御はアプリケーション側の責務

## 備考

- MTLファイル（マテリアル定義）は非対応。マテリアル名の参照（usemtl）のみ出力
- 複数のオブジェクトは単一ファイル内に順次出力される
- テクスチャ、アニメーション、スキニング情報は出力されない
- 頂点カラーはPoints出力時のみ対応（v行に追記）

---

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

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

### 推奨読解順序

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

OBJExporterはシンプルな構造のため、出力フォーマットを先に理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OBJExporter.js | `examples/jsm/exporters/OBJExporter.js` | 1-8行目: インポートされるThree.jsクラスを確認 |

**読解のコツ**: Vector3, Vector2, Matrix3, Colorの役割を理解しておくと、座標変換の処理が理解しやすい。

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

parseメソッドの全体構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | OBJExporter.js | `examples/jsm/exporters/OBJExporter.js` | 22-32行目: OBJExporterクラスとparseメソッドのシグネチャ |

**主要処理フロー**:
1. **34-45行**: 出力文字列とインデックスカウンタの初期化
2. **280-300行**: object.traverseでオブジェクト走査、種別に応じた処理関数呼び出し
3. **302行**: 結合された出力文字列を返却

#### Step 3: parseMesh関数を理解する

メッシュ出力の核心部分を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | OBJExporter.js | `examples/jsm/exporters/OBJExporter.js` | 47-169行目: parseMesh関数の実装 |

**主要処理フロー**:
- **53-61行**: ジオメトリから属性を取得（position, normal, uv, index）
- **64-70行**: オブジェクト名とマテリアル名を出力
- **75-88行**: 頂点位置をワールド座標に変換して出力
- **93-103行**: UV座標を出力
- **108-124行**: 法線をワールド空間に変換して出力
- **128-162行**: 面定義（f行）を出力
- **165-167行**: インデックスカウンタを更新

#### Step 4: parseLine関数とparsePoints関数を理解する

ラインとポイントの出力処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | OBJExporter.js | `examples/jsm/exporters/OBJExporter.js` | 171-227行目: parseLine関数 |
| 4-2 | OBJExporter.js | `examples/jsm/exporters/OBJExporter.js` | 229-278行目: parsePoints関数 |

**主要処理フロー**:
- **200-212行**: Line種別によりl行の出力形式を変更（Line vs LineSegments）
- **249-257行**: Points用頂点カラーのsRGB変換と出力

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

```
OBJExporter.parse(object)
    │
    ├─ 変数初期化
    │      ├─ output = ''
    │      ├─ indexVertex = 0
    │      ├─ indexVertexUvs = 0
    │      └─ indexNormals = 0
    │
    └─ object.traverse(child)
           │
           ├─ child.isMesh === true
           │      └─ parseMesh(child)
           │             ├─ v 出力（頂点位置）
           │             ├─ vt 出力（UV座標）
           │             ├─ vn 出力（法線）
           │             └─ f 出力（面定義）
           │
           ├─ child.isLine === true
           │      └─ parseLine(child)
           │             ├─ v 出力（頂点位置）
           │             └─ l 出力（ライン定義）
           │
           └─ child.isPoints === true
                  └─ parsePoints(child)
                         ├─ v 出力（頂点位置+カラー）
                         └─ p 出力（ポイント定義）
```

### データフロー図

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

Object3D ───────────▶ traverse() ──────────────▶ 再帰走査
      │                                               │
      ▼                                               ▼
   Mesh ────────────▶ parseMesh() ─────────────▶ o {name}
      │                     │                   usemtl {mat}
      │                     │                   v x y z
      │                     │                   vt u v
      │                     │                   vn x y z
      │                     │                   f v/vt/vn
      ▼                     ▼
geometry.position ─▶ applyMatrix4() ───────────▶ v 行
geometry.uv ───────▶ そのまま ─────────────────▶ vt 行
geometry.normal ───▶ applyMatrix3().normalize() ▶ vn 行
geometry.index ────▶ +1（1始まり変換）─────────▶ f 行
      │
      ▼
 Line/Points ───────▶ parseLine/parsePoints() ─▶ l/p 行
      │
      ▼
 output文字列 ─────────────────────────────────▶ 返却値
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| OBJExporter.js | `examples/jsm/exporters/OBJExporter.js` | ソース | メインエクスポーター実装 |
| three.module.js | `build/three.module.js` | ソース | Three.jsコアライブラリ（Vector3, Matrix3, Color等） |
