Init Repo
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-09
|
||||
@@ -0,0 +1,70 @@
|
||||
## 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.
|
||||
@@ -0,0 +1,29 @@
|
||||
## Why
|
||||
|
||||
The current viewport uses a fixed internal camera with basic orbit/pan/zoom controls that do not match industry-standard DCC conventions. Users cannot view the scene through USD stage cameras (e.g. perspective cameras authored in the USD file), and the input mapping differs from Maya's Alt+button convention, making the tool feel foreign to target users. Adding Maya-style controls and the ability to frame the selected prim are essential for a usable USD editing workflow.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add Maya-style viewport navigation: Alt+LMB to orbit, Alt+MMB to pan, Alt+RMB (or scroll) to dolly/zoom, with optional Alt-free scroll zoom
|
||||
- Add the ability to switch the viewport camera between the built-in free camera and any camera prim found on the USD stage
|
||||
- Add a "Frame Selected" action (press F key) that frames the viewport on the currently selected prim's bounding box
|
||||
- Add a "Frame All" action (press A key) that frames the viewport on the entire stage bounds
|
||||
- Add a camera selector dropdown in the viewport toolbar to switch between free camera and stage cameras
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `maya-viewport-controls`: Maya-style Alt+button navigation scheme replacing the current LMB/MMB/scroll mapping
|
||||
- `viewport-camera-switching`: Switch viewport rendering between the built-in free camera and USD stage camera prims
|
||||
- `viewport-frame-selection`: Frame viewport on selected prim (F key) and frame all (A key)
|
||||
|
||||
### Modified Capabilities
|
||||
- `imgui-docking`: No spec-level requirement changes; the viewport toolbar UI integrates within the existing dockable panel system
|
||||
|
||||
## Impact
|
||||
|
||||
- **ViewportCamera**: Add support for driving the view from an external USD camera prim (reading its transform and projection); refactor internal state to support both free-camera and USD-camera modes
|
||||
- **ViewportPanel**: Rewrite `HandleInput()` to use Maya-style Alt+button conventions; add camera selector UI in the viewport toolbar; add Frame Selected / Frame All hotkeys; expose camera switching
|
||||
- **UsdSceneRenderer**: No changes required — it already accepts view/projection matrices
|
||||
- **Application**: Wire up the selected prim path from SceneHierarchyPanel to the viewport for frame-selected functionality
|
||||
- **Dependencies**: No new third-party dependencies; all changes use existing OpenUSD and ImGui APIs
|
||||
@@ -0,0 +1,49 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Alt+LMB orbit navigation
|
||||
|
||||
The viewport SHALL orbit the camera around the focal point when the user holds Alt and drags with the left mouse button.
|
||||
|
||||
#### Scenario: Alt+LMB drag orbits the camera
|
||||
- **WHEN** the user holds Alt and drags with the left mouse button inside the viewport
|
||||
- **THEN** the camera orbits around the focal point based on the drag delta
|
||||
|
||||
#### Scenario: LMB without Alt does not orbit
|
||||
- **WHEN** the user drags with the left mouse button without holding Alt
|
||||
- **THEN** no orbit occurs and the click is forwarded for prim selection
|
||||
|
||||
### Requirement: Alt+MMB pan navigation
|
||||
|
||||
The viewport SHALL pan the camera when the user holds Alt and drags with the middle mouse button.
|
||||
|
||||
#### Scenario: Alt+MMB drag pans the camera
|
||||
- **WHEN** the user holds Alt and drags with the middle mouse button inside the viewport
|
||||
- **THEN** the camera pans horizontally and vertically based on the drag delta
|
||||
|
||||
### Requirement: Alt+RMB dolly navigation
|
||||
|
||||
The viewport SHALL dolly (zoom) the camera when the user holds Alt and drags with the right mouse button. Vertical drag upward moves the camera closer; downward moves it farther.
|
||||
|
||||
#### Scenario: Alt+RMB drag dollies the camera
|
||||
- **WHEN** the user holds Alt and drags vertically with the right mouse button inside the viewport
|
||||
- **THEN** the camera dollies in or out based on the vertical drag delta
|
||||
|
||||
### Requirement: Scroll zoom without Alt
|
||||
|
||||
The viewport SHALL zoom the camera when the user scrolls the mouse wheel, without requiring the Alt modifier.
|
||||
|
||||
#### Scenario: Scroll wheel zooms
|
||||
- **WHEN** the user scrolls the mouse wheel while hovering over the viewport
|
||||
- **THEN** the camera zooms in or out based on the scroll direction
|
||||
|
||||
### Requirement: Legacy input mappings removed
|
||||
|
||||
The previous input mappings (LMB for orbit, MMB for pan, Shift+LMB for pan) SHALL be replaced entirely by the Maya-style Alt+button scheme.
|
||||
|
||||
#### Scenario: MMB without Alt does not pan
|
||||
- **WHEN** the user drags with the middle mouse button without holding Alt
|
||||
- **THEN** no pan occurs
|
||||
|
||||
#### Scenario: Shift+LMB does not pan
|
||||
- **WHEN** the user holds Shift and drags with the left mouse button
|
||||
- **THEN** no pan occurs
|
||||
@@ -0,0 +1,62 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Camera selector dropdown in viewport
|
||||
|
||||
The viewport panel SHALL display a camera selector dropdown above the rendering canvas. The dropdown SHALL list "Free Camera" as the first entry, followed by all UsdCamera-typed prims found on the current USD stage.
|
||||
|
||||
#### Scenario: Dropdown shows free camera and stage cameras
|
||||
- **WHEN** a USD stage is loaded and contains camera prims at paths `/cameras/main` and `/cameras/top`
|
||||
- **THEN** the camera selector dropdown lists: "Free Camera", "/cameras/main", "/cameras/top"
|
||||
|
||||
#### Scenario: Dropdown shows only free camera with no stage cameras
|
||||
- **WHEN** a USD stage is loaded and contains no camera prims
|
||||
- **THEN** the camera selector dropdown lists only "Free Camera"
|
||||
|
||||
#### Scenario: No stage loaded
|
||||
- **WHEN** no USD stage is loaded
|
||||
- **THEN** the camera selector dropdown is disabled or shows only "Free Camera"
|
||||
|
||||
### Requirement: Switch to USD camera prim view
|
||||
|
||||
When the user selects a USD camera prim from the dropdown, the viewport SHALL render the scene from that camera's viewpoint by reading its transform and projection attributes.
|
||||
|
||||
#### Scenario: Select a USD camera from dropdown
|
||||
- **WHEN** the user selects "/cameras/main" from the camera dropdown
|
||||
- **THEN** the viewport renders the scene using the view matrix derived from that camera prim's world transform and the projection matrix derived from its focalLength, horizontalAperture, and clippingRange attributes
|
||||
|
||||
#### Scenario: USD camera attribute mapping
|
||||
- **WHEN** the viewport is rendering through a UsdCamera prim
|
||||
- **THEN** the projection matrix SHALL be computed from the camera's `focalLength`, `horizontalAperture`, `verticalAperture`, `clippingRange`, and `projection` attributes
|
||||
- **AND** the view matrix SHALL be computed from the camera prim's world-space transform
|
||||
|
||||
### Requirement: Free camera navigation disabled in USD camera mode
|
||||
|
||||
When the viewport is in USD camera mode, orbit, pan, dolly, and zoom controls SHALL be disabled.
|
||||
|
||||
#### Scenario: Attempt orbit while in USD camera mode
|
||||
- **WHEN** the viewport is viewing through a USD camera and the user performs Alt+LMB drag
|
||||
- **THEN** no orbit occurs and the viewport remains at the USD camera's viewpoint
|
||||
|
||||
### Requirement: Free camera state preserved across mode switches
|
||||
|
||||
The free camera's position, focal point, and orientation SHALL be preserved when switching to a USD camera and restored when switching back.
|
||||
|
||||
#### Scenario: Switch to USD camera and back
|
||||
- **WHEN** the user positions the free camera, switches to a USD camera, then switches back to "Free Camera"
|
||||
- **THEN** the viewport returns to the exact free camera position and orientation before the switch
|
||||
|
||||
### Requirement: Camera list refreshed on stage change
|
||||
|
||||
The list of available USD camera prims SHALL be refreshed when the stage changes (file open, close, or stage reload).
|
||||
|
||||
#### Scenario: Open a new stage with different cameras
|
||||
- **WHEN** the user opens a new USD file containing cameras at different paths
|
||||
- **THEN** the camera selector dropdown updates to reflect the new stage's camera prims
|
||||
|
||||
### Requirement: Default camera mode is free camera
|
||||
|
||||
The viewport SHALL start in free camera mode every time a stage is loaded.
|
||||
|
||||
#### Scenario: Stage loaded defaults to free camera
|
||||
- **WHEN** a USD stage is loaded
|
||||
- **THEN** the camera selector is set to "Free Camera" and the viewport renders from the built-in free camera
|
||||
@@ -0,0 +1,46 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Frame selected prim with F key
|
||||
|
||||
The viewport SHALL frame the camera on the bounding box of the currently selected prim when the user presses the F key while the viewport is focused.
|
||||
|
||||
#### Scenario: Frame a selected mesh prim
|
||||
- **WHEN** the user has a mesh prim selected in the scene hierarchy and presses F while the viewport is focused
|
||||
- **THEN** the camera repositions to frame the bounding box of that prim and all its descendants in world space
|
||||
|
||||
#### Scenario: Frame with no prim selected
|
||||
- **WHEN** the user presses F with no prim selected
|
||||
- **THEN** no camera movement occurs
|
||||
|
||||
#### Scenario: Frame in USD camera mode
|
||||
- **WHEN** the viewport is in USD camera mode and the user presses F
|
||||
- **THEN** the viewport switches to free camera mode and frames the selected prim
|
||||
|
||||
### Requirement: Frame all with A key
|
||||
|
||||
The viewport SHALL frame the camera on the bounding box of the entire USD stage when the user presses the A key while the viewport is focused.
|
||||
|
||||
#### Scenario: Frame all prims in the stage
|
||||
- **WHEN** the user presses A while the viewport is focused and a stage is loaded
|
||||
- **THEN** the camera repositions to frame the bounding box of the entire stage
|
||||
|
||||
#### Scenario: Frame all with no stage loaded
|
||||
- **WHEN** the user presses A with no stage loaded
|
||||
- **THEN** no camera movement occurs
|
||||
|
||||
### Requirement: Selected prim path sourced from SceneHierarchyPanel
|
||||
|
||||
The viewport SHALL obtain the currently selected prim path from the `SceneHierarchyPanel` via a callback wired by the `Application` class.
|
||||
|
||||
#### Scenario: Selection change triggers prim path update
|
||||
- **WHEN** the user selects a different prim in the SceneHierarchyPanel
|
||||
- **THEN** the viewport's stored selected prim path updates to reflect the new selection
|
||||
- **AND** pressing F frames the newly selected prim
|
||||
|
||||
### Requirement: Bounding box computed via UsdGeomBBoxCache
|
||||
|
||||
The bounding box for framing a selected prim SHALL be computed using `UsdGeomBBoxCache` to correctly account for transforms, instancing, and purpose.
|
||||
|
||||
#### Scenario: Bounding box includes descendant geometry
|
||||
- **WHEN** the user frames a prim that has mesh children
|
||||
- **THEN** the bounding box encompasses the prim and all its descendants' geometry in world space
|
||||
@@ -0,0 +1,42 @@
|
||||
## 1. Maya-style Viewport Controls
|
||||
|
||||
- [x] 1.1 Refactor `ViewportPanel::HandleInput()` to use Alt+LMB for orbit, Alt+MMB for pan, Alt+RMB for dolly
|
||||
- [x] 1.2 Remove legacy input mappings (LMB orbit, MMB pan, Shift+LMB pan) from `HandleInput()`
|
||||
- [x] 1.3 Keep mouse scroll zoom working without Alt modifier
|
||||
- [x] 1.4 Add Alt key detection via `io.KeyAlt` in ImGui and ensure Alt is not consumed by ImGui menu navigation
|
||||
|
||||
## 2. ViewportCamera Dual-Mode Support
|
||||
|
||||
- [x] 2.1 Add `enum class CameraMode { Free, UsdCamera }` and a `m_mode` member to `ViewportCamera`
|
||||
- [x] 2.2 Add `m_usdCameraPath` (SdfPath) and `m_savedFreeCameraState` struct to persist free camera state across mode switches
|
||||
- [x] 2.3 Add `SetUsdCamera(const UsdStageRefPtr& stage, const SdfPath& cameraPath)` method that reads UsdCamera attributes (focalLength, horizontalAperture, verticalAperture, clippingRange, projection) and world transform
|
||||
- [x] 2.4 Implement `ComputeUsdCameraViewMatrix()` deriving the view matrix from the UsdCamera prim's world-space transform
|
||||
- [x] 2.5 Implement `ComputeUsdCameraProjectionMatrix()` deriving the projection matrix from UsdCamera focalLength, aperture, and clip attributes
|
||||
- [x] 2.6 Modify `GetViewMatrix()` and `GetProjectionMatrix()` to dispatch based on `m_mode` (free vs USD camera)
|
||||
- [x] 2.7 Add `SwitchToFreeCamera()` that restores the saved free camera state
|
||||
- [x] 2.8 Disable orbit/pan/zoom/dolly calls when `m_mode == UsdCamera` (no-op in USD camera mode)
|
||||
|
||||
## 3. Camera Selector UI
|
||||
|
||||
- [x] 3.1 Add method `FindCameraPrims(const UsdStageRefPtr& stage)` to traverse the stage and return a vector of SdfPaths for all UsdCamera-typed prims
|
||||
- [x] 3.2 Cache the camera list in `ViewportPanel` and invalidate on stage change
|
||||
- [x] 3.3 Add an ImGui combo dropdown above the viewport canvas in `ViewportPanel::Render()` with "Free Camera" as the first entry followed by discovered camera prim paths
|
||||
- [x] 3.4 Wire the dropdown selection to call `ViewportCamera::SetUsdCamera()` or `SwitchToFreeCamera()`
|
||||
- [x] 3.5 Reset the camera selector to "Free Camera" when a new stage is loaded via `ViewportPanel::SetStage()`
|
||||
|
||||
## 4. Frame Selected Prim
|
||||
|
||||
- [x] 4.1 Add `m_selectedPrimPath` string member to `ViewportPanel` and a setter `SetSelectedPrimPath(const std::string&)`
|
||||
- [x] 4.2 Wire `Application` to call `ViewportPanel::SetSelectedPrimPath()` from the `SceneHierarchyPanel::SetOnPrimSelected()` callback
|
||||
- [x] 4.3 Add F key handler in `ViewportPanel::HandleInput()`: compute bounding box of the selected prim using `UsdGeomBBoxCache` and call `FrameBoundingBox`
|
||||
- [x] 4.4 If viewport is in USD camera mode when F is pressed, switch to free camera mode first then frame
|
||||
- [x] 4.5 Add A key handler in `ViewportPanel::HandleInput()`: call `FrameScene()` to frame all
|
||||
|
||||
## 5. Integration and Verification
|
||||
|
||||
- [x] 5.1 Verify Maya-style Alt+button navigation works for orbit, pan, and dolly in the viewport
|
||||
- [x] 5.2 Verify camera selector dropdown lists free camera and all stage camera prims
|
||||
- [x] 5.3 Verify switching to a USD camera renders from that camera's viewpoint
|
||||
- [x] 5.4 Verify switching back to free camera restores the previous free camera position
|
||||
- [x] 5.5 Verify F key frames the selected prim and A key frames the entire stage
|
||||
- [x] 5.6 Build and run the application to confirm no regressions in existing viewport rendering
|
||||
Reference in New Issue
Block a user