From 9d8840ced67e478e0093857a71b94e622973dec1 Mon Sep 17 00:00:00 2001 From: indigo Date: Thu, 25 Jun 2026 09:34:00 +0800 Subject: [PATCH] Add per-viewport render settings persistence (delegate, grid, AA, bg, bbox, lighting) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ViewportTileSettings struct captures: render delegate, show grid, AA, background color, bbox mode, ambient-light-only, dome-light flags. ViewportPanel::SaveSettings() writes a simple INI file (layout + one [TileN] section per tile) to %APPDATA%\UsdLayerManager\viewport_settings.ini on Application::Shutdown(). ViewportPanel::LoadSettings() is called at the end of Application::Initialize() — after the viewport panel is constructed but before the first frame — so the saved render delegate is picked up by UsdSceneRenderer::InitRenderer() on the first Render() call. Layout (Single/HSplit/VSplit/Quad) and split ratios are also restored, creating the correct number of tiles before settings are applied. Co-Authored-By: Claude Sonnet 4.6 --- src/ui/Application.cpp | 12 +++++ src/ui/Application.h | 1 + src/ui/ViewportPanel.cpp | 107 +++++++++++++++++++++++++++++++++++++++ src/ui/ViewportPanel.h | 7 +++ src/ui/ViewportTile.cpp | 27 ++++++++++ src/ui/ViewportTile.h | 18 +++++++ 6 files changed, 172 insertions(+) diff --git a/src/ui/Application.cpp b/src/ui/Application.cpp index 286b31f..18635ff 100644 --- a/src/ui/Application.cpp +++ b/src/ui/Application.cpp @@ -138,6 +138,15 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig RefreshManagers(); } + // Load per-viewport settings (render delegate, grid, AA, etc.) 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_viewportPanel->LoadSettings(m_viewportSettingsPath); + } + LOG_INFO("Application initialized successfully"); return true; } @@ -155,6 +164,9 @@ void Application::Run() { } void Application::Shutdown() { + if (m_viewportPanel && !m_viewportSettingsPath.empty()) + m_viewportPanel->SaveSettings(m_viewportSettingsPath); + m_viewportPanel.reset(); m_sceneHierarchyPanel.reset(); m_propertyPanel.reset(); diff --git a/src/ui/Application.h b/src/ui/Application.h index 5b269ef..323f29d 100644 --- a/src/ui/Application.h +++ b/src/ui/Application.h @@ -72,6 +72,7 @@ private: bool m_running; MovieEncoder m_movieEncoder; + std::string m_viewportSettingsPath; ///< %APPDATA%\UsdLayerManager\viewport_settings.ini }; } // namespace UsdLayerManager diff --git a/src/ui/ViewportPanel.cpp b/src/ui/ViewportPanel.cpp index 3863ebd..d4c6b16 100644 --- a/src/ui/ViewportPanel.cpp +++ b/src/ui/ViewportPanel.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include namespace UsdLayerManager { @@ -608,4 +610,109 @@ void ViewportPanel::Render(bool* p_open) ImGui::PopStyleVar(); // outer WindowPadding } +// --------------------------------------------------------------------------- +// Settings persistence +// --------------------------------------------------------------------------- +void ViewportPanel::SaveSettings(const std::string& path) const +{ + std::ofstream f(path); + if (!f) return; + + f << "[Viewport]\n"; + f << "Layout=" << static_cast(m_layout) << "\n"; + f << "SplitH=" << m_splitH << "\n"; + f << "SplitV=" << m_splitV << "\n"; + + 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"; + } +} + +void ViewportPanel::LoadSettings(const std::string& path) +{ + std::ifstream f(path); + if (!f) return; + + int layoutMode = static_cast(m_layout); + float splitH = m_splitH; + float splitV = m_splitV; + std::vector tileSettings; // indexed by tile number + + int section = -1; // -1=Viewport, >=0 = tile index + std::string line; + while (std::getline(f, line)) { + // Strip leading whitespace and CR + while (!line.empty() && (line.front() == ' ' || line.front() == '\t')) + line.erase(line.begin()); + if (!line.empty() && line.back() == '\r') + line.pop_back(); + if (line.empty() || line.front() == '#') continue; + + if (line.front() == '[') { + size_t close = line.find(']'); + if (close == std::string::npos) continue; + std::string name = line.substr(1, close - 1); + if (name == "Viewport") { + section = -1; + } else if (name.size() > 4 && name.substr(0, 4) == "Tile") { + try { + int idx = std::stoi(name.substr(4)); + if (idx >= 0) { + section = idx; + if (static_cast(tileSettings.size()) <= idx) + tileSettings.resize(idx + 1); + } + } catch (...) { section = -1; } + } + 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); + + auto toInt = [](const std::string& v, int def) { try { return std::stoi(v); } catch (...) { return def; } }; + auto toFloat = [](const std::string& v, float def) { try { return std::stof(v); } catch (...) { return def; } }; + + if (section == -1) { + if (key == "Layout") layoutMode = toInt(val, layoutMode); + else if (key == "SplitH") splitH = toFloat(val, splitH); + else if (key == "SplitV") splitV = toFloat(val, splitV); + } else { + auto& ts = tileSettings[section]; + 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); + else if (key == "BgColorG") ts.bgColorG = toFloat(val, ts.bgColorG); + else if (key == "BgColorB") ts.bgColorB = toFloat(val, ts.bgColorB); + 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"); + } + } + + // Apply layout (creates/destroys tiles as needed) + m_splitH = splitH; + m_splitV = splitV; + SetLayout(static_cast(std::clamp(layoutMode, 0, 3))); + + // Apply per-tile settings to whichever tiles now exist + for (int i = 0; i < static_cast(tileSettings.size()); ++i) { + if (i < static_cast(m_tiles.size())) + m_tiles[i]->ApplySettings(tileSettings[i]); + } +} + } // namespace UsdLayerManager diff --git a/src/ui/ViewportPanel.h b/src/ui/ViewportPanel.h index e9ddecc..f635dcf 100644 --- a/src/ui/ViewportPanel.h +++ b/src/ui/ViewportPanel.h @@ -65,6 +65,13 @@ public: LayoutMode GetLayout() const { return m_layout; } int GetFocusedTileIndex() const { return m_focusedTileIndex; } + // ── 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; + private: // ── Tile rect helper ───────────────────────────────────────────────────── diff --git a/src/ui/ViewportTile.cpp b/src/ui/ViewportTile.cpp index 1757e8b..ffc8687 100644 --- a/src/ui/ViewportTile.cpp +++ b/src/ui/ViewportTile.cpp @@ -87,6 +87,33 @@ void ViewportTile::FrameScene() } } +// --------------------------------------------------------------------------- +ViewportTileSettings ViewportTile::GetSettings() const +{ + ViewportTileSettings s; + s.renderDelegate = m_renderer.GetCurrentRendererId().GetString(); + s.showGrid = m_renderer.ShowGrid(); + s.aaEnabled = m_renderer.GetAAEnabled(); + const pxr::GfVec3f& bg = m_renderer.GetBackgroundColor(); + s.bgColorR = bg[0]; s.bgColorG = bg[1]; s.bgColorB = bg[2]; + s.bboxMode = static_cast(m_renderer.GetBBoxMode()); + s.ambientLightOnly = m_renderer.GetAmbientLightOnly(); + s.domeLightEnabled = m_renderer.GetDomeLightEnabled(); + return s; +} + +void ViewportTile::ApplySettings(const ViewportTileSettings& s) +{ + if (!s.renderDelegate.empty()) + m_renderer.SetRendererPlugin(pxr::TfToken(s.renderDelegate)); + m_renderer.SetShowGrid(s.showGrid); + m_renderer.SetAAEnabled(s.aaEnabled); + m_renderer.SetBackgroundColor(pxr::GfVec3f(s.bgColorR, s.bgColorG, s.bgColorB)); + m_renderer.SetBBoxMode(static_cast(s.bboxMode)); + m_renderer.SetAmbientLightOnly(s.ambientLightOnly); + m_renderer.SetDomeLightEnabled(s.domeLightEnabled); +} + // --------------------------------------------------------------------------- void ViewportTile::RefreshCameraList() { diff --git a/src/ui/ViewportTile.h b/src/ui/ViewportTile.h index 5486700..b3ce184 100644 --- a/src/ui/ViewportTile.h +++ b/src/ui/ViewportTile.h @@ -17,6 +17,19 @@ namespace UsdLayerManager { +/// Serialisable snapshot of all per-tile render settings. +struct ViewportTileSettings { + std::string renderDelegate; ///< TfToken string, empty = default (Storm) + 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 + bool ambientLightOnly = false; + bool domeLightEnabled = false; +}; + /// Named orthographic view directions. /// When m_orthoView != None the tile renders an orthographic camera locked to /// that world-space direction; the free-camera orbital state (center + dist) @@ -87,6 +100,11 @@ public: void FrameScene(); + /// Snapshot all current render settings into a portable struct. + ViewportTileSettings GetSettings() const; + /// Apply a previously-saved settings snapshot (safe to call before first Render). + void ApplySettings(const ViewportTileSettings& s); + private: // ── Render sub-functions ───────────────────────────────────────────────── pxr::GfCamera ResolveCamera();