BT_POD_RGB: the authentic RGB-split output -- one window per VGA PORT
Implements what the pod actually does (pod-hardware.md THE RGB SPLIT): a VGA port's R/G/B lines each drive a separate mono MFD monitor, so a window is not one MFD -- it is one PORT carrying up to three. BT_POD_RGB=1 collapses the five MFD windows into the two ports the cab drives (Port A: Comm=red, Mfd2=green, Heat=blue; Port B: Mfd1=red, Mfd3=green) and composites each group's planes into the colour channels, leaving the radar on its own full-colour port. Channel comes from the live port (GetEnableID), never a hardcoded table, so an Eng-page swap follows automatically; BlankColor planes contribute nothing, exactly as on the pod. Implies BT_POD_SURFACES (bare 640x480 pictures -- the cab's buttons are physical). Verified locally (bare windows, "RGB COMPOSITE of 2 plane(s): Comm Mfd2") and LIVE ON NICK'S CRASH CART over the tailnet: Port A -> DISPLAY4, Port B -> DISPLAY2, radar -> DISPLAY1, all exact-fit. Pod scratch kit included (ssh helper, layout cfgs for both modes, launcher; the mission-egg launcher fix -- a bare MP.EGG exits the mission loop with no relay, and BT_FE_SOLO parks at the menu, so neither lights the panels). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e0e9dcc292
commit
e179c7033f
@@ -115,6 +115,14 @@ struct GWin
|
|||||||
int wantX, wantY;
|
int wantX, wantY;
|
||||||
int restored; // wantX/wantY came from glass_layout.cfg -> don't re-snap
|
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
|
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<n> twin, or NULL
|
||||||
|
int groupCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
static GWin gWins[8];
|
static GWin gWins[8];
|
||||||
@@ -124,7 +132,8 @@ static int pressedAddress = -1;
|
|||||||
static unsigned char latched[128];
|
static unsigned char latched[128];
|
||||||
static int gRadarRot = 3; // BT_GAUGE_SEC_ROT (default CW, upright)
|
static int gRadarRot = 3; // BT_GAUGE_SEC_ROT (default CW, upright)
|
||||||
static bool sRepositionedToMain = false;
|
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 titleFont = NULL;
|
||||||
static HFONT buttonFont = 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.
|
// The Flight Controls window (no surface at all) is not created.
|
||||||
// BT_POD_SURFACES=1 -> every display window goes bare
|
// BT_POD_SURFACES=1 -> every display window goes bare
|
||||||
// glass_layout.cfg "<title>=x,y,bare" -> just that one (mixed rigs)
|
// glass_layout.cfg "<title>=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
|
static int
|
||||||
PodSurfaceMode()
|
PodSurfaceMode()
|
||||||
{
|
{
|
||||||
@@ -425,6 +449,8 @@ static int
|
|||||||
{
|
{
|
||||||
const char *e = getenv("BT_POD_SURFACES");
|
const char *e = getenv("BT_POD_SURFACES");
|
||||||
m = (e != NULL && *e != '\0' && *e != '0') ? 1 : 0;
|
m = (e != NULL && *e != '\0' && *e != '0') ? 1 : 0;
|
||||||
|
if (!m && PodRgbSplitMode())
|
||||||
|
m = 1; // the RGB split is pod glass -- always bare
|
||||||
if (m)
|
if (m)
|
||||||
DEBUG_STREAM << "[glasswin] POD SURFACE MODE: bare surfaces, no "
|
DEBUG_STREAM << "[glasswin] POD SURFACE MODE: bare surfaces, no "
|
||||||
"button banks (physical pod buttons assumed)\n" << std::flush;
|
"button banks (physical pod buttons assumed)\n" << std::flush;
|
||||||
@@ -817,6 +843,71 @@ static void
|
|||||||
}
|
}
|
||||||
int ow = 0, oh = 0;
|
int ow = 0, oh = 0;
|
||||||
svga->ExpandPlaneToBGRA(mask, palId, tint, w->rotate, gStage, &ow, &oh);
|
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)
|
if (log)
|
||||||
{
|
{
|
||||||
int nz = 0, n = ow * oh;
|
int nz = 0, n = ow * oh;
|
||||||
@@ -1126,6 +1217,8 @@ void
|
|||||||
sRepositionedToMain = false;
|
sRepositionedToMain = false;
|
||||||
if (gStage == NULL)
|
if (gStage == NULL)
|
||||||
gStage = new unsigned long[640 * 480];
|
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).
|
// Build the seven windows. Order fixes the ComputeLayout roles (0..6).
|
||||||
BuildMfd (gWins[0], "Heat MFD", "Heat", NULL, 0x2F); // UL 0x28-0x2F
|
BuildMfd (gWins[0], "Heat MFD", "Heat", NULL, 0x2F); // UL 0x28-0x2F
|
||||||
@@ -1147,6 +1240,42 @@ void
|
|||||||
gWinCount = 6; // drop Flight Controls
|
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,
|
titleFont = CreateFontW(-11, 0, 0, 0, FW_BOLD, 0, 0, 0,
|
||||||
DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, L"Segoe UI");
|
DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, L"Segoe UI");
|
||||||
buttonFont = CreateFontW(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
buttonFont = CreateFontW(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0,
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
@echo off
|
@echo off
|
||||||
REM BT411 pod launch -- runs from the game's content dir in the CONSOLE session.
|
REM BT411 pod launch -- console session (started via schtasks /IT).
|
||||||
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).
|
|
||||||
cd /d C:\bt411\BT411_4.11.801\content
|
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=1
|
||||||
set BT_GLASS_PANELS=1
|
set BT_GLASS_PANELS=1
|
||||||
set BT_POD_SURFACES=1
|
set BT_POD_RGB=1
|
||||||
set BT_GLASS_LAYOUT=load
|
set BT_GLASS_LAYOUT=load
|
||||||
set BT_GLASS_LOG=1
|
set BT_GLASS_LOG=1
|
||||||
set BT_START_INSIDE=1
|
set BT_START_INSIDE=1
|
||||||
|
set BT_FE_SOLO=1
|
||||||
set BT_LOG=podrun.log
|
set BT_LOG=podrun.log
|
||||||
|
start "" ..\build\Release\btl4.exe
|
||||||
start "" ..\build\Release\btl4.exe -egg MP.EGG
|
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user