Cockpit surround: composite the gauges around the 3D view (default desktop layout)

Under BT_DEV_GAUGES the DEFAULT is now the pod-faithful cockpit surround: the 3D
world CENTERED with the six gauge surfaces composited AROUND it in the single main
window at 1/2 native scale, plus clickable RIO button lamps. Iterated as a visual
reference with the user, then landed in the engine.

- L4VB16.cpp/.h: BTCockpitLayout + BTCockpitComputeLayout (layout single source of
  truth, computed from the backbuffer canvas); BTDrawCockpitPanels (band fill ->
  button lamp quads -> 9-entry surface loop, phosphor-green mono / amber palette
  sec, Eng-sibling slot map -> GDI-atlas flight labels; D3DSBT_ALL state-block
  isolation = the floating-rocks trap); BTCockpitMouseDown/Up (client->bb hit-test
  + glass press/release/right-latch contract); BTApplyWorldViewport + DevGaugeDocked
  cockpit branches; shared BTLampBrightnessOf inline. Hooked at the top of
  BTDrawGaugeInset (before BT_SHOT, so shots include it).
- btl4main.cpp: glass preset (cockpit default, per-display panels opt-in); mode
  resolution -> gBTGaugeCockpit with work-area-clamped window sizing (-res = view
  size); WndProc WM_L/RBUTTON routing.
- L4VIDEO.cpp: BTWorldAspectOf cockpit branch (view rect on-screen aspect).
- L4VIDRND.cpp: CameraShipHUDRenderable::Render made viewport-relative.

Buttons = the L4GLASSWIN address banks x0.5 (Heat 0x2F, Mfd2 0x27, Comm 0x37, Mfd1
0x0F, Mfd3 0x07 red; radar rails 0x10-0x1B yellow; flight 0x38-0x47 blue, labeled).
Full rect is the hit target; the surface draws over it so only the lamp edge shows.
Precedence: BT_GLASS_PANELS=1 stands cockpit down > BT_COCKPIT=1 > _WINDOW/_DOCK
opt-out > cockpit default; BT_COCKPIT=0 = dock-bottom. Green tunable via
BT_COCKPIT_TINT. Renders in ALL builds; only the PadRIO click/lamp seam is
BT_GLASS-gated.

Verified (awaiting playtest): both build/ + build-glass/ compile (0 error C);
glass BT_SHOT matches the mockup; 500-click storm during a live mission survived
(Gitea #18 Receiver-gap fix holds); BT_COCKPIT=0 dock-bottom unregressed;
BT_GLASS_PANELS=1 stands cockpit down; pod build renders the panels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-20 20:39:34 -05:00
co-authored by Claude Opus 4.8
parent 5ba5697a08
commit 129b27499d
7 changed files with 793 additions and 41 deletions
+97 -24
View File
@@ -106,6 +106,32 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
// GetAsyncKeyState per frame instead (with a foreground guard), which
// is immune to message stealing.
return 0;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
// COCKPIT SURROUND: route clicks on the composited panel buttons to the
// RIO (glass build) via the layout hit-test. No-op in other modes.
{
extern int gBTGaugeCockpit;
extern int BTCockpitMouseDown(int, int, int, int, int);
if (gBTGaugeCockpit)
{
RECT rc; GetClientRect(hWnd, &rc);
int consumed = BTCockpitMouseDown(
(int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam),
(int)(rc.right - rc.left), (int)(rc.bottom - rc.top),
uMsg == WM_RBUTTONDOWN);
if (consumed && uMsg == WM_LBUTTONDOWN)
SetCapture(hWnd); // a release outside the button still clears it
}
}
return 0;
case WM_LBUTTONUP:
{
extern int gBTGaugeCockpit;
extern void BTCockpitMouseUp(void);
if (gBTGaugeCockpit) { BTCockpitMouseUp(); ReleaseCapture(); }
}
return 0;
case WM_SIZE:
// Window-resize aspect fix (task #20): rebuild the projection for the
// new client aspect so the scene doesn't stretch fat/skinny (the D3D9
@@ -330,19 +356,27 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
{
// GLASS profile (dev tooling, step 2e): the desktop cockpit --
// PadRIO input (XInput+keyboard, content\bindings.txt), the
// on-screen clickable button panel, the dev-composited MFD
// surfaces (dock-bottom default; BT_DEV_GAUGES_WINDOW=1 separate
// window / BT_DEV_GAUGES_DOCK=1 overlay inset), and the desktop
// plasma window. Each only when not already set, so a developer
// env overrides the preset.
// dev-composited MFD surfaces, and the desktop plasma window.
// Each only when not already set, so a developer env overrides.
if (getenv("L4CONTROLS") == NULL) putenv("L4CONTROLS=PAD,KEYBOARD");
if (getenv("BT_DEV_GAUGES") == NULL) putenv("BT_DEV_GAUGES=1");
if (getenv("L4PLASMA") == NULL) putenv("L4PLASMA=SCREEN");
if (getenv("BT_PAD_PANEL") == NULL) putenv("BT_PAD_PANEL=1");
// Break the secondary displays into per-display windows (surface + its RIO
// bank around it). Set BT_GLASS_PANELS=0 to fall back to the single
// combined pad panel + docked gauge strip.
if (getenv("BT_GLASS_PANELS") == NULL) putenv("BT_GLASS_PANELS=1");
// DEFAULT LAYOUT = the COCKPIT SURROUND (single window: the six gauge
// surfaces + clickable button lamps composited AROUND the centered 3D
// view). Opt out with BT_COCKPIT=0 (dock-bottom strip) or
// BT_GLASS_PANELS=1 (the per-display windows). When cockpit is the
// default the buttons live IN the main window, so no separate pad/panel
// window is created (leave BT_PAD_PANEL / BT_GLASS_PANELS unset).
int cockpitDefault = 1;
if (getenv("BT_COCKPIT") && getenv("BT_COCKPIT")[0] == '0') cockpitDefault = 0;
if (getenv("BT_GLASS_PANELS") && getenv("BT_GLASS_PANELS")[0] != '0') cockpitDefault = 0;
if (getenv("BT_DEV_GAUGES_WINDOW") || getenv("BT_DEV_GAUGES_DOCK")) cockpitDefault = 0;
if (!cockpitDefault)
{
if (getenv("BT_PAD_PANEL") == NULL) putenv("BT_PAD_PANEL=1");
if (getenv("BT_GLASS_PANELS") == NULL) putenv("BT_GLASS_PANELS=1");
}
}
#endif
else
@@ -562,29 +596,68 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
// restores the old separate MFD window.
{
extern int gBTGaugeDockBottom;
extern int gBTGaugeCockpit;
extern int gBTCockpitCanvasW, gBTCockpitCanvasH;
extern int BTGaugeStripHeightFor(int width);
extern void BTCockpitCanvasFor(int viewW, int viewH, int *cw, int *ch);
// BT_GLASS_PANELS breaks the gauges into their own per-display windows, so
// the world window keeps its normal size (no bottom strip).
// the world window keeps its normal size (no cockpit / bottom strip).
int glassOwnsGauges = 0;
#ifdef BT_GLASS
{ extern int BTGlassPanelsActive(); glassOwnsGauges = BTGlassPanelsActive(); }
#endif
if (!glassOwnsGauges &&
getenv("BT_DEV_GAUGES") != 0 && getenv("BT_DEV_GAUGES_WINDOW") == 0)
if (!glassOwnsGauges && getenv("BT_DEV_GAUGES") != 0)
{
gBTGaugeDockBottom = 1;
// Readability default (user-reported: the strip at 800 wide is a
// 0.6x downscale, hard to read): a WIDER world region (the
// projection is aspect-correct for any shape) buys strip pixels --
// 1100x600 world + 1100x400 strip = a 1000-tall window that fits a
// 1080p desktop with the panel at 83% of design size. -res still
// overrides both.
if (strstr(lpCmdLine ? lpCmdLine : "", "-res") == 0)
// Mode resolution (precedence): explicit BT_COCKPIT > separate-window /
// legacy-inset opt-out > COCKPIT SURROUND default under BT_DEV_GAUGES.
int cockpitOn;
const char *ck = getenv("BT_COCKPIT");
if (ck != NULL)
cockpitOn = (ck[0] != '0');
else
cockpitOn = (getenv("BT_DEV_GAUGES_WINDOW") == 0 &&
getenv("BT_DEV_GAUGES_DOCK") == 0);
if (cockpitOn)
{
winW = 1100;
winH = 600;
gBTGaugeCockpit = 1;
// -res W H = the WORLD VIEW size in cockpit mode; default 900x500.
int viewW = 900, viewH = 500;
if (strstr(lpCmdLine ? lpCmdLine : "", "-res") != 0) { viewW = winW; viewH = winH; }
// Clamp the CANVAS to the work area (minus the window frame): shrink
// viewH (floor 400) then viewW (floor 640) until it fits (1080p tight).
RECT wa = { 0, 0, 1920, 1080 };
SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0);
RECT fr = { 0, 0, 1000, 1000 };
AdjustWindowRect(&fr, WS_OVERLAPPEDWINDOW, FALSE);
int availW = (wa.right - wa.left) - ((fr.right - fr.left) - 1000);
int availH = (wa.bottom - wa.top) - ((fr.bottom - fr.top) - 1000);
int cw, ch;
BTCockpitCanvasFor(viewW, viewH, &cw, &ch);
while (ch > availH && viewH > 400) { viewH -= 20; BTCockpitCanvasFor(viewW, viewH, &cw, &ch); }
while (cw > availW && viewW > 640) { viewW -= 20; BTCockpitCanvasFor(viewW, viewH, &cw, &ch); }
winW = cw; winH = ch;
gBTCockpitCanvasW = winW; // windowed backbuffer = client canvas
gBTCockpitCanvasH = winH;
std::cout << "[cockpit] view " << viewW << "x" << viewH
<< " canvas " << winW << "x" << winH << std::endl << std::flush;
}
else if (getenv("BT_DEV_GAUGES_WINDOW") == 0)
{
gBTGaugeDockBottom = 1;
// Readability default (user-reported: the strip at 800 wide is a
// 0.6x downscale, hard to read): a WIDER world region (the
// projection is aspect-correct for any shape) buys strip pixels --
// 1100x600 world + 1100x400 strip = a 1000-tall window that fits a
// 1080p desktop with the panel at 83% of design size. -res still
// overrides both.
if (strstr(lpCmdLine ? lpCmdLine : "", "-res") == 0)
{
winW = 1100;
winH = 600;
}
winH += BTGaugeStripHeightFor(winW);
}
winH += BTGaugeStripHeightFor(winW);
}
}
RECT wr = { 0, 0, winW, winH };