Init Repo
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-30
|
||||
@@ -0,0 +1,71 @@
|
||||
## Context
|
||||
|
||||
The app currently has a single `ViewportPanel` class that owns one `ViewportCamera`, one `UsdSceneRenderer`, and draws a full-window render with toolbars + overlays. Selection propagates through `OnPrimPicked` / `OnPrimsPickedRect` callbacks to `SceneHierarchyPanel` and `PropertyPanel`. The `UsdSceneRenderer` owns a `GlfDrawTarget` FBO; the render loop calls `m_renderer.Render()` then renders overlays (axis, bbox, gizmo, manipulator toolbar) into the same FBO or directly to the screen via ImGui draw lists.
|
||||
|
||||
The `ViewportCamera` and `UsdSceneRenderer` classes are already designed as independent units — they can be instantiated per-viewport without modification. The key architectural challenge is sharing the selection state across N viewport tiles while ensuring only the focused tile renders the transform gizmo.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Support 1, 2 (horizontal/vertical split), and 4-quadrant viewport layouts
|
||||
- Each viewport tile has its own camera, renderer, and per-viewport settings (grid, AA, bbox mode, background color, render delegate)
|
||||
- Single shared selection: picking in any tile updates all tiles and propagates to panels
|
||||
- Transform manipulator (gizmo) renders only in the focused tile
|
||||
- Drag-based dividers to resize tiles within a layout
|
||||
- Backward-compatible `Application` API: `m_viewportPanel` stays as the entry point
|
||||
- Space-key maximize/restore: pressing Space over the hovered tile temporarily fills the entire viewport area; pressing Space again restores the previous layout (Maya-style)
|
||||
|
||||
**Non-Goals:**
|
||||
- Not adding tear-off/undockable viewport windows (ImGui docking handles that separately)
|
||||
- Not adding per-viewport layer visibility overrides (future feature)
|
||||
- Not supporting more than 4 tiles (can be extended later)
|
||||
- Not adding viewport sync (playblasting) or timeline scrubbing across tiles
|
||||
|
||||
## Decisions
|
||||
|
||||
1. **Extract `ViewportTile` from `ViewportPanel` rather than subclassing.**
|
||||
- The existing 1100-line `ViewportPanel::Render()` method contains everything: toolbar rendering, camera resolution, scene rendering, overlay drawing, context menu, and manipulator. Extracting a `ViewportTile` class that owns a camera + renderer + local per-viewport settings + the subset of overlay logic that is per-tile keeps the container clean.
|
||||
- `ViewportPanel` becomes the container: it owns `std::vector<std::unique_ptr<ViewportTile>>`, manages layout geometry, and delegates `Render()` calls to each tile.
|
||||
- Alternative considered: having `ViewportPanel` contain N inline structs. This was rejected because tiles need their own camera/resolution/overlay lifecycle, which calls for a proper class.
|
||||
|
||||
2. **Selection lives on the container (`ViewportPanel`), not on any tile.**
|
||||
- Each tile still calls `m_renderer.SetSelectedPaths()` and `m_renderer.AddSelected()` so its `UsdImagingGLEngine` highlights the correct prims, but the authoritative `m_selectedSdfPaths` / `m_selectedPrimPath` vector lives on `ViewportPanel`.
|
||||
- When a tile receives a pick hit, it calls back into the container to update the shared selection, and the container broadcasts it to all tiles and to the `OnPrimPicked`/`OnPrimsPickedRect` callbacks.
|
||||
- Alternative considered: each tile owns its selection and syncs via signals. Rejected because it adds complexity for a single-authority selection model.
|
||||
|
||||
3. **Manipulator ownership moves to `ViewportPanel` (container), not per tile.**
|
||||
- The `TransformManipulator` is a singleton — there should never be two gizmos active. The container owns it and only passes it to the focused tile for rendering.
|
||||
- The focused tile is the one that last received a `ImGui::IsWindowHovered()` click. Tile tracks `m_isFocused` set by container during `HandleInput()`.
|
||||
|
||||
4. **Layout state stored as simple enum + divider positions, no external dependency.**
|
||||
- Layout enum values: `Single`, `HSplit` (2 tiles side-by-side), `VSplit` (2 tiles top-bottom), `Quad` (4 tiles).
|
||||
- Divider positions stored as normalized floats (`0.0–1.0`). Dragging a divider updates the normalized position, then `ViewportPanel::Render()` recomputes tile rectangles.
|
||||
- No XML/JSON config for layout — state is ephemeral (could be persisted via future settings system).
|
||||
- Alternative considered: `ImGui::Splitter()` based approach from ImGui demos. We'll implement a lightweight version directly since we need precise control over tile rectangle subdivision.
|
||||
|
||||
5. **Render delegate can differ per tile.**
|
||||
- Each tile's `UsdSceneRenderer` independently calls `SetRendererPlugin()`. This means each tile FBO can use e.g. Storm vs HdEmbree independently.
|
||||
- This is useful for comparing renderers side-by-side and is already supported by `UsdSceneRenderer`'s per-instance design.
|
||||
|
||||
6. **Space-key maximize stored as a simple toggle on the container, not on individual tiles.**
|
||||
- `ViewportPanel` stores `m_maximizedTileIndex` (-1 = not maximized) and `m_layoutBeforeMaximize` (the `LayoutMode` enum value before Space was pressed).
|
||||
- When Space is pressed (with tile hovered and `m_maximizedTileIndex == -1`): save `m_layoutBeforeMaximize`, set `m_maximizedTileIndex` to the hovered tile, and render only that tile filling the entire viewport area (no dividers, no layout menu).
|
||||
- When Space is pressed again (or Escape): restore `m_maximizedTileIndex` to -1 and re-render the saved layout.
|
||||
- Keyboard shortcuts (F, A, Q/W/E/R) continue to target the maximized tile as the de facto focused tile.
|
||||
- Alternative considered: pushing a separate layout mode `Maximized`. Rejected because it conflates layout state with a transient display mode; storing the pre-maximize layout explicitly is cleaner and avoids state machine complexity.
|
||||
|
||||
7. **Per-tile settings stored on `ViewportTile`, not on a settings struct singleton.**
|
||||
- Grid/AA/bg-color/bbox-mode/render-delegate are all fields on `ViewportTile`. The compact toolbar in each tile reads/writes these directly.
|
||||
- For layout persistence (future), tiles could serialize their settings.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
| Risk | Mitigation |
|
||||
|---|---|
|
||||
| N renderers means N FBO draw targets, each at full viewport resolution. For a 4K monitor with Quad layout, each tile is ~960×540 → 4× FBO allocations at that size. GPU memory may spike. | Cap layout to 4 tiles. Add a `m_renderScale` per tile (default 1.0) if perf issues arise. OpenUSD's `UsdImagingGLEngine` already has `renderParams` for resolution scaling. |
|
||||
| Input routing: multiple tiles competing for Alt+LMB, Alt+RMB, scroll, etc. | The container dispatches `HandleInput()` only to the **hovered** tile each frame. The focused tile's manipulator is the only one that can consume input. |
|
||||
| Right-click context menu: which tile owns it? | The context menu appears on the hovered tile. The popup (`ViewportContextMenuN`) is opened per-tile using unique IDs. |
|
||||
| Divider drag area is thin — hard to click on high-DPI. | Dividers are 6px wide with a 2px visual line in the centre. Cursor changes to resize cursor on hover. |
|
||||
| Keyboard shortcuts (F=frame, A=frame all, Q/W/E/R=manipulator) need to target the focused tile. | Shortcuts in the container delegate to the focused tile's camera/manipulator. If no tile is focused (e.g. user just clicked a panel outside the viewport), shortcuts are ignored. |
|
||||
| Space key might conflict with ImGui docking or text input. | Guard with `!io.WantTextInput` (same as existing F/A shortcuts). Space is not a default ImGui docking shortcut, so no conflict with docking. |
|
||||
| User might accidentally press Space when no tile is hovered (e.g. focus is on a panel). | Space is a no-op when no tile is hovered and viewport is not maximized. If already maximized, Space always restores regardless of hover state. |
|
||||
@@ -0,0 +1,37 @@
|
||||
## Why
|
||||
|
||||
Multi-viewport support is a standard feature in DCC tools (Maya, Houdini, Blender) that lets users view the same scene from different camera angles simultaneously. Currently the app has a single `ViewportPanel` — users cannot compare views (e.g., free camera + a USD camera prim, or top/orthographic + perspective) without switching back and forth. This limits scene inspection, layout work, and side-by-side comparison during shading and lighting iteration.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Introduce a **viewport layout system** that manages 1–4 viewport tiles within a single ImGui window area
|
||||
- Each tile has its own `ViewportCamera`, `UsdSceneRenderer`, selection highlight set, and per-viewport settings (grid, AA, background color, bounding box mode)
|
||||
- Selection is **shared** across all viewports (pick in any viewport, all viewports highlight)
|
||||
- The main `ViewportPanel` becomes a **container** that holds N child viewports (N=1 default, up to 4)
|
||||
- **Layout presets**: single, 2-split horizontal/vertical, 4-quadrant
|
||||
- Per-viewport camera toolbar remains (camera selector, render delegate, view options) but is **compact** (icon-only) to conserve space
|
||||
- The manipulator gizmo is only active in the **focused** viewport (the one that last received a click)
|
||||
- **Space-key maximize**: pressing Space while hovering over a tile temporarily maximizes that tile to fill the entire viewport area, hiding dividers and other tiles. Pressing Space again restores the previous multi-tile layout (identical to Maya's viewport maximize behavior)
|
||||
- **BREAKING**: `Application`'s `m_viewportPanel` member type changes (it remains `ViewportPanel` but `ViewportPanel` switches from a single renderer to a container)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `viewport-layout`: Layout management — splitting, merging, preset switching, drag-resize dividers, Space-key maximize/restore, persistence of layout choice
|
||||
- `per-viewport-camera`: Each tile has an independent `ViewportCamera`, camera selector dropdown, and free/USD camera switching
|
||||
- `per-viewport-settings`: Per-tile grid toggle, AA, background color, bounding box mode, render delegate selection (independent per viewport)
|
||||
- `shared-selection`: Single selection model across all viewports — picking in any tile updates all others and propagates to SceneHierarchyPanel / PropertyPanel
|
||||
- `focused-viewport-manipulator`: Transform gizmo only renders and accepts input in the most recently clicked viewport tile
|
||||
|
||||
### Modified Capabilities
|
||||
- *(None — `imgui-docking` is purely an ImGui infrastructure change; the new viewport system builds on top of it and does not alter its spec)*
|
||||
|
||||
## Impact
|
||||
|
||||
- **`src/ui/ViewportPanel.h/.cpp`**: Rewritten from a single-viewport class to a container that owns N `ViewportTile` instances. Public API surface changes — `SetSelectedPrimPath`, `SetStage`, `GetCamera`, `GetRenderer` remain but are forwarded to the active/focused tile or broadcast to all tiles.
|
||||
- **`src/core/UsdSceneRenderer.h/.cpp`**: No changes needed (each tile owns its own `UsdSceneRenderer` already).
|
||||
- **`src/core/ViewportCamera.h/.cpp`**: No changes needed (each tile owns its own `ViewportCamera` already).
|
||||
- **`src/ui/Application.h/.cpp`**: Wire up `OnPrimPicked` / `OnPrimsPickedRect` as before on the container; the container relays selection to the `SceneHierarchyPanel` / `PropertyPanel`.
|
||||
- **New files**: `src/ui/ViewportTile.h/.cpp` — the per-tile rendering logic extracted from `ViewportPanel::Render()`.
|
||||
- **New files**: `src/ui/ViewportLayout.h/.cpp` — layout splitting/merge logic, divider drag, layout persistence.
|
||||
- Memory: N `UsdSceneRenderer` instances means N FBO draw targets. For N≤4 and typical 1080p viewports this is ~200 MB worst-case — acceptable for a DCC tool.
|
||||
@@ -0,0 +1,30 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Transform manipulator renders only in the focused viewport tile
|
||||
The transform gizmo (manipulator) SHALL render and accept input only in the tile that was most recently clicked. All other tiles SHALL NOT display the gizmo.
|
||||
|
||||
#### Scenario: Focused tile shows gizmo, other tile does not
|
||||
- **WHEN** the user clicks in tile A (making it focused), then selects a prim
|
||||
- **THEN** the transform gizmo appears over the selection in tile A only; tile B shows no gizmo
|
||||
|
||||
#### Scenario: Move focus to another tile
|
||||
- **WHEN** the user clicks in tile B while tile A was focused
|
||||
- **THEN** the gizmo disappears from tile A and appears in tile B over the selection
|
||||
|
||||
### Requirement: Manipulator mode and space are global
|
||||
The manipulator tool mode (Select/Move/Rotate/Scale) and transform space (World/Object) SHALL be global settings shared across all tiles, owned by the `ViewportPanel` container.
|
||||
|
||||
#### Scenario: Change manipulator mode in focused tile
|
||||
- **WHEN** the user presses W (Move) while tile A is focused
|
||||
- **THEN** the mode switches to Move globally, and if the user clicks in tile B, tile B's gizmo is in Move mode
|
||||
|
||||
### Requirement: Keyboard shortcuts route to focused tile
|
||||
Keyboard shortcuts for the manipulator (Q/W/E/R), frame selection (F), frame all (A), and maximize toggle (Space) SHALL only apply to the focused tile.
|
||||
|
||||
#### Scenario: Frames selection in focused tile only
|
||||
- **WHEN** the user presses F while tile A is focused
|
||||
- **THEN** tile A's camera frames the selection; other tiles' cameras remain unchanged
|
||||
|
||||
#### Scenario: Tool shortcut changes global mode
|
||||
- **WHEN** the user presses W while tile A is focused
|
||||
- **THEN** the global manipulator mode changes to Move, and the focused tile (A) shows the Move gizmo if a prim is selected
|
||||
@@ -0,0 +1,30 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Independent camera per viewport tile
|
||||
Each viewport tile SHALL own an independent `ViewportCamera` instance. Camera operations (orbit, pan, dolly, zoom, frame) in one tile SHALL NOT affect any other tile's camera.
|
||||
|
||||
#### Scenario: Orbit in one tile, other tiles unchanged
|
||||
- **WHEN** the user Alt+LMB drags to orbit in tile A
|
||||
- **THEN** tile A's camera rotates, while tiles B, C, D remain at their previous camera positions
|
||||
|
||||
#### Scenario: Frame selection in one tile
|
||||
- **WHEN** the user presses F in a tile
|
||||
- **THEN** that tile's camera frames the current selection, other tiles are unaffected
|
||||
|
||||
### Requirement: Per-tile camera selector dropdown
|
||||
Each tile SHALL have a compact camera selector (icon + dropdown) showing "Free Camera" and all USD camera prims on the stage. Selecting a USD camera in one tile SHALL NOT affect other tiles.
|
||||
|
||||
#### Scenario: Tile A uses Free Camera, Tile B uses a USD camera prim
|
||||
- **WHEN** the user selects a USD camera prim in tile B's camera dropdown
|
||||
- **THEN** tile B switches to USD camera mode and renders from that camera's view, while tile A continues in free camera mode
|
||||
|
||||
#### Scenario: Both tiles use the same USD camera
|
||||
- **WHEN** the user selects the same USD camera prim in both tile A and tile B
|
||||
- **THEN** both tiles render from that camera's view but can navigate independently when one tile goes into free camera mode
|
||||
|
||||
### Requirement: Camera list refreshes on dropdown open
|
||||
Each tile SHALL refresh its camera prim list from the stage whenever its camera dropdown is opened.
|
||||
|
||||
#### Scenario: New camera added, then dropdown opened
|
||||
- **WHEN** the user adds a new UsdGeomCamera prim to the stage and opens a tile's camera dropdown
|
||||
- **THEN** the dropdown includes the newly added camera prim
|
||||
@@ -0,0 +1,43 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Per-tile grid toggle
|
||||
Each viewport tile SHALL have an independent grid visibility setting. Toggling the grid in one tile SHALL NOT affect other tiles.
|
||||
|
||||
#### Scenario: Grid on in Tile A, off in Tile B
|
||||
- **WHEN** the user toggles grid ON in tile A and OFF in tile B
|
||||
- **THEN** tile A shows the ground grid and tile B does not
|
||||
|
||||
### Requirement: Per-tile anti-aliasing toggle
|
||||
Each viewport tile SHALL have an independent line-AA setting. Toggling AA in one tile SHALL NOT affect other tiles.
|
||||
|
||||
#### Scenario: AA on in Tile A, off in Tile B
|
||||
- **WHEN** the user toggles AA ON in tile A and OFF in tile B
|
||||
- **THEN** tile A renders line overlays with GL_LINE_SMOOTH, tile B renders without
|
||||
|
||||
### Requirement: Per-tile background color
|
||||
Each viewport tile SHALL have an independent background color. Changing the background color in one tile SHALL NOT affect other tiles.
|
||||
|
||||
#### Scenario: Two tiles show different background colors
|
||||
- **WHEN** the user sets tile A's background to "Black" and tile B's background to "Dark Gray"
|
||||
- **THEN** tile A renders with a black background and tile B with a dark gray background
|
||||
|
||||
### Requirement: Per-tile bounding box display
|
||||
Each viewport tile SHALL have an independent bounding box display mode (None / Per Object / All Selection). Changing the bbox mode in one tile SHALL NOT affect other tiles.
|
||||
|
||||
#### Scenario: BBox mode differs between tiles
|
||||
- **WHEN** the user sets tile A to "Per Object" bbox mode and tile B to "None"
|
||||
- **THEN** tile A draws bounding boxes on each selected prim, tile B draws none
|
||||
|
||||
### Requirement: Per-tile render delegate
|
||||
Each viewport tile SHALL support an independent render delegate selection. Changing the render delegate in one tile SHALL NOT affect other tiles.
|
||||
|
||||
#### Scenario: Storm in Tile A, HdEmbree in Tile B
|
||||
- **WHEN** the user selects "HdStorm" for tile A and "HdEmbree" for tile B
|
||||
- **THEN** tile A renders via HdStorm and tile B via HdEmbree
|
||||
|
||||
### Requirement: Compact per-tile toolbar
|
||||
Each tile's toolbar SHALL use compact icon-only buttons (16×16) arranged in a single horizontal row above the rendered image, consuming no more than 32px height. Tooltips SHALL be available on hover.
|
||||
|
||||
#### Scenario: Toolbar renders compactly in a small tile
|
||||
- **WHEN** a tile is 300px wide in an HSplit layout
|
||||
- **THEN** the toolbar fits within the tile width without clipping, using a horizontal scroll or overflow dropdown if necessary
|
||||
@@ -0,0 +1,26 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Shared selection across all viewport tiles
|
||||
Selection SHALL be shared across all viewport tiles. When a prim is picked in any tile (single click or rect drag), all tiles SHALL update their selection highlight display to show the same set of selected prims.
|
||||
|
||||
#### Scenario: Pick in Tile A, Tile B shows highlight
|
||||
- **WHEN** the user single-clicks a prim in tile A
|
||||
- **THEN** tile A and tile B both highlight that prim with the selection overlay
|
||||
|
||||
#### Scenario: Rect select in Tile B, Tile A updates
|
||||
- **WHEN** the user rect-drags over multiple prims in tile B
|
||||
- **THEN** both tile A and tile B highlight the same set of prims
|
||||
|
||||
### Requirement: Selection change propagates to SceneHierarchyPanel and PropertyPanel
|
||||
When selection changes via any viewport tile, the `OnPrimPicked` / `OnPrimsPickedRect` callbacks SHALL fire from the `ViewportPanel` container, just as they do today.
|
||||
|
||||
#### Scenario: Pick in viewport updates hierarchy
|
||||
- **WHEN** the user picks a prim in any tile
|
||||
- **THEN** the SceneHierarchyPanel and PropertyPanel update to show the selected prim's properties
|
||||
|
||||
### Requirement: Shift+click additive selection across tiles
|
||||
Shift+click SHALL work consistently across all tiles. Prim picked in any tile is added to or removed from the shared selection set.
|
||||
|
||||
#### Scenario: Shift+click in Tile A, then Tile B
|
||||
- **WHEN** the user selects prim P1 in tile A, then Shift+clicks prim P2 in tile B
|
||||
- **THEN** both P1 and P2 are in the shared selection, highlighted in both tiles
|
||||
@@ -0,0 +1,55 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Layout presets
|
||||
The viewport container SHALL support switching between four layout presets at any time: Single (1 tile), HSplit (2 tiles side-by-side), VSplit (2 tiles top-bottom), and Quad (4 tiles in a 2×2 grid).
|
||||
|
||||
#### Scenario: Switch layout from Single to HSplit
|
||||
- **WHEN** the user selects "Horizontal Split" from the layout menu
|
||||
- **THEN** the viewport splits into two equal-sized tiles side-by-side
|
||||
|
||||
#### Scenario: Switch layout from HSplit to Single
|
||||
- **WHEN** the user selects "Single" from the layout menu
|
||||
- **THEN** the two tiles merge back into a single tile, and the focused tile's camera/settings are preserved
|
||||
|
||||
#### Scenario: Switch from HSplit to Quad
|
||||
- **WHEN** the user switches from HSplit to Quad
|
||||
- **THEN** four tiles are created in a 2×2 grid, filling the viewport area
|
||||
|
||||
### Requirement: Draggable dividers
|
||||
The divider line between tiles SHALL be draggable to resize adjacent tiles. Dividers SHALL be 6px wide with visual hover feedback.
|
||||
|
||||
#### Scenario: Drag horizontal divider
|
||||
- **WHEN** the user clicks and drags the divider between two tiles
|
||||
- **THEN** the divider follows the mouse, and tiles resize proportionally
|
||||
|
||||
#### Scenario: Drag divider to edge
|
||||
- **WHEN** the user drags a divider to within 10% of the viewport edge
|
||||
- **THEN** the divider snaps back to the 10% boundary to prevent tiles from becoming too small (minimum 100px per dimension)
|
||||
|
||||
### Requirement: Layout applies to the entire viewport area
|
||||
When the layout changes, all tiles SHALL be redistributed to fill the entire available viewport panel content region. No gaps between tiles.
|
||||
|
||||
#### Scenario: Resize main window with Quad layout
|
||||
- **WHEN** the user resizes the main application window
|
||||
- **THEN** all four tiles resize proportionally to maintain their relative sizes and fill the viewport content area
|
||||
|
||||
### Requirement: Space-key maximize restores previous layout
|
||||
When maximized, pressing Space again SHALL restore the exact layout and divider positions from before maximization.
|
||||
|
||||
#### Scenario: Maximize and restore preserves layout
|
||||
- **WHEN** the user has an HSplit layout with divider at 0.3, then hovers tile B and presses Space, then presses Space again
|
||||
- **THEN** the viewport returns to HSplit layout with both tiles visible and the divider at 0.3
|
||||
|
||||
### Requirement: Escape restores maximized viewport
|
||||
When maximized, pressing Escape SHALL restore the previous layout (same behavior as Space toggle).
|
||||
|
||||
#### Scenario: Escape during maximize
|
||||
- **WHEN** the viewport is maximized and the user presses Escape
|
||||
- **THEN** the previous multi-tile layout is restored
|
||||
|
||||
### Requirement: Space requires tile hover in multi-tile layouts
|
||||
In Single layout (only 1 tile), Space SHALL be a no-op — there is nothing to maximize.
|
||||
|
||||
#### Scenario: Space ignored in Single layout
|
||||
- **WHEN** the viewport is already in Single layout and the user presses Space
|
||||
- **THEN** the viewport remains unchanged in Single layout
|
||||
@@ -0,0 +1,46 @@
|
||||
## 1. Extract ViewportTile class
|
||||
|
||||
- [x] 1.1 Create `src/ui/ViewportTile.h` with class declaration — owns `ViewportCamera`, `UsdSceneRenderer`, per-tile settings (grid, AA, bbox mode, bg color, render delegate, camera index), and a pointer to the shared selection + manipulator
|
||||
- [x] 1.2 Create `src/ui/ViewportTile.cpp` — extract per-tile rendering logic from `ViewportPanel::Render()`: camera resolution, scene rendering, overlay rendering (axis, bbox, camera wireframes), DrawSelectionRect, RenderManipulatorOverlay, RenderContextMenu, HandleInput
|
||||
- [x] 1.3 Verify tile renders standalone by temporarily instantiating one ViewportTile in Application
|
||||
|
||||
## 2. Refactor ViewportPanel into container
|
||||
|
||||
- [x] 2.1 Rewrite `ViewportPanel` header to own `std::vector<std::unique_ptr<ViewportTile>>`, `TransformManipulator`, shared selection state (`m_selectedSdfPaths`, `m_selectedPrimPath`), layout enum (`LayoutMode`), divider normalized positions, and maximize state (`m_maximizedTileIndex`, `m_layoutBeforeMaximize`)
|
||||
- [x] 2.2 Rewrite `ViewportPanel::Render()` to compute tile rectangles from layout state, iterate tiles calling `ViewportTile::Render()` + `ViewportTile::HandleInput()`, and draw dividers. When maximized, render only the maximized tile at full viewport area (no dividers)
|
||||
- [x] 2.3 Add `ViewportPanel::SetLayout(LayoutMode)` — creates/destroys tiles as needed, preserves focused tile's camera/settings when possible. Clear `m_maximizedTileIndex` on explicit layout change
|
||||
- [x] 2.4 Add divider-drag handling in `ViewportPanel::Render()` — hit-test divider regions, update normalized positions on drag. Skip divider hit-test when maximized
|
||||
- [x] 2.5 Add `ViewportPanel::SetLayoutMenu()` — a layout menu in the viewport title bar or right-click context menu
|
||||
- [x] 2.6 Add Space-key maximize/restore: detect Space key press (guarded by `!io.WantTextInput` and `LayoutMode != Single`), save pre-maximize layout, set `m_maximizedTileIndex` to the hovered tile's index. On second Space or Escape, restore `m_maximizedTileIndex` to -1 and reload saved layout. Add visual indicator (border glow or text label) on maximized tile
|
||||
- [x] 2.7 Expose `ViewportPanel::GetFocusedTileIndex()` and wire up `OnPrimPicked` / `OnPrimsPickedRect` from the focused tile to the shared selection callbacks. When maximized, the maximized tile is implicitly the focused tile
|
||||
|
||||
## 3. Implement shared selection
|
||||
|
||||
- [x] 3.1 Store `m_selectedSdfPaths` and `m_selectedPrimPath` on `ViewportPanel` (container)
|
||||
- [x] 3.2 On pick in any tile, update shared selection, then broadcast `m_renderer.SetSelectedPaths()` to all tiles
|
||||
- [x] 3.3 Fire `OnPrimPicked` / `OnPrimsPickedRect` from the container (not tiles) so downstream panels see a single source of truth
|
||||
|
||||
## 4. Implement focused-viewport manipulator
|
||||
|
||||
- [x] 4.1 Track `m_focusedTileIndex` on `ViewportPanel` — updated on LMB click in any tile
|
||||
- [x] 4.2 Only call `m_manipulator.Render()` and `m_manipulator.HandleInput()` for the focused tile
|
||||
- [x] 4.3 Wire keyboard shortcuts (Q/W/E/R) through the container to the global manipulator, and F/A to the focused tile's camera. Ensure Space maximize toggle works correctly when a tile is focused (not consumed by manipulator)
|
||||
|
||||
## 5. Add per-tile compact toolbar
|
||||
|
||||
- [x] 5.1 Implement compact icon-only toolbar in `ViewportTile::RenderToolbar()` — camera selector + render delegate button + grid/AA/bbox icons, all 16×16 with tooltips
|
||||
- [ ] 5.2 Add overflow handling: if toolbar exceeds tile width, collapse into a "..." dropdown menu
|
||||
|
||||
## 6. Wire up Application integration
|
||||
|
||||
- [x] 6.1 Update `Application::Initialize()` — `m_viewportPanel` still works, `SetStage` broadcasts to all tiles, `SetSelectedPrimPath` updates shared selection
|
||||
- [x] 6.2 Update `Application::RenderUI()` — ensure the Viewport window renders correctly with the new container layout
|
||||
- [ ] 6.3 Test all existing interaction flows: stage open/close, camera switching, selection sync, manipulator tools, context menu, frame shortcuts
|
||||
|
||||
## 7. Build and test
|
||||
|
||||
- [x] 7.1 Configure with `cmake --preset default`
|
||||
- [x] 7.2 Build Release with `cmake --build build --config Release` — fix any compilation errors
|
||||
- [x] 7.3 Install with `cmake --install build --config Release`
|
||||
- [ ] 7.4 Run `App.exe` and visually verify: single viewport works, switch to HSplit/VSplit/Quad, camera independence, shared selection, manipulator in focused tile only, divider dragging, Space-key maximize/restore with correct layout preservation, Escape restore, maximize in Quad layout, Space no-op in Single layout
|
||||
- [ ] 7.5 Run CTest: `ctest --test-dir build -C Release` — fix any test failures
|
||||
Reference in New Issue
Block a user