# 3D シーン

> client.scene3d と 3D シーンのノードを使って、TypeScript からプロンプトで編集できる 3D シーンを生成し、編集し、MP4 にレンダリングし、3D レンダリング Pro を実行します。

Source: https://nodaro.ai/ja/docs/developers/sdk/scenes-3d

**`client.scene3d`** は、編集できる 3D シーンを作成します。プロンプトからシーンを作成し、指示または正確な操作で編集し、リビジョンを MP4 にレンダリングします。**3D レンダリング Pro**（3D Render Pro）も実行します。これは、シーンの作成とレンダリングを 1 つの操作で行う機能です。また、レンダリングが納品したファイルも読み取ります。これらのメソッドは、[**3D シーン生成**（Generate 3D Scene）](https://nodaro.ai/docs/nodes/video/generate-3d-scene)、[**3D シーン編集**（Edit 3D Scene）](https://nodaro.ai/docs/nodes/video/edit-3d-scene)、[**動画レンダリング**（Render Video）](https://nodaro.ai/docs/nodes/video/render-video)、[**3D レンダリング Pro**（3D Render Pro）](https://nodaro.ai/docs/nodes/video/pro-3d-render)の各ノードを使います。エンドポイントについては、[3D シーン API](https://nodaro.ai/docs/developers/api/3d-scenes) を参照してください。

## メソッド
| メソッド | 内容 |
| --- | --- |
| [`capabilities()`](#capabilities) | この環境が対応しているエンジンと Pro のオプションを読み取ります |
| [`generate(params)` と `generateAndWait()`](#generateparams-and-generateandwaitparams-options) | プロンプトから新しいシーンを作成します |
| [`edit(params)` と `editAndWait()`](#editparams-and-editandwaitparams-options) | シーンの新しいリビジョンを作成します |
| [`render(params)` と `renderAndWait()`](#renderparams-and-renderandwaitparams-options) | 作成せずに、リビジョンを MP4 にレンダリングします |
| [`applyEdits(revisionId, params)`](#applyeditsrevisionid-params) | 作成や課金なしで、正確な編集を保存します |
| [`quotePro(params)`](#quoteproparams) | 3D レンダリング Pro の実行の料金を見積もります |
| [`runPro(params, options?)`](#runproparams-options) | 見積もり済みの 3D レンダリング Pro の実行を開始します |
| [`renderProAndWait(params, options?)`](#renderproandwaitparams-options) | 見積もり、実行、待機を 1 回の呼び出しで行います |
| [`getDelivery(jobId)`](#getdeliveryjobid) | レンダリングが納品したファイルを読み取ります |
| [`deliveryAssetBytes(jobId, asset, options?)`](#deliveryassetbytesjobid-asset-options) | 納品された 1 つのファイルをダウンロードします |
| [`retainedRecipe(jobId, options?)`](#retainedrecipejobid-options) | 拒否された Pro の実行が保持したレシピを読み取ります |
| [`assetBytes(revisionId, asset, options?)`](#assetbytesrevisionid-asset-options) | シーンのリビジョンの 1 つのファイルをダウンロードします |
| [`sourceBytes(revisionId, options?)`](#sourcebytesrevisionid-options) | リビジョンのネイティブのソースファイルをダウンロードします |

## プロンプトから MP4 まで
```ts
const created = await client.scene3d.generateAndWait({
prompt: "Orbit a single box on a floor over four seconds",
durationSeconds: 4,
fps: 24,
aspectRatio: "16:9",
})

const edited = await client.scene3d.editAndWait({
scenePlan: created.scenePlan,
expectedRevisionId: created.scenePlan.revisionId,
operations: [{ op: "set-camera", changes: { focalLengthMm: 50 } }],
})

const clip = await client.scene3d.renderAndWait({ planType: "3d-scene", plan: edited.scenePlan })
console.log(clip.videoUrl)
```

同じ 3 つの手順は、[`client.nodes.runAndWait()`](https://nodaro.ai/docs/developers/sdk/nodes) と、型付きパラメーターを持つノードタイプ `generate-3d-scene`、`edit-3d-scene`、`render-video` でも行えます。自分のページでシーンを回転させたり調整させたりできるようにするには、[3D シーンビューポートの埋め込み](https://nodaro.ai/docs/developers/embed/scene3d)を使います。レンダラーのコピーは不要で、メッセージにトークンが含まれることもありません。

## client.scene3d
### capabilities()
この環境が作成・レンダリングできるものを返します（`GET /v1/3d-scene/capabilities`）。

```ts
capabilities(): Promise<Scene3DCapabilities>
```

```ts
const caps = await client.scene3d.capabilities()
if (caps.advanced) console.log(caps.advanced.engines) // for example ["blender-cloud"]
if (caps.pro) console.log("3D Render Pro is available")
```

- **`basic`** は決定論的なエンジンで、`{ available, sceneSchemaVersions }` の形です。
- **`advanced`** は、利用できるエンジンを一覧にします。ない場合は `null` です。利用できない特定のエンジンを指定した場合は、ベーシックでの生成やそのクレジットのチェックより前に拒否されます。
- **`pro`** は 3D レンダリング Pro について説明します。この環境が提供するエンジン、品質プロファイル、スタイル、アスペクト比です。それら以外は提示しないでください。`pro` がない場合、そのノードは利用できません。

### generate(params) と generateAndWait(params, options?)
プロンプトから新しいシーンを作成します。`generate()` はジョブをすぐに返し、`generateAndWait()` はジョブを待って、`scenePlan` と任意の `changeSummary` を返します。

```ts
generate(params: GenerateScene3DParams): Promise<RunNodeResult>
generateAndWait(params: GenerateScene3DParams, options?: RunAndWaitOptions): Promise<Scene3DJobOutput>
```

<TypeTable
type={{
prompt: { type: 'string', required: true, description: "シーンで起きることです。" },
durationSeconds: { type: 'number', description: "長さ（秒）です。" },
fps: { type: 'number', description: "フレームレートです。" },
aspectRatio: { type: 'string', description: "フレームのアスペクト比で、16:9 のような値です。" },
references: { type: 'Scene3DReference[]', description: "画像と動画のリファレンスで、それぞれに appearance、layout、motion のいずれかのロールがあります。" },
inputAssets: { type: 'Scene3DInputAsset[]', description: "使用する既存の GLB ファイルで、最大 8 個です。それぞれ { id, revisionId, assetId, label? } の形式です。インポートに対応したアドバンスエンジンが必要です。" },
engine: { type: '"basic" | "blender-cloud" | "blender-local"', default: '"basic"', description: "シーン作成エンジンです。capabilities() を参照してください。" },
acceptedSceneSchemaVersions: { type: 'number[]', description: "クライアントがレンダリングできるシーンスキーマのバージョンです。" },
maxRepairPasses: { type: 'number', description: "アドバンスエンジンの修正予算です。" },
llmModel: { type: 'string', description: "シーンを計画する言語モデルです。" },
reasoningEffort: { type: 'string', description: "プランナーの推論の強度です。" },
workflowId: { type: 'string', description: "実行を一覧表示するワークフローです。" },
}}
/>

```ts
const { scenePlan } = await client.scene3d.generateAndWait({
prompt: "A paper boat drifts across a puddle as rain starts",
references: [{ id: "look", kind: "image", role: "appearance", url: moodImageUrl }],
})
```

**`inputAssets`** は、使用が許可されている GLB ファイルを、変わることのない ID で指定します。見た目用の画像とモーション用の動画は、引き続き `references` で渡します。アセットの URL やハッシュは送らないでください。サーバー自身がバイトデータの記録を提供します。ベーシックと、インポートに対応していないエンジンは、課金の前に `inputAssets` を拒否します。Pro の見積もりを送るときも、同じセレクターを再利用してください。

### edit(params) と editAndWait(params, options?)
シーンの**新しいリビジョン**を作成します。渡したシーン自体は変更されません。`prompt` で指示を伝えるか、正確な `operations` を渡してください。両方を同時には使えません。

```ts
edit(params: EditScene3DParams): Promise<RunNodeResult>
editAndWait(params: EditScene3DParams, options?: RunAndWaitOptions): Promise<Scene3DJobOutput>
```

<TypeTable
type={{
scenePlan: { type: 'Scene3DPlan', required: true, description: "編集元にするシーンです。" },
expectedRevisionId: { type: 'string', required: true, description: "編集するリビジョンです。" },
prompt: { type: 'string', description: "指示です。たとえば「カメラの動きを遅くして」のようなものです。" },
operations: { type: 'Scene3DEditOperation[]', description: "正確な編集です。たとえば { op: set-camera, changes: { focalLengthMm: 50 } } のようなものです。" },
references: { type: 'Scene3DReference[]', description: "追加するリファレンスです。ID をもとに統合されます。" },
replaceReferences: { type: 'boolean', description: "統合の代わりに、リファレンス一式をまるごと置き換えます。空のリストを渡すと、すべて消去されます。" },
lockedObjectIds: { type: 'string[]', description: "編集で変更してはいけないオブジェクトです。" },
selectedObjectIds: { type: 'string[]', description: "指示の対象となるオブジェクトです。" },
engine: { type: 'string', description: "作成エンジンです。" },
llmModel: { type: 'string', description: "プランナーのモデルです。" },
}}
/>

```ts
const { scenePlan: next, changeSummary } = await client.scene3d.editAndWait({
scenePlan,
expectedRevisionId: scenePlan.revisionId,
prompt: "Make the rain heavier and lower the camera",
})
```

### render(params) と renderAndWait(params, options?)
渡した正確なリビジョンを、作成や再構築を行わずに、MP4 にレンダリングします（`POST /v1/render-video/plan`）。

```ts
render(params: { planType: "3d-scene"; plan: Scene3DPlan; workflowId?: string; nodeId?: string }): Promise<RunNodeResult>
renderAndWait(params: RenderScene3DParams, options?: RunAndWaitOptions): Promise<NodeJobOutput>
```

<TypeTable
type={{
planType: { type: '"3d-scene"', required: true, description: "プランを 3D シーンとして示します。" },
plan: { type: 'Scene3DPlan', required: true, description: "レンダリングするリビジョンです。" },
workflowId: { type: 'string', description: "実行を一覧表示するワークフローです。" },
nodeId: { type: 'string', description: "実行が属するノードです。" },
}}
/>

```ts
const { videoUrl } = await client.scene3d.renderAndWait({ planType: "3d-scene", plan: scenePlan })
```

料金は、プラン自身の `width` と `height` によって決まります。

| フレーム | クレジット | 料金の識別子 |
| --- | --- | --- |
| 長辺が 1,920 ピクセルまで | 50 | `render-video` |
| それより大きく、5.12 メガピクセルまで | 75 | `render-video:3d-large` |
| 5.12 メガピクセルを超える場合 | 125 | `render-video:3d-xlarge` |

これらの識別子の現在の料金は、[`client.credits.modelCosts()`](https://nodaro.ai/docs/developers/sdk/models-and-credits) で読み取ってください。

### applyEdits(revisionId, params)
保存済みのリビジョンに、作成も課金もなしで、正確な編集を保存します（`POST /v1/3d-scene/revisions/:id/edits`）。新しい `scenePlan` と `changeSummary` を返します。

```ts
applyEdits(revisionId: string, params: {
newRevisionId: string
expectedContentHash: string
operations: Scene3DV2EditOperation[]
lockedObjectIds?: string[]
}): Promise<{ scenePlan: Scene3DPlanV2; changeSummary: string }>
```

<TypeTable
type={{
revisionId: { type: 'string', required: true, description: "編集するリビジョンです。" },
newRevisionId: { type: 'string', required: true, description: "新しいリビジョンの ID です。同じ編集を再試行するときは、同じ値を使ってください。" },
expectedContentHash: { type: 'string', required: true, description: "編集するリビジョンのコンテンツハッシュです。" },
operations: { type: 'Scene3DV2EditOperation[]', required: true, description: "正確な編集内容です。バージョン 2 のシーン用です。" },
lockedObjectIds: { type: 'string[]', description: "編集で変更してはいけないオブジェクトです。" },
}}
/>

```ts
const { scenePlan: saved } = await client.scene3d.applyEdits(revisionId, {
newRevisionId: crypto.randomUUID(),
expectedContentHash,
operations,
})
```

返されたシーンを採用してよいのは、ユーザーがまだ、編集の起点にしたリビジョンを編集している場合だけです。ジオメトリとカメラのファイルは再利用されます。ポスター、検証結果、ネイティブのダウンロードファイルは、新しいリビジョン用に作られた後になって初めて、再び添付されます。

## 3D レンダリング Pro
3D レンダリング Pro は 1 つの操作です。`source` を渡すと、1 つのジョブが、正確なコンポジションである `scenePlan` と、MP4 である `videoUrl` の両方を用意して完了します。結果には、リビジョン、ポスター、`shotStills`、検証結果、レンダラーの詳細も含まれます。`client.nodes.run("pro-3d-render")` と `runAndWait` も、同じ型付きパラメーターで同じルートを呼び出します。

エンジンがない環境は `503 SCENE_CAPABILITY_UNAVAILABLE` を返し、ベーシックへのフォールバックは行われません。料金が設定されていない環境は、何かを確保する前に `503 price_not_configured` を返します。モデルや推論の強度の設定はありません。プランナーはサーバー側で固定されています。

### quotePro(params)
実行を**開始せずに**、料金を見積もります。何も確保せず、何も消費しません。応答には `quoteId`、上限を示す `maxCredits`、表示用の `breakdown`、そして後で実行が照合される入力ハッシュが含まれます。

```ts
quotePro(params: Pro3DRenderParams): Promise<Pro3DRenderQuote>
```

<TypeTable
type={{
source: { type: 'Pro3DRenderSource', required: true, description: "レンダリングする対象です。下記の source を参照してください。" },
engine: { type: 'string', description: "capabilities().pro にあるエンジンです。不明または利用できないエンジンは拒否され、格下げされることはありません。" },
durationSeconds: { type: 'number', description: "長さです。scene ソースでは、シーンのタイミングを変更します。シーン自身のタイミングを保つには省略してください。" },
fps: { type: 'number', description: "フレームレートです。" },
aspectRatio: { type: 'string', description: "フレームのアスペクト比で、capabilities().pro にあるものです。" },
quality: { type: 'string', description: "capabilities().pro にある品質プロファイルです。" },
style: { type: 'string', description: "capabilities().pro にあるスタイルです。" },
maxRepairPasses: { type: 'number', description: "修正予算で、0〜2 です。各パスは有料です。" },
acceptedSceneSchemaVersions: { type: 'number[]', description: "クライアントがレンダリングできるシーンのバージョンです。prompt ソースはバージョン 2 を作るため、2 を含めてください。" },
localConnectionId: { type: 'string', description: "ローカル実行用の、ペアリング済みのデスクトップです。" },
forcePrivate: { type: 'boolean', description: "実行のすべてのファイルを、公開読み取り可能なストレージに置かないようにします。" },
}}
/>

`source` は、次の 3 つの形のいずれかです。

- **`{ kind: "prompt", prompt, references? }`** は、新しいシーンを作成します。
- **`{ kind: "scene", revisionId, sourceJobId }`** は、`editPrompt` がなければ、作成の課金なしで保存済みのシーンをエクスポートします。`editPrompt` を加えると、先にシーンを編集します。ジョブの履歴にしか残っていないベーシックのシーンには `sourceJobId` が必要です。
- **`{ kind: "local-export", exportId, connectionId }`** は、ペアリング済みのデスクトップを使います。

```ts
const quote = await client.scene3d.quotePro(params)
showPrice(quote.maxCredits, quote.breakdown)
```

### runPro(params, options?)
見積もり済みの実行を開始します。`quotePro()` から得た `quoteId` が必要なので、誰も見ていない料金で実行が始まることはありません。`Idempotency-Key` を送信します。送信するのは、呼び出しごとに新しく作られるキーか、`options.idempotencyKey` で渡した自分のキーです。タイムアウトした呼び出しを再試行するときは、自分のキーを再利用してください。

```ts
runPro(params: Pro3DRenderParams & { quoteId: string }, options?: { idempotencyKey?: string }): Promise<RunNodeResult>
```

<TypeTable
type={{
quoteId: { type: 'string', required: true, description: "実行が認められる根拠となる見積もりです。" },
'...params': { type: 'Pro3DRenderParams', required: true, description: "見積もったときと同じボディです。" },
idempotencyKey: { type: 'string', description: "この実行の再試行用トークンです。" },
}}
/>

```ts
await client.scene3d.runPro({ ...params, quoteId: quote.quoteId })
```

### renderProAndWait(params, options?)
`params` に `quoteId` がない場合は見積もりを行い、実行し、待機します。1 回の呼び出しで全体を行います。送信するリクエストは最大 2 回で、開始される有料のジョブは 1 つだけです。そのジョブは、見積もった内容とまったく同じもののハッシュに対して認められます。

```ts
renderProAndWait(params: Pro3DRenderParams | Pro3DRenderRunParams, options?: RunAndWaitOptions & { idempotencyKey?: string }): Promise<Pro3DRenderJobOutput>
```

<TypeTable
type={{
params: { type: 'Pro3DRenderParams', required: true, description: "実行内容です。quoteId があってもなくてもかまいません。" },
options: { type: 'RunAndWaitOptions & { idempotencyKey? }', description: "ポーリングのオプションと、再試行用のトークンです。" },
}}
/>

```ts
const caps = await client.scene3d.capabilities()
if (caps.pro?.available) {
const shot = await client.scene3d.renderProAndWait({
source: {
kind: "prompt",
prompt: "A red suitcase rolls behind a central pillar and reappears",
references: [{ id: "look", kind: "image", role: "appearance", url: appearanceImageUrl }],
},
durationSeconds: 30,
fps: 24,
aspectRatio: "21:9",
maxRepairPasses: 2,
acceptedSceneSchemaVersions: [2],
})
console.log(shot.videoUrl)        // the MP4
console.log(shot.sceneRevisionId) // export it again later, with no authoring charge
}
```

**ショットの静止画。**`shotStills` は `{ shotIndex, frame, assetId, url }` のリストで、各ショットの開始フレームの静止画が 1 枚ずつ、同じ実行の中で追加料金なしに作られます。ショットが 1 つだけのシーンには、フレーム 0 の 1 枚があります。このフィールドができる前に作られた結果にはこれがないため、`shot.shotStills ?? []` の形で読み取ってください。

各 `url` は、公開リンクではなく、自分の環境の**認証が必要な**エンドポイントです。実行に使ったものと同じ認証情報で取得してください。`img` タグやサードパーティのサービスからは 401 になります。

```ts
for (const still of shot.shotStills ?? []) {
const res = await fetch(still.url, { headers: { Authorization: `Bearer ${token}` } })
const bytes = await res.arrayBuffer() // use the bytes, or store them where your pipeline can read them
}
```

それでも、静止画をモデルのリファレンスとして使えます。読み取った URL を、そのまま `referenceImageUrls` に渡すか、ノードの `stills` 出力から渡します。プラットフォームは、その実行に対して、あなたの名前で、そのファイル 1 つだけを短時間読み取れる権限を与えます。その権限は数分後に失効するため、保存するのは認証済みの URL であり、権限そのものではありません。

### Pro の実行が自分自身について報告する内容
シーンを作成した実行は、何を仮定し、何を行ったかを報告します。どのフィールドも、任意のものとして読み取ってください。アドバンスエンジンのない環境や、古い結果には、これらのフィールドがまったくありません。

```ts
const summary = shot.metadata?.summary // what the planner says it built
const repairs = shot.repairPasses      // 0 means accepted the first time
const retries = shot.admissionRetries  // planner retries before a build
const mechanical = shot.mechanicalPasses
const restored = shot.restoredAssertions
const assumptions = (shot.validation?.warnings ?? [])
.filter((w) => w.code === "SCENE_AUTHORING_ASSUMPTION")
.map((w) => w.message)
```

- **`SCENE_AUTHORING_ASSUMPTION`** の警告は、プロンプトが指定しなかったために実行側が決めた内容を示します。警告コードは今後増える可能性があるため、知らないコードはエラーではなく情報として扱ってください。
- **`repairPasses`** は、作成のパスではなく修正の回数を数えます。そのため `0` は、シーンが最初の試行で受け入れられたことを意味します。`admissionRetries` は別のものを数えます。ビルドの前に、プランナーがレシピをもう一度求められた回数です。
- **`mechanicalPasses`** は、プランナーを介さずに、コンパイラー自身の対処によってエンジンが適用した修正の回数です。これには最大 2 回までの見積もり済みの許容量があり、使わなければ解放されます。1 回ごとに `REMEDY_AUTO_APPLIED` の警告が加わります。この許容量ができる前に見積もった実行では、こうしたパスも修正として課金されました。渡された見積もりを読んで確認してください。
- **`restoredAssertions`** は、プランナーが、変更を求められていないのに変更してしまった必須のチェックを、エンジンが元に戻したものを示します。1 件ごとに `ASSERTION_RESTORED` の警告も加わります。
- レンダリングのみのエクスポートは何も作成していないため、件数もサマリーもありません。特に `mechanicalPasses` では、フィールドがないことは `0` を意味しません。

`generateAndWait()` の結果にも、アドバンスエンジンがシーンを作成した場合は、同じフィールドが含まれます。ベーシックエンジンはモデルを呼び出さないため、これらのフィールドはまったく含まれません。

### ビジュアルレビューが承認しなかった納品物
**完了した**結果が、ビジュアルレビューの承認なしに届くことがあります。どちらの場合も、動画は本物であり、クレジットは消費されています。どちらに当たるかは、`metadata.review.verdict` が示します。

- **`"refused"`**：修正の予算を使い切り、必須のチェックはすべて合格したものの、レビューがそれでも異議を示しました。シーンは、その異議とともに納品されました。
- **`"unavailable"`**：レビューが、使える判定を返しませんでした。`reason` は、プロバイダーに接続できなかった場合は `"provider"`、回答が使えなかった場合は `"unusable"` です。`attempts` は、何回問い合わせたかを示します。**このシーンは、誰にも判定されていません。**

```ts

const review = scene3DReviewVerdictOf(shot)
if (review) {
console.log(scene3DReviewNote(review)) // one user-safe sentence for either verdict
if (review.verdict === "unavailable") console.log(`unreviewed (${review.reason}) after ${review.attempts} attempts`)
for (const objection of review.objections) console.log(objection.category, objection.what, objection.correction)
}
```

フィールドを自分で読み取るのではなく、`@nodaro/shared` のこの 2 つのヘルパーを使ってください。次の 3 つの読み方は、正しそうに見えて実際は正しくないためです。

- **`validation.status` は、それでも `"passed"` のままです。**必須のチェックには合格しているからこそ、シーンが納品されています。
- **`objections` は空になることがあります。**具体的な指摘がない異議も、やはり異議です。そのため `SCENE_REVIEW_REFUSED` の警告を数えるだけでは、これを見逃します。
- **`"unavailable"` の場合の objections は、判定ではありません。**これらは、失敗する前に回答したレビューのバッチから来たものです。この場合の空のリストは、判定がなかったことを意味し、承認を意味しません。

各異議は `{ category, what, correction?, frames }` の形です。`"unavailable"` の判定では、`SCENE_REVIEW_UNAVAILABLE` の警告が `validation.warnings` の先頭に来ます。ビジュアルの異議だけでは、もはやジョブは失敗しません。`SCENE_QUALITY_FAILED` は、必須のチェックが失敗したか、コンパイラーがレシピを拒否したことを意味します。そのような失敗した結果に何が残るかについては、[3D レンダリング Pro](https://nodaro.ai/docs/nodes/video/pro-3d-render) を参照してください。

## 納品物とファイル
これらのメソッドは、実行がすでに納品したファイルを読み取ります。レンダリングを新たに開始することはありません。どの読み取りも、納品物とそのソースの両方へのアクセス権を、ソースのリビジョンが削除された後であっても、そのたびに確認します。

### getDelivery(jobId)
納品物の記録を読み取ります（`GET /v1/3d-scene/deliveries/:jobId`）。その `sourceKind`、正確なソースのリビジョン、固定されたファイルです。

```ts
getDelivery(jobId: string): Promise<Scene3DDelivery>
```

<TypeTable
type={{
jobId: { type: 'string', required: true, description: "そのレンダリングのジョブ ID です。" },
}}
/>

```ts
const delivery = await client.scene3d.getDelivery(jobId)
const stills = delivery.assets.filter((a) => a.kind === "shot-still")
```

ファイルには 4 種類があります。すべての納品物にある `poster` と `validation-report`、ショットごとに 1 つずつある `shot-still`（それぞれに `shotIndex`、`frame`、`width`、`height` が付きます）、そして `refused-authoring` の納品物にのみある `source-json` です。`sourceKind` は `retained-revision`、`job-output`、`refused-authoring` のいずれかです。拒否された納品物では、`sceneRevisionId` と `sourcePlanSha256` が `null` になり、何もコンパイルされていないため、ポスターもありません。

### deliveryAssetBytes(jobId, asset, options?)
納品物が一覧するファイルを 1 つ、新しい認証情報とサイズの上限付きでダウンロードします。

```ts
deliveryAssetBytes(jobId: string, asset: Scene3DDeliveryAsset, options?: { signal?: AbortSignal }): Promise<ArrayBuffer>
```

<TypeTable
type={{
jobId: { type: 'string', required: true, description: "そのレンダリングのジョブ ID です。" },
asset: { type: 'Scene3DDeliveryAsset', required: true, description: "getDelivery() から得たファイル記述子で、そのまま渡します。" },
signal: { type: 'AbortSignal', description: "ダウンロードを停止します。" },
}}
/>

```ts
const bytes = await client.scene3d.deliveryAssetBytes(jobId, stills[0])
```

### retainedRecipe(jobId, options?)
拒否された 3D レンダリング Pro の実行が保持したレシピを、パース済みの形で読み取ります。ない場合は `null` です。拒否された実行のレシピはコンパイルされていないため、シーンのリビジョンもポスターもソースファイルもありません。レシピは、それでも残っているものです。

```ts
retainedRecipe(jobId: string, options?: { signal?: AbortSignal }): Promise<unknown | null>
```

<TypeTable
type={{
jobId: { type: 'string', required: true, description: "拒否された実行のジョブ ID です。" },
signal: { type: 'AbortSignal', description: "ダウンロードを停止します。" },
}}
/>

```ts
const recipe = await client.scene3d.retainedRecipe(jobId)
```

- **ジョブのワークフローへの編集権限が必要です。**それより低い権限の読み取り専用ユーザーには、レシピはまったく見えません。この場合の応答は、エラーではなく `null` です。
- **問い合わせるべきかどうかは、失敗したジョブ自身が示します。**レシピが保持されている場合、その `validation.sourceRetained` は `true` です。
- **これは入力ではなく証拠です。**拒否された実行はリビジョンを公開していないため、このレシピから再実行することはできません。何が試みられたかを見て、次のプロンプトを改善するために読んでください。読み取りにクレジットはかかりません。

### assetBytes(revisionId, asset, options?)
保存済みのシーンのリビジョンのファイルをダウンロードします。GLB ファイル、カメラトラック、ポスター、検証レポートのいずれかです。そのリビジョンから得た正確な記述子を渡してください。SDK は、宣言されたサイズにダウンロードを制限し、シーンのレンダラーも SHA-256 ダイジェストを確認します。

```ts
assetBytes(revisionId: string, asset: Scene3DAssetRef, options?: { signal?: AbortSignal }): Promise<ArrayBuffer>
```

<TypeTable
type={{
revisionId: { type: 'string', required: true, description: "シーンのリビジョンです。" },
asset: { type: 'Scene3DAssetRef', required: true, description: "種類が glb、camera-track-json、poster、validation-report のいずれかの記述子です。" },
signal: { type: 'AbortSignal', description: "ダウンロードを停止します。" },
}}
/>

```ts
const glb = await client.scene3d.assetBytes(scenePlan.revisionId, glbAsset)
```

### sourceBytes(revisionId, options?)
リビジョンのネイティブのソースファイル（`.blend` ファイルなど）を、専用の認可を通じてダウンロードします。保持されたレシピと同じ編集権限が必要です。ネイティブファイルが利用できるのは、それがまさにその採用されたリビジョンを表している場合だけです。

```ts
sourceBytes(revisionId: string, options?: { signal?: AbortSignal }): Promise<ArrayBuffer>
```

<TypeTable
type={{
revisionId: { type: 'string', required: true, description: "シーンのリビジョンです。" },
signal: { type: 'AbortSignal', description: "ダウンロードを停止します。" },
}}
/>

```ts
const blend = await client.scene3d.sourceBytes(revisionId)
```

どちらのバイトデータ用メソッドも、新しい認証情報を使い、キャンセルに対応し、通常の[型付きエラー](https://nodaro.ai/docs/developers/sdk/errors)をスローします。

## Frequently asked questions

### SDK で、プロンプトを 3D アニメーションにするには、どうすればよいですか？

プロンプトを指定して client.scene3d.generateAndWait を実行すると、編集できるシーンプランが得られます。必要なら editAndWait で変更し、renderAndWait で MP4 にレンダリングします。

### 3D シーンのレンダリングには、いくらかかりますか？

料金は、プランのフレームサイズによって決まります。長辺が 1,920 ピクセルまでは 50 クレジット、5.12 メガピクセルまでは 75 クレジット、それを超えると 125 クレジットです。現在の料金は、モデルコストの API から読み取ってください。

### 3D レンダリング Pro とは何ですか？

シーンの作成とレンダリングを 1 つの操作で行う機能で、結果にはシーンプランと MP4 の両方が含まれます。先に quotePro で見積もり、capabilities().pro で、自分の環境が対応しているかを確認してください。

### ショットの静止画の URL を、img タグで開けますか？

いいえ。ショットの静止画などの納品ファイルは、認証が必要なエンドポイントから提供されます。ジョブの実行に使ったものと同じ認証情報で取得し、そのバイトデータを表示するか保存してください。
