Render layers built on the existing USD sublayer architecture: each layer is a file-backed SdfLayer mounted as a sublayer and tagged via custom layer data, with membership stored in a UsdCollectionAPI on a hidden /RenderLayerData prim. Activating a layer applies its overrides and isolates it to its members. "Default" is virtual -- it means all render-layer sublayers muted. See docs/adr/0002-render-layer.md for the design and rationale. - RenderLayerManager: create/delete/rename/activate, membership, mute-based isolation. Sdf-level APIs are used while a layer is muted (Usd-level APIs can't see muted layers) and Usd-level APIs when active. - Commands: create/delete/rename/activate and add/remove members, all undoable. - RenderLayerPanel: layer list with one-click activation and a membership editor for the selected prims. - StageEditorPanel / SceneHierarchyPanel: badge render-layer-owned sublayers, disable their mute/remove controls (they're managed from the Render Layer panel so the two can't desync), and lock the edit target while a non-Default layer is active. - ConnectShaderAttrs/DisconnectShaderAttr: take an explicit editLayer captured at construction, so undo targets the same layer as execute even if the ambient edit target changed in between (e.g. a render-layer switch). Note: SceneHierarchyPanel.h also carries declarations for the sublayers section added in the following commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
USD Layer Manager
A C++17 / Windows desktop application providing a Maya-style USD scene and look-dev editor using OpenUSD v25.11, ImGui 1.92.7 (docking branch), and OpenGL 3.3 (via GLAD). Users can open, edit, and save USD files, manage layered stage overrides, inspect and edit prim properties, author UsdShade material graphs with a live shader-ball preview, animate with a Bezier curve editor, and interact with 3D scenes through a multi-viewport Hydra renderer with OCIO color management.
Features
- Layer Stack Management —
StageEditorPanel: create, reorder, mute/unmute sublayers, set the edit target, add existing files as sublayers, with full undo support - Scene Hierarchy — Browse prim tree with per-type SVG icons; create, delete, rename, reparent, and group prims; add/replace references
- Property Editor — Maya Channel Box-style attribute editing with type-aware drag inputs, color pickers, and undoable transforms
- Material Editor — Hypershade-style node-graph editor for UsdShade/MaterialX networks (
MaterialEditorPanel): material browser, drag-to-connect node canvas, per-node/per-pin display modes, Sdr-driven node creation menu and enum dropdowns, colorSpace authoring on texture inputs, and a live shader-ball preview (MaterialPreviewRenderer) with its own renderer/HDRI/preview-shape controls. In-canvas node thumbnails (NodeThumbnailCache) show texture previews and per-node shader-ball renders, amortized across frames and re-rendered until progressive delegates (Arnold/Cycles/Embree) fully converge - Timeline & Curve Editor — Transport controls with loop/bounce and Auto-Key (
TimelinePanel); a Maya-style F-curve editor (CurveEditorPanel) with Bezier round-trip to USD time samples; Playblast export to MP4 viaMovieEncoder(libavcodec) - 3D Viewport — Hydra rendering via
UsdImagingGLEngineinto an offscreen FBO; switchable render delegates (Storm, Embree, Arnold, Cycles); OCIO-based color management (sRGB or full display/view/look pipeline) applied as a post-Hydra GL pass; grid overlay; selection bounding boxes; camera/light wireframes - Multi-Viewport — Single, horizontal split, vertical split, and quad layouts; per-tile camera, render delegate, and AOV; Space to maximize/restore
- Camera Control — Free orbital camera (tumble/truck/dolly) or drive from USD camera prims; auto near/far clipping; frame selection; correct camera prim orientation for Y-up and Z-up stages
- Transform Gizmo — Pure ImDrawList-based manipulator (translate/rotate/scale) in object or world space
- Undo/Redo — Full command history (
ICommandsubclasses undersrc/core/commands/) covering prim creation/deletion/reparenting/grouping, attribute edits, transform changes, shader-node creation/connection, and layer operations; Ctrl+Z / Ctrl+Y hotkeys; Edit menu integration - Multi-select — Rectangular selection in viewport with cross-panel syncing
| Material Editor (Arnold) | Curve Editor | Quad Viewport (4 delegates) | MaterialX import |
|---|---|---|---|
Dependencies
| Dependency | Version | Notes |
|---|---|---|
| OpenUSD | v25.11 | Prebuilt; found via FindOpenUSD.cmake |
| Dear ImGui | v1.92.7 | Docking branch; Win32 + OpenGL3 backends |
| GLAD | — | OpenGL 3.3 core loader |
| Python | 3.12 | Required by OpenUSD runtime (python312.dll) |
Optional Render Delegates
| Delegate | Renderer Version | Variable | Notes |
|---|---|---|---|
| hdEmbree | Embree 4.3.3 | HDEMBREE_USD_ROOT (optional) |
Bundled directly in third_party/OpenUSD-v25.11 (built with --embree); set HDEMBREE_USD_ROOT only if using a separate USD build, and EMBREE_LOCATION if Embree DLLs aren't in its bin/ |
| hdArnold | Arnold 7.4.0 (MtoA 5.5.0) | HDARNOLD_ROOT, ARNOLD_LOCATION |
arnold-usd (tag Arnold-7.4.5.1, for USD 25.11's ndr→sdr API change) install dir + MtoA root for ai.dll |
| hdCycles | Cycles 5.2.0 | WITH_CYCLES=ON |
Built from source under third_party/cycles via ExternalProject; requires MSVC |
All delegate paths are pre-configured in CMakePresets.json.
Build
Prerequisites
- Visual Studio 17 2022 (MSVC v143) with C++17 support
- CMake 3.20+
- Python 3.12 installed at
%LOCALAPPDATA%\Programs\Python\Python312\
Steps
cmake --preset default
cmake --build build --config Release
cmake --install build --config Release
The installed executable lives in install/bin/.
Presets — default (Release, WITH_CYCLES=ON, all render delegates), debug (Debug), release (RelWithDebInfo, installs), no-tests (Release). All presets currently build with BUILD_TESTS=OFF.
Building with Cycles
WITH_CYCLES=ON is set in the default preset. The first build compiles Blender Cycles from third_party/cycles via ExternalProject — expect a significantly longer initial build. Subsequent builds are incremental.
Cycles runtime dependencies (embree4.dll, openvdb.dll, OpenImageDenoise.dll, tbb12.dll, etc.) are automatically deployed to the output directory by a post-build step. CRT DLLs bundled inside the Cycles install are excluded to avoid version conflicts with the app's own MSVC runtime.
Tests
There is currently no working test runner: BUILD_TESTS is OFF in every preset. The two test executables (ViewportDisplayTest, RendererDiagnosticTest) have stale USD include paths and don't build — fix those before turning BUILD_TESTS on.
Architecture
src/
├── main.cpp — Entry point (plugin path init, app lifecycle)
├── core/ — USD business logic (no UI dependency)
│ ├── UsdStageManager — Stage open/create/save/close lifecycle
│ ├── LayerManager — Layer stack introspection and mutation
│ ├── PropertyManager — Property read/write via edit target with undo
│ ├── MaterialManager — UsdShade graph introspection + Sdr node-type/enum discovery
│ ├── CommandHistory — Undo/redo stack for ICommand instances
│ ├── UsdSceneRenderer — Hydra rendering + picking + overlays + OCIO color correction
│ ├── ViewportCamera — Free / USD-camera-driven orbital camera
│ └── commands/ — ICommand subclasses for all mutable operations
│ ├── TransformCommand — Undoable translate/rotate/scale
│ ├── CreatePrimCommand — Undoable prim creation (with camera orientation)
│ ├── DeletePrimCommand — Undoable prim deletion (SdfCopySpec snapshot)
│ ├── RenamePrimCommand — Undoable prim rename
│ ├── ReparentPrimCommand — Undoable reparent via UsdNamespaceEditor
│ ├── GroupPrimsCommand — Group selected prims under a new Xform
│ ├── AttributeSetCommand — Type-agnostic undoable attribute writes
│ ├── AddReferenceCommand — Undoable reference addition
│ ├── ReplaceReferenceCommand — Undoable reference replacement
│ ├── CreateShaderNodeCommand — Undoable UsdShadeShader creation (Material Editor)
│ ├── ConnectShaderAttrsCommand — Undoable shader input↔output connection
│ ├── DisconnectShaderAttrCommand — Undoable shader connection removal
│ └── LayerCommands — Create, remove, reorder sublayers
├── ui/ — ImGui panels and application shell
│ ├── Application — App shell (owns managers/panels, docking, menus)
│ ├── ImGuiContext — Win32+OpenGL+ImGui initialization and loop
│ ├── ViewportPanel — Viewport container (layout, divider drag, maximize)
│ ├── ViewportTile — Single viewport tile (camera, rendering, overlays, picking)
│ ├── SceneHierarchyPanel — Prim tree browser with context menus
│ ├── PropertyPanel — Attribute/transform editor (channel box-style)
│ ├── StageEditorPanel — Layer stack editor (edit target, dirty state, drag-drop ordering)
│ ├── TimelinePanel — Transport controls, loop/bounce, frame scrub, Playblast
│ ├── CurveEditorPanel — Maya-style F-curve editor (Bezier ↔ USD time samples)
│ ├── MaterialEditorPanel — Hypershade-style UsdShade node-graph editor
│ ├── MaterialPreviewRenderer — Shader-ball preview: independent Hydra instance + scratch stage
│ ├── NodeThumbnailCache — Async texture decode + amortized shader-ball thumbnails for the node canvas
│ ├── TransformManipulator — ImDrawList gizmo (move/rotate/scale)
│ ├── IconManager — SVG icon rasterization via NanoSVG
│ └── NodeEditor/ — Vendored/patched imgui-node-editor
└── utils/ — Cross-cutting utilities
├── Logger — Thread-safe logging (file + console); also sinks USD TF_WARN/TF_ERROR
├── FileDialog — Win32 file open/save dialogs
├── PathUtils — Exe-relative path resolution
├── OcioConfigParser — Enumerates displays/views/colorspaces/looks from the active OCIO config
├── MovieEncoder — Streams RGBA frames to H.264/MP4 via libavcodec (Playblast)
└── GLExt — GLAD extension initialization
Key Design Patterns
- Command Pattern — All mutable USD operations go through
ICommand→CommandHistoryfor undo/redo - Panel-Manager Separation — UI panels hold pointers to core managers but contain no USD layer/prim logic
- Callback Wiring — Cross-panel communication via
std::functioncallbacks (e.g. viewport pick → hierarchy select → property panel update) - Render-Delegate Plugability —
UsdSceneRenderercan switch Hydra render plugins at runtime - Own Color Correction Pass — Hydra's
HdxColorCorrectionTaskis bypassed; a file-localViewportColorCorrector(insideUsdSceneRenderer.cpp) applies sRGB/OCIO as a fullscreen GL pass after Hydra renders linear (seedocs/adr/0001-viewport-color-correction.md) - Gizmo as 2D Overlay —
TransformManipulatoruses pureImDrawListcalls (no GL resources), same approach as ImGuizmo
Project Configuration
| Config | Purpose |
|---|---|
cmake/modules/FindOpenUSD.cmake |
OpenUSD SDK discovery |
cmake/modules/FindImgui.cmake |
ImGui source integration |
cmake/modules/FindGlad.cmake |
GLAD loader setup |
CMakePresets.json |
Build presets (VS 17 2022, x64) |
docs/adr/ |
Architecture decision records |
AGENTS.md |
AI agent build/architecture guide |
.kilo/ |
Kilo AI configuration (commands, agents, skills) |
openspec/changes/ |
Feature proposals and implementation tracking |
Development Workflow
This project uses OpenSpec for feature development:
- Propose — Create a new change under
openspec/changes/<name>/withproposal.md,design.md,specs/, andtasks.md - Implement — Work through checkbox-tracked tasks in
tasks.md - Archive — Move completed changes to
openspec/archive/when done
Use Kilo's openspec-* skills to streamline this workflow.
Runtime Requirements
- Python 3.12 — OpenUSD requires
python312.dllat runtime. CMake copies it to the output directory viaPOST_BUILDcommands. - USD Plugin Path — The app scans
<exe_dir>/usd/forplugInfo.jsonand registers plugins viapxr::PlugRegistry. - Render delegate DLLs — hdEmbree, hdArnold, and hdCycles each bring their own runtime dependencies (Embree, Arnold SDK, Cycles libs). CMake
POST_BUILDsteps deploy all required DLLs next to the executable automatically.
Contributing
Before writing any OpenUSD API call, verify the API exists in the actual SDK:
findstr /r /s "FunctionName" third_party\OpenUSD-v25.11\include\
Always build and install before manually verifying a change:
cmake --build build --config Release
cmake --install build --config Release
License
See LICENSE file for details.