## Context The USD Layer Manager application currently has a viewport panel (`ViewportPanel`) driven by a built-in `ViewportCamera` class. The camera supports orbit, pan, zoom, and framing via bounding box. Input handling uses a direct LMB/MMB/scroll mapping without modifier keys. There is no concept of switching to a USD stage camera prim — the viewport always renders from the free camera's viewpoint. The target users are Maya artists who expect Alt+button viewport navigation and the ability to view through authored cameras in the USD stage. The current input scheme is a friction point, and the inability to look through stage cameras limits creative preview workflows. ### Current State - `ViewportCamera` stores eye, focal point, orbit angles (yaw/pitch), distance, FOV, clip planes, aspect ratio - `ViewportPanel::HandleInput()` maps: LMB=orbit, MMB=pan, scroll=zoom, Shift+LMB=pan - `ViewportPanel::FrameScene()` frames on the entire stage bounds - No USD camera prim integration; no per-prim framing; no hotkey support ## Goals / Non-Goals **Goals:** - Replace the viewport input mapping with Maya-style Alt+button navigation (Alt+LMB orbit, Alt+MMB pan, Alt+RMB dolly, scroll zoom) - Allow switching between the built-in free camera and any UsdCamera prim on the stage - Support framing the viewport on the selected prim's bounding box (F key) and the full stage (A key) - Add a camera selector dropdown in the viewport panel UI **Non-Goals:** - Authoring or editing USD camera prims through the viewport (that belongs in the Property Panel) - Camera animation or sequencing (playblast, camera sequencer) - Multiple viewports or split-view layouts - Custom key binding configuration UI (hardcoded Maya-style for now) ## Decisions ### D1: Alt+button navigation with scroll exception **Decision**: Use Alt+LMB for orbit, Alt+MMB for pan, Alt+RMB for dolly. Mouse scroll zooms without Alt (matching Maya behavior). **Rationale**: This is the standard Maya viewport convention. Scroll zoom without Alt is expected because scroll has no other viewport conflict. **Alternative considered**: Keep current LMB/MMB/scroll mapping as an option — rejected to avoid maintaining two input schemes and confusing users about the default. ### D2: Dual-mode ViewportCamera (free vs USD camera) **Decision**: Extend `ViewportCamera` with an enum mode (`Free` / `UsdCamera`). In `UsdCamera` mode, the view/projection matrices are derived from the UsdCamera prim's transforms and attributes; the orbit/pan/zoom controls are disabled. In `Free` mode, behavior is unchanged. **Rationale**: Keeping both modes in one class avoids duplicating the projection math and makes switching seamless. Disabling manual navigation when looking through a USD camera matches Maya's behavior (you navigate the camera's transform instead, which is out of scope for now). **Alternative considered**: Separate `UsdCameraAdapter` class that wraps a UsdCamera into the same interface — more complex, no clear benefit until camera manipulation is needed. ### D3: Camera prim discovery and selection UI **Decision**: Traverse the stage for all `UsdCamera`-typed prims on each frame (or on stage change). Present them in an ImGui combo dropdown above the viewport canvas. The first entry is always "Free Camera". **Rationale**: Simple implementation; camera lists in typical USD scenes are small. Cache invalidation is handled by re-traversing on stage change notifications. **Alternative considered**: Maintain a persistent camera registry — over-engineered for the current scope. ### D4: Frame selected via SceneHierarchyPanel selection **Decision**: The `ViewportPanel` holds a callback/string for the selected prim path. `Application` wires the `SceneHierarchyPanel` selection callback to update this path. Pressing F computes the selected prim's bounding box and calls `FrameBoundingBox`. Pressing A calls the existing `FrameScene`. **Rationale**: Minimal coupling; `ViewportPanel` doesn't need to know about `SceneHierarchyPanel` directly. The Application layer already owns both panels and can wire them. ### D5: Bounding box computation for selected prim **Decision**: Use `UsdGeomBBoxCache` to compute the world-space bounding box of the selected prim and its descendants, then call `ViewportCamera::FrameBoundingBox`. **Rationale**: This is the standard OpenUSD approach for bounding box queries and handles instancing, transforms, and purpose correctly. ## Risks / Trade-offs - **[Alt key conflicts with ImGui]** → ImGui may consume Alt for menu activation. Mitigation: Set `ImGuiConfigFlags_NoNav` or handle Alt detection via GLFW directly before ImGui processes it. If ImGui captures Alt, fall back to detecting Alt via `io.KeyAlt` and consuming the input in the viewport's `HandleInput()`. - **[Camera prim traversal cost on large stages]** → Re-traversing every frame is wasteful for stages with thousands of prims. Mitigation: Cache the camera list and invalidate on stage change only. For initial implementation, traversal per frame is acceptable; optimize later. - **[USD camera attribute coverage]** → Not all UsdCamera attributes (e.g., lens distortion, stereo) can be represented in the current ViewportCamera projection. Mitigation: Support core attributes (focalLength, horizontalAperture, clippingRange, projection type) and ignore unsupported attributes gracefully. - **[Free camera state lost on switch]** → When switching from USD camera back to free camera, the free camera's last position is restored. Mitigation: Store the free camera state separately and restore it on switch-back.