Cycle each node through All -> Connected -> Collapsed -> All via a
custom-drawn icon (filled-dot count encodes how much is shown: 3/2/1).
Collapsed hides every pin on a side, linked or not, behind one
connector nub; existing links now redirect to that nub too
(EffectivePinId), so they visually converge like Hypershade's collapse
view instead of only hiding unconnected pins. Also moved the connector
nubs off the title row onto the node's bottom row. Persisted mode
switches from a bool (uiShowAllPins) to an int (uiPinDisplayMode).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each node gets an All/Conn toggle: connected-only mode hides
unconnected pins behind collapsed connector nubs on the title row;
dragging a link onto (or from) a nub opens a menu to pick which hidden
pin to wire, and the pin appears once connected. The toggle is drawn
and hit-tested geometrically, and its tooltip is deferred to after
NE::End() so it lands at the cursor. A new Preferences > Material
checkbox opts into persisting the per-node mode as USD customData
(uiShowAllPins); otherwise it stays session-only.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Texture nodes show their decoded source image (background thread);
material nodes show a shader-ball render, amortized one-per-frame.
Also classifies shader nodes by Hypershade-style category and adds
an Arnold preset graph alongside the existing USD/MaterialX ones.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Move the imgui-node-editor subset the build actually compiles from
third_party/imgui-node-editor to src/ui/NodeEditor (version-controlled,
MIT LICENSE included). FindImguiNodeEditor.cmake points at the new
location; CMakeLists excludes src/ui/NodeEditor from UI_SOURCES so it
isn't compiled twice.
Material editor presets: a Presets menu (disabled with no open material)
builds a UsdPreviewSurface + UsdUVTexture graph fed by an st primvar
reader, or a MaterialX standard_surface + image graph fed by a texcoord
node. Each is one idempotent, undoable command that re-normalizes layout.
Create Material becomes an icon button. New nodes route through
FindFreeCanvasSpot so a creation never lands on top of an existing node
(overlapping nodes fight over the editor hit test and become undraggable).
Shader-ball preview: pick a previewable output (terminal or 3/4-component
color-like) instead of always the first output, so scalar-only nodes keep
the whole-material preview rather than failing Storm codegen. Per-shape
camera frame-fit margins and auto-clip framing.
Fixes: DeletePrimCommand::Undo recreates missing destination ancestors
before SdfCopySpec (parent material may have been deleted after the
command ran). ConfigWindowsMoveFromTitleBarOnly stops a content-area drag
in the node canvas from moving the whole Material Editor window.
Temporary (marked for removal once the node-editor drag regression is
diagnosed): main.cpp mirrors LOG_INFO to %APPDATA%\UsdLayerManager\
debug.log; a g_AxNodeEditorDebugLog hook in the vendored editor plus a
[NodeGraph] event-trace block in RenderNodeGraphCanvas dump click/drag/
selection/position-save state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The create-node list is now a two-level tree: a header per node source
(USD, MaterialX, Arnold, Cycles - from the Sdr sourceType, with glslfx
and OSL merged into USD so core nodes don't appear twice) containing
Hypershade-style category sub-trees (Material / Texture / Geometry /
Utility / Light) derived from Sdr context, terminal output types, role,
and name keywords. Duplicate collapsing is per (source, label) so
same-named nodes from different providers both stay; searching forces
all sections open. The TAB popup shows [Source/Category] per entry.
The browser and preview columns get drag splitters (viewport-divider
styling, clamped widths); the canvas absorbs the remainder.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Browser column lists all scene materials plus a searchable create-node
list; Show Graph (or double-click) loads a material into the node-graph
work area. The graph shows the entire network including MaterialX
(.mtlx) node graphs, resolving connections through NodeGraph boundaries
via UsdShadeUtils::GetValueProducingAttributes, with layered auto-layout
for nodes lacking authored uiPosition. Canvas navigates viewport-style
(Alt+MMB pan / Alt+RMB zoom) and TAB opens a Nuke-style search popup.
Selecting a node shows a typed property editor (live-apply, one undo
command per edit) and previews that node's output on the shader ball.
The preview renders through its own Hydra engine into a scratch stage
that composes the material via a reference to the source root layer (so
referenced .mtlx materials work), re-renders until progressive delegates
(Arnold/Cycles/Embree) converge, and lights with HDR dome presets
(External/Room/Interior/Sunset; CC0 Poly Haven EXRs fetched at CMake
configure). texture:format is authored latlong explicitly - left
automatic, hdArnold falls back to Arnold's angular fisheye default -
and hdCycles gets a -90 X pole rotation to match Storm's +Y-pole
sampling.
Mutations go through new ICommand subclasses (create shader node,
connect/disconnect attrs). imgui-node-editor is vendored (gitignored)
with a local one-line patch: c_ScrollButtonIndex 1->2 for MMB pan.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Exposes UsdImagingGLCullStyle as a Cull Style sub-menu in the gear popup
(Back Unless Double-Sided / Back / Front / Nothing / No Opinion). Defaults
to BackUnlessDoubleSided, matching usdview. Setting persists per-viewport
to viewport_settings.ini.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes UsdGeomImageable purpose visibility as three per-viewport toggles
in the gear popup (Purpose: Guide / Proxy / Render), wired to
showGuides/showProxy/showRender on UsdImagingGLRenderParams. Defaults match
the USD render params defaults (Proxy on, Guide and Render off). Settings
persist per-viewport to viewport_settings.ini.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Surfaces each render delegate's settings (FLAG/INT/FLOAT/STRING) in a
per-viewport floating dialog opened from the gear popup. Settings are grouped
into tabs by key namespace prefix (split at ':'); unprefixed keys go into
General. Changes apply immediately; Reset Defaults restores defValues.
Settings list is cached per delegate to avoid TF_WARN flooding the log on
every frame.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exposes the render delegate's available AOVs in the gear popup under a new
"AOV" sub-menu. Selecting an AOV calls SetRendererAov on the engine and
persists the choice per-viewport in viewport_settings.ini. Switching the
render delegate resets the AOV to "color", matching stageView.py's
_handleRendererChanged. Menu is greyed-out when the delegate reports no AOVs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a per-viewport "Camera Guide" toggle (default off) that draws a full
frustum wireframe box (near quad + far quad + 4 connecting edges) in oracle
orange for every non-active UsdGeomCamera prim, matching usdview's
displayCameraOracles behaviour. Uses GfFrustum::ComputeCorners() and the
same 24-index line layout as DrawCameraGuides. Toggle exposed in the
right-click context menu and gear popup; persists to INI as ShowCameraGuide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the custom GL post-process (ViewportColorCorrector) that bypassed
HdxColorCorrectionTask. Instead pass colorCorrectionMode, ocioDisplay,
ocioView, ocioColorSpace, and ocioLook directly to UsdImagingGLRenderParams
and SetColorCorrectionSettings(), delegating correction to Hydra —
the same approach used in usdview's stageView.renderSinglePass().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Preferences (Edit > Preferences, tabbed General/Animation/Viewport):
- Viewport tab: Default Render Delegate and Color Correction settings
- Persisted to %APPDATA%\UsdLayerManager\preferences.ini
- Render delegate: startup default only (not live-applied to running tiles)
- Color correction: global, applied live to all viewports on change
Render delegate per-viewport:
- Each tile toolbar switches its own delegate independently
- Saved per-tile in viewport_settings.ini; restored on next launch
- Tiles with no saved delegate get the pref default on startup
Color correction global:
- Removed from gear popup; lives in Preferences > Viewport only
- Applied to all tiles on init and on live change from Preferences
Fix InitRenderer always applying m_currentRendererPlugin when set,
rather than skipping when UsdImagingGLEngine auto-selects a default.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Crash fixes (expired UsdPrim after namespace edits):
- RenderPrimNode: early-out after rename commit (renamed flag + column cleanup)
- RenderPrimNode: early-out after context menu op expires prim (prim.IsValid() guard)
- RenderContextMenu: EndPopup+return when Unparent/Group expires prim mid-popup
- Child iteration: snapshot GetChildren() into vector before recursing to guard
against live iterator invalidation from stage mutations deeper in the tree
- Rename: firstFrame guard prevents spurious IsItemDeactivated cancel on
SetKeyboardFocusHere activation frame (fixes second-rename not working)
- RenamePrimCommand: check RenamePrim() return value, log if rejected
New features:
- Context menu Add Child submenu: create typed child prim under selection
- Context menu Unparent to Root: move prim to stage root (undo-able)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Inline rename: double-click prim label or F2 to edit name in-place;
Enter commits, click-away or Esc cancels (undo-able)
- Drag-and-drop reparent: drag any prim onto another to make it a child;
invisible root drop zone below tree to reparent to stage root (undo-able)
- Ctrl+G group: creates Xform group at common parent, reparents selection
under it; filters descendant duplication; selects new group (undo-able)
- Delete key shortcut: opens existing remove-prim modal when panel focused
- Context menu: Rename (F2) and Group (Ctrl+G) items added
- Three new undo-able commands using UsdNamespaceEditor (USD 25.05):
RenamePrimCommand, ReparentPrimCommand, GroupPrimsCommand
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hydra's HdxColorCorrectionTask rendered prims black in OCIO mode and could
corrupt the GlfDrawTarget bind stack on failure (skipping Unbind), blacking
out every later frame including sRGB. Replace it with our own GL post-process:
the scene renders linear (RGBA16F) and is corrected by a fullscreen shader --
linear->sRGB encode, or OCIO via the OCIO 2.1 GPU API (GpuShaderDesc plus
uploaded 1D/3D LUT textures). OCIO build failures fall back to sRGB (never
black) and USD diagnostics are routed to the app log.
- core: ViewportColorCorrector + ApplyViewportColorCorrection in UsdSceneRenderer
- utils: OcioConfigParser enumerates displays/views/colorspaces/looks from $OCIO
- ui: gear-menu OCIO controls (ViewportTile) + per-viewport persistence (ViewportPanel)
- Application: point $OCIO at the bundled ACES 1.2 config
- CMake: link/copy OpenColorIO, download ACES 1.2 config; plus hdCycles build
config (disable OpenVDB/Embree, fix TBB/OpenSubdiv/Imath dirs, exclude CRT DLLs)
- main: pre-flight plugin DLL load check to skip plugins with missing deps
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a ⚙ icon button to the right of the AA toggle in each viewport
tile's compact toolbar. Clicking it opens a dropdown with:
- Shading submenu: Smooth Shaded / Flat Shaded / Wireframe on Surface /
Wireframe / Unlit (new ShadingMode enum driving UsdImagingGLDrawMode)
- Lighting submenu: Camera Headlight / Scene Lights + Dome Light toggle
- Show Grid and Anti-Aliasing toggles (mirrored from icon buttons)
- Bounding Box submenu: None / Per Object / All Selection
- Background submenu: Dark Gray / Black / Light Gray / Midnight Blue
ShadingMode is persisted in viewport_settings.ini alongside the existing
per-tile settings (saved on shutdown, restored on startup).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TimelinePanel: Playblast button opens modal with output dir (native
folder picker via IFileOpenDialog), custom resolution (HD/FHD/4K
presets), frame range, and movie export toggle
- UsdSceneRenderer: CaptureFrame() reads pixels from resolved MSAA FBO;
Render() stores last dimensions for off-screen re-render at capture res
- MovieEncoder: streams RGBA frames directly into MP4 using libavcodec/
libswscale (H.264, tries libx264 → nvenc → qsv → amf → openh264)
- Application: CapturePlayblastFrame() re-renders at capture resolution
each frame; opens/writes/closes MovieEncoder inline with the render loop
- FileDialog: BrowseFolder() using Vista-style IFileOpenDialog (COM)
- CMake: FindFFmpeg.cmake locates prebuilt BtbN GPL shared package in
third_party/ffmpeg; links and copies DLLs for main and test targets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors usdview behaviour -- when the stage has at least one UsdLux
light the default camera fill-light is disabled so the scene is lit
purely by its authored lights. StageHasAuthoredLights() early-exits
on the first HasAPI<UsdLuxLightAPI>() hit to keep the per-frame cost
minimal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Draw a type-appropriate wireframe gizmo for every UsdLux light in the stage,
mirroring the existing camera-wireframe overlay (dedicated VAO/VBO reusing the
bbox shader, drawn on top with depth-test off).
Shapes, built in the light's local space and pushed through its local-to-world
transform:
- SphereLight 3 orthogonal circles (radius); axis-cross when treatAsPoint
- RectLight width x height rectangle + emission direction line
- DiskLight circle + direction line
- CylinderLight two end-cap circles + connectors (radius/length)
- DistantLight sun disc + parallel rays
- DomeLight 3 large display-scaled circles
Selected lights draw accent orange, others warm yellow.
Verified by creating a SphereLight (3-circle sphere) and a RectLight
(rectangle) in-app: distinct shapes, correct selected/deselected colours,
gizmos visible over geometry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two issues caused the camera orientation to flip when tumbling a custom
USD camera:
1. Write-back decomposed the camera matrix to XYZ-euler via
Decompose(X,Y,Z) and re-authored it as a rotateXYZ op. That round-trip
is lossy — the angles Decompose returns do not reconstruct the same
matrix as a rotateXYZ op, so the orientation read back from the prim
differed from the free-camera view shown during the drag. The view
jumped every time a drag finished and snapped back on the next drag.
Now author the full camera-to-world transform as a single matrix op,
which round-trips exactly through UsdGeomCamera::GetCamera().
2. Orbit init relied on PullFromCameraTransform's Euler decomposition for
theta/phi, which is gimbal-affected. Added
ViewportCamera::InitOrbitFromEyeAndCenter to derive theta/phi directly
from the eye->center vector (zero roll), respecting the Z-up matrix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When switching to a USD camera, the orbit pivot was set from
GfCamera::GetFocusDistance() which is a depth-of-field attribute
that defaults to 5 scene units regardless of scene scale.
InitCameraNavigation() now computes the scene bounding box centroid,
projects it onto the camera view ray to get a proper orbit distance,
and calls SetFocalPoint/SetDist to place the pivot correctly.
Added SetDist() setter on ViewportCamera to support this.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add edit-target layer combo at top of Scene Hierarchy panel (always
visible when stage loaded — session, root, sublayers); routes through
LayerManager::SetEditTarget so StageEditor ET checkboxes stay in sync
- Replace custom Create Sublayer modal with native SaveFile dialog;
LayerManager::CreateNewSublayer creates the file on disk, inserts it
at the top of the sublayer stack, and activates it as edit target
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- SaveStage: explicitly call rootLayer->Save() before m_stage->Save() to
ensure root-layer metadata (subLayers field) is always written to disk
- SaveUsdFile: redirect to SaveAs when the root layer is anonymous so
Ctrl+S on an in-memory stage no longer silently does nothing
- SaveUsdFileAs: call RefreshManagers() after SaveStageAs so LayerManager
stays in sync with the newly opened file-backed stage; without this,
sublayers added after a Save As went to the old stale stage and were lost
- LayerCreateCommand::Execute: use GetSublayers() (sublayer-local indices)
instead of GetLayerStack() (full-stack indices) to record m_insertedIndex,
fixing undo of add-sublayer removing the wrong layer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace LayerPanel with StageEditorPanel for full sublayer composition workflow:
- Edit target (ET) checkbox on root/session and each sublayer; pen icon header
- Dirty indicator (*) per layer; muted layers remain visible in list
- Drag-drop and Move Up/Down reordering with undo (LayerReorderCommand)
- Add existing sublayer via file dialog; create new sublayer modal
- Fixed LayerReorderCommand::ApplyOrder using full-stack indices on sublayer-local
list (silent remove failure → duplicate insert → USD _ValidateEdit error)
- Fixed muted sublayer visibility: walk GetSubLayerPaths() + FindOrOpenRelativeToLayer
+ m_layerRefs to keep muted layers alive and visible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>