Add per-viewport render settings persistence (delegate, grid, AA, bg, bbox, lighting)

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 09:34:00 +08:00
parent 4b7323c24c
commit 9d8840ced6
6 changed files with 172 additions and 0 deletions
+12
View File
@@ -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();
+1
View File
@@ -72,6 +72,7 @@ private:
bool m_running;
MovieEncoder m_movieEncoder;
std::string m_viewportSettingsPath; ///< %APPDATA%\UsdLayerManager\viewport_settings.ini
};
} // namespace UsdLayerManager
+107
View File
@@ -3,6 +3,8 @@
#include <imgui.h>
#include <algorithm>
#include <string>
#include <fstream>
#include <sstream>
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<int>(m_layout) << "\n";
f << "SplitH=" << m_splitH << "\n";
f << "SplitV=" << m_splitV << "\n";
for (int i = 0; i < static_cast<int>(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<int>(m_layout);
float splitH = m_splitH;
float splitV = m_splitV;
std::vector<ViewportTileSettings> 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<int>(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<LayoutMode>(std::clamp(layoutMode, 0, 3)));
// Apply per-tile settings to whichever tiles now exist
for (int i = 0; i < static_cast<int>(tileSettings.size()); ++i) {
if (i < static_cast<int>(m_tiles.size()))
m_tiles[i]->ApplySettings(tileSettings[i]);
}
}
} // namespace UsdLayerManager
+7
View File
@@ -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 ─────────────────────────────────────────────────────
+27
View File
@@ -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<int>(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<BBoxMode>(s.bboxMode));
m_renderer.SetAmbientLightOnly(s.ambientLightOnly);
m_renderer.SetDomeLightEnabled(s.domeLightEnabled);
}
// ---------------------------------------------------------------------------
void ViewportTile::RefreshCameraList()
{
+18
View File
@@ -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();