Closes Phase D.2a. Launch with ACDREAM_DEVTOOLS=1 now shows a live
ImGui "Vitals" window whose HP bar reads CombatState.GetHealthPercent
for the local player. Without the env var the branches are dead code,
no ImGui context is created, and behaviour is identical to before.
GameWindow hunks:
- fields: _imguiBootstrap / _panelHost / _vitalsVm + DevToolsEnabled
- init (OnLoad): construct bootstrap + host, register VitalsPanel
- GUID push: _vitalsVm?.SetLocalPlayerGuid(chosen.Id) at live-connect
- frame begin: _imguiBootstrap.BeginFrame(dt) after GL clear
- frame end: _panelHost.RenderAll(ctx) + _imguiBootstrap.Render() after debug overlay
- input gating: skip WASD when ImGui.GetIO().WantCaptureKeyboard
Backend pivot: Hexa.NET.ImGui → ImGui.NET + Silk.NET.OpenGL.Extensions.ImGui.
First-light integration with the Hexa backend crashed 0xC0000005 inside
Hexa.NET.ImGui.Backends.OpenGL3.ImGuiImplOpenGL3.InitNative. Root cause:
Hexa's native OpenGL3 backend resolves GL function pointers via GLFW or
SDL internally; with Silk.NET (which uses neither) the pointers are null
and the native code crashes on first use. The mitigation path was
already planned — the design doc's Risk section called a pivot to
ImGui.NET a "one-morning operation" — and that's exactly what happened.
- Packages: Hexa.NET.ImGui 2.2.9 + Hexa.NET.ImGui.Backends 1.0.18
→ ImGui.NET 1.91.6.1 + Silk.NET.OpenGL.Extensions.ImGui 2.23.0
- ImGuiBootstrapper: was static Initialize(gl)+Shutdown() wrapping
Hexa's OpenGL3 init; now an IDisposable wrapping Silk.NET's
ImGuiController instance which handles GL backend init + input
subscription in one go.
- SilkInputBridge.cs deleted (~190 LOC): ImGuiController subscribes
IKeyboard / IMouse events itself, we don't need a bespoke bridge.
- ImGuiPanelRenderer: ImGuiNET.ImGui.* calls instead of
Hexa.NET.ImGui.ImGui.*. Widget surface unchanged.
Boundary discipline is preserved — no panel imports ImGuiNET; only
ImGuiPanelRenderer does. The D.2b custom toolkit will implement the
same IPanelRenderer contract without touching panel code.
Out of scope (tracked for follow-up):
- Stam/Mana currently return float? null (VitalsVM). Absolute values
need LocalPlayerState + PlayerDescription (0x0013) parsing to be
stored rather than discarded — filed as a post-D.2a issue.
- Mouse-capture gating (WorldMouseFallThrough-style click-through
tests) — not needed until we add clickable inventory items.
Roadmap + memory + architecture doc + UI framework plan updated in the
same commit per CLAUDE.md roadmap-discipline rules. 753 tests pass
(550 Core + 192 Core.Net + 11 new UI.Abstractions), 0 build warnings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Second piece of Phase D.2a: the ImGui-specific backend that implements
AcDream.UI.Abstractions' IPanelRenderer / IPanelHost. No GameWindow
hookup yet — compiles standalone for clean review before integration.
Packages:
* Hexa.NET.ImGui 2.2.9 (auto-generated from cimgui 1.92.2b)
* Hexa.NET.ImGui.Backends 1.0.18 (consolidated — OpenGL3 is here)
* Silk.NET.Input 2.23.0 + Silk.NET.OpenGL 2.23.0 (matches AcDream.App)
Files:
ImGuiBootstrapper.cs
One-shot static Initialize(glslVersion) / Shutdown() pair. Creates
the ImGui context, applies dark style, enables NavEnableKeyboard,
and boots ImGuiImplOpenGL3. Re-init is a no-op.
SilkInputBridge.cs
Event-driven Silk.NET -> ImGui IO bridge. Subscribes on construction;
Dispose() unsubscribes. Covers:
- KeyDown/Up -> ImGui.AddKeyEvent with modifier latching
(Ctrl/Shift/Alt/Super routed via both ModXxx flags AND named
key events so both IsKeyPressed checks and ImGui shortcut
matching work)
- KeyChar -> AddInputCharacter for text fields
- MouseMove -> AddMousePosEvent
- MouseDown/Up -> AddMouseButtonEvent (L=0, R=1, M=2)
- Scroll -> AddMouseWheelEvent (both axes)
Silk.NET.Input.Key -> ImGuiKey map covers WASD, arrows, modifiers,
letters, digits, function keys. Unmapped keys silently ignored.
BeginFrame(displaySize, dt) sets IO.DisplaySize + IO.DeltaTime.
ImGuiPanelRenderer.cs
IPanelRenderer impl — one-line wrappers on ImGui.Begin/End,
TextUnformatted, SameLine, Separator, ProgressBar. The ONLY place
Hexa.NET.ImGui types appear outside bootstrap/input plumbing. Panels
still never import ImGui.
ImGuiPanelHost.cs
IPanelHost impl. Dictionary keyed by IPanel.Id for idempotent
Register. RenderAll iterates visible panels and calls their Render.
Does NOT call ImGui.NewFrame / ImGui.Render — ownership belongs to
the caller (GameWindow) so GL state is explicit. Diagnostic `Count`
property.
No behavior change yet; next commit wires this into GameWindow behind
ACDREAM_DEVTOOLS=1 and ships the first visible VitalsPanel.