gauges: secondary/radar MFD composites live on a dev box (Milestone B)
The pod's secondary/radar cockpit surface now renders as an inset in the 800x600 dev window under BT_DEV_GAUGES: radar/tactical grid, SPEED/HEADING/ MAGNETIC/PROP dials, and the color-coded ARMOR DAMAGE mech schematic -- real gauge content (nzSec=27247), composited from the shared CPU pixelBuffer. Composite pass (engine): SVGA16::DrawDevInset palette-expands the secondary plane into a MANAGED texture on the MAIN device and draws an XYZRHW inset quad; BTDrawGaugeInset() reaches the gauge renderer's 'sec' port and is called from DPLRenderer::ExecuteImplementation as the last draw before EndScene. Three reconstruction bugs fixed to get real content: 1. BTL4Application::MakeGaugeRenderer signature mismatch (REAL fix): the reconstructed no-arg override only HID the 2007-engine's widened 3-arg virtual MakeGaugeRenderer(int*,int*,int*), so the engine built a base L4GaugeRenderer whose ctor never parsed gauge/l4gauge.cfg -> empty symbol table -> "undefined label 'bhk1Init'" -> no ports/gauges. Matched the 3-arg signature so it truly overrides (args ignored; BT is fullscreen). Same bug class as the BTL4GaugeRenderer(false,NULL,NULL,NULL) ctor fix. 2. Parse hung on undefined primitives (gated dev accommodation): the BT gauge primitive table (BTL4MethodDescription) is still a stub, so an unknown primitive -> ReportParsingError -> Fail() -> a MODAL dialog freezing the parse. Under BT_DEV_GAUGES, skip the unknown primitive's params so labels register and the base "configure" primitive builds the ports. 3. Gauge widgets AV on NULL data bindings (gated): NumericDisplayScalar's NULL value_pointer -> GaugeConnectionDirectOf ctor deref (bind NULL->static zero); RankAndScore::Execute derefs unreconstructed game state (Gauge::GuardedExecute SEH wrapper -> Disable(True) on first fault). All guards gated on BT_DEV_GAUGES: default DEV un-regressed (TARGET DESTROYED, 0 crashes) and the pod path is byte-unchanged (strict Fail, no guards). Remaining (docs/GAUGE_COMPOSITE.md): the real gauge WIDGET reconstruction (BTL4MethodDescription method table + gauge->game-state data bindings), then Step 2 (RP<->BT MFD port-name reconcile) + Step 3 (2-window layout). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c13e72d0ba
commit
a256813395
+63
-4
@@ -116,7 +116,66 @@ So the beachhead's real question shifted from "composite a working buffer" to "h
|
||||
recon." FAITHFUL FOLLOW-UPS: fix the `BTL4GaugeRenderer` lamp-manager + warehouse reconstruction (shadowed
|
||||
members) so per-mech gauges + lamps work (needed for real MFD content anyway).
|
||||
|
||||
**Milestone B (NEXT) — the composite pass:** a new `SVGA16` method (has access to `pixelBuffer` + the `sec`
|
||||
port + palette) creates a texture on the MAIN device (`l4_application->GetVideoRenderer()->GetDevice()`),
|
||||
runs the secondary palette-expand (L4VB16.cpp:4124-4147) into it, and the main renderer draws it as an inset
|
||||
quad (state save/restore) after the 3D pass. This is what reveals whether the content is real or garbage.
|
||||
**Milestone B DONE (2026-07) — THE SECONDARY/RADAR SURFACE COMPOSITES LIVE ON A DEV BOX** (opt-in
|
||||
`BT_DEV_GAUGES`; default DEV + pod paths verified un-regressed). The inset in the bottom-left of the 800×600
|
||||
window renders the authentic BT cockpit secondary MFD: the **radar/tactical grid** (SCALE/SECTOR + crosshair),
|
||||
the **SPEED / HEADING / MAGNETIC / PROP dials**, and the color-coded **ARMOR DAMAGE mech schematic**
|
||||
(green intact / red damaged) — real content (`nzSec=27247` non-zero secondary-plane pixels), not garbage.
|
||||
The composite chain: `SVGA16::DrawDevInset(device, secMask, paletteID)` (L4VB16.cpp) lazily creates a MANAGED
|
||||
R5G6B5 640×480 texture on the MAIN device, palette-expands the secondary plane into it (mirrors
|
||||
`SVGA16::Update` case 0), and draws an `XYZRHW|TEX1` inset quad; the free entry `BTDrawGaugeInset(mDevice)`
|
||||
reaches `application->GetGaugeRenderer()->GetGraphicsPort("sec")->graphicsDisplay` and is called from
|
||||
`DPLRenderer::ExecuteImplementation` as the LAST draw before `EndScene` (no state save/restore needed).
|
||||
|
||||
**THREE reconstruction bugs had to be fixed to get from "woken but empty" to "real content" (each a genuine
|
||||
faithful fix or a clearly-gated dev accommodation):**
|
||||
1. **⭐ `MakeGaugeRenderer` OVERRIDE SIGNATURE MISMATCH (real bug — FIXED).** The reconstructed
|
||||
`BTL4Application::MakeGaugeRenderer()` took NO args, but the 2007 WinTesla engine had WIDENED the base
|
||||
virtual to `MakeGaugeRenderer(int* secondaryIndex, int* aux1, int* aux2)` (called from `Application::
|
||||
Initialize`, APP.cpp:382). A no-arg method only HIDES the virtual — it never overrides it — so the engine
|
||||
ran `L4Application::MakeGaugeRenderer` instead and built a **base `L4GaugeRenderer`** whose ctor never
|
||||
calls `BuildConfigurationFile` → `gauge\l4gauge.cfg` was NEVER PARSED → empty symbol table → the observed
|
||||
`GaugeInterpreter: undefined label 'bhk1Init'` → no ports, no gauges. FIX: match the 3-arg signature so
|
||||
it truly overrides (args ignored — BT is fullscreen/no-aux, the `BTL4GaugeRenderer` ctor hardcodes
|
||||
`false,NULL,NULL,NULL`). Same class of bug as the `BTL4GaugeRenderer(false,NULL,NULL,NULL)` ctor fix:
|
||||
reconstruction written against the ORIGINAL signature, compiled against the WIDER 2007 engine virtual.
|
||||
(btl4app.cpp / btl4app.hpp.) **Label facts:** the label = `mission->GetGameModel()` + `"Init"` = `bhk1Init`
|
||||
(lowercase, no capitalization — the binary `FUN_0046ff64` does a plain copy + `Str_Cat`); the CFG defines
|
||||
`Bhk1Init` but the symbol-table lookup is `stricmp` (case-insensitive), so the case doesn't matter — the
|
||||
only reason it was "undefined" was the empty table. `MechInit` (CFG:4390) is the shared macro every
|
||||
`<Mech>Init` calls that builds the `sec`/overlay/Mfd*/Heat/Comm PORTS via the base `configure` primitive.
|
||||
2. **Gauge-config parse HUNG on undefined primitives (dev accommodation, gated).** The BT-specific gauge
|
||||
primitive table `BTL4MethodDescription` (btl4grnd.cpp) is still a STUB (only the chain-to-base link; the
|
||||
BT gauge widget classes like `PlayerStatus` aren't reconstructed), so `GaugeInterpreter::GetProcedureBody`
|
||||
hits an unknown primitive → `ReportParsingError` → `Fail()` → a MODAL assert dialog that FREEZES the game
|
||||
mid-parse (cdb confirmed: `MessageBoxW` ← `abort` ← `ReportParsingError` ← `GetProcedureBody`). FIX
|
||||
(GAUGREND.cpp, gated `BT_DEV_GAUGES`): when a primitive doesn't resolve, SKIP its parameter list (paren
|
||||
depth walk + `UngetPreviousToken`) and continue — the parse then registers ALL labels (`Bhk1Init`→
|
||||
`MechInit`) and runs the base `configure` primitives that build the PORTS. The base `configure`
|
||||
(`MakeConfigMethodDescription`) IS in `L4MethodDescription`, so the `sec`/radar SURFACE builds even though
|
||||
the BT widgets are skipped. Pod keeps the strict `Fail` (it needs a complete real table).
|
||||
3. **Gauge widgets AV on NULL data bindings (dev accommodation, gated).** Two flavors: (a) value-bound gauges
|
||||
(`NumericDisplayScalar`) get `value_pointer = parameterList[8].data.attributePointer` = NULL (the mech data
|
||||
binding isn't reconstructed; the source even comments "(Scalar *) is VERY dangerous!") → the
|
||||
`GaugeConnectionDirectOf<float>` ctor derefs NULL (gauge.h:198). FIX: the ctor binds a NULL source to a
|
||||
static zero (gated). (b) game-state gauges (`RankAndScore::Execute`) directly deref unreconstructed
|
||||
mission/player data. FIX: `Gauge::Update` runs `Execute()` under a SEH wrapper `Gauge::GuardedExecute()`
|
||||
(a separate fn — `__try/__except` can't share a frame with the `ChainIteratorOf` unwinding local); a gauge
|
||||
that faults once is `Disable(True)`d (rate=0) so it never retries. Both gated `BT_DEV_GAUGES`.
|
||||
|
||||
**⚠ KEY TAKEAWAY:** the gauge subsystem is now PROVEN end-to-end on dev (renderer→parse→port config→raster→
|
||||
palette-expand→composite), and the gauges whose data binds correctly (radar/armor/dials) show REAL content.
|
||||
The remaining work is the **BT gauge WIDGET reconstruction** — the `BTL4MethodDescription` method table (the
|
||||
BT gauge classes: `PlayerStatus` + the ~16 recovered `methodDescription` entries) AND the gauge→game-state
|
||||
DATA BINDINGS (why `RankAndScore` / some `NumericDisplayScalar` resolve NULL). Those are what the dev
|
||||
accommodations (skip/guard) stand in for. Files touched: `L4VB16.{h,cpp}` (DrawDevInset/BTDrawGaugeInset),
|
||||
`L4VIDEO.cpp` (the EndScene hook), `btl4app.{cpp,hpp}` (the override fix), `GAUGREND.cpp` (tolerant skip),
|
||||
`gauge.h` + `GAUGE.{h,cpp}` (NULL-source + SEH guards).
|
||||
|
||||
## Follow-ups (after Milestone B)
|
||||
|
||||
- **Widget recon (the real fix):** reconstruct `BTL4MethodDescription` (BT gauge classes) + the gauge→mech
|
||||
data bindings so the skipped/guarded gauges show real data and the POD path (strict `Fail`, no guards) works.
|
||||
- **Step 2** — reconcile the MFD port names (`auxUL2/auxC/...` in `SVGA16::Update` vs `Heat/Comm/Mfd1-3` in
|
||||
`L4GAUGE.CFG`) → the 5 MFD surfaces render (only `sec`/radar works today).
|
||||
- **Step 3** — the 2-window layout (3D-view window + an instrument-panel window tiling radar + the 5 MFDs).
|
||||
|
||||
Reference in New Issue
Block a user