# 機能設計書 178-XRManager

## 概要

本ドキュメントは、Three.jsのWebXRセッション管理クラス「XRManager」の機能設計について記述する。XRManagerはWebXR Device APIを使用したVR/ARアプリケーションのセッション管理、カメラ制御、レンダリング設定を統括する。

### 本機能の処理概要

XRManagerクラスは、Three.jsのレンダラーとWebXR Device APIの間のブリッジとして機能する包括的なXRセッション管理クラスである。XRセッションの開始・終了、ステレオカメラの管理、コントローラーの追跡、マルチビューレンダリング、XRレイヤー（Quad/Cylinder）の管理など、VR/ARレンダリングに必要なすべての機能を提供する。

**業務上の目的・背景**：WebXR対応のVR/ARアプリケーションでは、通常のWebレンダリングとは異なる多くの設定が必要となる。XRManagerはこれらの複雑な設定を抽象化し、開発者が簡単にVR/AR体験を構築できるようにする。また、WebGL 2バックエンドとの統合、フレームバッファ管理、参照空間の設定など、低レベルの詳細を隠蔽する。

**機能の利用シーン**：
- VRヘッドセット向けアプリケーションの開発
- ARグラス向けアプリケーションの開発
- ステレオ3D表示の実装
- ハンドトラッキングの利用
- XRレイヤーを使用した高品質なUI表示
- マルチビューレンダリングによる性能最適化

**主要な処理内容**：
1. setSession: XRセッションの設定と初期化
2. updateCamera: XRカメラの更新とステレオビュー同期
3. getController/getControllerGrip/getHand: コントローラーアクセス
4. createQuadLayer/createCylinderLayer: XRレイヤーの作成
5. renderLayers: XRレイヤーのレンダリング

**関連システム・外部連携**：
- WebGPURenderer: XRレンダリングの実行
- WebXR Device API: XRSession, XRFrame, XRReferenceSpace等
- WebXRController: コントローラーの管理
- ArrayCamera: ステレオカメラの実装

**権限による制御**：WebXRセッションの権限（immersive-vr, immersive-ar等）に依存

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | WebXRサンプル | 主機能 | WebXRセッション管理 |

## 機能種別

セッション管理 / カメラ制御 / レンダリング管理 / XR

## 入力仕様

### 入力パラメータ

#### コンストラクタ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| renderer | Renderer | Yes | レンダラーインスタンス | - |
| multiview | boolean | No | マルチビューを有効化（デフォルト: false） | - |

#### setSession メソッド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| session | XRSession | Yes | XRセッション | - |

#### createQuadLayer メソッド

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| width | number | Yes | レイヤー幅（ワールド単位） | - |
| height | number | Yes | レイヤー高さ（ワールド単位） | - |
| translation | Vector3 | Yes | レイヤー位置 | - |
| quaternion | Quaternion | Yes | レイヤー向き | - |
| pixelwidth | number | Yes | レンダーターゲット幅（ピクセル） | - |
| pixelheight | number | Yes | レンダーターゲット高さ（ピクセル） | - |
| rendercall | Function | Yes | レンダリングコールバック | - |
| attributes | Object | No | 追加属性（stencil等） | - |

### 入力データソース

- XRSession: WebXR APIから取得したセッション
- PerspectiveCamera: ユーザー定義のカメラ（XRカメラに変換）
- XRFrame: 毎フレームのXR状態情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| cameraXR | ArrayCamera | ステレオカメラ |
| controller | Group | コントローラーの座標空間 |
| isPresenting | boolean | XRプレゼンテーション中フラグ |

### 出力先

- XRディスプレイ（HMD）
- イベントリスナー（sessionstart/sessionend/planesdetectedイベント）

## 処理フロー

### 処理シーケンス

#### setSession

```
1. WebGPUバックエンドチェック（XRはWebGL 2のみサポート）
2. セッションイベントリスナーを登録
3. XR互換性を確保（makeXRCompatible）
4. 現在のレンダラー状態を保存
5. XRProjectionLayerまたはXRWebGLLayerを作成
6. 参照空間を取得
7. XRレンダーターゲットを作成
8. フォビエーションを設定
9. アニメーションループを切り替え
10. isPresentingをtrueに設定
11. sessionstartイベントを発火
```

#### updateCamera

```
1. セッションがnullなら終了
2. ユーザーカメラのnear/farをXRカメラに適用
3. セッションのrenderStateを更新
4. カメラのレイヤーマスクを継承
5. 親オブジェクトの変換を適用
6. ステレオカメラの投影行列を計算（setProjectionFromUnion）
7. ユーザーカメラをXRカメラの変換で更新
```

#### onAnimationFrame (内部)

```
1. frameがundefinedならリターン
2. viewerPoseを取得
3. 各ビューについてループ：
   └─ サブイメージを取得
   └─ ビューポートを設定
   └─ カメラ行列と投影行列を更新
4. 出力レンダーターゲットを設定
5. コントローラーを更新
6. ユーザーのアニメーションループを呼び出し
7. planesdetectedイベントを発火（該当時）
```

### フローチャート

```mermaid
flowchart TD
    A[setSession開始] --> B{WebGPUバックエンド?}
    B -->|Yes| C[エラースロー]
    B -->|No| D[イベントリスナー登録]
    D --> E[makeXRCompatible]
    E --> F{layers対応?}
    F -->|Yes| G[XRProjectionLayer作成]
    F -->|No| H[XRWebGLLayer作成]
    G --> I[参照空間を取得]
    H --> I
    I --> J[XRRenderTarget作成]
    J --> K[フォビエーション設定]
    K --> L[アニメーションループ切替]
    L --> M[sessionstartイベント発火]
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-178-01 | WebGL専用 | XRはWebGL 2バックエンドでのみサポート | setSession実行時 |
| BR-178-02 | マルチビュー最適化 | OVR_multiview2拡張があればマルチビューを使用 | setSession時に判定 |
| BR-178-03 | レイヤーフォールバック | XRProjectionLayerが使えない場合はXRWebGLLayerを使用 | setSession時 |
| BR-178-04 | 参照空間タイプ | デフォルトは'local-floor' | setSession時 |
| BR-178-05 | プレゼンテーション中の制限 | framebufferScaleFactor等はプレゼンテーション中変更不可 | 各setter |

### 計算ロジック

**ステレオカメラの統合投影行列計算（setProjectionFromUnion）**：
```
1. 左右カメラの位置からIPD（瞳孔間距離）を計算
2. 左カメラのフラスタムパラメータを抽出
3. 右カメラのフラスタムパラメータを抽出
4. 統合されたフラスタムを計算
5. オフセットを適用した新しい投影行列を生成
```

**無限遠平面のチェック**：
```
if (projL[10] === -1.0) {
    // 無限遠平面を使用、左カメラの投影行列をそのまま使用
}
```

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| Error | 例外スロー | WebGPUバックエンドでXRを使用しようとした場合 | forceWebGL: trueを指定 |
| 警告 | コンソール警告 | プレゼンテーション中に設定変更を試みた場合 | 警告を出力して処理を中断 |

### リトライ仕様

リトライ処理は実装されていない

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

トランザクション処理は適用されない（リアルタイムXR処理）

## パフォーマンス要件

- XRフレームレート（72-120fps）への対応
- マルチビューレンダリングによる描画コール削減
- フォビエーションによる解像度最適化

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

- WebXRセッションはHTTPS環境が必要
- 各種XR機能はユーザー許可が必要
- ハンドトラッキングは追加の権限が必要

## 備考

- XRManagerはEventDispatcherを継承
- XRは現在WebGL 2バックエンドでのみサポート（WebGPUは将来対応予定）
- XRレイヤーはネイティブXRCompositingを使用して高品質な表示が可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | XRManager.js | `src/renderers/common/XRManager.js` | 32-396行目のコンストラクタとプロパティ定義 |

**読解のコツ**: 多数のプライベートプロパティがあるが、主要なものは_session, _cameraXR, _controllers, _xrRenderTarget。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | XRManager.js | `src/renderers/common/XRManager.js` | 918-1092行目 setSessionメソッド |

**主要処理フロー**:
1. **931行目**: WebGPUバックエンドチェック
2. **933-940行目**: イベントリスナー登録
3. **953-981行目**: XRProjectionLayer作成（layers対応時）
4. **1040-1075行目**: XRWebGLLayerフォールバック
5. **1082-1088行目**: アニメーションループ切替

#### Step 3: カメラ更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | XRManager.js | `src/renderers/common/XRManager.js` | 1101-1168行目 updateCameraメソッド |

**主要処理フロー**:
- **1107-1108行目**: near/farの同期
- **1118-1130行目**: セッションのrenderState更新
- **1133-1135行目**: レイヤーマスクの設定
- **1151-1161行目**: ステレオ投影行列の統合

#### Step 4: アニメーションループを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | XRManager.js | `src/renderers/common/XRManager.js` | 1550-1675行目 onAnimationFrame関数 |

**主要処理フロー**:
- **1561-1566行目**: viewerPoseの取得
- **1579-1612行目**: マルチビュー処理時のサブイメージ取得
- **1625-1641行目**: カメラ行列・投影行列の更新
- **1652-1663行目**: コントローラーの更新

#### Step 5: XRレイヤーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | XRManager.js | `src/renderers/common/XRManager.js` | 653-726行目 createQuadLayer |
| 5-2 | XRManager.js | `src/renderers/common/XRManager.js` | 744-818行目 createCylinderLayer |
| 5-3 | XRManager.js | `src/renderers/common/XRManager.js` | 826-895行目 renderLayers |

**主要処理フロー**:
- **653-726行目**: Quad（平面）レイヤーの作成
- **744-818行目**: Cylinder（円筒）レイヤーの作成
- **841-884行目**: 各レイヤーのレンダリング

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

```
XRManager (extends EventDispatcher)
    │
    ├─ setSession(session)
    │      ├─ WebGPUチェック
    │      ├─ イベントリスナー登録
    │      │      ├─ select/selectstart/selectend
    │      │      ├─ squeeze/squeezestart/squeezeend
    │      │      ├─ end → onSessionEnd()
    │      │      └─ inputsourceschange → onInputSourcesChange()
    │      ├─ backend.makeXRCompatible()
    │      ├─ getBinding() → XRWebGLBinding
    │      ├─ createProjectionLayer() または XRWebGLLayer
    │      ├─ session.requestReferenceSpace()
    │      └─ new XRRenderTarget()
    │
    ├─ updateCamera(camera)
    │      ├─ near/farの同期
    │      ├─ session.updateRenderState()
    │      ├─ updateCamera(camera, parent)
    │      ├─ setProjectionFromUnion(cameraXR, cameraL, cameraR)
    │      └─ updateUserCamera(camera, cameraXR, parent)
    │
    ├─ getController(index)
    │      └─ _getController(index).getTargetRaySpace()
    │
    ├─ getControllerGrip(index)
    │      └─ _getController(index).getGripSpace()
    │
    ├─ getHand(index)
    │      └─ _getController(index).getHandSpace()
    │
    ├─ createQuadLayer(...)
    │      └─ new XRRenderTarget() + Mesh
    │
    ├─ createCylinderLayer(...)
    │      └─ new XRRenderTarget() + Mesh
    │
    └─ renderLayers()
           └─ 各layer.rendercall()
```

### データフロー図

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

XRSession ─────────────────▶ setSession() ──────────────────▶ 'sessionstart'イベント
                                  │
                                  ├─ XRWebGLBinding作成
                                  ├─ XRProjectionLayer作成
                                  ├─ XRRenderTarget作成
                                  └─ アニメーションループ切替


PerspectiveCamera ─────────▶ updateCamera() ────────────────▶ ArrayCamera更新
                                  │
                                  ├─ near/far同期
                                  ├─ ステレオ投影計算
                                  └─ ユーザーカメラ同期


XRFrame ───────────────────▶ onAnimationFrame() ────────────▶ XRディスプレイ出力
                                  │
                                  ├─ viewerPose取得
                                  ├─ 各ビューのカメラ更新
                                  ├─ コントローラー更新
                                  └─ ユーザーアニメーションループ呼び出し


layer定義 ─────────────────▶ createQuadLayer() ─────────────▶ Mesh + XRRenderTarget
                                  │
                                  └─ XRQuadLayer作成（セッション中）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| XRManager.js | `src/renderers/common/XRManager.js` | ソース | XRセッション管理の本体 |
| WebXRController.js | `src/renderers/webxr/WebXRController.js` | ソース | コントローラー管理 |
| XRRenderTarget.js | `src/renderers/common/XRRenderTarget.js` | ソース | XR用レンダーターゲット |
| ArrayCamera.js | `src/cameras/ArrayCamera.js` | ソース | ステレオカメラ |
| PerspectiveCamera.js | `src/cameras/PerspectiveCamera.js` | ソース | 左右カメラの型 |
| EventDispatcher.js | `src/core/EventDispatcher.js` | ソース | イベント発火機能（基底クラス） |
| QuadMesh.js | `src/renderers/common/QuadMesh.js` | ソース | XRレイヤー用メッシュ |
| NodeMaterial.js | `src/materials/nodes/NodeMaterial.js` | ソース | XRレイヤー用マテリアル |
