Glass cockpit: per-display windows (BT_GLASS_PANELS)

Break the desktop glass cockpit's secondary displays out of the single
combined pad panel + D3D gauge strip into ONE window per pod display, each
carrying that display's surface with its RIO button bank, arranged
pod-faithfully around the main view.  New TU engine/MUNGA_L4/L4GLASSWIN.*
(BT_GLASS-only), selected by the -platform glass preset via the new runtime
gate BT_GLASS_PANELS (=0 falls back to the legacy single panel + dock).

- Surfaces are CPU-expanded from the shared gauge buffer
  (SVGA16::ExpandPlaneToBGRA, no D3D) and StretchDIBits'd in -- the D3D
  dev-composite path (dock / window / overlay) stands down while active.
- Buttons sit UNDER the imagery: big hidden click targets that reach into
  the display, with only a small protruding edge showing as a lamp light.
  Red MFD banks tile the top/bottom; radar rails run the sides + a bottom
  row; a Flight Controls window hosts the no-display banks.
- Windows: Heat / Engineering / Comm / Left Weapons / Right Weapons / radar
  + Flight Controls; edge-to-edge (no in-client margin/title; OS caption
  labels each).
- Fix blank surfaces: application/ghWnd are duplicate-defined and bind
  non-deterministically under /FORCE, so the fresh L4GLASSWIN TU read NULL.
  Reach the renderer/window via BTResolveGaugeRenderer/BTResolveMainWindow
  defined in btl4main.cpp off the real btl4App/hWnd pointers.  New gotcha:
  reconstruction-gotchas.md S6 duplicate-GLOBAL corollary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-20 14:43:40 -05:00
co-authored by Claude Opus 4.8
parent 157da70ec9
commit f338595685
10 changed files with 1175 additions and 10 deletions
+105
View File
@@ -6,6 +6,14 @@
#include "L4VIDEO.h"
#include "DXUtils.h"
#ifdef BT_GLASS
#include "l4glasswin.h" // BTGlassPanelsActive(): the per-display windows own the surfaces
#endif
// BTResolveGaugeRenderer()/BTResolveMainWindow() (l4vb16.h) are DEFINED in
// game/btl4main.cpp off the real app/window pointers -- NOT here: an engine TU
// can bind the NULL /FORCE-duplicate copy of `application`/`ghWnd`, and which
// copy an obj binds is non-deterministic per link.
#if defined(TRACE_SCREEN_COPY)
static BitTrace Screen_Copy("Screen Copy");
#define SET_SCREEN_COPY() Screen_Copy.Set()
@@ -153,6 +161,18 @@ static bool DevGaugeDocked()
return v != 0 || gBTGaugeDockBottom != 0;
}
// GLASS per-display windows (L4GLASSWIN) own the instrument surfaces in their OWN
// GDI windows -- when active, the whole D3D dev-composite path (dock / separate
// window / overlay) must stand down so nothing double-draws into the main window.
static bool GlassPanelsOwnSurfaces()
{
#ifdef BT_GLASS
return BTGlassPanelsActive() != 0;
#else
return false;
#endif
}
//===========================================================================//
// 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
@@ -260,6 +280,89 @@ void SVGA16::DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int pa
device->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, quad, sizeof(InsetVert));
}
//===========================================================================//
// GLASS per-display windows -- the CPU (no-D3D) analog of DrawDevSurface: expand
// one bit-plane of the shared gauge pixelBuffer into a 32-bit BGRA image that the
// GDI window blits with StretchDIBits (L4GLASSWIN.cpp). The rotation the D3D path
// did via texture-UV remap is baked in here by transposing into `dst`.
//===========================================================================//
void SVGA16::ExpandPlaneToBGRA(int mask, int paletteID, int monoTint, int rotateQuadrant,
unsigned long *dst, int *outW, int *outH)
{
int w = pixelBuffer.Data.Size.x; // 640
int h = pixelBuffer.Data.Size.y; // 480
if (dst == NULL || w <= 0 || h <= 0)
{
if (outW) *outW = 0;
if (outH) *outH = 0;
return;
}
Word *base = pixelBuffer.Data.MapPointer;
SVGA16Palette *pal = &palette[paletteID];
if (rotateQuadrant == 0)
{
// Native orientation, top-down straight copy.
for (int y = 0; y < h; y++)
{
Word *src = base + y * w;
unsigned long *d = dst + y * w;
for (int x = 0; x < w; x++)
{
Word s = src[x];
if (monoTint < 0)
{
PaletteTriplet *pe = &(pal->paletteData.Color[s & mask]);
d[x] = ((unsigned long)pe->Red << 16) |
((unsigned long)pe->Green << 8) | (unsigned long)pe->Blue;
}
else
{
d[x] = (s & mask) ? (unsigned long)monoTint : 0UL;
}
}
}
if (outW) *outW = w;
if (outH) *outH = h;
return;
}
// 90-degree rotation: output is transposed (ow = h, oh = w). rotate 3 = CW,
// rotate 1 = CCW (the DrawDevSurface convention; BT_GAUGE_SEC_ROT picks it).
int ow = h, oh = w;
for (int oy = 0; oy < oh; oy++)
{
unsigned long *d = dst + oy * ow;
for (int ox = 0; ox < ow; ox++)
{
int sx, sy;
if (rotateQuadrant == 3) // 90 CW: out(ox,oy) = src(oy, h-1-ox)
{
sx = oy;
sy = h - 1 - ox;
}
else // 90 CCW: out(ox,oy) = src(w-1-oy, ox)
{
sx = w - 1 - oy;
sy = ox;
}
Word s = base[sy * w + sx];
if (monoTint < 0)
{
PaletteTriplet *pe = &(pal->paletteData.Color[s & mask]);
d[ox] = ((unsigned long)pe->Red << 16) |
((unsigned long)pe->Green << 8) | (unsigned long)pe->Blue;
}
else
{
d[ox] = (s & mask) ? (unsigned long)monoTint : 0UL;
}
}
}
if (outW) *outW = ow;
if (outH) *outH = oh;
}
//
// The pod's SIX instrument surfaces, all bit-plane views of the ONE shared 640x480
// gauge buffer (masks/positions from content/GAUGE/L4GAUGE.CFG MechInit @4395):
@@ -401,6 +504,7 @@ void BTDrawGaugeSurfaces(LPDIRECT3DDEVICE9 device, float px, float py, float pw,
void BTDrawGaugeInset(LPDIRECT3DDEVICE9 device)
{
if (!DevGaugeComposite() || !DevGaugeDocked() || device == NULL) return;
if (GlassPanelsOwnSurfaces()) return; // the per-display glass windows draw the surfaces
if (gBTGaugeDockBottom)
{
// DOCK-BOTTOM: the strip owns the bottom band of the target (the world
@@ -544,6 +648,7 @@ static bool BTGaugeWindowEnsure(LPDIRECT3DDEVICE9 device)
void BTGaugeWindowRenderAndPresent(LPDIRECT3DDEVICE9 device)
{
if (!DevGaugeComposite() || DevGaugeDocked() || device == NULL) return;
if (GlassPanelsOwnSurfaces()) return; // the per-display glass windows draw the surfaces
if (!BTGaugeWindowEnsure(device)) return;
IDirect3DSurface9 *gaugeBB = NULL;