From 8316e419badb320b4ac558d71b9aff7b087024f4 Mon Sep 17 00:00:00 2001 From: indigo Date: Thu, 25 Jun 2026 09:56:49 +0800 Subject: [PATCH] Add gear icon toolbar button with viewport options dropdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a ⚙ icon button to the right of the AA toggle in each viewport tile's compact toolbar. Clicking it opens a dropdown with: - Shading submenu: Smooth Shaded / Flat Shaded / Wireframe on Surface / Wireframe / Unlit (new ShadingMode enum driving UsdImagingGLDrawMode) - Lighting submenu: Camera Headlight / Scene Lights + Dome Light toggle - Show Grid and Anti-Aliasing toggles (mirrored from icon buttons) - Bounding Box submenu: None / Per Object / All Selection - Background submenu: Dark Gray / Black / Light Gray / Midnight Blue ShadingMode is persisted in viewport_settings.ini alongside the existing per-tile settings (saved on shutdown, restored on startup). Co-Authored-By: Claude Sonnet 4.6 --- resources/icons/gear.svg | 3 ++ src/core/UsdSceneRenderer.cpp | 21 +++++++- src/core/UsdSceneRenderer.h | 13 +++++ src/ui/IconManager.cpp | 3 +- src/ui/IconManager.h | 1 + src/ui/ViewportPanel.cpp | 2 + src/ui/ViewportTile.cpp | 96 +++++++++++++++++++++++++++++++++++ src/ui/ViewportTile.h | 1 + 8 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 resources/icons/gear.svg diff --git a/resources/icons/gear.svg b/resources/icons/gear.svg new file mode 100644 index 0000000..112381c --- /dev/null +++ b/resources/icons/gear.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/core/UsdSceneRenderer.cpp b/src/core/UsdSceneRenderer.cpp index e6c80e7..056b941 100644 --- a/src/core/UsdSceneRenderer.cpp +++ b/src/core/UsdSceneRenderer.cpp @@ -131,6 +131,7 @@ UsdSceneRenderer::UsdSceneRenderer() : m_showGrid(true) , m_aaEnabled(false) , m_backgroundColor(0.15f, 0.15f, 0.15f) + , m_shadingMode(ShadingMode::SmoothShaded) , m_ambientLightOnly(true) , m_domeLightEnabled(false) , m_stageIsZup(false) @@ -616,11 +617,27 @@ void UsdSceneRenderer::Render(int width, int height) m_renderParams = pxr::UsdImagingGLRenderParams(); m_renderParams.frame = m_currentTime; m_renderParams.complexity = 1.0f; - m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_SHADED_SMOOTH; + // Apply shading mode → drawMode + enableLighting + switch (m_shadingMode) { + case ShadingMode::FlatShaded: + m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_SHADED_FLAT; + m_renderParams.enableLighting = true; break; + case ShadingMode::WireframeOnSurface: + m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_WIREFRAME_ON_SURFACE; + m_renderParams.enableLighting = true; break; + case ShadingMode::Wireframe: + m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_WIREFRAME; + m_renderParams.enableLighting = false; break; + case ShadingMode::Unlit: + m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_SHADED_SMOOTH; + m_renderParams.enableLighting = false; break; + default: // SmoothShaded + m_renderParams.drawMode = pxr::UsdImagingGLDrawMode::DRAW_SHADED_SMOOTH; + m_renderParams.enableLighting = true; break; + } m_renderParams.showGuides = true; m_renderParams.showProxy = true; m_renderParams.showRender = false; - m_renderParams.enableLighting = true; m_renderParams.enableSampleAlphaToCoverage = true; m_renderParams.gammaCorrectColors = false; m_renderParams.cullStyle = pxr::UsdImagingGLCullStyle::CULL_STYLE_BACK_UNLESS_DOUBLE_SIDED; diff --git a/src/core/UsdSceneRenderer.h b/src/core/UsdSceneRenderer.h index 9ad6179..6f9ea2f 100644 --- a/src/core/UsdSceneRenderer.h +++ b/src/core/UsdSceneRenderer.h @@ -31,6 +31,15 @@ enum class BBoxMode { AllSelection ///< One combined box for the entire selection }; +/// Surface shading mode — maps to UsdImagingGLDrawMode + enableLighting. +enum class ShadingMode { + SmoothShaded, ///< Smooth shading with materials (default) + FlatShaded, ///< Flat shading with materials + WireframeOnSurface, ///< Wireframe overlay on solid surface + Wireframe, ///< Wireframe only, no fill + Unlit, ///< Smooth geometry, lighting disabled +}; + class UsdSceneRenderer { public: UsdSceneRenderer(); @@ -199,6 +208,9 @@ public: bool GetDomeLightEnabled() const { return m_domeLightEnabled; } void SetDomeLightEnabled(bool val) { m_domeLightEnabled = val; } + ShadingMode GetShadingMode() const { return m_shadingMode; } + void SetShadingMode(ShadingMode m) { m_shadingMode = m; m_forceRefresh = true; } + /// Default material ambient (kA, default 0.2 — matches viewSettingsDataModel.py). float GetDefaultMaterialAmbient() const { return m_defaultMaterialAmbient; } void SetDefaultMaterialAmbient(float v) { m_defaultMaterialAmbient = v; } @@ -298,6 +310,7 @@ private: pxr::GfVec3f m_backgroundColor; // Lighting settings (mirrors stageView.py viewSettings) + ShadingMode m_shadingMode; // draw mode + lighting flags bool m_ambientLightOnly; // camera headlight bool m_domeLightEnabled; // dome/IBL light bool m_stageIsZup; // used for dome light rotation diff --git a/src/ui/IconManager.cpp b/src/ui/IconManager.cpp index 6c26576..7a8b899 100644 --- a/src/ui/IconManager.cpp +++ b/src/ui/IconManager.cpp @@ -60,6 +60,7 @@ static const char* IconFilename(Icon icon) { case Icon::Refresh: return "arrows-rotate.svg"; case Icon::FilePlus: return "file-plus.svg"; case Icon::Pen: return "pen.svg"; + case Icon::Settings: return "gear.svg"; default: return nullptr; } } @@ -75,7 +76,7 @@ static constexpr Icon kAllIcons[] = { Icon::LayoutSingle, Icon::LayoutHSplit, Icon::LayoutVSplit, Icon::LayoutQuad, Icon::SkipBack, Icon::StepBack, Icon::PlayBack, Icon::Play, Icon::Pause, Icon::StepForward, Icon::SkipEnd, Icon::Loop, Icon::Bounce, - Icon::Refresh, Icon::FilePlus, Icon::Pen, + Icon::Refresh, Icon::FilePlus, Icon::Pen, Icon::Settings, }; // --------------------------------------------------------------------------- diff --git a/src/ui/IconManager.h b/src/ui/IconManager.h index f13a899..47ed9e4 100644 --- a/src/ui/IconManager.h +++ b/src/ui/IconManager.h @@ -54,6 +54,7 @@ enum class Icon { Refresh, // reload / refresh FilePlus, // create new file Pen, // edit / edit target + Settings, // gear / viewport options }; /// Loads SVG files from disk, rasterizes them with NanoSVG, uploads them as diff --git a/src/ui/ViewportPanel.cpp b/src/ui/ViewportPanel.cpp index d4c6b16..aca4c84 100644 --- a/src/ui/ViewportPanel.cpp +++ b/src/ui/ViewportPanel.cpp @@ -635,6 +635,7 @@ void ViewportPanel::SaveSettings(const std::string& path) const f << "BBoxMode=" << s.bboxMode << "\n"; f << "AmbientLightOnly=" << (s.ambientLightOnly ? 1 : 0) << "\n"; f << "DomeLightEnabled=" << (s.domeLightEnabled ? 1 : 0) << "\n"; + f << "ShadingMode=" << s.shadingMode << "\n"; } } @@ -700,6 +701,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); } } diff --git a/src/ui/ViewportTile.cpp b/src/ui/ViewportTile.cpp index ffc8687..4362e6b 100644 --- a/src/ui/ViewportTile.cpp +++ b/src/ui/ViewportTile.cpp @@ -99,6 +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()); return s; } @@ -112,6 +113,7 @@ void ViewportTile::ApplySettings(const ViewportTileSettings& s) m_renderer.SetBBoxMode(static_cast(s.bboxMode)); m_renderer.SetAmbientLightOnly(s.ambientLightOnly); m_renderer.SetDomeLightEnabled(s.domeLightEnabled); + m_renderer.SetShadingMode(static_cast(s.shadingMode)); } // --------------------------------------------------------------------------- @@ -963,6 +965,100 @@ void ViewportTile::RenderCompactToolbar(int tileIndex) if (iconToggle(aaId.c_str(), "AA", Icon::Antialias, aa, "Toggle anti-aliasing")) m_renderer.SetAAEnabled(!aa); + ImGui::SameLine(); + ImGui::TextDisabled("|"); + ImGui::SameLine(); + + // -- Gear / viewport options popup --------------------------------------- + { + std::string gearId = "##gear_" + std::to_string(tileIndex); + std::string gearPopupId = "GearPopup_" + std::to_string(tileIndex); + + if (m_iconManager) { + ImTextureID tex = m_iconManager->Get(Icon::Settings); + ImGui::ImageButton(gearId.c_str(), ImTextureRef(tex), ImVec2(16.f, 16.f)); + } else { + ImGui::Button("*"); + } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Viewport options"); + + if (ImGui::BeginPopupContextItem(gearPopupId.c_str(), + ImGuiPopupFlags_MouseButtonLeft)) + { + // -- Shading mode ------------------------------------------------ + if (ImGui::BeginMenu("Shading")) { + ShadingMode cur = m_renderer.GetShadingMode(); + auto shadingItem = [&](const char* label, ShadingMode mode) { + if (ImGui::MenuItem(label, nullptr, cur == mode)) + m_renderer.SetShadingMode(mode); + }; + shadingItem("Smooth Shaded", ShadingMode::SmoothShaded); + shadingItem("Flat Shaded", ShadingMode::FlatShaded); + shadingItem("Wireframe on Surface", ShadingMode::WireframeOnSurface); + shadingItem("Wireframe", ShadingMode::Wireframe); + shadingItem("Unlit", ShadingMode::Unlit); + ImGui::EndMenu(); + } + + // -- Lighting ---------------------------------------------------- + if (ImGui::BeginMenu("Lighting")) { + bool ambient = m_renderer.GetAmbientLightOnly(); + if (ImGui::MenuItem("Camera Headlight", nullptr, ambient)) + m_renderer.SetAmbientLightOnly(true); + if (ImGui::MenuItem("Scene Lights", nullptr, !ambient)) + m_renderer.SetAmbientLightOnly(false); + ImGui::Separator(); + bool dome = m_renderer.GetDomeLightEnabled(); + if (ImGui::MenuItem("Dome Light", nullptr, dome)) + m_renderer.SetDomeLightEnabled(!dome); + ImGui::EndMenu(); + } + + ImGui::Separator(); + + // -- Display toggles --------------------------------------------- + { + bool grid = m_renderer.ShowGrid(); + if (ImGui::MenuItem("Show Grid", nullptr, grid)) + m_renderer.SetShowGrid(!grid); + + bool aaItem = m_renderer.GetAAEnabled(); + if (ImGui::MenuItem("Anti-Aliasing", nullptr, aaItem)) + m_renderer.SetAAEnabled(!aaItem); + } + + ImGui::Separator(); + + // -- BBox -------------------------------------------------------- + if (ImGui::BeginMenu("Bounding Box")) { + BBoxMode bbox = m_renderer.GetBBoxMode(); + if (ImGui::MenuItem("None", nullptr, bbox == BBoxMode::None)) + m_renderer.SetBBoxMode(BBoxMode::None); + if (ImGui::MenuItem("Per Object", nullptr, bbox == BBoxMode::PerObject)) + m_renderer.SetBBoxMode(BBoxMode::PerObject); + if (ImGui::MenuItem("All Selection", nullptr, bbox == BBoxMode::AllSelection)) + m_renderer.SetBBoxMode(BBoxMode::AllSelection); + ImGui::EndMenu(); + } + + // -- Background -------------------------------------------------- + if (ImGui::BeginMenu("Background")) { + auto& bg = m_renderer.GetBackgroundColor(); + if (ImGui::MenuItem("Dark Gray", nullptr, bg == pxr::GfVec3f(0.15f, 0.15f, 0.15f))) + m_renderer.SetBackgroundColor(pxr::GfVec3f(0.15f, 0.15f, 0.15f)); + if (ImGui::MenuItem("Black", nullptr, bg == pxr::GfVec3f(0.0f, 0.0f, 0.0f))) + m_renderer.SetBackgroundColor(pxr::GfVec3f(0.0f, 0.0f, 0.0f)); + if (ImGui::MenuItem("Light Gray", nullptr, bg == pxr::GfVec3f(0.45f, 0.45f, 0.45f))) + m_renderer.SetBackgroundColor(pxr::GfVec3f(0.45f, 0.45f, 0.45f)); + if (ImGui::MenuItem("Midnight Blue", nullptr, bg == pxr::GfVec3f(0.1f, 0.1f, 0.2f))) + m_renderer.SetBackgroundColor(pxr::GfVec3f(0.1f, 0.1f, 0.2f)); + ImGui::EndMenu(); + } + + ImGui::EndPopup(); + } + } + ImGui::PopStyleVar(); // ItemSpacing } diff --git a/src/ui/ViewportTile.h b/src/ui/ViewportTile.h index b3ce184..1403930 100644 --- a/src/ui/ViewportTile.h +++ b/src/ui/ViewportTile.h @@ -28,6 +28,7 @@ struct ViewportTileSettings { int bboxMode = 0; ///< BBoxMode cast to int bool ambientLightOnly = false; bool domeLightEnabled = false; + int shadingMode = 0; ///< ShadingMode cast to int (0 = SmoothShaded) }; /// Named orthographic view directions.