# Establish the Cube Link

The Attic Portal is live, but it is still only a window. You can see the glowing Memory Cube; a future robot application needs a structured object identity and a command route it can trust.

**What you are learning:** `ovstream` transports browser input, application commands, rendered video, and application state. It does not call `ovrtx` directly. Your application connects the two by validating messages, queuing renderer-owned work, and publishing the result.

**Next Mission:** Prepare the Memory Cube as a dependable action target. Prove the portal can find the correct OpenUSD prim, control it without drift, and focus a camera on its full motion envelope without touching the source attic.

**Why this matters for a robot-ready scene:** A robot cannot act on “the glowing object over there.” Its software needs a stable identity, an explicit transform contract, and confirmed state. It also needs repeatable inspection views that remain valid as the target moves. These contracts let a robotics team connect perception and planning to the same object without depending on appearance or an operator’s current camera.

**Your 3D skills at work:** Scene hierarchy and naming become machine identity. Rigging and animation knowledge becomes safe transform authoring, coordinate-space discipline, and repeatable poses. Camera and layout craft becomes sensor placement, coverage, and consistent evidence rather than a purely aesthetic shot.

## Establish the Cube Link: Identity, Control, Focus

The ideas behind this page’s build. Step through the slides, then make the 3D decisions that shape the two mission briefs below.

### 1

The Command Relay

Same components, one job each.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-broadcast slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M8.75 8.582v5.668a.75.75 0 0 1-1.5 0V8.582a1.75 1.75 0 1 1 1.5 0Zm3.983-7.125a.75.75 0 0 1 1.06.026A7.976 7.976 0 0 1 16 7c0 2.139-.84 4.083-2.207 5.517a.75.75 0 1 1-1.086-1.034A6.474 6.474 0 0 0 14.5 7a6.474 6.474 0 0 0-1.793-4.483.75.75 0 0 1 .026-1.06Zm-9.466 0c.3.286.312.76.026 1.06A6.474 6.474 0 0 0 1.5 7a6.47 6.47 0 0 0 1.793 4.483.75.75 0 0 1-1.086 1.034A7.973 7.973 0 0 1 0 7c0-2.139.84-4.083 2.207-5.517a.75.75 0 0 1 1.06-.026Zm8.556 2.321A4.988 4.988 0 0 1 13 7a4.988 4.988 0 0 1-1.177 3.222.75.75 0 1 1-1.146-.967A3.487 3.487 0 0 0 11.5 7c0-.86-.309-1.645-.823-2.255a.75.75 0 0 1 1.146-.967Zm-6.492.958A3.48 3.48 0 0 0 4.5 7a3.48 3.48 0 0 0 .823 2.255.75.75 0 0 1-1.146.967A4.981 4.981 0 0 1 3 7a4.982 4.982 0 0 1 1.188-3.236.75.75 0 1 1 1.143.972Z"></path></svg> **`ovstream`** is the courier - it carries input and commands out, video and state back.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-shield-check slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg> **The application** is the checkpoint and traffic controller - it validates and queues work.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-cpu slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M6.5.75V2h3V.75a.75.75 0 0 1 1.5 0V2h1.25c.966 0 1.75.784 1.75 1.75V5h1.25a.75.75 0 0 1 0 1.5H14v3h1.25a.75.75 0 0 1 0 1.5H14v1.25A1.75 1.75 0 0 1 12.25 14H11v1.25a.75.75 0 0 1-1.5 0V14h-3v1.25a.75.75 0 0 1-1.5 0V14H3.75A1.75 1.75 0 0 1 2 12.25V11H.75a.75.75 0 0 1 0-1.5H2v-3H.75a.75.75 0 0 1 0-1.5H2V3.75C2 2.784 2.784 2 3.75 2H5V.75a.75.75 0 0 1 1.5 0Zm5.75 11.75a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25ZM5.75 5h4.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-4.5A.75.75 0 0 1 5.75 5Zm.75 4.5h3v-3h-3Z"></path></svg> **`ovrtx`** is the RTX worker - it produces the requested camera or sensor output.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-stack slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M7.122.392a1.75 1.75 0 0 1 1.756 0l5.003 2.902c.83.481.83 1.68 0 2.162L8.878 8.358a1.75 1.75 0 0 1-1.756 0L2.119 5.456a1.251 1.251 0 0 1 0-2.162ZM8.125 1.69a.248.248 0 0 0-.25 0l-4.63 2.685 4.63 2.685a.248.248 0 0 0 .25 0l4.63-2.685ZM1.601 7.789a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0L1.874 8.814A.75.75 0 0 1 1.6 7.789Zm0 3.5a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0l-5.248-3.044a.75.75 0 0 1-.273-1.025Z"></path></svg> **OpenUSD** is the scene record; **React** is the control surface.

### 2

The Frame Lifecycle: Apply, Step, Read

The application bridges `ovstream` and `ovrtx`.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-broadcast slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M8.75 8.582v5.668a.75.75 0 0 1-1.5 0V8.582a1.75 1.75 0 1 1 1.5 0Zm3.983-7.125a.75.75 0 0 1 1.06.026A7.976 7.976 0 0 1 16 7c0 2.139-.84 4.083-2.207 5.517a.75.75 0 1 1-1.086-1.034A6.474 6.474 0 0 0 14.5 7a6.474 6.474 0 0 0-1.793-4.483.75.75 0 0 1 .026-1.06Zm-9.466 0c.3.286.312.76.026 1.06A6.474 6.474 0 0 0 1.5 7a6.47 6.47 0 0 0 1.793 4.483.75.75 0 0 1-1.086 1.034A7.973 7.973 0 0 1 0 7c0-2.139.84-4.083 2.207-5.517a.75.75 0 0 1 1.06-.026Zm8.556 2.321A4.988 4.988 0 0 1 13 7a4.988 4.988 0 0 1-1.177 3.222.75.75 0 1 1-1.146-.967A3.487 3.487 0 0 0 11.5 7c0-.86-.309-1.645-.823-2.255a.75.75 0 0 1 1.146-.967Zm-6.492.958A3.48 3.48 0 0 0 4.5 7a3.48 3.48 0 0 0 .823 2.255.75.75 0 0 1-1.146.967A4.981 4.981 0 0 1 3 7a4.982 4.982 0 0 1 1.188-3.236.75.75 0 1 1 1.143.972Z"></path></svg> An **`ovstream`** callback stays short: it validates the command and queues an intent.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-arrow-switch slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M5.22 14.78a.75.75 0 0 0 1.06-1.06L4.56 12h8.69a.75.75 0 0 0 0-1.5H4.56l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3a.75.75 0 0 0 0 1.06l3 3Zm5.56-6.5a.75.75 0 1 1-1.06-1.06l1.72-1.72H2.75a.75.75 0 0 1 0-1.5h8.69L9.72 2.28a.75.75 0 0 1 1.06-1.06l3 3a.75.75 0 0 1 0 1.06l-3 3Z"></path></svg> The renderer owner **applies** the change, calls **`step()`**, and **reads** the output.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-cpu slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M6.5.75V2h3V.75a.75.75 0 0 1 1.5 0V2h1.25c.966 0 1.75.784 1.75 1.75V5h1.25a.75.75 0 0 1 0 1.5H14v3h1.25a.75.75 0 0 1 0 1.5H14v1.25A1.75 1.75 0 0 1 12.25 14H11v1.25a.75.75 0 0 1-1.5 0V14h-3v1.25a.75.75 0 0 1-1.5 0V14H3.75A1.75 1.75 0 0 1 2 12.25V11H.75a.75.75 0 0 1 0-1.5H2v-3H.75a.75.75 0 0 1 0-1.5H2V3.75C2 2.784 2.784 2 3.75 2H5V.75a.75.75 0 0 1 1.5 0Zm5.75 11.75a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25ZM5.75 5h4.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-4.5A.75.75 0 0 1 5.75 5Zm.75 4.5h3v-3h-3Z"></path></svg> Only the renderer-owning loop may write transforms or step **`ovrtx`**.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-check-circle slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg> The app prepares the result and **`ovstream`** delivers it.

### 3

Named Poses Beat Relative Jogs

A robot application needs a deterministic state contract.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-arrow-switch slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M5.22 14.78a.75.75 0 0 0 1.06-1.06L4.56 12h8.69a.75.75 0 0 0 0-1.5H4.56l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3a.75.75 0 0 0 0 1.06l3 3Zm5.56-6.5a.75.75 0 1 1-1.06-1.06l1.72-1.72H2.75a.75.75 0 0 1 0-1.5h8.69L9.72 2.28a.75.75 0 0 1 1.06-1.06l3 3a.75.75 0 0 1 0 1.06l-3 3Z"></path></svg> **Relative jog** adds movement from the current state: HOME → −15 → −30 → −45.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-milestone slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M7.75 0a.75.75 0 0 1 .75.75V3h3.634c.414 0 .814.147 1.13.414l2.07 1.75a1.75 1.75 0 0 1 0 2.672l-2.07 1.75a1.75 1.75 0 0 1-1.13.414H8.5v5.25a.75.75 0 0 1-1.5 0V10H2.75A1.75 1.75 0 0 1 1 8.25v-3.5C1 3.784 1.784 3 2.75 3H7V.75A.75.75 0 0 1 7.75 0Zm4.384 8.5a.25.25 0 0 0 .161-.06l2.07-1.75a.248.248 0 0 0 0-.38l-2.07-1.75a.25.25 0 0 0-.161-.06H2.75a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h9.384Z"></path></svg> **Named pose** derives every target from a recorded HOME: LEFT, HOME, RIGHT.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-check-circle slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm1.5 0a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm10.28-1.72-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l1.47 1.47 3.97-3.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg> **Idempotence** - replaying the same command reaches the same final transform.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-shield-check slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg> Automation gets three fixed transforms it can trust and replay.

## Mission 2.b — Establish the Cube Link

### 1. Inspect — Define the Object and Transform Contract

You can recognize the glowing Memory Cube by sight. A robot application needs a more dependable connection: the cube’s exact OpenUSD prim path and a transform command with a predictable result.

Before the scene is approved for robot integration, prove the application can:

- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> Resolve the correct prim in the composed OpenUSD stage.
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> Record its complete starting transform.
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> Move it to three known poses.
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> Report the resulting state in the browser.

The Signal Panel will receive three controls:

```text
LEFT POSE −15
HOME
RIGHT POSE +15
```

These are the named poses we extend from the browser to the Memory Cube:

```text
LEFT  = HOME transform with parent-space X −15
HOME  = exact recorded starting transform
RIGHT = HOME transform with parent-space X +15
```

For this lab, `15` means **15 authored scene units along the X axis of the cube’s parent** - not screen pixels or “left from the current camera.” Because every target derives from the recorded HOME, repeating a named-pose command produces the same final transform. This property is **idempotence**, and it is why R-17 uses a named-pose interface rather than a relative jog: LEFT, HOME, and RIGHT must always refer to the same three transforms.

You’ll make this concrete in [**Specify — Author Your Cube-Link Brief**]() below: decide which object is safe to command and what replay-safe evidence you will require before you approve the result.

This is where rigging and animation knowledge becomes physical-AI engineering. You know that “move the cube” is incomplete until the object boundary, pivot, parent space, reference pose, and replay behavior are explicit. The application then divides the runtime work cleanly: React requests and displays; `ovstream` transports; the Python server validates and queues; OpenUSD supplies identity and authored state; and the renderer owner uses `ovrtx` to apply, step, and read.

### How the Skill Combination Divides the Work

| `ovrtx` Skill        | Responsibility                                            |
|----------------------|-----------------------------------------------------------|
| `stage-queries`      | Find and identify prims in the loaded stage.              |
| `reading-attributes` | Read and preserve authored values.                        |
| `writing-transforms` | Write a prim’s transform safely and repeatedly.           |
| `camera-outputs-rt2` | Select camera AOVs such as color, depth, or semantic IDs. |

| Responsibility                       | Skill                    |
|--------------------------------------|--------------------------|
| Find the Memory Cube prim.           | `stage-queries`          |
| Capture its complete HOME transform. | `reading-attributes`     |
| Apply the selected named pose.       | `writing-transforms`     |
| Produce a frame showing the result.  | `stepping-and-rendering` |

The hero skill is **`writing-transforms`**, but it cannot complete the workflow alone. The agent must first identify the prim and read its starting state, then render the result after the write.

The skills do not add a new transform feature to `ovrtx`. They give the agent the version-specific procedure for using the transform capability correctly.

<a id="specify-cube-link-brief"></a>

### 2. Specify — Author Your Cube-Link Brief

Now turn that judgment into your brief. Decide which authored object the application may command, and define what evidence proves a pose is replay-safe: repeating LEFT must land on the identical transform, and HOME must restore the exact recorded pose. Open the [Prompt Builder](https://docs.nvidia.com/learning/physical-ai/physical-ai-agent-bootcamp/latest/prompt-builder.md), choose the **Ray Trace Your Way to a Better Life** session, and select **Mission 2.b - Establish the Cube Link**. Record your object boundary and that replay-safe evidence in `Context` and `Done when`. Leave the optional fields blank to keep `/Root/Workshop/Cube` and the tested LEFT/HOME/RIGHT contract.

### 3. Build — Add Your Transform Judgment to the Mission Brief

### Show a finished prompt

```text
Goal:      Extend the R-17 Signal Panel with a Memory Cube Link. Query the loaded
           stage for exactly one prim whose mission:role is "Memory Cube" and display
           its resolved OpenUSD prim path. Record that prim's complete starting
           transform once as HOME. Add LEFT POSE −15, HOME, and RIGHT POSE +15
           controls. Use a short visible linear transition for accepted changes, but
           derive every final target from HOME rather than the cube's current position.
Skills:    Read the installed ovrtx skills for stage-queries, reading-attributes,
           writing-transforms, and stepping-and-rendering. Use writing-transforms as the
           hero skill. Read ~/RTXViewport/skills/omniverse-realtime-viewer/SKILL.md
           and only its focused prim-transform-safety, streaming-messages,
           viewer-control-patterns, viewer-feedback-status, and validation references.
           Before implementing, read ~/RTXViewport/ATTIC_PORTAL_STARTER.md and reuse
           the snippets in ~/RTXViewport/Attic_Portal_Starter_Kit for AppStreamer
           messages, ovrtx transform helpers, R-17 typed envelopes/hooks,
           request-correlated buttons, and validation.
Context:   Extend ~/RTXViewport/Attic_Portal and its existing R-17 Signal Panel.
           Use the R17CommandButton component, request-correlated r17-command-v1 sender,
           and r17-state-v1 subscription created in Part 1. Do not create another React
           provider, message protocol, renderer, stream, or WebRTC connection. Add these
           controls to
           ~/RTXViewport/Attic_Portal/frontend/src/components/R17SignalPanel.tsx:
           LEFT POSE −15 sends cube.setPose with { pose: "LEFT" }; HOME sends cube.setPose
           with { pose: "HOME" }; and RIGHT POSE +15 sends cube.setPose with
           { pose: "RIGHT" }. Each command carries a requestId. The server validates it and
           queues one named-pose intent; only the renderer-owning loop may write omni:xform
           or call renderer.step(). Capture the cube's full HOME transform before the first
           mission command and preserve its rotation and scale when deriving LEFT and RIGHT.
           Use a short visible linear translation inside the existing frame loop, disable
           all three pose controls until matching READY or ERROR state returns, and allow
           only one cube-pose request in flight. A duplicate requestId or a request for the
           already active pose must acknowledge the current result without restarting
           movement. If /Root/Workshop/MemoryCubeGlow exists, keep its translation
           synchronized with the cube without treating the light as the mission target.
           Publish server-authoritative r17-state-v1 fields for requestId, cubePath,
           requestedPose, offsetX constrained to −15, 0, or +15 relative to HOME, and
           WAITING, APPLYING, READY, or ERROR. The browser may change button status only
           from state carrying the matching requestId. Never modify files inside
           ~/RTXViewport/Attic_Nvidia.
Done when: The focused Cube Link check passes; the panel displays /Root/Workshop/Cube and
           working LEFT POSE −15, HOME, and RIGHT POSE +15 controls; each accepted change
           finishes at its exact HOME-relative target; duplicate or already-active commands
           do not restart or change the final transform; HOME restores the complete recorded
           transform; rotation and scale remain unchanged; matching server state drives
           status; the existing renderer and stream remain healthy; source hashes are
           unchanged; and the agent reports the changed files, browser URL, start/restart
           command, and concise validation evidence. Return control without waiting for my
           browser interaction.
```

![Agent terminal output planning the Cube Link mission - it selects writing-transforms as the hero ovrtx skill supported by stage-queries, reading-attributes, and stepping-and-rendering, then lists a four-step plan to read the required skills, inspect the portal, apply the prepared overlay, and run focused validation.](images/lab-1-agent-plan-cube-link.png)

### 4. Verify — Approve the Transform Contract

Test **HOME → LEFT → LEFT → RIGHT → HOME**. The repeated LEFT request must finish at the identical transform rather than accumulating another offset. HOME must restore the complete recorded transform, including rotation and scale. Confirm that the panel reports the same stable prim path and that matching server state—not a local button animation—proves completion.

Then compare the result with the brief you authored. Your hierarchy and rigging judgment defined which prim was safe to command and what replay-safe meant; the skills taught the agent how to implement that contract with the installed `ovrtx` version.

![The R-17 Signal Panel Memory Cube Link section showing the resolved prim path /Root/Workshop/Cube and LEFT POSE minus 15, HOME, and RIGHT POSE plus 15 controls, with a READY status.](images/lab-1-cube-link-controls.png)

## Mission 2.c — Build a Trustworthy Cube Focus View

### 1. Inspect — Treat the Camera as a Virtual Sensor

The Cube Link proves the application can name and control the correct prim. Now commission a close inspection view without modifying the Old Attic.

**Why this matters for a robot-ready scene:** A useful inspection camera must cover the target’s full expected motion, not merely compose one attractive frame. That repeatable view gives robotics engineers a controlled observation for debugging perception, checking clearances, and comparing runs.

**Your 3D skills at work:** Camera blocking, lens choice, bounds, and composition become virtual-sensor coverage. OpenUSD layering brings the non-destructive editorial workflow you already use in production: the application can add a calibrated view while the canonical environment remains unchanged.

- Its transform defines **extrinsics** - where it is and where it points.
- Its lens attributes define **intrinsics** - how it observes the world.
- Its target bounds determine whether the object stays visible across the cube’s expected motion.
- Its viewer-owned layer keeps this inspection configuration separate from the shared digital twin.

There is no single “create a camera” skill. A camera is an OpenUSD prim, so the workflow combines several kinds of guidance:

1. **Protect the source scene.** OpenUSD composition and Realtime Viewer `stage-loading` put the camera in a viewer-owned layer above the attic.
2. **Design the observation.** Realtime Viewer `camera-controls` helps calculate a valid Z-up pose covering the full target envelope.
3. **Apply the preset.** `ovrtx` `writing-transforms` applies its pose, and `writing-attributes` applies its lens values.
4. **Connect it to the application.** Realtime Viewer `camera-picker` routes the preset command and reports the active view.

![Diagram titled Camera Preset in a Nondestructive Layer, a conceptual OpenUSD view. On the left, the stage tree shows /Root/Workshop/Cube alongside a new /Session/Cameras/R17CubeFocus camera prim. In the center, the attic viewport switches between a default view and a Cube Focus view framing the glowing cube. On the right, camera attributes for /Session/Cameras/R17CubeFocus list translate, rotate, focalLength 50.0, horizontalAperture 36.0, and clippingRange, authored in R17_Camera.usda. At the bottom, the layer stack shows viewer-owned editable R17_Camera.usda composed above the protected read-only old_attic.usd, so camera changes live in the viewer layer while the attic geometry and materials remain unchanged.](images/lab-1-camera-preset-nondestructive-layer.png)

The skills tell the agent how to implement the camera. They cannot decide whether the framing is useful. Your layout and cinematography experience defines coverage, readable scale, necessary context, potential occlusion, and what would make the observation unacceptable.

### 2. Specify — Author the Camera Brief

Choose the framing, required attic context, coverage margin across LEFT/HOME/RIGHT, and the condition that should fail review. Open the [Prompt Builder](https://docs.nvidia.com/learning/physical-ai/physical-ai-agent-bootcamp/latest/prompt-builder.md), choose the **Ray Trace Your Way to a Better Life** session, and select **Mission 2.c - Commission the Cube Focus Camera**. Turn those choices into camera intent for `Context` and acceptance criteria for `Done when`. Leave the optional fields blank to keep the tested balanced framing.

### 3. Build — Add Your Camera Judgment to the Mission Brief

### Show a finished prompt

```text
Goal:      Create an R-17 Cube Focus camera preset that frames /Root/Workshop/Cube for
           close inspection. Author /Session/Cameras/R17CubeFocus in
           ~/RTXViewport/Attic_Portal/usd/R17_Camera.usda, compose that viewer-owned
           layer above the protected scene, and add DEFAULT VIEW and CUBE FOCUS controls
           to the R-17 Signal Panel.
Skills:    Read the installed ovrtx stage-queries, writing-transforms, and
           writing-attributes skills. Read
           ~/RTXViewport/skills/omniverse-realtime-viewer/SKILL.md and only its
           stage-loading, camera-controls, camera-picker, streaming-messages,
           viewer-control-patterns, and validation references. Do not use camera-outputs-rt2
           as a camera-creation skill; it selects requested render outputs, often called
           AOVs, rather than authoring cameras.
Context:   Reuse the Cube Link's resolved prim path, R17CommandButton, request-correlated
           r17-command-v1 sender, and r17-state-v1 subscription. Add DEFAULT VIEW and CUBE
           FOCUS to R17SignalPanel.tsx without creating another provider or connection.
           DEFAULT VIEW sends camera.setView with { view: "DEFAULT" }; CUBE FOCUS sends
           camera.setView with { view: "CUBE_FOCUS" }; every command carries a requestId.
           Compute one valid Z-up focus pose from the union of the cube's LEFT, HOME, and
           RIGHT world bounds so the target remains framed at every named pose while enough
           attic context remains visible. Keep R17_Camera.usda application-owned and never
           save over ~/RTXViewport/Attic_Nvidia. DEFAULT VIEW is the fitted startup transform
           and lens preset captured in Part 1, not the learner's camera position when this
           prompt runs. Treat R17CubeFocus as another authored preset: on a validated
           command, the renderer owner uses writing-transforms for its pose and
           writing-attributes for its lens values on the existing active viewer camera,
           preserving one RenderProduct and one video track. Publish requestId, activeView,
           and READY or ERROR as matching server state only after a successful frame. An
           accepted native orbit, pan, or zoom intent changes activeView to CUSTOM. Preserve
           navigation after every preset selection.
Done when: The focused camera check passes; R17_Camera.usda contains
           /Session/Cameras/R17CubeFocus; DEFAULT VIEW restores the Part 1 startup preset;
           CUBE FOCUS frames the full three-pose cube envelope; the panel reports DEFAULT,
           CUBE_FOCUS, or CUSTOM from matching server state; orbit, pan, and zoom still
           work; no second renderer, stream, or WebRTC connection exists; source hashes are
           unchanged; and the agent reports the new layer, camera path, changed files, and
           concise evidence. Return control without waiting for my clicks.
```

### 4. Verify — Approve the Observation

Switch between **DEFAULT VIEW** and **CUBE FOCUS**, then test LEFT, HOME, and RIGHT. Judge the result against the framing, context, margin, and rejection criteria you authored—not against whether the camera merely changed. Orbit once and confirm the view becomes `CUSTOM`, then restore the two presets.

Verify the nondestructive contract as well: `/Session/Cameras/R17CubeFocus` belongs to viewer-owned `R17_Camera.usda`, while the supplied attic remains unchanged. The prompt’s workflow skills implemented the camera; your camera judgment determined whether its evidence was trustworthy.

![The R-17 Signal Panel with the added Camera View section - DEFAULT VIEW and CUBE FOCUS presets - and the viewport framed closely on the glowing Memory Cube.](images/lab-1-cube-focus-view.png)

Identity, control, and focus are proven. Next, add observations a machine does not have to guess in [Add Machine Perception](https://docs.nvidia.com/learning/physical-ai/physical-ai-agent-bootcamp/latest/lab-1-rtx-viewport/give-r17-machine-perception.md).
