# 帳票設計書 6-USDZExporter

## 概要

本ドキュメントは、Three.jsライブラリにおけるUSDZExporterの帳票設計書である。USDZExporterは3DシーンをUSDZ形式でエクスポートするためのモジュールであり、Apple QuickLook対応のAR用3Dアセット出力に対応する。

### 本帳票の処理概要

USDZExporterは、Three.jsのシーン、メッシュ、マテリアル、テクスチャをUSDZ形式（ZIP圧縮されたUSD）に変換してエクスポートする機能を提供する。USDZ形式はAppleのAR QuickLook機能で直接プレビュー可能なフォーマットである。

**業務上の目的・背景**：USDZ形式はAppleがiOS/macOSのAR体験のために採用した3Dフォーマットであり、Safari、メッセージ、メール等のネイティブアプリで直接3Dプレビューが可能。eコマースでの商品AR表示、教育用インタラクティブコンテンツ、不動産や家具のバーチャル配置など、Apple製品ユーザー向けのAR体験提供に必要となる。

**帳票の利用シーン**：eコマースサイトでの商品3D/AR表示、教育アプリでの3Dモデル配布、不動産・インテリア業界でのAR家具配置、マーケティング用インタラクティブコンテンツ作成時に利用される。

**主要な出力内容**：
1. USDZ（ZIP圧縮コンテナ）
2. model.usda（USD ASCIIファイル、シーン構造定義）
3. ジオメトリファイル（geometries/Geometry_N.usda）
4. テクスチャ画像（textures/Texture_N.png）
5. マテリアル定義（UsdPreviewSurface）
6. AR配置設定（アンカリングタイプ、平面アライメント）

**帳票の出力タイミング**：ユーザーがエクスポート操作を実行した時点でparseAsyncメソッドを呼び出すことで非同期に出力される。テクスチャ処理等があるため必ず非同期処理となる。

**帳票の利用者**：eコマース開発者、ARアプリ開発者、マーケティング担当者、インテリアデザイナー、教育コンテンツ制作者が主な利用者である。

## 帳票種別

3Dモデルデータ出力（ZIP圧縮アーカイブ）

## 利用画面

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

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | USDZ（ZIP圧縮されたUSD） |
| 用紙サイズ | N/A（アーカイブファイル） |
| 向き | N/A |
| ファイル名 | 任意（アプリケーション側で指定） |
| 出力方法 | ArrayBuffer として返却 |
| 文字コード | UTF-8（USD ASCII） |

### USDZ固有設定

| 項目 | 内容 |
|-----|------|
| maxTextureSize | テクスチャの最大サイズ（デフォルト: 1024） |
| includeAnchoringProperties | AR配置プロパティを含めるか（デフォルト: true） |
| onlyVisible | 可視オブジェクトのみ出力（デフォルト: true） |
| ar.anchoring.type | アンカリングタイプ（デフォルト: 'plane'） |
| ar.planeAnchoring.alignment | 平面アライメント（デフォルト: 'horizontal'） |
| quickLookCompatible | QuickLookバグ対応モード（デフォルト: false） |

## 帳票レイアウト

### レイアウト概要

USDZファイルはZIPアーカイブで、内部にUSDA（ASCII形式USD）ファイルとテクスチャ画像を含む。

```
┌─────────────────────────────────────┐
│           USDZアーカイブ             │
│  ┌───────────────────────────────┐  │
│  │ model.usda（メインシーン）      │  │
│  │   - Root Xform               │  │
│  │   - Scenes Scope             │  │
│  │   - Scene Xform              │  │
│  │   - メッシュ参照               │  │
│  │   - Materials定義            │  │
│  └───────────────────────────────┘  │
│  ┌───────────────────────────────┐  │
│  │ geometries/Geometry_N.usda   │  │
│  │   - Mesh定義                 │  │
│  │   - 頂点位置、法線、UV         │  │
│  └───────────────────────────────┘  │
│  ┌───────────────────────────────┐  │
│  │ textures/Texture_N.png       │  │
│  │   - テクスチャ画像            │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘
```

### ヘッダー部（model.usda）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | #usda 1.0 | USDAバージョン | 固定値 | "#usda 1.0" |
| 2 | customLayerData | メタデータ | 固定値 | creator = "Three.js USDZExporter" |
| 3 | defaultPrim | デフォルトプリミティブ | 固定値 | "Root" |
| 4 | metersPerUnit | 単位 | 固定値 | 1 |
| 5 | upAxis | 上軸 | 固定値 | "Y" |

### 明細部（シーン構造）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | def Xform "Root" | ルートノード | 固定 | USDノード定義 |
| 2 | def Scope "Scenes" | シーンライブラリ | 固定 | kind = "sceneLibrary" |
| 3 | def Xform "Scene" | シーンノード | Scene | AR設定含む |
| 4 | def Xform "MeshName" | メッシュ参照 | Mesh | ジオメトリファイル参照 |
| 5 | def "Materials" | マテリアル群 | Material | UsdPreviewSurface |

### フッター部

USDZフォーマットには明示的なフッターは存在しない（ZIP構造の終端がフッター）。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| scene | エクスポート対象のObject3D | Yes |
| options | エクスポートオプション | No |
| MeshStandardMaterial | 対応マテリアル | メッシュ出力時 |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | シーン階層順 | 深さ優先 |
| 2 | 子オブジェクト順 | 配列インデックス順 |

### 改ページ条件

N/A（単一アーカイブ出力）

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

### 参照テーブル一覧

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

| データソース | 用途 | 参照方法 |
|-------------|------|---------|
| Object3D | シーン階層 | children配列 |
| Mesh | メッシュデータ | isMesh プロパティで判定 |
| Camera | カメラデータ | isCamera プロパティで判定 |
| MeshStandardMaterial | マテリアル | isMeshStandardMaterial で判定 |
| Texture | テクスチャ | map等のプロパティ |

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

#### BufferGeometry

| 参照項目（属性名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| position | point3f[] points | 必須 | ワールド座標変換なし |
| normal | normal3f[] normals | 存在時 | 必須属性 |
| uv, uv2, uv3 | texCoord2f[] primvars:st* | 存在時 | 複数UVチャンネル対応 |
| color | color3f[] primvars:displayColor | 存在時 | 頂点カラー |
| index | int[] faceVertexIndices | 存在時 | 三角形インデックス |

#### MeshStandardMaterial

| 参照項目（プロパティ） | 帳票項目との対応 | 取得条件 | 備考 |
|----------------------|----------------|---------|------|
| color | diffuseColor | 必須 | RGB |
| map | Texture_diffuse | 存在時 | PNG出力 |
| emissive | emissiveColor | 存在時 | RGB |
| emissiveMap | Texture_emissive | 存在時 | PNG出力 |
| normalMap | Texture_normal | 存在時 | PNG出力 |
| aoMap | inputs:occlusion | 存在時 | PNG出力 |
| roughness | inputs:roughness | 必須 | 0-1 |
| roughnessMap | Texture_roughness | 存在時 | Gチャンネル |
| metalness | inputs:metallic | 必須 | 0-1 |
| metalnessMap | Texture_metallic | 存在時 | Bチャンネル |
| opacity | inputs:opacity | 必須 | 0-1 |
| alphaMap | Texture_opacity | 存在時 | Rチャンネル |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| 変換行列 | matrix4d xformOp:transform | なし | 4x4行列 |
| テクスチャスケール | texture.repeat | なし | float2 inputs:scale |
| テクスチャオフセット | texture.offset + 回転補正 | なし | float2 inputs:translation |
| テクスチャ回転 | texture.rotation * (180/PI) | なし | float inputs:rotation (度) |
| UV反転 | 1 - y | なし | Three.js→USD座標系変換 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[parseAsync呼び出し] --> B[オプションマージ]
    B --> C[ルートノード生成]
    C --> D[Scenes/Sceneノード生成]
    D --> E[buildHierarchy]
    E --> F{オブジェクト種別}
    F -->|Mesh + MeshStandardMaterial| G[ジオメトリファイル生成]
    F -->|Camera| H[buildCamera]
    F -->|その他| I[buildXform]
    G --> J[buildMesh]
    J --> K[materials辞書に追加]
    H --> L[childNode生成]
    I --> L
    K --> L
    L --> M[再帰処理]
    M --> E
    E --> N[buildMaterials]
    N --> O[テクスチャ処理]
    O --> P{圧縮テクスチャ?}
    P -->|Yes| Q[textureUtils.decompress]
    P -->|No| R[imageToCanvas]
    Q --> R
    R --> S[PNG変換]
    S --> T[files辞書に追加]
    T --> U[64バイトアライメント]
    U --> V[zipSync]
    V --> W[ArrayBuffer返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 非対応マテリアル | MeshStandardMaterial以外 | "Unsupported material type (USDZ only supports MeshStandardMaterial)" | console.warn、スキップ |
| 負のスケール | matrix.determinant() < 0 | "USDZ does not support negative scales" | console.warn、出力継続 |
| 両面マテリアル | DoubleSide | "USDZ does not support double sided materials" | console.warn、出力継続 |
| 圧縮テクスチャ | textureUtils未設定 | "setTextureUtils() must be called to process compressed textures" | Error throw |
| 無効画像 | 画像データ取得不可 | "No valid image data found. Unable to process texture" | Error throw |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | メッシュ数: 〜100、テクスチャ数: 〜50 |
| 目標出力時間 | テクスチャ数・サイズ依存（数秒〜数十秒） |
| 同時出力数上限 | 1（非同期処理だが同時実行は非推奨） |

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

- テクスチャ画像はPNG形式で再エンコードされて出力される
- 圧縮テクスチャ処理にはtextureUtilsの事前設定が必要
- 出力ファイルへのアクセス制御はアプリケーション側の責務

## 備考

- MeshStandardMaterial（PBRマテリアル）のみ対応
- MeshPhysicalMaterialはclearcoat、clearcoatRoughness、iorプロパティも出力
- アニメーション、スキニングは非対応
- Apple Feedback FB10036297, FB11442287のQuickLookバグ対応オプションあり
- 64バイトアライメントはfflateライブラリの要件

---

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

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

### 推奨読解順序

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

USD/USDZフォーマットとUSDNodeクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | 12-125行目: USDNodeクラスの定義 |

**読解のコツ**: USDNodeは階層的なノードツリーを構築し、toString()でUSDA形式の文字列を生成する。

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

USDZExporterクラスとparseAsyncメソッドの全体構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | 137-167行目: USDZExporterクラスとsetTextureUtils |
| 2-2 | USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | 190-328行目: parseAsyncメソッド |

**主要処理フロー**:
1. **192-204行**: デフォルトオプションのマージ
2. **206-237行**: ルートノード、Scenes、Sceneノードの構築
3. **244行**: buildHierarchyでシーン走査
4. **246-257行**: マテリアルノード構築とUSDA生成
5. **262-295行**: テクスチャ処理（圧縮解除、PNG変換）
6. **300-322行**: 64バイトアライメントとZIP圧縮

#### Step 3: シーン階層構築を理解する

buildHierarchyとbuildXform関数を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | 430-498行目: buildHierarchy関数 |
| 3-2 | USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | 501-541行目: buildXform関数 |

**主要処理フロー**:
- **436行**: onlyVisible判定
- **440-478行**: Mesh処理（ジオメトリファイル生成、マテリアル登録）
- **480-482行**: Camera処理
- **501-541行**: Xform（変換ノード）生成、行列またはTRS

#### Step 4: マテリアル構築を理解する

buildMaterial関数とテクスチャノード生成を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | 759-1132行目: buildMaterial関数 |

**主要処理フロー**:
- **765-884行**: buildTextureNodes関数（PrimvarReader、Transform2d、Texture）
- **895-1123行**: PreviewSurfaceノード構築（diffuse、emissive、normal、ao、roughness、metallic、opacity、clearcoat等）

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

```
USDZExporter.parseAsync(scene, options)
    │
    ├─ オプションマージ
    │      ├─ maxTextureSize: 1024
    │      ├─ includeAnchoringProperties: true
    │      ├─ onlyVisible: true
    │      └─ quickLookCompatible: false
    │
    ├─ USDNode構築
    │      ├─ Root (Xform)
    │      └─ Scenes (Scope)
    │             └─ Scene (Xform)
    │                    └─ AR設定プロパティ
    │
    ├─ buildHierarchy(scene, sceneNode, materials, ...)
    │      │
    │      ├─ child.isMesh && MeshStandardMaterial
    │      │      ├─ buildMeshObject(geometry) → geometries/Geometry_N.usda
    │      │      ├─ materials[uuid] = material
    │      │      └─ buildMesh(child, ...) → メッシュXform
    │      │
    │      ├─ child.isCamera
    │      │      └─ buildCamera(child, ...) → Camera定義
    │      │
    │      └─ その他
    │             └─ buildXform(child, ...) → Xform定義
    │
    ├─ buildMaterials(materials, textures, quickLookCompatible)
    │      │
    │      └─ buildMaterial(material, textures, ...)
    │             ├─ buildTextureNodes() × 各テクスチャ
    │             └─ PreviewSurfaceノード
    │
    ├─ テクスチャ処理ループ
    │      ├─ isCompressedTexture → textureUtils.decompress()
    │      ├─ imageToCanvas(flipY, maxTextureSize)
    │      └─ canvas.toBlob('image/png') → files['textures/...']
    │
    ├─ 64バイトアライメント処理
    │
    └─ zipSync(files, { level: 0 })
```

### データフロー図

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

Object3D ───────────▶ buildHierarchy() ────────▶ USDNodeツリー
      │                     │
      ▼                     ▼
   Mesh ────────────▶ buildMeshObject() ────────▶ geometries/*.usda
      │                     │
      ▼                     ▼
BufferGeometry ────▶ buildMeshNode() ──────────▶ Mesh定義
      │                     │                    (points, normals, UVs)
      ▼                     │
Material ──────────▶ buildMaterials() ─────────▶ Materials定義
      │                     │
      ▼                     ▼
  Texture ─────────▶ buildTextureNodes() ──────▶ Shader定義
      │                     │
      ▼                     ▼
   Image ──────────▶ imageToCanvas() ──────────▶ textures/*.png
                            │
                            ▼
                      USDA文字列結合
                            │
                            ▼
                    files辞書（strToU8）
                            │
                            ▼
                    64バイトアライメント
                            │
                            ▼
                      zipSync() → ArrayBuffer
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| USDZExporter.js | `examples/jsm/exporters/USDZExporter.js` | ソース | メインエクスポーター実装 |
| fflate.module.js | `examples/jsm/libs/fflate.module.js` | ライブラリ | ZIP圧縮（strToU8, zipSync） |
| three.module.js | `build/three.module.js` | ソース | Three.jsコアライブラリ |
| WebGLTextureUtils.js | `examples/jsm/utils/WebGLTextureUtils.js` | ソース | 圧縮テクスチャ解除（WebGL） |
| WebGPUTextureUtils.js | `examples/jsm/utils/WebGPUTextureUtils.js` | ソース | 圧縮テクスチャ解除（WebGPU） |
