diff --git a/engine/MUNGA_L4/L4GLASSWIN.cpp b/engine/MUNGA_L4/L4GLASSWIN.cpp index 806594f..cbab125 100644 --- a/engine/MUNGA_L4/L4GLASSWIN.cpp +++ b/engine/MUNGA_L4/L4GLASSWIN.cpp @@ -115,6 +115,14 @@ struct GWin int wantX, wantY; int restored; // wantX/wantY came from glass_layout.cfg -> don't re-snap int noFrame; // ",noframe" in the cfg -> WS_POPUP, drag by the surface + + // RGB CHANNEL GROUP (BT_POD_RGB): on splitter-wired pod glass this window + // is one VGA PORT feeding up to three mono monitors through R/G/B. These + // are the OTHER surfaces sharing the port; each is composited into its own + // channel by BlitSurface. groupCount 0 = ordinary single-surface window. + const char *groupPort[3]; + const char *groupAlt[3]; // the Eng twin, or NULL + int groupCount; }; static GWin gWins[8]; @@ -124,7 +132,8 @@ static int pressedAddress = -1; static unsigned char latched[128]; static int gRadarRot = 3; // BT_GAUGE_SEC_ROT (default CW, upright) static bool sRepositionedToMain = false; -static unsigned long *gStage = NULL; // 640*480 BGRA staging (shared, main-thread) +static unsigned long *gStage = NULL; // 640*480 BGRA staging (shared, main-thread) +static unsigned long *gStage2 = NULL; // RGB composite scratch (BT_POD_RGB) static HFONT titleFont = NULL; static HFONT buttonFont = NULL; @@ -417,6 +426,21 @@ static const char *layoutFileName = "glass_layout.cfg"; // The Flight Controls window (no surface at all) is not created. // BT_POD_SURFACES=1 -> every display window goes bare // glass_layout.cfg "=x,y,bare" -> just that one (mixed rigs) +// BT_POD_RGB=1 -- the pod's REAL video wiring (see pod-hardware.md §THE RGB +// SPLIT): one VGA port per window, its R/G/B analog lines split to three +// monochrome MFD monitors. Implies pod surface mode. +static int + PodRgbSplitMode() +{ + static int m = -1; + if (m < 0) + { + const char *e = getenv("BT_POD_RGB"); + m = (e != NULL && e[0] != 0 && e[0] != '0') ? 1 : 0; + } + return m; +} + static int PodSurfaceMode() { @@ -425,6 +449,8 @@ static int { const char *e = getenv("BT_POD_SURFACES"); m = (e != NULL && *e != '\0' && *e != '0') ? 1 : 0; + if (!m && PodRgbSplitMode()) + m = 1; // the RGB split is pod glass -- always bare if (m) DEBUG_STREAM << "[glasswin] POD SURFACE MODE: bare surfaces, no " "button banks (physical pod buttons assumed)\n" << std::flush; @@ -817,6 +843,71 @@ static void } int ow = 0, oh = 0; svga->ExpandPlaneToBGRA(mask, palId, tint, w->rotate, gStage, &ow, &oh); + + // --------------------------------------------------------------------- + // RGB CHANNEL COMPOSITE (BT_POD_RGB, 2026-08-06) -- the AUTHENTIC pod + // output. The cab does not give each mono MFD its own video port: one + // VGA port's R/G/B analog lines are SPLIT to three monochrome monitors, + // and the game puts a different surface in each colour channel of one + // palettized framebuffer (L4GAUGE.CFG: Comm=red, Mfd2=green, Heat=blue + // on clut2; Mfd1=red, Mfd3=green on clut1 -- and + // L4GraphicsPort::BuildSecondaryColor writes exactly ONE component per + // port, which IS the split). So on splitter-wired glass this window is + // not one MFD: it is one VGA PORT, carrying up to three. Composite the + // group's planes into this frame, each in its own channel, and let the + // hardware separate them again. + // + // Channel comes from the port itself (GetEnableID), so the authored + // config -- including a page swap that hands the channel to an Eng + // plane -- decides, never a hardcoded table here. + // --------------------------------------------------------------------- + if (w->groupCount > 0 && ow > 0 && oh > 0) + { + const int n = ow * oh; + for (int gi = 0; gi < w->groupCount; ++gi) + { + L4GraphicsPort *gp = + static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(w->groupPort[gi])); + if (gp == NULL) + continue; + // honour the live Mfd<n>/Eng<n> page swap: take whichever twin + // currently owns a channel + if (gp->GetEnableID() == L4GraphicsPort::BlankColor + && w->groupAlt[gi] != NULL) + { + L4GraphicsPort *ga = + static_cast<L4GraphicsPort*>(gr->GetGraphicsPort(w->groupAlt[gi])); + if (ga != NULL && ga->GetEnableID() != L4GraphicsPort::BlankColor) + gp = ga; + } + const int ch = gp->GetEnableID(); + unsigned long chMask; + switch (ch & 0x7F) + { + case L4GraphicsPort::RedChannel: chMask = 0x00FF0000ul; break; + case L4GraphicsPort::GreenChannel: chMask = 0x0000FF00ul; break; + case L4GraphicsPort::BlueChannel: chMask = 0x000000FFul; break; + default: continue; // blank / direct-colour -> contributes nothing + } + int gw = 0, gh = 0; + // -1 tint would palette-expand; pass white so the plane comes back + // as full-intensity mono, then keep only this port's channel. + svga->ExpandPlaneToBGRA(gp->GetBitMask(), gp->paletteID, + (int)0x00FFFFFF, w->rotate, gStage2, &gw, &gh); + if (gw != ow || gh != oh) + continue; + for (int i = 0; i < n; ++i) + gStage[i] |= (gStage2[i] & chMask); + } + if (log) + { + DEBUG_STREAM << "[glass] '" << w->title << "' RGB COMPOSITE of " + << w->groupCount << " plane(s):"; + for (int gi = 0; gi < w->groupCount; ++gi) + DEBUG_STREAM << " " << w->groupPort[gi]; + DEBUG_STREAM << "\n" << std::flush; + } + } if (log) { int nz = 0, n = ow * oh; @@ -1126,6 +1217,8 @@ void sRepositionedToMain = false; if (gStage == NULL) gStage = new unsigned long[640 * 480]; + if (gStage2 == NULL) + gStage2 = new unsigned long[640 * 480]; // Build the seven windows. Order fixes the ComputeLayout roles (0..6). BuildMfd (gWins[0], "Heat MFD", "Heat", NULL, 0x2F); // UL 0x28-0x2F @@ -1147,6 +1240,42 @@ void gWinCount = 6; // drop Flight Controls } + // RGB SPLIT MODE (BT_POD_RGB): the cab's real wiring -- ONE VGA port per + // window, its R/G/B lines split to three mono monitors. Collapse the five + // separate MFD windows into the TWO ports the pod actually drives + // (L4GAUGE.CFG clut2 = Comm/Mfd2/Heat, clut1 = Mfd1/Mfd3), each window + // compositing its group into the colour channels; the radar keeps its own + // full-colour port. Channels are read live from each port, so a page swap + // to an Eng plane follows automatically. + if (PodRgbSplitMode()) + { + GWin port2 = gWins[0]; // reuse the Heat window's shape + port2.title = "VGA Port A"; // Comm=red, Mfd2=green, Heat=blue + port2.portPrimary = "Heat"; // base plane (blue); group adds the rest + port2.portAlt = NULL; + port2.groupCount = 2; + port2.groupPort[0] = "Comm"; port2.groupAlt[0] = NULL; + port2.groupPort[1] = "Mfd2"; port2.groupAlt[1] = "Eng2"; + port2.monoTint = (int)0x000000FF; // Heat rides BLUE + + GWin port1 = gWins[3]; // the Mfd1 window's shape + port1.title = "VGA Port B"; // Mfd1=red, Mfd3=green (blue spare) + port1.portPrimary = "Mfd1"; + port1.portAlt = "Eng1"; + port1.groupCount = 1; + port1.groupPort[0] = "Mfd3"; port1.groupAlt[0] = "Eng3"; + port1.monoTint = (int)0x00FF0000; // Mfd1 rides RED + + GWin radar = gWins[5]; // unchanged: its own colour port + + gWins[0] = port2; + gWins[1] = port1; + gWins[2] = radar; + gWinCount = 3; + DEBUG_STREAM << "[glasswin] RGB SPLIT MODE: 2 VGA ports + radar " + "(each port's R/G/B feeds three mono panels)\n" << std::flush; + } + titleFont = CreateFontW(-11, 0, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, L"Segoe UI"); buttonFont = CreateFontW(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0, diff --git a/scratchpad/pod/glass_layout_rgb.cfg b/scratchpad/pod/glass_layout_rgb.cfg new file mode 100644 index 0000000..0d095e6 --- /dev/null +++ b/scratchpad/pod/glass_layout_rgb.cfg @@ -0,0 +1,9 @@ +# BT411 -- ALPHA-MR, RGB SPLIT mode (BT_POD_RGB=1). Each line is ONE VGA PORT +# whose R/G/B lines feed three mono MFD monitors: +# VGA Port A : Comm=red, Engineering/Mfd2=green, Heat=blue +# VGA Port B : Mfd1=red, Mfd3=green (blue spare) +# Console-session displays: DISPLAY3 800x600 primary (main view), +# DISPLAY4 800,122 / DISPLAY2 1440,123 / DISPLAY1 2080,124 (all 640x480). +VGA Port A=800,122,640,480,noframe,bare +VGA Port B=1440,123,640,480,noframe,bare +Secondary / Radar=2080,124,480,640,noframe,bare diff --git a/scratchpad/pod/runpod.bat b/scratchpad/pod/runpod.bat index a5476ad..b05ed13 100644 --- a/scratchpad/pod/runpod.bat +++ b/scratchpad/pod/runpod.bat @@ -1,18 +1,12 @@ @echo off -REM BT411 pod launch -- runs from the game's content dir in the CONSOLE session. -REM Started via schtasks /IT so the windows land on the REAL panels (an SSH -REM session is session 0 with a dummy display and would render nowhere). +REM BT411 pod launch -- console session (started via schtasks /IT). cd /d C:\bt411\BT411_4.11.801\content - -REM glass cockpit, per-display windows, POD surface mode (bare 640x480 pictures, -REM no on-screen button banks -- the cab's buttons are physical), sticky layout -REM loaded from glass_layout.cfg beside this file. set BT_GLASS=1 set BT_GLASS_PANELS=1 -set BT_POD_SURFACES=1 +set BT_POD_RGB=1 set BT_GLASS_LAYOUT=load set BT_GLASS_LOG=1 set BT_START_INSIDE=1 +set BT_FE_SOLO=1 set BT_LOG=podrun.log - -start "" ..\build\Release\btl4.exe -egg MP.EGG +start "" ..\build\Release\btl4.exe diff --git a/scratchpad/pod/runpod2.bat b/scratchpad/pod/runpod2.bat new file mode 100644 index 0000000..39227ef --- /dev/null +++ b/scratchpad/pod/runpod2.bat @@ -0,0 +1,12 @@ +@echo off +REM BT411 pod launch -- console session (started via schtasks /IT). +cd /d C:\bt411\BT411_4.11.801\content +set BT_GLASS=1 +set BT_GLASS_PANELS=1 +set BT_POD_RGB=1 +set BT_GLASS_LAYOUT=load +set BT_GLASS_LOG=1 +set BT_START_INSIDE=1 +REM no front end -- straight into a mission +set BT_LOG=podrun.log +start "" ..\build\Release\btl4.exe -egg PODTEST.EGG