A Live Cam keeps the map and drops the panes
A Live Cam host raced with the full glass cockpit over its view: five instrument MFD panes with no pod behind them, compositing as black rectangles across the viewscreen, and the map stood on end. Both are cabinet artefacts. The panes belong to a pod; the map is portrait because that is how the glass was bolted into the cabinet. A camera has no cabinet, so it keeps the map - landscape - and nothing else. The arcade got the same result by running its camera cabinet with -lc. The role is picked in the lobby, so there is no -lc on the command line to carry it to the renderers. Application::IsCameraStation joins suppressGauges, set by the front end where the egg settles owner_is_camera - before the renderers exist, since the single-binary race loop builds a fresh application per race after the menu. Set on every launch either way: a stale True would strip the cockpit off the next pod. In the compositor: the five MFD panes are not built, which LayoutCockpit and FillSplitMFD already tolerate, so no layout surgery; the map's glass swaps to 432x324 while still honouring L4RADARSCALE and L4RADARPOS; and the per-frame copy runs straight through rather than rotating 90 degrees clockwise. The pane's source dimensions swap to match, so the buffer the copy fills is the size the pane allocated - checked both ways. Untested on screen, unlike the rest of this work: it only engages with a lobby member present and that needs the second machine. What is checked is that it builds, that the buffer arithmetic agrees, and that every pane access on the glass-cockpit path is NULL-guarded. L4MFDSPLIT=2 and =0 are left alone. The exploded view still opens five empty MFD windows for a camera; it is a diagnostic view where that is arguably the point, and its layout derefs the panes unguarded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
Application *application = NULL;
|
||||
int Exit_Code = 0;
|
||||
Logical Application::suppressGauges = False;
|
||||
Logical Application::cameraStation = False;
|
||||
|
||||
//
|
||||
// RP412CAMLOG - see app.h. Cached: the waiting trace asks once a second
|
||||
|
||||
+16
@@ -454,6 +454,21 @@ public:
|
||||
|
||||
static Logical DoSuppressGauges() { return suppressGauges; }
|
||||
|
||||
//
|
||||
// A Live Cam station. It has no pod, so none of the five instrument
|
||||
// MFDs have anything to put on them - the panes would just composite
|
||||
// as black holes over the view. It DOES keep the map, but landscape:
|
||||
// the pod's is portrait because that is how the glass was physically
|
||||
// mounted in the cabinet, and a camera has no cabinet.
|
||||
//
|
||||
// Settable because the role is picked in the lobby rather than passed
|
||||
// as -lc on the command line, so the front end has to hand it over.
|
||||
// That happens before the renderers are built - the single-binary race
|
||||
// loop makes a fresh application per race, after the menu.
|
||||
//
|
||||
static Logical IsCameraStation() { return cameraStation; }
|
||||
static void SetCameraStation(Logical state) { cameraStation = state; }
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Modules
|
||||
//
|
||||
@@ -489,6 +504,7 @@ protected:
|
||||
*modeManager;
|
||||
|
||||
static Logical suppressGauges;
|
||||
static Logical cameraStation;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Module Creation
|
||||
|
||||
+93
-35
@@ -4054,11 +4054,24 @@ void
|
||||
<< percent[SplitMap] << "%\n" << std::flush;
|
||||
}
|
||||
|
||||
//
|
||||
// A Live Cam's map lies down: the pod's is 324x432 because the glass
|
||||
// was mounted portrait in the cabinet, and a camera station has no
|
||||
// cabinet, so the same pixels are shown the way they are drawn.
|
||||
//
|
||||
const Logical camera_station = Application::IsCameraStation();
|
||||
|
||||
GlassSize glass[SplitViewCount];
|
||||
for (int view = 0; view < SplitViewCount; ++view)
|
||||
{
|
||||
int base_w = (view == SplitMap) ? cockpitRadarBaseW : cockpitMfdBaseW;
|
||||
int base_h = (view == SplitMap) ? cockpitRadarBaseH : cockpitMfdBaseH;
|
||||
if (view == SplitMap && camera_station)
|
||||
{
|
||||
int swap = base_w;
|
||||
base_w = base_h;
|
||||
base_h = swap;
|
||||
}
|
||||
glass[view].w = (base_w * percent[view]) / 100;
|
||||
glass[view].h = (base_h * percent[view]) / 100;
|
||||
}
|
||||
@@ -4656,31 +4669,53 @@ SVGA16::SVGA16(
|
||||
GlassSize glass[SplitViewCount];
|
||||
CockpitGlassSizes(scale_num, glass);
|
||||
|
||||
splitView[SplitMFDUpperLeft] = new MFDSplitView(
|
||||
"MFD upper left", init_width, init_height,
|
||||
glass[SplitMFDUpperLeft].w, glass[SplitMFDUpperLeft].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x2F, 0, cockpit);
|
||||
splitView[SplitMFDUpperCenter] = new MFDSplitView(
|
||||
"MFD upper center", init_width, init_height,
|
||||
glass[SplitMFDUpperCenter].w, glass[SplitMFDUpperCenter].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x27, 0, cockpit);
|
||||
splitView[SplitMFDUpperRight] = new MFDSplitView(
|
||||
"MFD upper right", init_width, init_height,
|
||||
glass[SplitMFDUpperRight].w, glass[SplitMFDUpperRight].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x37, 0, cockpit);
|
||||
splitView[SplitMFDLowerLeft] = new MFDSplitView(
|
||||
"MFD lower left", init_width, init_height,
|
||||
glass[SplitMFDLowerLeft].w, glass[SplitMFDLowerLeft].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x0F, 0, cockpit);
|
||||
splitView[SplitMFDLowerRight] = new MFDSplitView(
|
||||
"MFD lower right", init_width, init_height,
|
||||
glass[SplitMFDLowerRight].w, glass[SplitMFDLowerRight].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x07, 0, cockpit);
|
||||
// map is portrait: source rotated 90 degrees clockwise
|
||||
splitView[SplitMap] = new MFDSplitView(
|
||||
"Map", init_height, init_width,
|
||||
glass[SplitMap].w, glass[SplitMap].h, 0, 0,
|
||||
MFDSplitView::SideColumns, 0x10, 0x18, cockpit);
|
||||
//---------------------------------------------------------------
|
||||
// A Live Cam gets the map and nothing else. The five instrument
|
||||
// MFDs belong to a pod: with no pod behind them their panes carry
|
||||
// no image at all and composite as black rectangles over the
|
||||
// viewscreen, which is what the arcade's camera cabinet avoided by
|
||||
// running with -lc. LayoutCockpit and FillSplitMFD both already
|
||||
// skip a NULL pane, so leaving them unbuilt is all it takes.
|
||||
//---------------------------------------------------------------
|
||||
if (!Application::IsCameraStation())
|
||||
{
|
||||
splitView[SplitMFDUpperLeft] = new MFDSplitView(
|
||||
"MFD upper left", init_width, init_height,
|
||||
glass[SplitMFDUpperLeft].w, glass[SplitMFDUpperLeft].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x2F, 0, cockpit);
|
||||
splitView[SplitMFDUpperCenter] = new MFDSplitView(
|
||||
"MFD upper center", init_width, init_height,
|
||||
glass[SplitMFDUpperCenter].w, glass[SplitMFDUpperCenter].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x27, 0, cockpit);
|
||||
splitView[SplitMFDUpperRight] = new MFDSplitView(
|
||||
"MFD upper right", init_width, init_height,
|
||||
glass[SplitMFDUpperRight].w, glass[SplitMFDUpperRight].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x37, 0, cockpit);
|
||||
splitView[SplitMFDLowerLeft] = new MFDSplitView(
|
||||
"MFD lower left", init_width, init_height,
|
||||
glass[SplitMFDLowerLeft].w, glass[SplitMFDLowerLeft].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x0F, 0, cockpit);
|
||||
splitView[SplitMFDLowerRight] = new MFDSplitView(
|
||||
"MFD lower right", init_width, init_height,
|
||||
glass[SplitMFDLowerRight].w, glass[SplitMFDLowerRight].h, 0, 0,
|
||||
MFDSplitView::MFDStrips, 0x07, 0, cockpit);
|
||||
}
|
||||
//
|
||||
// The map's source canvas is landscape and the pod rotates it 90
|
||||
// degrees clockwise to match its portrait mounting, which is why
|
||||
// the pane is built with the source dimensions swapped. A camera
|
||||
// shows it as drawn, so it is not swapped and not rotated (see the
|
||||
// copy in ExecuteBackground).
|
||||
//
|
||||
splitView[SplitMap] = Application::IsCameraStation()
|
||||
? new MFDSplitView(
|
||||
"Map", init_width, init_height,
|
||||
glass[SplitMap].w, glass[SplitMap].h, 0, 0,
|
||||
MFDSplitView::SideColumns, 0x10, 0x18, cockpit)
|
||||
: new MFDSplitView(
|
||||
"Map", init_height, init_width,
|
||||
glass[SplitMap].w, glass[SplitMap].h, 0, 0,
|
||||
MFDSplitView::SideColumns, 0x10, 0x18, cockpit);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Everything above built the panes at a starting size; the
|
||||
@@ -5136,23 +5171,46 @@ Logical SVGA16::Update(Logical forceAll)
|
||||
{
|
||||
if (splitView[SplitMap] != NULL)
|
||||
{
|
||||
// Map is portrait-mounted: rotate 90 degrees clockwise.
|
||||
// dest(x,y) = source(row = srcH-1-x, col = y)
|
||||
Word *source_base = pixelBuffer.Data.MapPointer;
|
||||
unsigned long *dest = splitView[SplitMap]->Pixels();
|
||||
int src_w = pixelBuffer.Data.Size.x;
|
||||
int src_h = pixelBuffer.Data.Size.y;
|
||||
|
||||
for (int dy = 0; dy < src_w; ++dy)
|
||||
if (Application::IsCameraStation())
|
||||
{
|
||||
for (int dx = 0; dx < src_h; ++dx)
|
||||
//
|
||||
// Live Cam: straight through, row by row. The pod's
|
||||
// rotation below is about how the glass was bolted
|
||||
// into the cabinet, and there is no cabinet here.
|
||||
//
|
||||
for (int dy = 0; dy < src_h; ++dy)
|
||||
{
|
||||
Word pixel = source_base[(src_h - 1 - dx) * src_w + dy];
|
||||
PaletteTriplet *entry =
|
||||
&secPalette->paletteData.Color[pixel & secMask];
|
||||
*dest++ = ((unsigned long) entry->Red << 16) |
|
||||
((unsigned long) entry->Green << 8) |
|
||||
((unsigned long) entry->Blue);
|
||||
for (int dx = 0; dx < src_w; ++dx)
|
||||
{
|
||||
Word pixel = source_base[dy * src_w + dx];
|
||||
PaletteTriplet *entry =
|
||||
&secPalette->paletteData.Color[pixel & secMask];
|
||||
*dest++ = ((unsigned long) entry->Red << 16) |
|
||||
((unsigned long) entry->Green << 8) |
|
||||
((unsigned long) entry->Blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Map is portrait-mounted: rotate 90 degrees clockwise.
|
||||
// dest(x,y) = source(row = srcH-1-x, col = y)
|
||||
for (int dy = 0; dy < src_w; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < src_h; ++dx)
|
||||
{
|
||||
Word pixel = source_base[(src_h - 1 - dx) * src_w + dy];
|
||||
PaletteTriplet *entry =
|
||||
&secPalette->paletteData.Color[pixel & secMask];
|
||||
*dest++ = ((unsigned long) entry->Red << 16) |
|
||||
((unsigned long) entry->Green << 8) |
|
||||
((unsigned long) entry->Blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
splitView[SplitMap]->Repaint();
|
||||
|
||||
@@ -1621,6 +1621,19 @@ namespace
|
||||
<< std::flush;
|
||||
}
|
||||
|
||||
//
|
||||
// Tell the renderers, which are built after this menu and need to
|
||||
// know not to hang five empty instrument panes over the view. Set
|
||||
// either way: the same process races again after this one, and a
|
||||
// stale True would strip the cockpit off a pod.
|
||||
//
|
||||
Application::SetCameraStation(owner_is_camera);
|
||||
if (owner_is_camera)
|
||||
{
|
||||
DEBUG_STREAM << "FE: Live Cam host - no instrument panes, map landscape\n"
|
||||
<< std::flush;
|
||||
}
|
||||
|
||||
for (int p = 0; p < pilot_count; ++p)
|
||||
{
|
||||
Logical camera_entry = (p == 0 && owner_is_camera);
|
||||
|
||||
Reference in New Issue
Block a user