Add Maya-style Render Layer panel
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>
This commit is contained in:
@@ -41,6 +41,7 @@ Application::Application()
|
||||
: m_showDemoWindow(false)
|
||||
, m_showStageInfo(true)
|
||||
, m_showStageEditor(true)
|
||||
, m_showRenderLayerPanel(true)
|
||||
, m_showSceneHierarchy(true)
|
||||
, m_showViewport(true)
|
||||
, m_showPropertyPanel(true)
|
||||
@@ -67,15 +68,23 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig
|
||||
// Create managers
|
||||
m_stageManager = std::make_unique<UsdStageManager>();
|
||||
m_layerManager = std::make_unique<LayerManager>();
|
||||
m_renderLayerManager = std::make_unique<RenderLayerManager>();
|
||||
m_renderLayerManager->SetLayerManager(m_layerManager.get());
|
||||
m_propertyManager = std::make_unique<PropertyManager>();
|
||||
m_propertyManager->SetCommandHistory(&m_commandHistory);
|
||||
m_stageEditorPanel = std::make_unique<StageEditorPanel>();
|
||||
m_stageEditorPanel->SetLayerManager(m_layerManager.get());
|
||||
m_stageEditorPanel->SetCommandHistory(&m_commandHistory);
|
||||
m_stageEditorPanel->SetRenderLayerManager(m_renderLayerManager.get());
|
||||
m_sceneHierarchyPanel = std::make_unique<SceneHierarchyPanel>();
|
||||
m_sceneHierarchyPanel->SetPropertyManager(m_propertyManager.get());
|
||||
m_sceneHierarchyPanel->SetCommandHistory(&m_commandHistory);
|
||||
m_sceneHierarchyPanel->SetLayerManager(m_layerManager.get());
|
||||
m_sceneHierarchyPanel->SetRenderLayerManager(m_renderLayerManager.get());
|
||||
m_renderLayerPanel = std::make_unique<RenderLayerPanel>();
|
||||
m_renderLayerPanel->SetRenderLayerManager(m_renderLayerManager.get());
|
||||
m_renderLayerPanel->SetCommandHistory(&m_commandHistory);
|
||||
m_renderLayerPanel->SetSceneHierarchyPanel(m_sceneHierarchyPanel.get());
|
||||
m_viewportPanel = std::make_unique<ViewportPanel>();
|
||||
m_viewportPanel->SetCommandHistory(&m_commandHistory);
|
||||
m_propertyPanel = std::make_unique<PropertyPanel>();
|
||||
@@ -116,6 +125,7 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig
|
||||
m_timelinePanel->SetIconManager(m_iconManager.get());
|
||||
m_stageEditorPanel->SetIconManager(m_iconManager.get());
|
||||
m_materialEditorPanel->SetIconManager(m_iconManager.get());
|
||||
m_renderLayerPanel->SetIconManager(m_iconManager.get());
|
||||
|
||||
m_sceneHierarchyPanel->SetOnPrimSelected(
|
||||
[this](const std::string& path) {
|
||||
@@ -235,10 +245,12 @@ void Application::Shutdown() {
|
||||
m_sceneHierarchyPanel.reset();
|
||||
m_propertyPanel.reset();
|
||||
m_stageEditorPanel.reset();
|
||||
m_renderLayerPanel.reset();
|
||||
// Owns the shader-ball preview's Hydra engine + GL draw target — must be
|
||||
// destroyed while the GL context still exists, like m_viewportPanel.
|
||||
m_materialEditorPanel.reset();
|
||||
m_propertyManager.reset();
|
||||
m_renderLayerManager.reset();
|
||||
m_layerManager.reset();
|
||||
|
||||
if (m_iconManager) {
|
||||
@@ -263,8 +275,12 @@ void Application::RefreshManagers() {
|
||||
if (m_stageManager->HasStage()) {
|
||||
auto stage = m_stageManager->GetStage();
|
||||
m_layerManager->SetStage(stage);
|
||||
// After LayerManager (ActivateRenderLayer routes edit-target changes
|
||||
// through it) but before RestorePersistedActiveLayer needs it.
|
||||
m_renderLayerManager->SetStage(stage);
|
||||
m_propertyManager->SetStage(stage);
|
||||
m_sceneHierarchyPanel->SetStage(stage);
|
||||
m_renderLayerPanel->SetStage(stage);
|
||||
m_viewportPanel->SetStage(stage);
|
||||
m_viewportPanel->FrameScene();
|
||||
m_propertyPanel->SetStage(stage);
|
||||
@@ -272,10 +288,16 @@ void Application::RefreshManagers() {
|
||||
m_curveEditorPanel->SetStage(stage);
|
||||
m_materialManager->SetStage(stage);
|
||||
m_materialEditorPanel->SetStage(stage);
|
||||
// Re-applies whichever render layer was active last time this stage
|
||||
// was saved (mute state itself isn't serialized — see
|
||||
// RenderLayerManager::RestorePersistedActiveLayer).
|
||||
m_renderLayerManager->RestorePersistedActiveLayer();
|
||||
} else {
|
||||
m_layerManager->SetStage(nullptr);
|
||||
m_renderLayerManager->SetStage(nullptr);
|
||||
m_propertyManager->SetStage(nullptr);
|
||||
m_sceneHierarchyPanel->SetStage(nullptr);
|
||||
m_renderLayerPanel->SetStage(nullptr);
|
||||
m_viewportPanel->SetStage(nullptr);
|
||||
m_propertyPanel->SetStage(nullptr);
|
||||
m_timelinePanel->SetStage(nullptr);
|
||||
@@ -327,6 +349,12 @@ void Application::RenderUI() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (m_showRenderLayerPanel) {
|
||||
ImGui::Begin("Render Layers", &m_showRenderLayerPanel, ImGuiWindowFlags_NoCollapse);
|
||||
m_renderLayerPanel->Render();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (m_showViewport) {
|
||||
m_viewportPanel->Render(&m_showViewport);
|
||||
}
|
||||
@@ -777,6 +805,7 @@ void Application::RenderMenuBar() {
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Stage Editor", nullptr, &m_showStageEditor);
|
||||
ImGui::MenuItem("Render Layers", nullptr, &m_showRenderLayerPanel);
|
||||
ImGui::MenuItem("Scene Hierarchy", nullptr, &m_showSceneHierarchy);
|
||||
ImGui::MenuItem("Viewport", nullptr, &m_showViewport);
|
||||
ImGui::MenuItem("Property Panel", nullptr, &m_showPropertyPanel);
|
||||
@@ -872,7 +901,9 @@ void Application::SaveUsdFile() {
|
||||
|
||||
if (!m_stageManager->SaveStage()) {
|
||||
LOG_ERROR("Failed to save USD file: " + m_stageManager->GetLastError());
|
||||
return;
|
||||
}
|
||||
SaveDirtyRenderLayers();
|
||||
}
|
||||
|
||||
void Application::SaveUsdFileAs() {
|
||||
@@ -891,6 +922,11 @@ void Application::SaveUsdFileAs() {
|
||||
if (!m_stageManager->SaveStageAs(filePath)) {
|
||||
LOG_ERROR("Failed to save USD file: " + m_stageManager->GetLastError());
|
||||
} else {
|
||||
// m_renderLayerManager's layer refs are identifier-keyed SdfLayer
|
||||
// objects, independent of which UsdStage currently references
|
||||
// them — safe to save before the stage swap RefreshManagers()
|
||||
// below performs.
|
||||
SaveDirtyRenderLayers();
|
||||
// SaveStageAs reopens m_stageManager's stage from the new file path.
|
||||
// RefreshManagers syncs m_layerManager (and others) to that new stage;
|
||||
// without this, subsequent sublayer edits go to the old (now stale) stage
|
||||
@@ -900,6 +936,15 @@ void Application::SaveUsdFileAs() {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::SaveDirtyRenderLayers() {
|
||||
if (!m_renderLayerManager) return;
|
||||
for (const auto& info : m_renderLayerManager->GetRenderLayers()) {
|
||||
if (info.isDefault || !info.layer) continue;
|
||||
if (info.layer->IsDirty())
|
||||
info.layer->Save();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::CloseUsdFile() {
|
||||
m_stageManager->CloseStage();
|
||||
// Re-create a fresh default stage so the app is always in an editable state.
|
||||
|
||||
Reference in New Issue
Block a user