From cb0d72d9801b9a5bc913c6c5fb59654fc7e43522 Mon Sep 17 00:00:00 2001 From: indigo Date: Sun, 28 Jun 2026 11:43:32 +0800 Subject: [PATCH] Add global Preferences dialog and per-viewport render delegate 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 --- src/core/UsdSceneRenderer.cpp | 28 ++-- src/core/UsdSceneRenderer.h | 2 +- src/ui/Application.cpp | 245 +++++++++++++++++++++++++++++++++- src/ui/Application.h | 46 +++++-- src/ui/ViewportPanel.cpp | 63 ++++++--- src/ui/ViewportPanel.h | 13 +- src/ui/ViewportTile.cpp | 174 ++++-------------------- src/ui/ViewportTile.h | 21 +-- 8 files changed, 376 insertions(+), 216 deletions(-) diff --git a/src/core/UsdSceneRenderer.cpp b/src/core/UsdSceneRenderer.cpp index 1933c0c..3791d5b 100644 --- a/src/core/UsdSceneRenderer.cpp +++ b/src/core/UsdSceneRenderer.cpp @@ -547,23 +547,25 @@ void UsdSceneRenderer::InitRenderer() { if (!hasCycles) LOG_WARNING("HdCyclesPlugin not available (see startup log for DLL load errors)."); - pxr::TfToken currentPlugin = m_renderer->GetCurrentRendererId(); - if (currentPlugin.IsEmpty() && !plugins.empty()) { + // Apply the requested plugin. m_currentRendererPlugin is set from global + // preferences before InitRenderer runs; always honour it rather than + // checking whether the engine already chose something by default. + if (!m_currentRendererPlugin.IsEmpty()) { + pxr::TfToken cur = m_renderer->GetCurrentRendererId(); + if (cur != m_currentRendererPlugin) + m_renderer->SetRendererPlugin(m_currentRendererPlugin); + } else if (!plugins.empty()) { + // Fallback heuristic: prefer Storm/GL pxr::TfToken best; - // If a plugin was previously selected, honour it - if (!m_currentRendererPlugin.IsEmpty()) { - best = m_currentRendererPlugin; - } else { - for (const auto& p : plugins) { - std::string n(p.GetText()); - if (n.find("Storm") != std::string::npos || - n.find("GL") != std::string::npos) { best = p; break; } - } - if (best.IsEmpty()) best = plugins[0]; + for (const auto& p : plugins) { + std::string n(p.GetText()); + if (n.find("Storm") != std::string::npos || + n.find("GL") != std::string::npos) { best = p; break; } } + if (best.IsEmpty()) best = plugins[0]; m_renderer->SetRendererPlugin(best); - LOG_INFO("Selected renderer: " + std::string(m_renderer->GetCurrentRendererId().GetText())); } + LOG_INFO("Selected renderer: " + std::string(m_renderer->GetCurrentRendererId().GetText())); // Enable AOV "color" (matches stageView._handleRendererChanged) m_renderer->SetRendererAov(pxr::TfToken("color")); diff --git a/src/core/UsdSceneRenderer.h b/src/core/UsdSceneRenderer.h index 9f3fec9..6e3ca60 100644 --- a/src/core/UsdSceneRenderer.h +++ b/src/core/UsdSceneRenderer.h @@ -313,7 +313,7 @@ private: pxr::UsdStageRefPtr m_stage; std::shared_ptr m_renderer; - pxr::TfToken m_currentRendererPlugin; ///< Active plugin ID (empty = default) + pxr::TfToken m_currentRendererPlugin; ///< empty = use Storm/GL heuristic on init pxr::GlfDrawTargetRefPtr m_drawTarget; pxr::UsdImagingGLRenderParams m_renderParams; diff --git a/src/ui/Application.cpp b/src/ui/Application.cpp index 622b169..7a1fd97 100644 --- a/src/ui/Application.cpp +++ b/src/ui/Application.cpp @@ -2,6 +2,7 @@ #include "../utils/Logger.h" #include "../utils/FileDialog.h" #include "../utils/PathUtils.h" +#include "../utils/OcioConfigParser.h" #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -132,6 +134,7 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig m_curveEditorPanel->SetSelectedPrimPath(paths.empty() ? "" : paths.front()); }; + if (!m_stageManager->CreateInMemoryStage()) { LOG_ERROR("Failed to create default in-memory stage"); } else { @@ -150,13 +153,21 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig } } - // Load per-viewport settings (render delegate, grid, AA, etc.) from AppData. + // Load per-viewport settings and global preferences from AppData. if (const char* appData = getenv("APPDATA")) { namespace fs = std::filesystem; fs::path dir = fs::path(appData) / "UsdLayerManager"; fs::create_directories(dir); m_viewportSettingsPath = (dir / "viewport_settings.ini").string(); + m_prefsPath = (dir / "preferences.ini").string(); m_viewportPanel->LoadSettings(m_viewportSettingsPath); + LoadPreferences(); + // Apply pref delegate only to tiles that have no saved delegate. + m_viewportPanel->ApplyDefaultDelegate(m_prefs.renderDelegate); + // Color correction is always global — apply to all tiles. + m_viewportPanel->ApplyGlobalColorCorrection( + m_prefs.colorCorrectionMode, m_prefs.ocioDisplay, + m_prefs.ocioView, m_prefs.ocioColorSpace, m_prefs.ocioLook); } LOG_INFO("Application initialized successfully"); @@ -178,6 +189,8 @@ void Application::Run() { void Application::Shutdown() { if (m_viewportPanel && !m_viewportSettingsPath.empty()) m_viewportPanel->SaveSettings(m_viewportSettingsPath); + if (!m_prefsPath.empty()) + SavePreferences(); m_viewportPanel.reset(); m_sceneHierarchyPanel.reset(); @@ -300,6 +313,9 @@ void Application::RenderUI() { ImGui::End(); } + if (m_showPreferences) + RenderPreferencesDialog(); + // Capture happens after the timeline so that the "Start" button click in // this frame is detected, and after the viewport has rendered the scene. if (m_timelinePanel->IsPlayblasting()) { @@ -346,6 +362,229 @@ void Application::RenderStatusBar() { ImGui::End(); } +// --------------------------------------------------------------------------- +// Global preferences persistence +// --------------------------------------------------------------------------- +void Application::LoadPreferences() +{ + std::ifstream f(m_prefsPath); + if (!f) return; + std::string line; + while (std::getline(f, line)) { + if (!line.empty() && line.back() == '\r') line.pop_back(); + if (line.empty() || line.front() == '#' || line.front() == '[') continue; + size_t eq = line.find('='); + if (eq == std::string::npos) continue; + std::string key = line.substr(0, eq); + std::string val = line.substr(eq + 1); + if (key == "RenderDelegate") m_prefs.renderDelegate = val; + else if (key == "ColorCorrectionMode") + m_prefs.colorCorrectionMode = [&]{ try { return std::stoi(val); } catch(...){ return 1; } }(); + else if (key == "OcioDisplay") m_prefs.ocioDisplay = val; + else if (key == "OcioView") m_prefs.ocioView = val; + else if (key == "OcioColorSpace") m_prefs.ocioColorSpace = val; + else if (key == "OcioLook") m_prefs.ocioLook = val; + } +} + +void Application::SavePreferences() +{ + std::ofstream f(m_prefsPath); + if (!f) return; + f << "[Preferences]\n"; + f << "RenderDelegate=" << m_prefs.renderDelegate << "\n"; + f << "ColorCorrectionMode=" << m_prefs.colorCorrectionMode << "\n"; + f << "OcioDisplay=" << m_prefs.ocioDisplay << "\n"; + f << "OcioView=" << m_prefs.ocioView << "\n"; + f << "OcioColorSpace=" << m_prefs.ocioColorSpace << "\n"; + f << "OcioLook=" << m_prefs.ocioLook << "\n"; +} + +void Application::ApplyPrefsToAllViewports() +{ + if (!m_viewportPanel) return; + m_viewportPanel->ApplyGlobalColorCorrection( + m_prefs.colorCorrectionMode, + m_prefs.ocioDisplay, + m_prefs.ocioView, + m_prefs.ocioColorSpace, + m_prefs.ocioLook); +} + +void Application::RenderPreferencesDialog() +{ + ImGui::SetNextWindowSize(ImVec2(520, 400), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), + ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + if (!ImGui::Begin("Preferences", &m_showPreferences, + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) + { + ImGui::End(); + return; + } + + bool ccChanged = false; + + if (ImGui::BeginTabBar("PrefTabs")) { + + // ── General ───────────────────────────────────────────────────────── + if (ImGui::BeginTabItem("General")) { + ImGui::Spacing(); + ImGui::TextDisabled("No general settings yet."); + ImGui::EndTabItem(); + } + + // ── Animation ─────────────────────────────────────────────────────── + if (ImGui::BeginTabItem("Animation")) { + ImGui::Spacing(); + ImGui::TextDisabled("No animation settings yet."); + ImGui::EndTabItem(); + } + + // ── Viewport ──────────────────────────────────────────────────────── + if (ImGui::BeginTabItem("Viewport")) { + ImGui::Spacing(); + + // Render Delegate default — applied to viewports with no saved delegate on startup. + // Each viewport's toolbar controls its delegate independently after load. + ImGui::SeparatorText("Default Render Delegate"); + ImGui::TextDisabled("Applied to viewports with no saved setting on startup."); + ImGui::Spacing(); + auto plugins = UsdSceneRenderer::GetRendererPlugins(); + pxr::TfToken curId(m_prefs.renderDelegate); + for (const auto& pluginId : plugins) { + std::string name = UsdSceneRenderer::GetRendererDisplayName(pluginId); + if (name.empty()) name = pluginId.GetString(); + bool sel = (pluginId == curId); + if (ImGui::RadioButton(name.c_str(), sel)) + m_prefs.renderDelegate = pluginId.GetString(); + } + + ImGui::Spacing(); + + // Color Correction + ImGui::SeparatorText("Color Correction"); + + auto ccItem = [&](const char* label, int mode) { + if (ImGui::RadioButton(label, m_prefs.colorCorrectionMode == mode)) { + m_prefs.colorCorrectionMode = mode; + m_prefOcioFieldsSynced = false; + ccChanged = true; + } + }; + ccItem("Disabled", 0); + ccItem("sRGB", 1); + ccItem("OpenColorIO", 2); + + if (m_prefs.colorCorrectionMode == 2) { + ImGui::Spacing(); + const OcioConfig& ocfg = GetCurrentOcioConfig(); + + if (!m_prefOcioFieldsSynced) { + std::string disp = m_prefs.ocioDisplay.empty() ? ocfg.defaultDisplay : m_prefs.ocioDisplay; + std::string view = m_prefs.ocioView.empty() ? ocfg.defaultView : m_prefs.ocioView; + strncpy(m_prefOcioDisplayBuf, disp.c_str(), 127); + strncpy(m_prefOcioViewBuf, view.c_str(), 127); + strncpy(m_prefOcioColorSpaceBuf, m_prefs.ocioColorSpace.c_str(), 127); + strncpy(m_prefOcioLookBuf, m_prefs.ocioLook.c_str(), 127); + if (m_prefs.ocioDisplay.empty()) m_prefs.ocioDisplay = disp; + if (m_prefs.ocioView.empty()) m_prefs.ocioView = view; + m_prefOcioFieldsSynced = true; + } + + ImGui::PushItemWidth(280.f); + + if (ImGui::BeginCombo("Display##pref", m_prefOcioDisplayBuf)) { + for (const auto& d : ocfg.displays) { + bool sel = (d == m_prefOcioDisplayBuf); + if (ImGui::Selectable(d.c_str(), sel)) { + strncpy(m_prefOcioDisplayBuf, d.c_str(), 127); + m_prefs.ocioDisplay = d; + auto vit = ocfg.views.find(d); + if (vit != ocfg.views.end() && !vit->second.empty()) { + strncpy(m_prefOcioViewBuf, vit->second[0].c_str(), 127); + m_prefs.ocioView = vit->second[0]; + } + ccChanged = true; + } + if (sel) ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + { + static const std::vector kEmpty; + auto vit = ocfg.views.find(std::string(m_prefOcioDisplayBuf)); + const auto& views = (vit != ocfg.views.end()) ? vit->second : kEmpty; + if (ImGui::BeginCombo("View##pref", m_prefOcioViewBuf)) { + for (const auto& v : views) { + bool sel = (v == m_prefOcioViewBuf); + if (ImGui::Selectable(v.c_str(), sel)) { + strncpy(m_prefOcioViewBuf, v.c_str(), 127); + m_prefs.ocioView = v; + ccChanged = true; + } + if (sel) ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } + + if (ImGui::BeginCombo("Color Space##pref", + m_prefOcioColorSpaceBuf[0] ? m_prefOcioColorSpaceBuf : "(default)")) { + if (ImGui::Selectable("(default)", m_prefOcioColorSpaceBuf[0] == '\0')) { + m_prefOcioColorSpaceBuf[0] = '\0'; + m_prefs.ocioColorSpace.clear(); + ccChanged = true; + } + if (m_prefOcioColorSpaceBuf[0] == '\0') ImGui::SetItemDefaultFocus(); + for (const auto& cs : ocfg.colorSpaces) { + bool sel = (cs == m_prefOcioColorSpaceBuf); + if (ImGui::Selectable(cs.c_str(), sel)) { + strncpy(m_prefOcioColorSpaceBuf, cs.c_str(), 127); + m_prefs.ocioColorSpace = cs; + ccChanged = true; + } + if (sel) ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (ImGui::BeginCombo("Look##pref", + m_prefOcioLookBuf[0] ? m_prefOcioLookBuf : "(none)")) { + if (ImGui::Selectable("(none)", m_prefOcioLookBuf[0] == '\0')) { + m_prefOcioLookBuf[0] = '\0'; + m_prefs.ocioLook.clear(); + ccChanged = true; + } + if (m_prefOcioLookBuf[0] == '\0') ImGui::SetItemDefaultFocus(); + for (const auto& look : ocfg.looks) { + bool sel = (look == m_prefOcioLookBuf); + if (ImGui::Selectable(look.c_str(), sel)) { + strncpy(m_prefOcioLookBuf, look.c_str(), 127); + m_prefs.ocioLook = look; + ccChanged = true; + } + if (sel) ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + ImGui::PopItemWidth(); + } + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + if (ccChanged) + ApplyPrefsToAllViewports(); + + ImGui::End(); +} + void Application::RenderMenuBar() { if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMenu("File")) { @@ -403,6 +642,10 @@ void Application::RenderMenuBar() { m_commandHistory.Redo(); ImGui::EndDisabled(); + ImGui::Separator(); + if (ImGui::MenuItem("Preferences...")) + m_showPreferences = true; + ImGui::EndMenu(); } } diff --git a/src/ui/Application.h b/src/ui/Application.h index 323f29d..4a5d655 100644 --- a/src/ui/Application.h +++ b/src/ui/Application.h @@ -18,15 +18,25 @@ namespace UsdLayerManager { +/// Global (non-per-viewport) rendering preferences persisted to preferences.ini. +struct AppPreferences { + std::string renderDelegate = "HdStormRendererPlugin"; + int colorCorrectionMode = 1; ///< ColorCorrectionMode cast to int (1 = sRGB) + std::string ocioDisplay; + std::string ocioView; + std::string ocioColorSpace; + std::string ocioLook; +}; + class Application { public: Application(); ~Application(); - + bool Initialize(const std::string& windowTitle = "USD Layer Manager", int width = 1280, int height = 720); void Run(); void Shutdown(); - + private: void Update(); void RenderUI(); @@ -34,21 +44,27 @@ private: void RenderStatusBar(); void RenderStageInfo(); void RefreshManagers(); - + // File operations void OpenUsdFile(); - void CreateNewUsdFile(); // creates fresh in-memory stage + void CreateNewUsdFile(); void SaveUsdFile(); void SaveUsdFileAs(); - void CloseUsdFile(); // closes file-backed stage, falls back to default stage + void CloseUsdFile(); - // Stage editing operations (also exposed via Stage menu) + // Stage editing operations void AddReferenceToStage(); void CreatePrimOnStage(const std::string& typeName); - // Playblast — called each frame while a capture is in progress. + // Playblast void CapturePlayblastFrame(); - + + // Preferences + void LoadPreferences(); + void SavePreferences(); + void ApplyPrefsToAllViewports(); + void RenderPreferencesDialog(); + std::unique_ptr m_imguiContext; std::unique_ptr m_iconManager; std::unique_ptr m_stageManager; @@ -71,8 +87,18 @@ private: bool m_showCurveEditor; bool m_running; - MovieEncoder m_movieEncoder; - std::string m_viewportSettingsPath; ///< %APPDATA%\UsdLayerManager\viewport_settings.ini + MovieEncoder m_movieEncoder; + std::string m_viewportSettingsPath; + std::string m_prefsPath; + AppPreferences m_prefs; + bool m_showPreferences = false; + + // OCIO edit buffers for Preferences dialog + char m_prefOcioDisplayBuf[128] = {}; + char m_prefOcioViewBuf[128] = {}; + char m_prefOcioColorSpaceBuf[128] = {}; + char m_prefOcioLookBuf[128] = {}; + bool m_prefOcioFieldsSynced = false; }; } // namespace UsdLayerManager diff --git a/src/ui/ViewportPanel.cpp b/src/ui/ViewportPanel.cpp index 4cedb74..51ed475 100644 --- a/src/ui/ViewportPanel.cpp +++ b/src/ui/ViewportPanel.cpp @@ -67,6 +67,7 @@ void ViewportPanel::WireCallbacks(int i) BroadcastSelection(); if (OnPrimsPickedRect) OnPrimsPickedRect(paths); }; + } // --------------------------------------------------------------------------- @@ -610,6 +611,34 @@ void ViewportPanel::Render(bool* p_open) ImGui::PopStyleVar(); // outer WindowPadding } +// --------------------------------------------------------------------------- +// Global preferences +// --------------------------------------------------------------------------- +void ViewportPanel::ApplyDefaultDelegate(const std::string& delegate) +{ + if (delegate.empty()) return; + for (auto& tile : m_tiles) { + if (tile->GetRenderer().GetCurrentRendererId().IsEmpty()) + tile->GetRenderer().SetRendererPlugin(pxr::TfToken(delegate)); + } +} + +void ViewportPanel::ApplyGlobalColorCorrection(int ccMode, + const std::string& ocioDisplay, + const std::string& ocioView, + const std::string& ocioColorSpace, + const std::string& ocioLook) +{ + for (auto& tile : m_tiles) { + auto& r = tile->GetRenderer(); + r.SetColorCorrectionMode(static_cast(ccMode)); + r.SetOcioDisplay(ocioDisplay); + r.SetOcioView(ocioView); + r.SetOcioColorSpace(ocioColorSpace); + r.SetOcioLook(ocioLook); + } +} + // --------------------------------------------------------------------------- // Settings persistence // --------------------------------------------------------------------------- @@ -626,21 +655,16 @@ void ViewportPanel::SaveSettings(const std::string& path) const for (int i = 0; i < static_cast(m_tiles.size()); ++i) { ViewportTileSettings s = m_tiles[i]->GetSettings(); f << "\n[Tile" << i << "]\n"; - f << "RenderDelegate=" << s.renderDelegate << "\n"; - f << "ShowGrid=" << (s.showGrid ? 1 : 0) << "\n"; - f << "AAEnabled=" << (s.aaEnabled ? 1 : 0) << "\n"; - f << "BgColorR=" << s.bgColorR << "\n"; - f << "BgColorG=" << s.bgColorG << "\n"; - f << "BgColorB=" << s.bgColorB << "\n"; - f << "BBoxMode=" << s.bboxMode << "\n"; - f << "AmbientLightOnly=" << (s.ambientLightOnly ? 1 : 0) << "\n"; - f << "DomeLightEnabled=" << (s.domeLightEnabled ? 1 : 0) << "\n"; - f << "ShadingMode=" << s.shadingMode << "\n"; - f << "ColorCorrectionMode=" << s.colorCorrectionMode << "\n"; - f << "OcioDisplay=" << s.ocioDisplay << "\n"; - f << "OcioView=" << s.ocioView << "\n"; - f << "OcioColorSpace=" << s.ocioColorSpace << "\n"; - f << "OcioLook=" << s.ocioLook << "\n"; + f << "RenderDelegate=" << s.renderDelegate << "\n"; + f << "ShowGrid=" << (s.showGrid ? 1 : 0) << "\n"; + f << "AAEnabled=" << (s.aaEnabled ? 1 : 0) << "\n"; + f << "BgColorR=" << s.bgColorR << "\n"; + f << "BgColorG=" << s.bgColorG << "\n"; + f << "BgColorB=" << s.bgColorB << "\n"; + f << "BBoxMode=" << s.bboxMode << "\n"; + f << "AmbientLightOnly=" << (s.ambientLightOnly ? 1 : 0) << "\n"; + f << "DomeLightEnabled=" << (s.domeLightEnabled ? 1 : 0) << "\n"; + f << "ShadingMode=" << s.shadingMode << "\n"; } } @@ -697,7 +721,7 @@ void ViewportPanel::LoadSettings(const std::string& path) else if (key == "SplitV") splitV = toFloat(val, splitV); } else { auto& ts = tileSettings[section]; - if (key == "RenderDelegate") ts.renderDelegate = val; + if (key == "RenderDelegate") ts.renderDelegate = val; else if (key == "ShowGrid") ts.showGrid = (val == "1"); else if (key == "AAEnabled") ts.aaEnabled = (val == "1"); else if (key == "BgColorR") ts.bgColorR = toFloat(val, ts.bgColorR); @@ -706,12 +730,7 @@ void ViewportPanel::LoadSettings(const std::string& path) else if (key == "BBoxMode") ts.bboxMode = toInt(val, ts.bboxMode); else if (key == "AmbientLightOnly") ts.ambientLightOnly = (val == "1"); else if (key == "DomeLightEnabled") ts.domeLightEnabled = (val == "1"); - else if (key == "ShadingMode") ts.shadingMode = toInt(val, ts.shadingMode); - else if (key == "ColorCorrectionMode") ts.colorCorrectionMode = toInt(val, ts.colorCorrectionMode); - else if (key == "OcioDisplay") ts.ocioDisplay = val; - else if (key == "OcioView") ts.ocioView = val; - else if (key == "OcioColorSpace") ts.ocioColorSpace = val; - else if (key == "OcioLook") ts.ocioLook = val; + else if (key == "ShadingMode") ts.shadingMode = toInt(val, ts.shadingMode); } } diff --git a/src/ui/ViewportPanel.h b/src/ui/ViewportPanel.h index f635dcf..3f81e8d 100644 --- a/src/ui/ViewportPanel.h +++ b/src/ui/ViewportPanel.h @@ -52,7 +52,7 @@ public: // ── Main render (called from Application::RenderUI) ────────────────────── void Render(bool* p_open = nullptr); - // ── Pick callbacks (wired by Application after construction) ───────────── + // ── Callbacks (wired by Application after construction) ────────────────── std::function OnPrimPicked; std::function&)> OnPrimsPickedRect; @@ -65,9 +65,18 @@ public: LayoutMode GetLayout() const { return m_layout; } int GetFocusedTileIndex() const { return m_focusedTileIndex; } + // ── Global preferences ──────────────────────────────────────────────────── + /// Set render delegate on tiles that have no saved delegate yet (empty token). + /// Called once on startup so the pref default applies to fresh tiles. + void ApplyDefaultDelegate(const std::string& delegate); + /// Apply color correction to every tile (always global). + void ApplyGlobalColorCorrection(int ccMode, const std::string& ocioDisplay, + const std::string& ocioView, + const std::string& ocioColorSpace, + const std::string& ocioLook); + // ── Settings persistence ───────────────────────────────────────────────── /// Load layout and per-tile render settings from a simple INI file. - /// Safe to call immediately after construction (before any Render call). void LoadSettings(const std::string& path); /// Save current layout and per-tile render settings to a simple INI file. void SaveSettings(const std::string& path) const; diff --git a/src/ui/ViewportTile.cpp b/src/ui/ViewportTile.cpp index 5ee020f..d49180e 100644 --- a/src/ui/ViewportTile.cpp +++ b/src/ui/ViewportTile.cpp @@ -1,6 +1,5 @@ #include "ViewportTile.h" #include "../utils/Logger.h" -#include "../utils/OcioConfigParser.h" #include #include @@ -100,12 +99,7 @@ ViewportTileSettings ViewportTile::GetSettings() const s.bboxMode = static_cast(m_renderer.GetBBoxMode()); s.ambientLightOnly = m_renderer.GetAmbientLightOnly(); s.domeLightEnabled = m_renderer.GetDomeLightEnabled(); - s.shadingMode = static_cast(m_renderer.GetShadingMode()); - s.colorCorrectionMode = static_cast(m_renderer.GetColorCorrectionMode()); - s.ocioDisplay = m_renderer.GetOcioDisplay(); - s.ocioView = m_renderer.GetOcioView(); - s.ocioColorSpace = m_renderer.GetOcioColorSpace(); - s.ocioLook = m_renderer.GetOcioLook(); + s.shadingMode = static_cast(m_renderer.GetShadingMode()); return s; } @@ -120,12 +114,6 @@ void ViewportTile::ApplySettings(const ViewportTileSettings& s) m_renderer.SetAmbientLightOnly(s.ambientLightOnly); m_renderer.SetDomeLightEnabled(s.domeLightEnabled); m_renderer.SetShadingMode(static_cast(s.shadingMode)); - m_renderer.SetColorCorrectionMode(static_cast(s.colorCorrectionMode)); - m_renderer.SetOcioDisplay(s.ocioDisplay); - m_renderer.SetOcioView(s.ocioView); - m_renderer.SetOcioColorSpace(s.ocioColorSpace); - m_renderer.SetOcioLook(s.ocioLook); - m_ocioFieldsSynced = false; // re-seed OCIO edit buffers from restored values } // --------------------------------------------------------------------------- @@ -912,29 +900,31 @@ void ViewportTile::RenderCompactToolbar(int tileIndex) ImGui::TextDisabled("|"); ImGui::SameLine(); - // -- Render delegate ----------------------------------------------------- - pxr::TfToken currentId = m_renderer.GetCurrentRendererId(); - std::string displayName = currentId.IsEmpty() - ? "Rdr" - : UsdSceneRenderer::GetRendererDisplayName(currentId); - if (displayName.size() > 8) displayName = displayName.substr(0, 8); + // -- Render delegate (global) -------------------------------------------- + { + pxr::TfToken currentId = m_renderer.GetCurrentRendererId(); + std::string displayName = currentId.IsEmpty() + ? "Rdr" + : UsdSceneRenderer::GetRendererDisplayName(currentId); + if (displayName.size() > 8) displayName = displayName.substr(0, 8); - std::string rdrBtnId = "##Rdr_" + std::to_string(tileIndex); - ImGui::Button(displayName.c_str(), ImVec2(72.0f, 0.0f)); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Render delegate -- click to change"); + std::string rdrPopupId = "RdrPopup_" + std::to_string(tileIndex); + ImGui::Button(displayName.c_str(), ImVec2(72.0f, 0.0f)); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Render delegate"); - ImGuiPopupFlags popupFlags = ImGuiPopupFlags_MouseButtonLeft; - std::string rdrPopupId = "RdrPopup_" + std::to_string(tileIndex); - if (ImGui::BeginPopupContextItem(rdrPopupId.c_str(), popupFlags)) { - for (const auto& pluginId : UsdSceneRenderer::GetRendererPlugins()) { - std::string name = UsdSceneRenderer::GetRendererDisplayName(pluginId); - if (name.empty()) name = pluginId.GetString(); - bool selected = (pluginId == currentId); - if (ImGui::MenuItem(name.c_str(), nullptr, selected)) - if (!selected) m_renderer.SetRendererPlugin(pluginId); + if (ImGui::BeginPopupContextItem(rdrPopupId.c_str(), + ImGuiPopupFlags_MouseButtonLeft)) + { + for (const auto& pluginId : UsdSceneRenderer::GetRendererPlugins()) { + std::string name = UsdSceneRenderer::GetRendererDisplayName(pluginId); + if (name.empty()) name = pluginId.GetString(); + bool selected = (pluginId == currentId); + if (ImGui::MenuItem(name.c_str(), nullptr, selected) && !selected) + m_renderer.SetRendererPlugin(pluginId); + } + ImGui::EndPopup(); } - ImGui::EndPopup(); } ImGui::SameLine(); @@ -997,124 +987,6 @@ void ViewportTile::RenderCompactToolbar(int tileIndex) if (ImGui::BeginPopupContextItem(gearPopupId.c_str(), ImGuiPopupFlags_MouseButtonLeft)) { - // -- Color correction -------------------------------------------- - if (ImGui::BeginMenu("Color Correction")) { - ColorCorrectionMode ccm = m_renderer.GetColorCorrectionMode(); - auto ccItem = [&](const char* label, ColorCorrectionMode mode) { - if (ImGui::MenuItem(label, nullptr, ccm == mode)) { - m_renderer.SetColorCorrectionMode(mode); - m_ocioFieldsSynced = false; - } - }; - ccItem("Disabled", ColorCorrectionMode::Disabled); - ccItem("sRGB", ColorCorrectionMode::sRGB); - ccItem("OpenColorIO", ColorCorrectionMode::OpenColorIO); - - if (ccm == ColorCorrectionMode::OpenColorIO) { - ImGui::Separator(); - const OcioConfig& ocfg = GetCurrentOcioConfig(); - - // Seed buffers from renderer, falling back to OCIO config defaults - if (!m_ocioFieldsSynced) { - std::string disp = m_renderer.GetOcioDisplay(); - std::string view = m_renderer.GetOcioView(); - if (disp.empty()) disp = ocfg.defaultDisplay; - if (view.empty()) view = ocfg.defaultView; - strncpy(m_ocioDisplayBuf, disp.c_str(), 127); - strncpy(m_ocioViewBuf, view.c_str(), 127); - strncpy(m_ocioColorSpaceBuf, m_renderer.GetOcioColorSpace().c_str(), 127); - strncpy(m_ocioLookBuf, m_renderer.GetOcioLook().c_str(), 127); - if (m_renderer.GetOcioDisplay().empty() && !disp.empty()) - m_renderer.SetOcioDisplay(disp); - if (m_renderer.GetOcioView().empty() && !view.empty()) - m_renderer.SetOcioView(view); - m_ocioFieldsSynced = true; - } - - ImGui::PushItemWidth(200.f); - - // Display combo - if (ImGui::BeginCombo("Display##ocio", m_ocioDisplayBuf)) { - for (const auto& d : ocfg.displays) { - bool sel = (d == m_ocioDisplayBuf); - if (ImGui::Selectable(d.c_str(), sel)) { - strncpy(m_ocioDisplayBuf, d.c_str(), 127); - m_renderer.SetOcioDisplay(d); - // Auto-select default view for new display - auto vit = ocfg.views.find(d); - if (vit != ocfg.views.end() && !vit->second.empty()) { - strncpy(m_ocioViewBuf, vit->second[0].c_str(), 127); - m_renderer.SetOcioView(vit->second[0]); - } - } - if (sel) ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - - // View combo — filtered by current display - { - static const std::vector kEmpty; - auto vit = ocfg.views.find(std::string(m_ocioDisplayBuf)); - const auto& views = (vit != ocfg.views.end()) ? vit->second : kEmpty; - if (ImGui::BeginCombo("View##ocio", m_ocioViewBuf)) { - for (const auto& v : views) { - bool sel = (v == m_ocioViewBuf); - if (ImGui::Selectable(v.c_str(), sel)) { - strncpy(m_ocioViewBuf, v.c_str(), 127); - m_renderer.SetOcioView(v); - } - if (sel) ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - } - - // Color Space combo - if (ImGui::BeginCombo("Color Space##ocio", - m_ocioColorSpaceBuf[0] ? m_ocioColorSpaceBuf : "(default)")) { - if (ImGui::Selectable("(default)", m_ocioColorSpaceBuf[0] == '\0')) { - m_ocioColorSpaceBuf[0] = '\0'; - m_renderer.SetOcioColorSpace(""); - } - if (m_ocioColorSpaceBuf[0] == '\0') ImGui::SetItemDefaultFocus(); - for (const auto& cs : ocfg.colorSpaces) { - bool sel = (cs == m_ocioColorSpaceBuf); - if (ImGui::Selectable(cs.c_str(), sel)) { - strncpy(m_ocioColorSpaceBuf, cs.c_str(), 127); - m_renderer.SetOcioColorSpace(cs); - } - if (sel) ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - - // Look combo - if (ImGui::BeginCombo("Look##ocio", - m_ocioLookBuf[0] ? m_ocioLookBuf : "(none)")) { - if (ImGui::Selectable("(none)", m_ocioLookBuf[0] == '\0')) { - m_ocioLookBuf[0] = '\0'; - m_renderer.SetOcioLook(""); - } - if (m_ocioLookBuf[0] == '\0') ImGui::SetItemDefaultFocus(); - for (const auto& look : ocfg.looks) { - bool sel = (look == m_ocioLookBuf); - if (ImGui::Selectable(look.c_str(), sel)) { - strncpy(m_ocioLookBuf, look.c_str(), 127); - m_renderer.SetOcioLook(look); - } - if (sel) ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } - - ImGui::PopItemWidth(); - } - ImGui::EndMenu(); - } - - ImGui::Separator(); - // -- Shading mode ------------------------------------------------ if (ImGui::BeginMenu("Shading")) { ShadingMode cur = m_renderer.GetShadingMode(); diff --git a/src/ui/ViewportTile.h b/src/ui/ViewportTile.h index 2d900c6..2a0aff1 100644 --- a/src/ui/ViewportTile.h +++ b/src/ui/ViewportTile.h @@ -17,23 +17,19 @@ namespace UsdLayerManager { -/// Serialisable snapshot of all per-tile render settings. +/// Serialisable snapshot of per-tile render settings. +/// Color correction is global (AppPreferences); render delegate is per-tile. struct ViewportTileSettings { - std::string renderDelegate; ///< TfToken string, empty = default (Storm) + std::string renderDelegate; ///< empty = use AppPreferences default on first load bool showGrid = true; bool aaEnabled = false; float bgColorR = 0.15f; float bgColorG = 0.15f; float bgColorB = 0.15f; - int bboxMode = 0; ///< BBoxMode cast to int + int bboxMode = 0; bool ambientLightOnly = true; bool domeLightEnabled = false; - int shadingMode = 0; ///< ShadingMode cast to int (0 = SmoothShaded) - int colorCorrectionMode = 1; ///< ColorCorrectionMode cast to int (1 = sRGB) - std::string ocioDisplay; - std::string ocioView; - std::string ocioColorSpace; - std::string ocioLook; + int shadingMode = 0; }; /// Named orthographic view directions. @@ -183,13 +179,6 @@ private: // ── Orthographic view ──────────────────────────────────────────────────── OrthoView m_orthoView = OrthoView::None; - // ── OCIO InputText edit buffers (per-tile, seeded on mode activation) ──── - char m_ocioDisplayBuf[128] = {}; - char m_ocioViewBuf[128] = {}; - char m_ocioColorSpaceBuf[128] = {}; - char m_ocioLookBuf[128] = {}; - bool m_ocioFieldsSynced = false; - // ── Per-frame interaction flags ────────────────────────────────────────── bool m_wasClickedThisFrame = false; bool m_wasHoveredThisFrame = false;