Skip to content

Commit 651c418

Browse files
committed
refactor: extract post processing menu
1 parent 525f8a8 commit 651c418

6 files changed

Lines changed: 122 additions & 58 deletions

File tree

NEXT.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ replacing the former flat `src/MeshCraft/MeshCraftApplication_*.cpp` layout.
1919
a compatibility forwarder. Actual UI components currently cover Validation,
2020
Registry results, Toolbar tools/display/snap-surface/proportional/grid controls,
2121
Properties delegation, and the View-menu panel/overlay/direction/focus/
22-
Camera-Bookmarks/Walk-Mode presentation, plus the Add/CSG and Help menu
22+
Camera-Bookmarks/Walk-Mode/Bloom-SSAO presentation, plus the Add/CSG and Help menu
2323
presentation and the Edit-history, clipboard, object-action, and
2424
selection-action groups, the Select-by-Type/Tag/Material, Align-Selection,
2525
Distribute-Selection, and Mirror-Selection submenus, and the Copy-Properties,
2626
Convert-to-Definition, Export-Subtree, Break-Instance, Drop-to-Ground,
2727
Snap-to-Grid, Group-Scale, Linear-Array, and Scatter-Along-Curve items plus the
28-
Group/Ungroup pair, plus every File-menu action through Open Recent. The detailed
28+
Group/Ungroup pair and every File-menu action through Open Recent. The detailed
2929
toolbar Snap interval contents and the remaining MenuBar sections are still
3030
application-owned. Camera bookmark, walk,
3131
document, undo, clipboard, selection, grid, and dialog state have **not** moved
@@ -100,11 +100,12 @@ before when explicitly requested (`SYS-W14-##` rows).
100100

101101
## 2. Current status
102102

103-
- **Last full build: clean after the current Phase 13 File-exit
104-
slice.** Testing is enabled in the current Ninja Release tree, and
103+
- **Last full build: clean after the current Phase 13 File-open-recent and
104+
View-Bloom/SSAO slices.** Testing is enabled in the current Ninja Release tree, and
105105
`CCACHE_DISABLE=1 cmake --build b-release -j4` linked all targets successfully
106106
on EASYGL. Alternate-backend runtime qualification remains blocked.
107-
- **Tests:** the fresh Release tree registers 181 tests. All passed in two
107+
- **Tests:** the fresh Release tree registers 181 tests. All passed again after
108+
the current Phase 13 slices in two
108109
disjoint groups: 147/147 non-render tests and 34/34 render-labelled tests
109110
under Xvfb (with local loopback/X11 socket access). SVG-specific
110111
verification passes with `-j4`: external and inline SVG export to glTF PNGs,
@@ -170,7 +171,7 @@ before when explicitly requested (`SYS-W14-##` rows).
170171
by application/UI ownership, and is extracting UI presentation through
171172
narrow contexts. Validation, Registry results, Toolbar controls, Properties
172173
delegation, and the View-menu directions/focus/overlays/panels/
173-
Camera-Bookmarks/Walk-Mode presentation, plus the Add/CSG and Help menu
174+
Camera-Bookmarks/Walk-Mode/Bloom-SSAO presentation, plus the Add/CSG and Help menu
174175
presentation and the Edit-history, clipboard, object-action, and
175176
selection-action groups, the Select-by-Type/Tag/Material, Align-Selection,
176177
Distribute-Selection, and Mirror-Selection submenus, and the Copy-Properties,
@@ -191,6 +192,17 @@ before when explicitly requested (`SYS-W14-##` rows).
191192
--target MeshCraft` and `ctest --test-dir b-release -R '^mc3_commands$'
192193
--output-on-failure` passed; the latter already exercises recent-files'
193194
load, save, MRU de-duplication, cap, and restart round-trip contract.
195+
- **Recently implemented (2026-07-25):** `SYS-W3-01` Phase 13 moved the
196+
`View → Bloom/SSAO` controls into `Application::UI::MenuBar` through
197+
`ViewPostProcessingContext`. It receives the application-computed
198+
ShaderEffect-capability gate and value snapshots, then reports changes only
199+
through five setters; renderer capability checks and all post-processing
200+
state remain application-owned. The supported/unsupported presentation,
201+
labels, slider ranges, 140px widths, and `AlwaysClamp` safety guards are
202+
unchanged. `CCACHE_DISABLE=1 cmake --build b-release -j4 --target MeshCraft`
203+
and the Xvfb-hosted `bloom_test` render regression passed. The test-hook
204+
comments now also correctly state that `MESHCRAFT_TEST_FORCE_POSTFX` forces
205+
only Bloom; SSAO has its own `MESHCRAFT_TEST_FORCE_SSAO` hook.
194206
- **Recently implemented (2026-07-20 through 2026-07-25):** all 7
195207
raw-OpenGL(ES)-vs-CNA migrations, `AUD-082` through `AUD-088` — full
196208
detail with file:line evidence and
@@ -792,9 +804,9 @@ git stash pop && cmake --build b-release -j4 --target <affected-target>
792804
No actionable follow-up audit task remains: `AUD-089` through `AUD-091` are
793805
complete, while Android (`AUD-042`) is environment/owner deferred.
794806
`SYS-W3-01` has 12 completed subsystem phases and an active Phase 13 for
795-
application/UI ownership. The authorized Camera Bookmarks, Walk Mode, Help,
796-
Add/CSG, Edit-history, Edit-clipboard, and Edit-object-actions menu slices are
797-
implemented and verified, together with Edit-selection-actions,
807+
application/UI ownership. The authorized Camera Bookmarks, Walk Mode, View
808+
Bloom/SSAO, Help, Add/CSG, Edit-history, Edit-clipboard, and Edit-object-actions
809+
menu slices are implemented and verified, together with Edit-selection-actions,
798810
Edit-select-by-type/tag/material, Edit-copy-properties, Edit-grouping,
799811
Edit-convert-to-definition, Edit-export-subtree, Edit-break-instance,
800812
Edit-align-selection, Edit-distribute-selection, Edit-drop-to-ground,
@@ -1009,6 +1021,11 @@ pending-action state, dialog buffers and state, file loading, document
10091021
replacement, selection and undo initialization, title and status updates, error
10101022
handling, and the Ctrl+O keyboard route remain in their existing owners;
10111023
`MenuBar` owns only the unchanged label, shortcut, and click dispatch.
1024+
`ViewPostProcessingContext` exposes only the application-computed text-shader
1025+
capability, Bloom/SSAO value snapshots, and setters. The application retains
1026+
the capability check, effect state, CNA targets/effects, and rendering; the UI
1027+
component preserves the supported/unsupported text, labels, ranges, widths,
1028+
`AlwaysClamp` flags, and callback dispatch.
10121029
`FileOpenRecentContext` exposes only current availability, a lazy recent-files
10131030
provider, one open callback, and one clear callback. Recent-file storage and
10141031
persistence, unsaved-change handling, pending-action state, file loading,
@@ -1027,13 +1044,8 @@ The sequence below is planning only. Per `CLAUDE.md`, each item must be
10271044
described and explicitly confirmed immediately before implementation; finishing
10281045
one item does not authorize the next.
10291046

1030-
1. **View → Bloom/SSAO controls.** Audit and extract only the text-shader
1031-
effects block, including its supported and unsupported presentation. Preserve
1032-
the conditional controls, labels, slider ranges, `AlwaysClamp` flags, and
1033-
existing state changes; renderer-capability checks and effect state remain
1034-
application-owned.
1035-
2. **Post-menu boundary audit.** Once the remaining File and View blocks are
1036-
complete, identify one new narrow presentation boundary before changing
1047+
1. **Post-menu boundary audit.** The remaining File and View blocks are
1048+
complete; identify one new narrow presentation boundary before changing
10371049
`MeshCraftApplication`. Do not start a broad application refactor.
10381050

10391051
### Tracked work that is not implementation-ready

include/MeshCraft/Application/UI/MenuBar.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ struct WalkModeContext {
2525
std::function<void()> toggle;
2626
};
2727

28+
struct ViewPostProcessingContext {
29+
bool textShaderEffectsSupported;
30+
bool bloomEnabled;
31+
float bloomStrength;
32+
bool ssaoEnabled;
33+
float ssaoStrength;
34+
float ssaoRadius;
35+
std::function<void(bool)> setBloomEnabled;
36+
std::function<void(float)> setBloomStrength;
37+
std::function<void(bool)> setSsaoEnabled;
38+
std::function<void(float)> setSsaoStrength;
39+
std::function<void(float)> setSsaoRadius;
40+
};
41+
2842
struct HelpMenuContext {
2943
std::function<void()> openPreferences;
3044
std::function<void()> openCommandPalette;
@@ -313,6 +327,7 @@ class MenuBar final {
313327
static void drawFocusSelection(const std::function<void()>& focus);
314328
static void drawCameraBookmarks(const CameraBookmarksContext& context);
315329
static void drawWalkMode(const WalkModeContext& context);
330+
static void drawViewPostProcessing(const ViewPostProcessingContext& context);
316331
static void drawHelpMenu(const HelpMenuContext& context);
317332
};
318333

plan.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ still internally consistent.
164164
research pass.
165165
6. **SYS-W3-01 (P2/W3), Phase 13 is active:** application/UI ownership is
166166
being reduced one narrow presentation slice at a time. The authorized
167-
Camera Bookmarks, Walk Mode, Help, Add/CSG, Edit-history, Edit-clipboard,
167+
Camera Bookmarks, Walk Mode, View Bloom/SSAO, Help, Add/CSG, Edit-history,
168+
Edit-clipboard,
168169
Edit-object-actions, Edit-selection-actions, Edit-select-by-type,
169170
Edit-select-by-tag/material, Edit-copy-properties, Edit-grouping,
170171
Edit-convert-to-definition, Edit-export-subtree, Edit-break-instance,
@@ -180,9 +181,8 @@ still internally consistent.
180181
File-exit menu slices are implemented and verified. Their state remains in
181182
the existing editor/application owners;
182183
`Application::UI::MenuBar` receives only the read-only values and callbacks
183-
required for presentation. The remaining ordered Phase 13 queue is:
184-
**View → Bloom/SSAO controls** (preserve the capability gate, ranges, and
185-
clamping), then a fresh audit for a new narrow boundary.
184+
required for presentation. The remaining ordered Phase 13 queue is a fresh
185+
audit for a new narrow boundary.
186186
Every item requires its own confirmation per `CLAUDE.md`.
187187

188188
---
@@ -607,7 +607,10 @@ _All items in this workstream are DONE — archived to [`docs/history/plan_20260
607607
narrow delegation facade over the existing `Scene::PropertiesPanel`.
608608
`Application::UI::MenuBar` now owns the View-menu panel toggles, overlay
609609
toggles, direction choices, focus action, Camera Bookmarks presentation,
610-
and the Walk Mode menu item. The bookmark state remains the already-extracted
610+
and the Walk Mode menu item. `ViewPostProcessingContext` also owns the
611+
Bloom/SSAO presentation, receiving only the capability result, value
612+
snapshots, and setters; the application retains effect state and rendering.
613+
The bookmark state remains the already-extracted
611614
`Editor::CameraBookmarks`; a `CameraBookmarksContext` exposes only the
612615
read-only slots plus Save/Restore callbacks, leaving camera mutation and
613616
status reporting in the application. Likewise, `WalkModeContext` exposes
@@ -753,9 +756,10 @@ _All items in this workstream are DONE — archived to [`docs/history/plan_20260
753756
Macro Editor… item is now component-owned through `EditMacroEditorContext`,
754757
which exposes only one open-dialog callback. Dialog state, macro steps,
755758
recording and playback operations, macro context, file buffer, save/load
756-
behavior, and status reporting remain in their existing owners. File, the
757-
remaining Edit groups, and the remaining View controls are still
758-
application-owned.
759+
behavior, and status reporting remain in their existing owners. The File-menu
760+
action slices and the View menu presentation are now component-owned; any
761+
remaining extraction target must be selected by a fresh, narrow boundary audit
762+
rather than assumed from its location in the menu.
759763
The historical audit references retain their former paths as time-accurate
760764
evidence.
761765
Static undo-audit and snapshot-lint path checks pass after their tracked
@@ -781,7 +785,7 @@ _All items in this workstream are DONE — archived to [`docs/history/plan_20260
781785
Edit-hide-selection, Edit-show-all-hidden, File-merge-scene,
782786
File-export-selection, File-export-GLB, File-export-OBJ, File-save,
783787
File-save-as, File-import-OBJ, File-new, File-open, File-open-recent, and
784-
File-exit slices,
788+
File-exit slices, plus the Bloom/SSAO controls,
785789
each incremental Release link and the same 147/147 + 34/34 partitions pass
786790
again.
787791
For the current MenuBar slices, the public UI header also compiles as a

src/MeshCraft/Application/Application.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ MeshCraftApplication::MeshCraftApplication(std::filesystem::path filePath, std::
8181
, autoExportPath_(std::move(exportPath))
8282
, autoExportCountdown_(autoExportPath_.empty() ? 0 : 2)
8383
{
84-
// Test-only hook: bloom/SSAO are UI-menu-only toggles with no CLI/scene-
85-
// file equivalent. Force both on for a screenshot so their CNA render
86-
// targets and effects are exercised in headless regression tests too.
84+
// Test-only hooks: Bloom and SSAO are UI-menu-only toggles with no CLI/
85+
// scene-file equivalent. Keep their force switches independent so each
86+
// headless regression test exercises only the effect it intends to cover.
8787
if (std::getenv("MESHCRAFT_TEST_FORCE_POSTFX")) {
8888
bloomEnabled_ = true;
8989
}

src/MeshCraft/Application/UI/MenuBar.cpp

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -531,33 +531,20 @@ float MeshCraftApplication::drawMenuBar()
531531
ImGui::Separator();
532532
UI::MenuBar::drawOverlays(showEdgeOverlay_, showWireframeMode_,
533533
showStatsOverlay_, shadowDebugEnabled_, snapEnabled_);
534-
if (supportsTextShaderEffects()) {
535-
ImGui::MenuItem("Bloom (emissive glow)", nullptr, &bloomEnabled_);
536-
if (bloomEnabled_) {
537-
ImGui::SetNextItemWidth(140);
538-
// AlwaysClamp: without it, Ctrl+Click lets a typed value go
539-
// out of [min,max] (incl. negative), which the composite
540-
// shader has no other guard against (STAB-0325).
541-
ImGui::SliderFloat(" Strength##bloom", &bloomStrength_, 0.5f, 8.0f, "%.1f",
542-
ImGuiSliderFlags_AlwaysClamp);
543-
}
544-
ImGui::MenuItem("SSAO (ambient occlusion)", nullptr, &ssaoEnabled_);
545-
if (ssaoEnabled_) {
546-
ImGui::SetNextItemWidth(140);
547-
// AlwaysClamp: same out-of-bounds-via-Ctrl+Click risk as
548-
// bloom strength above (STAB-0326).
549-
ImGui::SliderFloat(" Strength##ssao", &ssaoStrength_, 0.0f, 1.0f, "%.2f",
550-
ImGuiSliderFlags_AlwaysClamp);
551-
ImGui::SetNextItemWidth(140);
552-
// Same fix applied here too: a negative/zero radius from an
553-
// unclamped Ctrl+Click entry would break the SSAO sample
554-
// kernel, same root cause as the two strength sliders above.
555-
ImGui::SliderFloat(" Radius##ssao", &ssaoRadius_, 0.05f, 2.0f, "%.2f",
556-
ImGuiSliderFlags_AlwaysClamp);
557-
}
558-
} else {
559-
ImGui::TextDisabled("Bloom and SSAO require cross-backend ShaderEffect support");
560-
}
534+
const UI::ViewPostProcessingContext viewPostProcessingContext{
535+
.textShaderEffectsSupported = supportsTextShaderEffects(),
536+
.bloomEnabled = bloomEnabled_,
537+
.bloomStrength = bloomStrength_,
538+
.ssaoEnabled = ssaoEnabled_,
539+
.ssaoStrength = ssaoStrength_,
540+
.ssaoRadius = ssaoRadius_,
541+
.setBloomEnabled = [this](bool enabled) { bloomEnabled_ = enabled; },
542+
.setBloomStrength = [this](float strength) { bloomStrength_ = strength; },
543+
.setSsaoEnabled = [this](bool enabled) { ssaoEnabled_ = enabled; },
544+
.setSsaoStrength = [this](float strength) { ssaoStrength_ = strength; },
545+
.setSsaoRadius = [this](float radius) { ssaoRadius_ = radius; },
546+
};
547+
UI::MenuBar::drawViewPostProcessing(viewPostProcessingContext);
561548
UI::MenuBar::drawPanelToggles(showTimeline_, showRegistryPanel_,
562549
showAiPanel_, showValidationPanel_);
563550
ImGui::EndMenu();
@@ -1030,6 +1017,52 @@ void MenuBar::drawWalkMode(const WalkModeContext& context) {
10301017
if (ImGui::MenuItem("Walk Mode", "F5", context.active)) context.toggle();
10311018
}
10321019

1020+
void MenuBar::drawViewPostProcessing(const ViewPostProcessingContext& context) {
1021+
if (!context.textShaderEffectsSupported) {
1022+
ImGui::TextDisabled("Bloom and SSAO require cross-backend ShaderEffect support");
1023+
return;
1024+
}
1025+
1026+
bool bloomEnabled = context.bloomEnabled;
1027+
if (ImGui::MenuItem("Bloom (emissive glow)", nullptr, &bloomEnabled)) {
1028+
context.setBloomEnabled(bloomEnabled);
1029+
}
1030+
if (bloomEnabled) {
1031+
float bloomStrength = context.bloomStrength;
1032+
ImGui::SetNextItemWidth(140);
1033+
// AlwaysClamp: without it, Ctrl+Click lets a typed value go out of
1034+
// [min,max] (including negative), which the composite shader has no
1035+
// other guard against (STAB-0325).
1036+
if (ImGui::SliderFloat(" Strength##bloom", &bloomStrength, 0.5f, 8.0f, "%.1f",
1037+
ImGuiSliderFlags_AlwaysClamp)) {
1038+
context.setBloomStrength(bloomStrength);
1039+
}
1040+
}
1041+
1042+
bool ssaoEnabled = context.ssaoEnabled;
1043+
if (ImGui::MenuItem("SSAO (ambient occlusion)", nullptr, &ssaoEnabled)) {
1044+
context.setSsaoEnabled(ssaoEnabled);
1045+
}
1046+
if (ssaoEnabled) {
1047+
float ssaoStrength = context.ssaoStrength;
1048+
ImGui::SetNextItemWidth(140);
1049+
// AlwaysClamp: same out-of-bounds-via-Ctrl+Click risk as the bloom
1050+
// strength above (STAB-0326).
1051+
if (ImGui::SliderFloat(" Strength##ssao", &ssaoStrength, 0.0f, 1.0f, "%.2f",
1052+
ImGuiSliderFlags_AlwaysClamp)) {
1053+
context.setSsaoStrength(ssaoStrength);
1054+
}
1055+
float ssaoRadius = context.ssaoRadius;
1056+
ImGui::SetNextItemWidth(140);
1057+
// A negative or zero radius from an unclamped Ctrl+Click entry would
1058+
// break the SSAO sample kernel; same root cause as the strength sliders.
1059+
if (ImGui::SliderFloat(" Radius##ssao", &ssaoRadius, 0.05f, 2.0f, "%.2f",
1060+
ImGuiSliderFlags_AlwaysClamp)) {
1061+
context.setSsaoRadius(ssaoRadius);
1062+
}
1063+
}
1064+
}
1065+
10331066
void MenuBar::drawHelpMenu(const HelpMenuContext& context) {
10341067
if (!ImGui::BeginMenu("Help")) return;
10351068

test/bloom.mc3.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
halo point just outside the box's own rendered silhouette is pure
55
background (no geometry there at all) with an SSAO ao=1.0 fast-path
66
(kSsaoFS returns vec4(1.0) unconditionally when the depth sample is the
7-
far clear value), so it is unaffected by SSAO even when
8-
MESHCRAFT_TEST_FORCE_POSTFX forces both post-effects on together.
9-
Any brightening there can only be Bloom's additive glow. -->
7+
far clear value), so it would be unaffected even if an SSAO test enabled
8+
its separate hook. MESHCRAFT_TEST_FORCE_POSTFX enables only Bloom here,
9+
so any brightening at that point can only be Bloom's additive glow. -->
1010
<mc3 version="0.3" model="BloomFixture">
1111

1212
<environment>

0 commit comments

Comments
 (0)