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:
arcattack
2026-07-06 10:59:45 -05:00
co-authored by Claude Opus 4.8
parent c13e72d0ba
commit a256813395
10 changed files with 308 additions and 10 deletions
+30
View File
@@ -645,6 +645,36 @@ so a forced L4GAUGE on non-pod hardware degrades cleanly; (3) **pod main-view fu
pod's 800×600 main monitor, but pod fullscreen-on-a-chosen-adapter is a Phase-8 detail). The Phase-4 HUD/MFD
render work now targets the profile's LOGICAL surfaces, not a hardcoded single window.
**✅✅ GAUGE DEV-COMPOSITE — the pod's SECONDARY/RADAR MFD renders LIVE on a dev box (opt-in `BT_DEV_GAUGES`;
full map + findings in `docs/GAUGE_COMPOSITE.md`).** The FOLLOW-UP (1) above is DONE for the secondary surface:
an inset in the bottom-left of the 800×600 window shows the authentic BT cockpit secondary MFD — **radar/
tactical grid** (SCALE/SECTOR + crosshair), **SPEED/HEADING/MAGNETIC/PROP dials**, and the color-coded
**ARMOR DAMAGE mech schematic** (green intact / red damaged) — REAL content (`nzSec=27247` non-zero pixels).
The draw model is CPU-raster→texture-upload, DEVICE-INDEPENDENT: gauge widgets raster 16-bit R5G6B5 into ONE
shared `Video16BitBuffered::pixelBuffer`; `SVGA16::DrawDevInset` (L4VB16.cpp) palette-expands the secondary
plane into a MANAGED texture on the MAIN device + draws an `XYZRHW|TEX1` inset quad; `BTDrawGaugeInset(mDevice)`
is called from `DPLRenderer::ExecuteImplementation` as the last draw before `EndScene`. **THREE recon bugs
fixed to get real content (1 genuine fix + 2 gated dev accommodations):** (a) **`BTL4Application::
MakeGaugeRenderer` SIGNATURE MISMATCH** — the reconstructed no-arg override only HID the 2007-engine's widened
3-arg virtual `MakeGaugeRenderer(int*,int*,int*)` (APP.cpp:382), so the engine built a BASE `L4GaugeRenderer`
whose ctor never calls `BuildConfigurationFile``gauge\l4gauge.cfg` was NEVER PARSED → `undefined label
'bhk1Init'` → no ports. FIXED to the 3-arg signature (args ignored; BT is fullscreen). Same bug class as the
`BTL4GaugeRenderer(false,NULL,NULL,NULL)` ctor fix — recon written to the ORIGINAL signature vs the WIDER 2007
virtual. (The gauge label = `GetGameModel()`+`"Init"`=`bhk1Init`; CFG has `Bhk1Init` but the symbol lookup is
`stricmp`, so case is irrelevant — the table was just empty. `MechInit`, CFG:4390, is the shared macro that
builds the sec/overlay/Mfd/Heat/Comm PORTS via the base `configure` primitive.) (b) **parse HUNG on undefined
primitives** — `BTL4MethodDescription` (btl4grnd.cpp) is a STUB (BT gauge widget classes unreconstructed), so
an unknown primitive → `ReportParsingError``Fail()`→a MODAL dialog freezing mid-parse; gated skip
(GAUGREND.cpp) walks past the unknown primitive's params so labels register + base `configure` builds the
ports. (c) **gauge widgets AV on NULL data bindings**`NumericDisplayScalar` value_pointer NULL →
`GaugeConnectionDirectOf` ctor deref (bind NULL→static zero, gauge.h); `RankAndScore::Execute` derefs
unreconstructed game state (`Gauge::GuardedExecute` SEH wrapper → `Disable(True)` on first fault). All gated
`BT_DEV_GAUGES`; **default DEV (no gauges) un-regressed (TARGET DESTROYED, 0 crashes) + pod path byte-unchanged**
(strict `Fail`, no guards). **REMAINING = the real gauge WIDGET reconstruction:** the `BTL4MethodDescription`
method table (BT gauge classes: `PlayerStatus` + ~16 recovered `methodDescription` entries) + the gauge→
game-state DATA BINDINGS (why some gauges resolve NULL) — that is what the dev skip/guards stand in for, and
what the POD needs. Then Step 2 (RP↔BT MFD port-name reconcile → the 5 MFDs) + Step 3 (2-window layout).
**Alternate route considered:** run original `btrel410.exe` under DOSBox — **blocked**: it needs the
VPX/IG board DOSBox can't emulate; bypassing it = the shim work anyway.
+63 -4
View File
@@ -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).
+39 -1
View File
@@ -568,7 +568,24 @@ Logical
//-----------------------------------------
// Execute gauge code
//-----------------------------------------
Execute();
{
// BT DEV-GAUGES bring-up: run Execute() under SEH so a gauge whose
// game-state binding isn't reconstructed yet is DISABLED (rate=0)
// after its first fault instead of AV'ing every frame. Gated on
// BT_DEV_GAUGES; the pod build calls Execute() directly.
static int s_devGaugesUpd = -1;
if (s_devGaugesUpd < 0)
s_devGaugesUpd = (getenv("BT_DEV_GAUGES") != NULL) ? 1 : 0;
if (s_devGaugesUpd)
{
if (!GuardedExecute())
Disable(True);
}
else
{
Execute();
}
}
actually_ran = True;
}
Check_Fpu();
@@ -581,6 +598,27 @@ void
Fail("Gauge::Execute not overridden");
}
//
// GuardedExecute -- BT DEV-GAUGES bring-up. Run Execute() under a structured
// exception handler so a gauge whose game-state data binding is not yet
// reconstructed is skipped instead of AV'ing the whole game. (A separate
// function because __try/__except may not share a frame with C++ objects that
// require unwinding, which Gauge::Update does.) Returns False on fault.
//
Logical
Gauge::GuardedExecute()
{
__try
{
Execute();
return True;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return False;
}
}
struct TierLookup
{
GaugeRate rate;
+26
View File
@@ -193,6 +193,26 @@ template <class T> GaugeConnectionDirectOf<T>::GaugeConnectionDirectOf(
{
Check_Pointer(this);
Check_Pointer(data_destination);
//
// BT DEV-GAUGES bring-up: some gauge widgets bind to mech data sources that
// are not reconstructed yet, so the attribute resolves to a NULL data_source
// (see the "(Scalar *) is VERY dangerous!" note in L4GAUGE.cpp). Rather than
// AV here (and later in Update()), bind to a static zero so the gauge simply
// reads 0. Gated on BT_DEV_GAUGES so the POD path is byte-unchanged.
//
if (data_source == NULL)
{
static int s_devGauges = -1;
if (s_devGauges < 0)
s_devGauges = (getenv("BT_DEV_GAUGES") != NULL) ? 1 : 0;
if (s_devGauges)
{
static T s_gaugeNullSource = T();
dataSource = &s_gaugeNullSource;
data_source = &s_gaugeNullSource;
}
}
Check_Pointer(data_source);
*dataDestination = *dataSource;
@@ -344,6 +364,12 @@ public:
protected:
virtual void
Execute(); // Execute internal processes to display the gauge
// BT DEV-GAUGES bring-up: SEH wrapper around Execute() so a gauge whose
// game-state data binding isn't reconstructed yet is skipped (not crashed).
// Returns False if Execute() raised a structured exception (e.g. an AV).
Logical
GuardedExecute();
//----------------------------------------------------------------------
// Protected data
//----------------------------------------------------------------------
+36 -1
View File
@@ -1332,7 +1332,42 @@ void
) == False
)
{
ReportParsingError("Undefined primitive");
//
// BT DEV-GAUGES bring-up: the BT-specific gauge primitive
// table (BTL4MethodDescription, btl4grnd.cpp) is still a stub
// -- the BT gauge widget classes (PlayerStatus, ...) aren't
// reconstructed yet, so they don't resolve here. Rather than
// Fail() -- which pops a modal assert dialog and FREEZES the
// game mid-parse -- SKIP the unknown primitive's parameter
// list so the parse can continue: it still registers every
// label (e.g. Bhk1Init -> MechInit) and runs the base
// "configure" primitives that build the gauge PORTS/surfaces
// (the beachhead needs the 'sec'/radar surface, not the
// widgets). Gated on BT_DEV_GAUGES so the POD path keeps the
// strict Fail (it needs a complete, real method table).
//
if (getenv("BT_DEV_GAUGES") != NULL)
{
const char
*skip_token;
int
paren_depth = 1; // the '(' already consumed above
while (
(paren_depth > 0) &&
((skip_token = GetToken()) != NULL)
)
{
if (stricmp(skip_token, "(") == 0)
++paren_depth;
else if (stricmp(skip_token, ")") == 0)
--paren_depth;
}
UngetPreviousToken(); // leave ')' for the check below
}
else
{
ReportParsingError("Undefined primitive");
}
}
if (stricmp(GetToken(), ")") != 0)
{
+84
View File
@@ -109,6 +109,89 @@ static bool DevGaugeComposite()
return v != 0;
}
//===========================================================================//
// DEV-COMPOSITE (option B) -- draw the woken gauge renderer's SECONDARY/radar
// surface as an inset in the MAIN window. The gauge widgets have already
// rastered into the shared CPU pixelBuffer; here we palette-expand the secondary
// plane into a texture on the MAIN device (mirrors SVGA16::Update case 0) and blit
// a quad. Called by the main renderer as the LAST draw before EndScene, so no
// render-state save/restore is needed (the next frame resets device state).
//===========================================================================//
void SVGA16::DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID)
{
int w = pixelBuffer.Data.Size.x; // 640
int h = pixelBuffer.Data.Size.y; // 480
if (device == NULL || w <= 0 || h <= 0) return;
// Lazily create the inset texture on the MAIN device (MANAGED -> lockable +
// survives device reset).
if (mDevInsetTex == NULL)
device->CreateTexture(w, h, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &mDevInsetTex, NULL);
if (mDevInsetTex == NULL) return;
// Palette-expand the secondary plane into the texture (== SVGA16::Update case 0).
SVGA16Palette *pal = &palette[paletteID];
D3DLOCKED_RECT rect;
if (SUCCEEDED(mDevInsetTex->LockRect(0, &rect, NULL, 0)))
{
Word *source = pixelBuffer.Data.MapPointer;
Word *dest = (Word*)rect.pBits;
int postRowIncrement = (rect.Pitch / 2) - w;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
PaletteTriplet *pe = &(pal->paletteData.Color[*source & secMask]);
*dest = ((pe->Red >> 3) << 11) | ((pe->Green >> 2) << 5) | (pe->Blue >> 3);
dest++; source++;
}
dest += postRowIncrement;
}
mDevInsetTex->UnlockRect(0);
}
// Draw the inset: a 320x240 panel in the bottom-left of the (800x600) backbuffer.
const float iw = 320.0f, ih = 240.0f, ix = 8.0f, iy = 600.0f - ih - 8.0f;
struct InsetVert { float x, y, z, rhw, u, v; };
InsetVert quad[4] =
{
{ ix, iy, 0.0f, 1.0f, 0.0f, 0.0f },
{ ix + iw, iy, 0.0f, 1.0f, 1.0f, 0.0f },
{ ix + iw, iy + ih, 0.0f, 1.0f, 1.0f, 1.0f },
{ ix, iy + ih, 0.0f, 1.0f, 0.0f, 1.0f },
};
device->SetTexture(0, mDevInsetTex);
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
device->SetRenderState(D3DRS_ZENABLE, FALSE);
device->SetRenderState(D3DRS_LIGHTING, FALSE);
device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(InsetVert));
}
//
// Free entry point the MAIN renderer calls (L4VIDEO DPLRenderer::ExecuteImplementation,
// just before EndScene). No-op unless BT_DEV_GAUGES is set + the gauge renderer + its
// 'sec' port exist. Reaches the gauge renderer's SVGA16 and blits the secondary inset.
//
void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device)
{
if (!DevGaugeComposite() || device == NULL) return;
GaugeRenderer *gr = application ? application->GetGaugeRenderer() : NULL;
if (gr == NULL) return;
// The secondary/radar surface ('sec') is the one port that resolves without
// the RP<->BT MFD port-name reconciliation (Step 2), so it is the beachhead.
L4GraphicsPort *secPort = static_cast<L4GraphicsPort*>(gr->GetGraphicsPort("sec"));
if (secPort == NULL) return;
SVGA16 *svga = (SVGA16*)secPort->graphicsDisplay;
if (svga == NULL) return;
svga->DrawDevInset(device, secPort->GetBitMask(), secPort->paletteID);
}
void SVGA16::BuildWindows(unsigned int width, unsigned int height, bool windowed, int *secondaryIndex, int *aux1Index, int *aux2Index)
{
NUMGAUGEWINDOWS = 0;
@@ -3844,6 +3927,7 @@ SVGA16::SVGA16(
):Video16BitBuffered(init_width, init_height)
{
BuildWindows(init_width,init_height,windowed, secondaryIndex, aux1Index, aux2Index);
mDevInsetTex = NULL; // DEV-COMPOSITE (option B): lazily created on first inset draw
//STUBBED: VIDEO RB 1/15/07
# if defined(DEBUG)
Tell("SVGA16::SVGA16()\n");
+7
View File
@@ -291,6 +291,13 @@ private:
RECT *mSurfaceRects;
int mDisplayToUpdate;
LPDIRECT3DTEXTURE9 mDevInsetTex; // DEV-COMPOSITE (option B): the secondary/radar
// plane uploaded to a texture on the MAIN device.
public:
// DEV-COMPOSITE (option B): palette-expand the secondary plane into mDevInsetTex on
// the given (main) device + draw it as an inset quad. See BTDrawGaugeInset (L4VB16.cpp).
void DrawDevInset(LPDIRECT3DDEVICE9 device, int secMask, int paletteID);
};
//########################################################################
+5
View File
@@ -6963,6 +6963,11 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
if (mCamShipHUD)
mCamShipHUD->Render(0, &viewTransform);
// DEV-COMPOSITE (option B): blit the woken gauge renderer's secondary/radar surface
// as an inset in this window (LAST draw before EndScene; no-op unless BT_DEV_GAUGES).
extern void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device);
BTDrawGaugeInset(mDevice);
hr = mDevice->EndScene();
// DIAG (turn-hitch hunt): draw CPU is _rt0..here; Present blocks on the GPU.
+17 -3
View File
@@ -343,13 +343,27 @@ BTL4Application::SharedData
//#############################################################################
//
GaugeRenderer*
BTL4Application::MakeGaugeRenderer()
BTL4Application::MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index)
{
//
// Only build the gauge renderer when the L4GAUGE environment variable
// is set -- matches RPL4Application's getenv("L4GAUGE") gate. Note
// the BT override takes no arguments (unlike RP's secondary/aux idx).
// is set -- matches RPL4Application's getenv("L4GAUGE") gate.
//
// SIGNATURE NOTE: the shipped BT override (@004d368c) took no arguments,
// but the 2007 WinTesla engine WIDENED the base virtual to
// Application::MakeGaugeRenderer(int* secondaryIndex, int* aux1, int* aux2)
// (the secondary/aux surface indices for windowed mode) and calls THAT
// 3-arg virtual from Application::Initialize (APP.cpp:382). A no-arg
// method here only HIDES the virtual -- it never overrides it -- so the
// engine's L4Application::MakeGaugeRenderer ran instead and built a BASE
// L4GaugeRenderer (which never parses gauge\l4gauge.cfg -> empty symbol
// table -> "undefined label 'bhk1Init'" -> no ports). Matching the 3-arg
// signature makes this the real override. BT is fullscreen with no
// secondary/aux gauge indices, so the args are ignored (the
// BTL4GaugeRenderer ctor hardcodes windowed=false / NULL indices).
//
(void)secondaryIndex; (void)aux1Index; (void)aux2Index;
if (getenv("L4GAUGE") == NULL) // FUN_004dee74
{
return NULL;
+1 -1
View File
@@ -70,7 +70,7 @@
MakeAudioRenderer();
GaugeRenderer*
MakeGaugeRenderer();
MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index);
ModeManager*
MakeModeManager();